mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-08 08:40:55 +00:00
Add an option to verify Map() writes don't overrun. Closes #42
* Has a couple of limitations - won't check deferred context or NO_OVERWRITE Map()s except in a captured frame. This could in theory be implemented but it'd be complex and I don't want to complicate/break the normal path. * When an overrun is detected, a messagebox pops up to block the thread, and if you hit yes, it will debugbreak.
This commit is contained in:
@@ -67,7 +67,7 @@ struct CaptureOptions
|
||||
CaptureCallstacks(false),
|
||||
CaptureCallstacksOnlyDraws(false),
|
||||
DelayForDebugger(0),
|
||||
CacheStateObjects(false),
|
||||
VerifyMapWrites(true),
|
||||
HookIntoChildren(false),
|
||||
RefAllResources(false),
|
||||
SaveAllInitials(false),
|
||||
@@ -101,8 +101,9 @@ struct CaptureOptions
|
||||
// creating or injecting into a process, before continuing to allow it to run.
|
||||
uint32_t DelayForDebugger;
|
||||
|
||||
// Deprecated, ignored.
|
||||
bool32 CacheStateObjects;
|
||||
// Verify any writes to mapped buffers, to check that they don't overwrite the
|
||||
// bounds of the pointer returned.
|
||||
bool32 VerifyMapWrites;
|
||||
|
||||
// Hooks any system API events that create child processes, and injects
|
||||
// renderdoc into them recursively with the same options.
|
||||
@@ -141,6 +142,7 @@ struct CaptureOptions
|
||||
>> CaptureCallstacks
|
||||
>> CaptureCallstacksOnlyDraws
|
||||
>> DelayForDebugger
|
||||
>> VerifyMapWrites
|
||||
>> HookIntoChildren
|
||||
>> RefAllResources
|
||||
>> SaveAllInitials
|
||||
@@ -157,6 +159,7 @@ struct CaptureOptions
|
||||
<< CaptureCallstacks << " "
|
||||
<< CaptureCallstacksOnlyDraws << " "
|
||||
<< DelayForDebugger << " "
|
||||
<< VerifyMapWrites << " "
|
||||
<< HookIntoChildren << " "
|
||||
<< RefAllResources << " "
|
||||
<< SaveAllInitials << " "
|
||||
|
||||
@@ -913,8 +913,11 @@ void WrappedID3D11DeviceContext::RefreshDrawcallIDs(DrawcallTreeNode &node)
|
||||
// assign new drawcall IDs
|
||||
for(size_t i=0; i < node.children.size(); i++)
|
||||
{
|
||||
m_CurEventID++;
|
||||
|
||||
node.children[i].draw.eventID = m_CurEventID;
|
||||
node.children[i].draw.drawcallID = m_CurDrawcallID;
|
||||
|
||||
|
||||
// markers don't increment drawcall ID
|
||||
if((node.children[i].draw.flags & (eDraw_SetMarker|eDraw_PushMarker)) == 0)
|
||||
m_CurDrawcallID++;
|
||||
|
||||
@@ -64,6 +64,8 @@ struct MapIntercept
|
||||
D3D11_MAP MapType;
|
||||
UINT MapFlags;
|
||||
|
||||
bool verifyWrite;
|
||||
|
||||
void CopyFromD3D();
|
||||
void CopyToD3D(size_t RangeStart = 0, size_t RangeEnd = 0);
|
||||
};
|
||||
|
||||
@@ -6188,6 +6188,7 @@ bool WrappedID3D11DeviceContext::Serialise_Map(ID3D11Resource *pResource, UINT S
|
||||
}
|
||||
|
||||
intercept = MapIntercept();
|
||||
intercept.verifyWrite = (RenderDoc::Inst().GetCaptureOptions().VerifyMapWrites != 0);
|
||||
intercept.SetD3D(mappedResource);
|
||||
intercept.InitWrappedResource(resMap, Subresource, appMem);
|
||||
intercept.MapType = MapType;
|
||||
@@ -6205,11 +6206,44 @@ bool WrappedID3D11DeviceContext::Serialise_Map(ID3D11Resource *pResource, UINT S
|
||||
mapLength = record->Length;
|
||||
|
||||
intercept = MapIntercept();
|
||||
intercept.verifyWrite = (RenderDoc::Inst().GetCaptureOptions().VerifyMapWrites != 0);
|
||||
intercept.SetD3D(mappedResource);
|
||||
intercept.InitWrappedResource(pResource, Subresource, record->GetDataPtr());
|
||||
intercept.MapType = MapType;
|
||||
intercept.MapFlags = MapFlags;
|
||||
|
||||
if(intercept.verifyWrite)
|
||||
{
|
||||
int ctxMapID = 0;
|
||||
|
||||
ResourceId Resource = GetIDForResource(pResource);
|
||||
|
||||
if(GetType() == D3D11_DEVICE_CONTEXT_DEFERRED)
|
||||
{
|
||||
if(m_MapResourceRecordAllocs[Resource] == 0)
|
||||
m_MapResourceRecordAllocs[Resource] = record->GetContextID();
|
||||
|
||||
ctxMapID = m_MapResourceRecordAllocs[Resource];
|
||||
|
||||
RDCASSERT(ctxMapID != 0);
|
||||
}
|
||||
|
||||
byte *appMem = record->GetShadowPtr(ctxMapID, 0);
|
||||
|
||||
if(appMem == NULL)
|
||||
{
|
||||
record->AllocShadowStorage(ctxMapID, mapLength);
|
||||
appMem = record->GetShadowPtr(ctxMapID, 0);
|
||||
}
|
||||
|
||||
memcpy(appMem, record->GetDataPtr(), mapLength);
|
||||
|
||||
intercept.InitWrappedResource(pResource, Subresource, appMem);
|
||||
}
|
||||
else
|
||||
{
|
||||
intercept.InitWrappedResource(pResource, Subresource, record->GetDataPtr());
|
||||
}
|
||||
|
||||
*pMappedResource = intercept.app;
|
||||
|
||||
m_OpenMaps[MappedResource(GetIDForResource(pResource), Subresource)] = intercept;
|
||||
@@ -6310,9 +6344,10 @@ HRESULT WrappedID3D11DeviceContext::Map(ID3D11Resource *pResource, UINT Subresou
|
||||
if(record->NumSubResources > (int)Subresource)
|
||||
record = (D3D11ResourceRecord *)record->SubResources[Subresource];
|
||||
|
||||
record->UpdateCount++;
|
||||
if(RenderDoc::Inst().GetCaptureOptions().VerifyMapWrites == 0)
|
||||
record->UpdateCount++;
|
||||
|
||||
if(record->UpdateCount > 60)
|
||||
if(record->UpdateCount > 60 && RenderDoc::Inst().GetCaptureOptions().VerifyMapWrites == 0)
|
||||
{
|
||||
m_HighTrafficResources.insert(pResource);
|
||||
m_pDevice->GetResourceManager()->MarkDirtyResource(Id);
|
||||
@@ -6356,6 +6391,8 @@ bool WrappedID3D11DeviceContext::Serialise_Unmap(ID3D11Resource *pResource, UINT
|
||||
|
||||
MapIntercept intercept;
|
||||
|
||||
int ctxMapID = 0;
|
||||
|
||||
if(m_State >= WRITING)
|
||||
{
|
||||
auto it = m_OpenMaps.find(mapIdx);
|
||||
@@ -6365,6 +6402,27 @@ bool WrappedID3D11DeviceContext::Serialise_Unmap(ID3D11Resource *pResource, UINT
|
||||
intercept = it->second;
|
||||
|
||||
m_OpenMaps.erase(it);
|
||||
|
||||
if(GetType() == D3D11_DEVICE_CONTEXT_DEFERRED && (m_State == WRITING_CAPFRAME || intercept.verifyWrite))
|
||||
{
|
||||
ctxMapID = m_MapResourceRecordAllocs[mapIdx.resource];
|
||||
|
||||
RDCASSERT(ctxMapID != 0);
|
||||
}
|
||||
|
||||
if(intercept.verifyWrite && record)
|
||||
{
|
||||
if(!record->VerifyShadowStorage(ctxMapID))
|
||||
{
|
||||
int res = MessageBoxA(NULL,
|
||||
"Breakpoint now to see callstack,\nor click 'Yes' to debugbreak.",
|
||||
"Map() overwrite detected!", MB_YESNO|MB_ICONERROR);
|
||||
if(res == IDYES)
|
||||
{
|
||||
OS_DEBUG_BREAK();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(m_State < WRITING || m_State == WRITING_CAPFRAME)
|
||||
@@ -6378,15 +6436,6 @@ bool WrappedID3D11DeviceContext::Serialise_Unmap(ID3D11Resource *pResource, UINT
|
||||
size_t diffStart = 0;
|
||||
size_t diffEnd = len;
|
||||
|
||||
int ctxMapID = 0;
|
||||
|
||||
if(GetType() == D3D11_DEVICE_CONTEXT_DEFERRED && m_State == WRITING_CAPFRAME)
|
||||
{
|
||||
ctxMapID = m_MapResourceRecordAllocs[mapIdx.resource];
|
||||
|
||||
RDCASSERT(ctxMapID != 0);
|
||||
}
|
||||
|
||||
if(m_State == WRITING_CAPFRAME && len > 512 && intercept.MapType != D3D11_MAP_WRITE_DISCARD)
|
||||
{
|
||||
bool found = FindDiffRange(appWritePtr, record->GetShadowPtr(ctxMapID, 1), len, diffStart, diffEnd);
|
||||
@@ -6526,6 +6575,10 @@ bool WrappedID3D11DeviceContext::Serialise_Unmap(ID3D11Resource *pResource, UINT
|
||||
if(m_State < WRITING)
|
||||
SAFE_DELETE_ARRAY(buf);
|
||||
}
|
||||
else if(intercept.verifyWrite)
|
||||
{
|
||||
memcpy(record->GetDataPtr(), intercept.app.pData, len);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -2379,6 +2379,8 @@ void WrappedID3D11Device::StartFrameCapture(void *wnd)
|
||||
|
||||
GetResourceManager()->MarkResourceFrameReferenced(m_ResourceID, eFrameRef_Write);
|
||||
|
||||
m_pImmediateContext->FreeCaptureData();
|
||||
|
||||
m_pImmediateContext->AttemptCapture();
|
||||
m_pImmediateContext->BeginCaptureFrame();
|
||||
|
||||
|
||||
@@ -27,6 +27,13 @@
|
||||
#include "driver/d3d11/d3d11_context.h"
|
||||
#include "driver/d3d11/d3d11_resources.h"
|
||||
|
||||
byte D3D11ResourceRecord::markerValue[32] = {
|
||||
0xaa, 0xbb, 0xcc, 0xdd,
|
||||
0x88, 0x77, 0x66, 0x55,
|
||||
0x01, 0x23, 0x45, 0x67,
|
||||
0x98, 0x76, 0x54, 0x32,
|
||||
};
|
||||
|
||||
ID3D11DeviceChild *D3D11ResourceManager::UnwrapResource(ID3D11DeviceChild *res)
|
||||
{
|
||||
if(res == NULL) return res;
|
||||
|
||||
@@ -43,6 +43,8 @@ struct D3D11ResourceRecord : public ResourceRecord
|
||||
{
|
||||
enum { NullResource = NULL };
|
||||
|
||||
static byte markerValue[32];
|
||||
|
||||
D3D11ResourceRecord(ResourceId id)
|
||||
: ResourceRecord(id, true)
|
||||
{
|
||||
@@ -60,11 +62,27 @@ struct D3D11ResourceRecord : public ResourceRecord
|
||||
{
|
||||
if(ShadowPtr[ctx][0] == NULL)
|
||||
{
|
||||
ShadowPtr[ctx][0] = Serialiser::AllocAlignedBuffer(size);
|
||||
ShadowPtr[ctx][1] = Serialiser::AllocAlignedBuffer(size);
|
||||
ShadowPtr[ctx][0] = Serialiser::AllocAlignedBuffer(size + sizeof(markerValue));
|
||||
ShadowPtr[ctx][1] = Serialiser::AllocAlignedBuffer(size + sizeof(markerValue));
|
||||
|
||||
memcpy(ShadowPtr[ctx][0] + size, markerValue, sizeof(markerValue));
|
||||
memcpy(ShadowPtr[ctx][1] + size, markerValue, sizeof(markerValue));
|
||||
|
||||
ShadowSize[ctx] = size;
|
||||
}
|
||||
}
|
||||
|
||||
bool VerifyShadowStorage(int ctx)
|
||||
{
|
||||
if(ShadowPtr[ctx][0] && memcmp(ShadowPtr[ctx][0] + ShadowSize[ctx], markerValue, sizeof(markerValue)))
|
||||
return false;
|
||||
|
||||
if(ShadowPtr[ctx][1] && memcmp(ShadowPtr[ctx][1] + ShadowSize[ctx], markerValue, sizeof(markerValue)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FreeShadowStorage()
|
||||
{
|
||||
for(int i=0; i < 32; i++)
|
||||
@@ -109,6 +127,7 @@ struct D3D11ResourceRecord : public ResourceRecord
|
||||
|
||||
private:
|
||||
byte *ShadowPtr[32][2];
|
||||
size_t ShadowSize[32];
|
||||
|
||||
bool contexts[32];
|
||||
};
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace renderdoc
|
||||
public bool CaptureCallstacks;
|
||||
public bool CaptureCallstacksOnlyDraws;
|
||||
public UInt32 DelayForDebugger;
|
||||
public bool CacheStateObjects;
|
||||
public bool VerifyMapWrites;
|
||||
public bool HookIntoChildren;
|
||||
public bool RefAllResources;
|
||||
public bool SaveAllInitials;
|
||||
@@ -90,7 +90,7 @@ namespace renderdoc
|
||||
defs.CaptureCallstacks = false;
|
||||
defs.CaptureCallstacksOnlyDraws = false;
|
||||
defs.DelayForDebugger = 0;
|
||||
defs.CacheStateObjects = false;
|
||||
defs.VerifyMapWrites = true;
|
||||
defs.HookIntoChildren = false;
|
||||
defs.RefAllResources = false;
|
||||
defs.SaveAllInitials = false;
|
||||
|
||||
+17
-4
@@ -56,6 +56,7 @@
|
||||
this.SaveAllInitials = new System.Windows.Forms.CheckBox();
|
||||
this.RefAllResources = new System.Windows.Forms.CheckBox();
|
||||
this.CaptureAllCmdLists = new System.Windows.Forms.CheckBox();
|
||||
this.VerifyMapWrites = new System.Windows.Forms.CheckBox();
|
||||
this.AutoStart = new System.Windows.Forms.CheckBox();
|
||||
this.panel2 = new System.Windows.Forms.Panel();
|
||||
this.load = new System.Windows.Forms.Button();
|
||||
@@ -181,7 +182,7 @@
|
||||
this.queueFrameCap.Location = new System.Drawing.Point(3, 3);
|
||||
this.queueFrameCap.Name = "queueFrameCap";
|
||||
this.queueFrameCap.Size = new System.Drawing.Size(151, 24);
|
||||
this.queueFrameCap.TabIndex = 19;
|
||||
this.queueFrameCap.TabIndex = 20;
|
||||
this.queueFrameCap.Text = "Queue Capture of Frame";
|
||||
this.queueFrameCap.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -201,7 +202,7 @@
|
||||
0});
|
||||
this.queuedCapFrame.Name = "queuedCapFrame";
|
||||
this.queuedCapFrame.Size = new System.Drawing.Size(120, 20);
|
||||
this.queuedCapFrame.TabIndex = 20;
|
||||
this.queuedCapFrame.TabIndex = 21;
|
||||
this.queuedCapFrame.ThousandsSeparator = true;
|
||||
this.queuedCapFrame.Value = new decimal(new int[] {
|
||||
2,
|
||||
@@ -278,6 +279,7 @@
|
||||
this.capOptsFlow.Controls.Add(this.SaveAllInitials);
|
||||
this.capOptsFlow.Controls.Add(this.RefAllResources);
|
||||
this.capOptsFlow.Controls.Add(this.CaptureAllCmdLists);
|
||||
this.capOptsFlow.Controls.Add(this.VerifyMapWrites);
|
||||
this.capOptsFlow.Controls.Add(this.AutoStart);
|
||||
this.capOptsFlow.Location = new System.Drawing.Point(3, 16);
|
||||
this.capOptsFlow.MaximumSize = new System.Drawing.Size(640, 0);
|
||||
@@ -412,12 +414,22 @@
|
||||
"as an overhead but ensures if you hold onto a list it will be captured.");
|
||||
this.CaptureAllCmdLists.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// VerifyMapWrites
|
||||
//
|
||||
this.VerifyMapWrites.Location = new System.Drawing.Point(275, 55);
|
||||
this.VerifyMapWrites.Name = "VerifyMapWrites";
|
||||
this.VerifyMapWrites.Size = new System.Drawing.Size(130, 20);
|
||||
this.VerifyMapWrites.TabIndex = 18;
|
||||
this.VerifyMapWrites.Text = "Verify Map() Writes";
|
||||
this.toolTip.SetToolTip(this.AutoStart, "When enabled, causes Map() pointers to be validated to check for buffer overruns.");
|
||||
this.VerifyMapWrites.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// AutoStart
|
||||
//
|
||||
this.AutoStart.Location = new System.Drawing.Point(275, 55);
|
||||
this.AutoStart.Location = new System.Drawing.Point(411, 55);
|
||||
this.AutoStart.Name = "AutoStart";
|
||||
this.AutoStart.Size = new System.Drawing.Size(130, 20);
|
||||
this.AutoStart.TabIndex = 18;
|
||||
this.AutoStart.TabIndex = 19;
|
||||
this.AutoStart.Text = "Auto start";
|
||||
this.toolTip.SetToolTip(this.AutoStart, "If these capture settings are saved & run, auto start the capture instantly on lo" +
|
||||
"ad");
|
||||
@@ -733,6 +745,7 @@
|
||||
private System.Windows.Forms.CheckBox SaveAllInitials;
|
||||
private System.Windows.Forms.CheckBox RefAllResources;
|
||||
private System.Windows.Forms.CheckBox CaptureCallstacksOnlyDraws;
|
||||
private System.Windows.Forms.CheckBox VerifyMapWrites;
|
||||
private System.Windows.Forms.CheckBox AutoStart;
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
private System.Windows.Forms.FlowLayoutPanel capOptsFlow;
|
||||
|
||||
@@ -79,6 +79,7 @@ namespace renderdocui.Windows.Dialogs
|
||||
RefAllResources.Checked = settings.Options.RefAllResources;
|
||||
SaveAllInitials.Checked = settings.Options.SaveAllInitials;
|
||||
DelayForDebugger.Value = settings.Options.DelayForDebugger;
|
||||
VerifyMapWrites.Checked = settings.Options.VerifyMapWrites;
|
||||
AutoStart.Checked = settings.AutoStart;
|
||||
|
||||
if (settings.AutoStart)
|
||||
@@ -117,6 +118,7 @@ namespace renderdocui.Windows.Dialogs
|
||||
ret.Options.SaveAllInitials = SaveAllInitials.Checked;
|
||||
ret.Options.CaptureAllCmdLists = CaptureAllCmdLists.Checked;
|
||||
ret.Options.DelayForDebugger = (uint)DelayForDebugger.Value;
|
||||
ret.Options.VerifyMapWrites = VerifyMapWrites.Checked;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user