Add error checking and handling while loading captures

* Previously once we started loading a capture we'd blindly continue
  until we loaded it (and then it's assumed to be successful), or we
  crash.
* Now errors can be reported during serialisation and bubbled up to
  abort the file load process. The next steps are to add error checking
  in each function serialise before doing any replay calls to the API
  with potentially corrupt data, and on top of that catching API-only
  errors when the serialisation is (seemingly) fine, and propagating
  those in a reasonable way.
* We also harden the serialisation a bit so that if it reads an
  obviously invalid byte length for a buffer or array count, it won't
  continue. It's still not perfect as the sizes could still be large and
  invalid but within range, but it should catch the worst cases.
This commit is contained in:
baldurk
2017-11-24 18:14:21 +00:00
parent 2349fe4c05
commit 01e2e00b5f
36 changed files with 312 additions and 129 deletions
+6
View File
@@ -423,7 +423,13 @@ void ReplayManager::run(int proxyRenderer, const QString &capturefile, float *pr
}
if(m_Renderer == NULL)
{
if(m_CaptureFile)
m_CaptureFile->Shutdown();
m_CaptureFile = NULL;
return;
}
qInfo() << "QRenderDoc - renderer created for" << capturefile;
+14 -5
View File
@@ -423,10 +423,18 @@ void MainWindow::LoadCapture(const QString &filename, bool temporary, bool local
{
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
if(file->OpenFile(filename.toUtf8().data(), "rdc") != ReplayStatus::Succeeded)
ReplayStatus status = file->OpenFile(filename.toUtf8().data(), "rdc");
if(status != ReplayStatus::Succeeded)
{
RDDialog::critical(NULL, tr("Error opening capture"),
tr("Couldn't open file '%1'").arg(filename));
QString text = tr("Couldn't open file '%1'\n").arg(filename);
QString message = file->ErrorString();
if(message.isEmpty())
text += tr("%1").arg(ToQStr(status));
else
text += tr("%1: %2").arg(ToQStr(status)).arg(message);
RDDialog::critical(NULL, tr("Error opening capture"), text);
file->Shutdown();
return;
@@ -559,6 +567,8 @@ void MainWindow::LoadCapture(const QString &filename, bool temporary, bool local
}
}
statusText->setText(tr("Loading %1...").arg(origFilename));
m_Ctx.LoadCapture(fileToLoad, origFilename, temporary, local);
}
@@ -566,8 +576,6 @@ void MainWindow::LoadCapture(const QString &filename, bool temporary, bool local
{
m_Ctx.Config().LastCaptureFilePath = QFileInfo(filename).absolutePath();
}
statusText->setText(tr("Loading %1...").arg(origFilename));
}
}
@@ -976,6 +984,7 @@ void MainWindow::setProgress(float val)
if(val < 0.0f || val >= 1.0f)
{
statusProgress->setVisible(false);
statusText->setText(QString());
}
else
{
+10
View File
@@ -1406,6 +1406,16 @@ representation back to native RDC.
)");
virtual ReplayStatus Convert(const char *filename, const char *filetype, float *progress) = 0;
DOCUMENT(R"(Returns the human-readable error string for the last error received.
The error string is not reset by calling this function so it's safe to call multiple times. However
any other function call may reset the error string to empty.
:return: The error string, if one exists, or an empty string.
:rtype: str
)");
virtual rdcstr ErrorString() = 0;
DOCUMENT(R"(Returns the list of capture file formats.
:return: The list of capture file formats available.
+9 -3
View File
@@ -39,10 +39,16 @@ std::string DoStringise(const ReplayStatus &el)
STRINGISE_ENUM_CLASS_NAMED(FileIOFailed, "File I/O failed");
STRINGISE_ENUM_CLASS_NAMED(FileIncompatibleVersion, "File of incompatible version");
STRINGISE_ENUM_CLASS_NAMED(FileCorrupted, "File corrupted");
STRINGISE_ENUM_CLASS_NAMED(APIUnsupported, "API unsupported");
STRINGISE_ENUM_CLASS_NAMED(APIUnsupported, "API is not unsupported");
STRINGISE_ENUM_CLASS_NAMED(APIInitFailed, "API initialisation failed");
STRINGISE_ENUM_CLASS_NAMED(APIIncompatibleVersion, "API incompatible version");
STRINGISE_ENUM_CLASS_NAMED(APIHardwareUnsupported, "API hardware unsupported");
STRINGISE_ENUM_CLASS_NAMED(APIIncompatibleVersion,
"Captured API data has an incompatible version");
STRINGISE_ENUM_CLASS_NAMED(
APIHardwareUnsupported,
"Current hardware unsupported or incompatible with captured hardware");
STRINGISE_ENUM_CLASS_NAMED(APIDataCorrupted,
"Replaying the capture encountered invalid/corrupted data");
STRINGISE_ENUM_CLASS_NAMED(APIReplayFailed, "Replaying the capture failed at the API level");
}
END_ENUM_STRINGISE();
}
+11
View File
@@ -2833,6 +2833,15 @@ a remote server.
.. data:: APIHardwareUnsupported
The API is not supported on the currently available hardware.
.. data:: APIDataCorrupted
While loading the capture for replay, the driver encountered corrupted or invalid serialised data.
.. data:: APIReplayFailed
The API failed to replay the capture, with some runtime error that couldn't be determined until
the replay began.
)");
enum class ReplayStatus : uint32_t
{
@@ -2853,6 +2862,8 @@ enum class ReplayStatus : uint32_t
APIInitFailed,
APIIncompatibleVersion,
APIHardwareUnsupported,
APIDataCorrupted,
APIReplayFailed,
};
DECLARE_REFLECTION_ENUM(ReplayStatus);
+4 -1
View File
@@ -151,7 +151,10 @@ public:
FrameRecord GetFrameRecord() { return m_FrameRecord; }
const D3D11Pipe::State &GetD3D11PipelineState() { return m_PipelineState; }
// other operations are dropped/ignored, to avoid confusion
void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) {}
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
return ReplayStatus::Succeeded;
}
const SDFile &GetStructuredFile() { return m_File; }
void RenderMesh(uint32_t eventID, const vector<MeshFormat> &secondaryDraws, const MeshDisplay &cfg)
{
+18 -6
View File
@@ -205,6 +205,7 @@ static void ActiveRemoteClientThread(ClientThread *threadData)
ReadSerialiser reader(new StreamReader(client, Ownership::Nothing), Ownership::Stream);
writer.SetStreamingMode(true);
reader.SetStreamingMode(true);
while(client)
{
@@ -429,15 +430,25 @@ static void ActiveRemoteClientThread(ClientThread *threadData)
}
else
{
driver->ReadLogInitialisation(rdc, false);
status = driver->ReadLogInitialisation(rdc, false);
RenderDoc::Inst().SetProgressPtr(NULL);
if(status != ReplayStatus::Succeeded)
{
RDCERR("Failed to initialise remote driver.");
kill = true;
Threading::JoinThread(ticker);
Threading::CloseThread(ticker);
driver->Shutdown();
driver = NULL;
}
else
{
RenderDoc::Inst().SetProgressPtr(NULL);
proxy = new ReplayProxy(reader, writer, driver);
kill = true;
Threading::JoinThread(ticker);
Threading::CloseThread(ticker);
proxy = new ReplayProxy(reader, writer, driver);
}
}
}
else
@@ -966,6 +977,7 @@ public:
writer(new StreamWriter(sock, Ownership::Nothing), Ownership::Stream)
{
writer.SetStreamingMode(true);
reader.SetStreamingMode(true);
std::map<RDCDriver, std::string> m = RenderDoc::Inst().GetReplayDrivers();
+4 -1
View File
@@ -120,7 +120,10 @@ public:
bool IsRemoteProxy() { return !m_RemoteServer; }
void Shutdown() { delete this; }
void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) {}
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
return ReplayStatus::Succeeded;
}
vector<WindowingSystem> GetSupportedWindowSystems()
{
if(m_Proxy)
+2
View File
@@ -76,6 +76,7 @@ void RenderDoc::TargetControlClientThread(Network::Socket *client)
ReadSerialiser reader(new StreamReader(client, Ownership::Nothing), Ownership::Stream);
writer.SetStreamingMode(true);
reader.SetStreamingMode(true);
std::string api = "";
RDCDriver driver;
@@ -404,6 +405,7 @@ public:
std::vector<byte> payload;
writer.SetStreamingMode(true);
reader.SetStreamingMode(true);
m_PID = 0;
+11 -3
View File
@@ -912,17 +912,17 @@ void WrappedID3D11Device::Serialise_CaptureScope(SerialiserType &ser)
}
}
void WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
ReplayStatus WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture);
if(sectionIdx < 0)
return;
return ReplayStatus::FileCorrupted;
StreamReader *reader = rdc->ReadSection(sectionIdx);
if(reader->IsErrored())
return;
return ReplayStatus::FileIOFailed;
ReadSerialiser ser(reader, Ownership::Stream);
@@ -961,10 +961,16 @@ void WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructur
chunkIdx++;
if(reader->IsErrored())
return ReplayStatus::APIDataCorrupted;
ProcessChunk(ser, context);
ser.EndChunk();
if(reader->IsErrored())
return ReplayStatus::APIDataCorrupted;
uint64_t offsetEnd = reader->GetOffset();
RenderDoc::Inst().SetProgress(FileInitialRead, float(offsetEnd) / float(reader->GetSize()));
@@ -1027,6 +1033,8 @@ void WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructur
RDCDEBUG("Allocating %llu persistant bytes of memory for the log.",
m_FrameRecord.frameInfo.persistentSize);
return ReplayStatus::Succeeded;
}
void WrappedID3D11Device::ReplayLog(uint32_t startEventID, uint32_t endEventID,
+1 -1
View File
@@ -514,7 +514,7 @@ public:
m_SectionVersion = sectionVersion;
m_State = CaptureState::StructuredExport;
}
void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
void ProcessChunk(ReadSerialiser &ser, D3D11Chunk context);
void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType);
+6 -5
View File
@@ -1218,9 +1218,9 @@ void D3D11Replay::SavePipelineState()
}
}
void D3D11Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
ReplayStatus D3D11Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers);
return m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers);
}
void D3D11Replay::ReplayLog(uint32_t endEventID, ReplayLogType replayType)
@@ -1896,7 +1896,7 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver)
if(!D3D11InitParams::IsSupportedVersion(ver))
{
RDCERR("Incompatible D3D11 serialise version %llu", ver);
return ReplayStatus::APIUnsupported;
return ReplayStatus::APIIncompatibleVersion;
}
StreamReader *reader = rdc->ReadSection(sectionIdx);
@@ -2051,9 +2051,10 @@ void D3D11_ProcessStructured(RDCFile *rdc, SDFile &output)
return;
device.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version);
device.ReadLogInitialisation(rdc, true);
ReplayStatus status = device.ReadLogInitialisation(rdc, true);
device.GetStructuredFile().swap(output);
if(status == ReplayStatus::Succeeded)
device.GetStructuredFile().swap(output);
}
static StructuredProcessRegistration D3D11ProcessRegistration(RDC_D3D11, &D3D11_ProcessStructured);
+1 -1
View File
@@ -77,7 +77,7 @@ public:
void FreeTargetResource(ResourceId id);
void FreeCustomShader(ResourceId id);
void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
void ReplayLog(uint32_t endEventID, ReplayLogType replayType);
const SDFile &GetStructuredFile();
+11 -3
View File
@@ -2367,17 +2367,17 @@ void WrappedID3D12Device::AddResourceCurChunk(ResourceId id)
AddResourceCurChunk(GetReplay()->GetResourceDesc(id));
}
void WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
ReplayStatus WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture);
if(sectionIdx < 0)
return;
return ReplayStatus::FileCorrupted;
StreamReader *reader = rdc->ReadSection(sectionIdx);
if(reader->IsErrored())
return;
return ReplayStatus::FileIOFailed;
ReadSerialiser ser(reader, Ownership::Stream);
@@ -2416,10 +2416,16 @@ void WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructur
chunkIdx++;
if(reader->IsErrored())
return ReplayStatus::APIDataCorrupted;
ProcessChunk(ser, context);
ser.EndChunk();
if(reader->IsErrored())
return ReplayStatus::APIDataCorrupted;
uint64_t offsetEnd = reader->GetOffset();
RenderDoc::Inst().SetProgress(FileInitialRead, float(offsetEnd) / float(reader->GetSize()));
@@ -2498,6 +2504,8 @@ void WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructur
RDCDEBUG("Allocating %llu persistant bytes of memory for the log.",
m_FrameRecord.frameInfo.persistentSize);
return ReplayStatus::Succeeded;
}
void WrappedID3D12Device::ReplayLog(uint32_t startEventID, uint32_t endEventID,
+1 -1
View File
@@ -474,7 +474,7 @@ public:
bool Serialise_DynamicDescriptorCopies(SerialiserType &ser,
const std::vector<DynamicDescriptorCopy> &DescriptorCopies);
void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType);
void SetStructuredExport(uint64_t sectionVersion)
+6 -5
View File
@@ -59,9 +59,9 @@ void D3D12Replay::Shutdown()
D3D12Replay::PostDeviceShutdownCounters();
}
void D3D12Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
ReplayStatus D3D12Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers);
return m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers);
}
APIProperties D3D12Replay::GetAPIProperties()
@@ -1752,7 +1752,7 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver)
if(!D3D12InitParams::IsSupportedVersion(ver))
{
RDCERR("Incompatible D3D11 serialise version %llu", ver);
return ReplayStatus::APIUnsupported;
return ReplayStatus::APIIncompatibleVersion;
}
StreamReader *reader = rdc->ReadSection(sectionIdx);
@@ -1816,9 +1816,10 @@ void D3D12_ProcessStructured(RDCFile *rdc, SDFile &output)
return;
device.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version);
device.ReadLogInitialisation(rdc, true);
ReplayStatus status = device.ReadLogInitialisation(rdc, true);
device.GetStructuredFile().swap(output);
if(status == ReplayStatus::Succeeded)
device.GetStructuredFile().swap(output);
}
static StructuredProcessRegistration D3D12ProcessRegistration(RDC_D3D12, &D3D12_ProcessStructured);
+1 -1
View File
@@ -75,7 +75,7 @@ public:
void FreeTargetResource(ResourceId id);
void FreeCustomShader(ResourceId id);
void ReadLogInitialisation(RDCFile *rdc, bool readStructuredBuffers);
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool readStructuredBuffers);
void ReplayLog(uint32_t endEventID, ReplayLogType replayType);
const SDFile &GetStructuredFile();
+11 -3
View File
@@ -3054,17 +3054,17 @@ void WrappedOpenGL::AddResourceInitChunk(GLResource res)
}
}
void WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
ReplayStatus WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture);
if(sectionIdx < 0)
return;
return ReplayStatus::FileCorrupted;
StreamReader *reader = rdc->ReadSection(sectionIdx);
if(reader->IsErrored())
return;
return ReplayStatus::FileIOFailed;
ReadSerialiser ser(reader, Ownership::Stream);
@@ -3103,10 +3103,16 @@ void WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuff
chunkIdx++;
if(reader->IsErrored())
return ReplayStatus::APIDataCorrupted;
ProcessChunk(ser, context);
ser.EndChunk();
if(reader->IsErrored())
return ReplayStatus::APIDataCorrupted;
uint64_t offsetEnd = reader->GetOffset();
RenderDoc::Inst().SetProgress(FileInitialRead, float(offsetEnd) / float(reader->GetSize()));
@@ -3161,6 +3167,8 @@ void WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuff
RDCDEBUG("Allocating %llu persistant bytes of memory for the log.",
m_FrameRecord.frameInfo.persistentSize);
return ReplayStatus::Succeeded;
}
void WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk)
+1 -1
View File
@@ -559,7 +559,7 @@ public:
// replay interface
void Initialise(GLInitParams &params, uint64_t sectionVersion);
void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType);
void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
GLuint GetFakeBBFBO() { return m_FakeBB_FBO; }
GLuint GetFakeVAO() { return m_FakeVAO; }
+5 -4
View File
@@ -73,10 +73,10 @@ void GLReplay::Shutdown()
GLReplay::PostContextShutdownCounters();
}
void GLReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
ReplayStatus GLReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
MakeCurrentReplayContext(&m_ReplayCtx);
m_pDriver->ReadLogInitialisation(rdc, storeStructuredBuffers);
return m_pDriver->ReadLogInitialisation(rdc, storeStructuredBuffers);
}
void GLReplay::ReplayLog(uint32_t endEventID, ReplayLogType replayType)
@@ -3341,9 +3341,10 @@ void GL_ProcessStructured(RDCFile *rdc, SDFile &output)
return;
device.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version);
device.ReadLogInitialisation(rdc, true);
ReplayStatus status = device.ReadLogInitialisation(rdc, true);
device.GetStructuredFile().swap(output);
if(status == ReplayStatus::Succeeded)
device.GetStructuredFile().swap(output);
}
static StructuredProcessRegistration GLProcessRegistration(RDC_OpenGL, &GL_ProcessStructured);
+1 -1
View File
@@ -121,7 +121,7 @@ public:
const VKPipe::State &GetVulkanPipelineState() { return m_VKState; }
void FreeTargetResource(ResourceId id);
void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
void ReplayLog(uint32_t endEventID, ReplayLogType replayType);
const SDFile &GetStructuredFile();
+1 -1
View File
@@ -73,7 +73,7 @@ ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver)
if(!GLInitParams::IsSupportedVersion(ver))
{
RDCERR("Incompatible D3D11 serialise version %llu", ver);
return ReplayStatus::APIUnsupported;
return ReplayStatus::APIIncompatibleVersion;
}
StreamReader *reader = rdc->ReadSection(sectionIdx);
+1 -1
View File
@@ -108,7 +108,7 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver)
if(!GLInitParams::IsSupportedVersion(ver))
{
RDCERR("Incompatible D3D11 serialise version %llu", ver);
return ReplayStatus::APIUnsupported;
return ReplayStatus::APIIncompatibleVersion;
}
StreamReader *reader = rdc->ReadSection(sectionIdx);
+1 -1
View File
@@ -70,7 +70,7 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver)
if(!GLInitParams::IsSupportedVersion(ver))
{
RDCERR("Incompatible D3D11 serialise version %llu", ver);
return ReplayStatus::APIUnsupported;
return ReplayStatus::APIIncompatibleVersion;
}
StreamReader *reader = rdc->ReadSection(sectionIdx);
+11 -3
View File
@@ -1420,17 +1420,17 @@ void WrappedVulkan::AddResourceCurChunk(ResourceId id)
AddResourceCurChunk(GetReplay()->GetResourceDesc(id));
}
void WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
ReplayStatus WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture);
if(sectionIdx < 0)
return;
return ReplayStatus::FileCorrupted;
StreamReader *reader = rdc->ReadSection(sectionIdx);
if(reader->IsErrored())
return;
return ReplayStatus::FileIOFailed;
ReadSerialiser ser(reader, Ownership::Stream);
@@ -1469,10 +1469,16 @@ void WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuff
chunkIdx++;
if(reader->IsErrored())
return ReplayStatus::APIDataCorrupted;
ProcessChunk(ser, context);
ser.EndChunk();
if(reader->IsErrored())
return ReplayStatus::APIDataCorrupted;
uint64_t offsetEnd = reader->GetOffset();
RenderDoc::Inst().SetProgress(FileInitialRead, float(offsetEnd) / float(reader->GetSize()));
@@ -1533,6 +1539,8 @@ void WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuff
RDCASSERT(m_Device != VK_NULL_HANDLE && m_Queue != VK_NULL_HANDLE &&
m_InternalCmds.cmdpool != VK_NULL_HANDLE);
}
return ReplayStatus::Succeeded;
}
void WrappedVulkan::ContextReplayLog(CaptureState readType, uint32_t startEventID,
+1 -1
View File
@@ -763,7 +763,7 @@ public:
}
void Shutdown();
void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType);
void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
SDFile &GetStructuredFile() { return *m_StructuredFile; }
FrameRecord &GetFrameRecord() { return m_FrameRecord; }
+6 -5
View File
@@ -659,9 +659,9 @@ APIProperties VulkanReplay::GetAPIProperties()
return ret;
}
void VulkanReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
ReplayStatus VulkanReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
m_pDriver->ReadLogInitialisation(rdc, storeStructuredBuffers);
return m_pDriver->ReadLogInitialisation(rdc, storeStructuredBuffers);
}
void VulkanReplay::ReplayLog(uint32_t endEventID, ReplayLogType replayType)
@@ -5360,7 +5360,7 @@ ReplayStatus Vulkan_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver)
if(!VkInitParams::IsSupportedVersion(ver))
{
RDCERR("Incompatible Vulkan serialise version %llu", ver);
return ReplayStatus::APIUnsupported;
return ReplayStatus::APIIncompatibleVersion;
}
StreamReader *reader = rdc->ReadSection(sectionIdx);
@@ -5428,9 +5428,10 @@ void Vulkan_ProcessStructured(RDCFile *rdc, SDFile &output)
return;
vulkan.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version);
vulkan.ReadLogInitialisation(rdc, true);
ReplayStatus status = vulkan.ReadLogInitialisation(rdc, true);
vulkan.GetStructuredFile().swap(output);
if(status == ReplayStatus::Succeeded)
vulkan.GetStructuredFile().swap(output);
}
static StructuredProcessRegistration VulkanProcessRegistration(RDC_Vulkan, &Vulkan_ProcessStructured);
+1 -1
View File
@@ -163,7 +163,7 @@ public:
const VKPipe::State &GetVulkanPipelineState() { return m_VulkanPipelineState; }
void FreeTargetResource(ResourceId id);
void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
void ReplayLog(uint32_t endEventID, ReplayLogType replayType);
const SDFile &GetStructuredFile();
+6 -2
View File
@@ -113,7 +113,7 @@ public:
ReplayStatus OpenFile(const char *filename, const char *filetype);
ReplayStatus OpenBuffer(const bytebuf &buffer, const char *filetype);
bool CopyFileTo(const char *filename);
rdcstr ErrorString() { return m_ErrorString; }
void Shutdown() { delete this; }
ReplaySupport LocalReplaySupport() { return m_Support; }
const char *DriverName() { return m_DriverName.c_str(); }
@@ -183,7 +183,7 @@ private:
SDFile m_StructuredData;
std::string m_DriverName, m_Ident;
std::string m_DriverName, m_Ident, m_ErrorString;
ReplaySupport m_Support = ReplaySupport::Unsupported;
};
@@ -214,6 +214,7 @@ ReplayStatus CaptureFile::OpenFile(const char *filename, const char *filetype)
if(ret != ReplayStatus::Succeeded)
{
m_ErrorString = StringFormat::Fmt("Importer '%s' failed to import file.", filetype);
delete m_RDC;
return ret;
}
@@ -249,6 +250,7 @@ ReplayStatus CaptureFile::OpenBuffer(const bytebuf &buffer, const char *filetype
if(ret != ReplayStatus::Succeeded)
{
m_ErrorString = StringFormat::Fmt("Importer '%s' failed to import file.", filetype);
delete m_RDC;
return ret;
}
@@ -278,6 +280,8 @@ ReplayStatus CaptureFile::Init()
if(!m_RDC)
return ReplayStatus::InternalError;
m_ErrorString = m_RDC->ErrorString();
switch(m_RDC->ErrorCode())
{
case ContainerError::FileNotFound: return ReplayStatus::FileNotFound; break;
+7 -1
View File
@@ -1586,7 +1586,10 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd
{
m_pDevice = device;
m_pDevice->ReadLogInitialisation(rdc, false);
ReplayStatus status = m_pDevice->ReadLogInitialisation(rdc, false);
if(status != ReplayStatus::Succeeded)
return status;
FetchPipelineState();
@@ -1612,6 +1615,9 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd
m_FrameRecord = m_pDevice->GetFrameRecord();
if(m_FrameRecord.drawcallList.empty())
return ReplayStatus::APIReplayFailed;
DrawcallDescription *previous = NULL;
SetupDrawcallPointers(&m_Drawcalls, m_FrameRecord.drawcallList, NULL, previous);
+1 -1
View File
@@ -117,7 +117,7 @@ public:
virtual FrameRecord GetFrameRecord() = 0;
virtual void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) = 0;
virtual ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) = 0;
virtual void ReplayLog(uint32_t endEventID, ReplayLogType replayType) = 0;
virtual const SDFile &GetStructuredFile() = 0;
+57 -66
View File
@@ -223,11 +223,17 @@ struct BinarySectionHeader
};
};
#define RETURNCORRUPT(...) \
{ \
RDCERR(__VA_ARGS__); \
m_Error = ContainerError::Corrupt; \
return; \
#define SETERROR(error, ...) \
{ \
m_ErrorString = StringFormat::Fmt(__VA_ARGS__); \
RDCERR("%s", m_ErrorString.c_str()); \
m_Error = error; \
}
#define RETURNERROR(error, ...) \
{ \
SETERROR(error, __VA_ARGS__); \
return; \
}
RDCFile::~RDCFile()
@@ -245,8 +251,7 @@ void RDCFile::Open(const char *path)
// empty path.
if(path == NULL || path[0] == 0)
{
m_Error = ContainerError::FileNotFound;
return;
RETURNERROR(ContainerError::FileNotFound, "Invalid file path specified");
}
RDCLOG("Opening RDCFile %s", path);
@@ -260,9 +265,8 @@ void RDCFile::Open(const char *path)
if(!m_File)
{
RDCERR("Can't open capture file '%s' for read - errno %d", path, errno);
m_Error = ContainerError::FileNotFound;
return;
RETURNERROR(ContainerError::FileNotFound, "Can't open capture file '%s' for read - errno %d",
path, errno);
}
// try to identify if this is an image
@@ -318,31 +322,30 @@ void RDCFile::Init(StreamReader &reader)
if(reader.IsErrored())
{
RDCERR("I/O error reading magic number");
m_Error = ContainerError::FileIO;
return;
RETURNERROR(ContainerError::FileIO, "I/O error reading magic number");
}
if(header.magic != MAGIC_HEADER)
{
RDCWARN("Invalid capture file. Expected magic %08x, got %08x.", MAGIC_HEADER,
(uint32_t)header.magic);
m_Error = ContainerError::Corrupt;
return;
RETURNERROR(ContainerError::Corrupt, "Invalid capture file. Expected magic %08x, got %08x.",
MAGIC_HEADER, (uint32_t)header.magic);
}
m_SerVer = header.version;
if(m_SerVer != SERIALISE_VERSION)
{
RDCERR(
"Capture file from wrong version. This program (v%s) is logfile version %llu, file is "
"logfile version %llu capture on %s.",
MAJOR_MINOR_VERSION_STRING, SERIALISE_VERSION, header.version, header.progVersion);
if(header.version < V1_0_VERSION)
{
RDCEraseEl(header.progVersion);
memcpy(header.progVersion, "v0.x", sizeof("v0.x"));
}
m_Error = ContainerError::UnsupportedVersion;
return;
RETURNERROR(
ContainerError::UnsupportedVersion,
"Capture file from wrong version. This program (v%s) uses logfile version %u, this file is "
"logfile version %u captured on %s.",
MAJOR_MINOR_VERSION_STRING, SERIALISE_VERSION, header.version, header.progVersion);
}
BinaryThumbnail thumb;
@@ -350,15 +353,13 @@ void RDCFile::Init(StreamReader &reader)
if(reader.IsErrored())
{
RDCERR("I/O error reading thumbnail header");
m_Error = ContainerError::FileIO;
return;
RETURNERROR(ContainerError::FileIO, "I/O error reading thumbnail header");
}
// check the thumbnail size is sensible
if(thumb.length > 10 * 1024 * 1024)
{
RETURNCORRUPT("Thumbnail byte length invalid: %u", thumb.length);
RETURNERROR(ContainerError::Corrupt, "Thumbnail byte length invalid: %u", thumb.length);
}
byte *thumbData = new byte[thumb.length];
@@ -366,10 +367,8 @@ void RDCFile::Init(StreamReader &reader)
if(reader.IsErrored())
{
RDCERR("I/O error reading thumbnail data");
delete[] thumbData;
m_Error = ContainerError::FileIO;
return;
RETURNERROR(ContainerError::FileIO, "I/O error reading thumbnail data");
}
CaptureMetaData meta;
@@ -377,16 +376,15 @@ void RDCFile::Init(StreamReader &reader)
if(reader.IsErrored())
{
RDCERR("I/O error reading capture metadata");
delete[] thumbData;
m_Error = ContainerError::FileIO;
return;
RETURNERROR(ContainerError::FileIO, "I/O error reading capture metadata");
}
if(meta.driverNameLength == 0)
{
delete[] thumbData;
RETURNCORRUPT("Driver name length is invalid, must be at least 1 to contain NULL terminator");
RETURNERROR(ContainerError::Corrupt,
"Driver name length is invalid, must be at least 1 to contain NULL terminator");
}
char *driverName = new char[meta.driverNameLength];
@@ -394,11 +392,9 @@ void RDCFile::Init(StreamReader &reader)
if(reader.IsErrored())
{
RDCERR("I/O error reading driver name");
delete[] thumbData;
delete[] driverName;
m_Error = ContainerError::FileIO;
return;
RETURNERROR(ContainerError::FileIO, "I/O error reading driver name");
}
driverName[meta.driverNameLength - 1] = '\0';
@@ -421,9 +417,7 @@ void RDCFile::Init(StreamReader &reader)
if(reader.GetOffset() > header.headerLength)
{
RDCERR("I/O error seeking to end of header");
m_Error = ContainerError::FileIO;
return;
RETURNERROR(ContainerError::FileIO, "I/O error seeking to end of header");
}
reader.SkipBytes(header.headerLength - (uint32_t)reader.GetOffset());
@@ -447,10 +441,10 @@ void RDCFile::Init(StreamReader &reader)
char c = 0;
reader.Read(c);
if(reader.IsErrored())
RETURNCORRUPT("Invalid ASCII data section '%hhx'", c);
RETURNERROR(ContainerError::Corrupt, "Invalid ASCII data section '%hhx'", c);
if(reader.AtEnd())
RETURNCORRUPT("Invalid truncated ASCII data section");
RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section");
uint64_t length = 0;
@@ -468,7 +462,7 @@ void RDCFile::Init(StreamReader &reader)
}
if(reader.IsErrored() || reader.AtEnd())
RETURNCORRUPT("Invalid truncated ASCII data section");
RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section");
uint32_t type = 0;
@@ -486,7 +480,7 @@ void RDCFile::Init(StreamReader &reader)
}
if(reader.IsErrored() || reader.AtEnd())
RETURNCORRUPT("Invalid truncated ASCII data section");
RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section");
uint64_t version = 0;
@@ -504,7 +498,7 @@ void RDCFile::Init(StreamReader &reader)
}
if(reader.IsErrored() || reader.AtEnd())
RETURNCORRUPT("Invalid truncated ASCII data section");
RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section");
std::string name;
@@ -521,7 +515,7 @@ void RDCFile::Init(StreamReader &reader)
}
if(reader.IsErrored() || reader.AtEnd())
RETURNCORRUPT("Invalid truncated ASCII data section");
RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section");
SectionProperties props;
props.flags = SectionFlags::ASCIIStored;
@@ -539,7 +533,8 @@ void RDCFile::Init(StreamReader &reader)
reader.SkipBytes(loc.diskLength);
if(reader.IsErrored())
RETURNCORRUPT("Error seeking past ASCII section '%s' data", name.c_str());
RETURNERROR(ContainerError::Corrupt, "Error seeking past ASCII section '%s' data",
name.c_str());
m_Sections.push_back(props);
m_SectionLocations.push_back(loc);
@@ -550,7 +545,7 @@ void RDCFile::Init(StreamReader &reader)
reader.Read(reading, offsetof(BinarySectionHeader, name) - 1);
if(reader.IsErrored())
RETURNCORRUPT("Error reading binary section header");
RETURNERROR(ContainerError::Corrupt, "Error reading binary section header");
SectionProperties props;
props.flags = sectionHeader.sectionFlags;
@@ -561,7 +556,8 @@ void RDCFile::Init(StreamReader &reader)
if(sectionHeader.sectionNameLength == 0 || sectionHeader.sectionNameLength > 2 * 1024)
{
RETURNCORRUPT("Invalid section name length %u", sectionHeader.sectionNameLength);
RETURNERROR(ContainerError::Corrupt, "Invalid section name length %u",
sectionHeader.sectionNameLength);
}
props.name.resize(sectionHeader.sectionNameLength - 1);
@@ -569,12 +565,12 @@ void RDCFile::Init(StreamReader &reader)
reader.Read(&props.name[0], sectionHeader.sectionNameLength - 1);
if(reader.IsErrored())
RETURNCORRUPT("Error reading binary section header");
RETURNERROR(ContainerError::Corrupt, "Error reading binary section header");
reader.SkipBytes(1);
if(reader.IsErrored())
RETURNCORRUPT("Error reading binary section header");
RETURNERROR(ContainerError::Corrupt, "Error reading binary section header");
SectionLocation loc;
loc.headerOffset = headerOffset;
@@ -587,17 +583,18 @@ void RDCFile::Init(StreamReader &reader)
reader.SkipBytes(loc.diskLength);
if(reader.IsErrored())
RETURNCORRUPT("Error seeking past binary section '%s' data", props.name.c_str());
RETURNERROR(ContainerError::Corrupt, "Error seeking past binary section '%s' data",
props.name.c_str());
}
else
{
RETURNCORRUPT("Unrecognised section type '%hhx'", sectionHeader.isASCII);
RETURNERROR(ContainerError::Corrupt, "Unrecognised section type '%hhx'", sectionHeader.isASCII);
}
}
if(SectionIndex(SectionType::FrameCapture) == -1)
{
RETURNCORRUPT("Capture file doesn't have a frame capture");
RETURNERROR(ContainerError::Corrupt, "Capture file doesn't have a frame capture");
}
}
@@ -649,9 +646,8 @@ void RDCFile::Create(const char *filename)
if(!m_File)
{
RDCERR("Can't open capture file '%s' for write, errno %d", filename, errno);
m_Error = ContainerError::FileIO;
return;
RETURNERROR(ContainerError::FileIO, "Can't open capture file '%s' for write, errno %d",
filename, errno);
}
RDCDEBUG("Opened capture file for write");
@@ -686,9 +682,7 @@ void RDCFile::Create(const char *filename)
if(writer.IsErrored())
{
RDCERR("Error writing file header");
m_Error = ContainerError::FileIO;
return;
RETURNERROR(ContainerError::FileIO, "Error writing file header");
}
}
@@ -1033,8 +1027,7 @@ StreamWriter *RDCFile::WriteSection(const SectionProperties &props)
if(numWritten != offsetof(BinarySectionHeader, name) + name.size() + 1)
{
RDCERR("Error seeking to end of file, errno %d", errno);
m_Error = ContainerError::FileIO;
SETERROR(ContainerError::FileIO, "Error seeking to end of file, errno %d", errno);
return new StreamWriter(StreamWriter::InvalidStream);
}
@@ -1097,9 +1090,7 @@ StreamWriter *RDCFile::WriteSection(const SectionProperties &props)
if(bytesWritten != 2 * sizeof(uint64_t))
{
RDCERR("Error applying fixup to section header, errno %d", errno);
m_Error = ContainerError::FileIO;
return;
RETURNERROR(ContainerError::FileIO, "Error applying fixup to section header, errno %d", errno);
}
});
+6
View File
@@ -54,6 +54,10 @@ public:
// API that interprets the stream that can be bumped.
static const uint32_t SERIALISE_VERSION = 0x00000100;
// this must never be changed - files before this were in the v0.x series and didn't have embedded
// version numbers
static const uint32_t V1_0_VERSION = 0x00000100;
~RDCFile();
// opens an existing file for read and/or modification. Error if file doesn't exist
@@ -70,6 +74,7 @@ public:
void Create(const char *filename);
ContainerError ErrorCode() const { return m_Error; }
std::string ErrorString() const { return m_ErrorString; }
RDCDriver GetDriver() const { return m_Driver; }
const std::string &GetDriverName() const { return m_DriverName; }
uint64_t GetMachineIdent() const { return m_MachineIdent; }
@@ -102,6 +107,7 @@ private:
RDCThumb m_Thumb;
ContainerError m_Error = ContainerError::NoError;
std::string m_ErrorString;
struct SectionLocation
{
+1 -1
View File
@@ -198,7 +198,7 @@ void Serialiser<SerialiserMode::Reading>::EndChunk()
// only skip remaining bytes if we have a valid length - if we have a length of 0 we wrote this
// chunk in 'streaming mode' (see SetStreamingMode and the Writing EndChunk() impl) so there's
// nothing to skip.
if(m_ChunkMetadata.length > 0)
if(m_ChunkMetadata.length > 0 && !m_Read->IsErrored())
{
// this will be a no-op if the last chunk length was accurate. If it was a
// conservative estimate of the length then we'll skip some padding bytes
+74
View File
@@ -146,6 +146,14 @@ public:
uint32_t BeginChunk(uint32_t chunkID, uint32_t byteLength);
void EndChunk();
std::string GetCurChunkName()
{
if(m_ChunkLookup)
return m_ChunkLookup(m_ChunkMetadata.chunkID);
return StringFormat::Fmt("<No Chunk Lookup: %u>", m_ChunkMetadata.chunkID);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
@@ -213,7 +221,10 @@ public:
}
if(IsReading())
{
VerifyArraySize(count);
byteSize = count;
}
if(ExportStructure())
{
@@ -343,6 +354,11 @@ public:
m_InternalElement = false;
}
if(IsReading())
{
VerifyArraySize(count);
}
if(ExportStructure())
{
if(m_StructureStack.empty())
@@ -411,6 +427,11 @@ public:
m_InternalElement = false;
}
if(IsReading())
{
VerifyArraySize(count);
}
if(ExportStructure())
{
if(m_StructureStack.empty())
@@ -651,7 +672,10 @@ public:
}
if(IsReading())
{
VerifyArraySize(count);
arrayCount = count;
}
if(ExportStructure())
{
@@ -750,6 +774,11 @@ public:
m_InternalElement = false;
}
if(IsReading())
{
VerifyArraySize(size);
}
if(ExportStructure())
{
if(m_StructureStack.empty())
@@ -815,6 +844,11 @@ public:
m_InternalElement = false;
}
if(IsReading())
{
VerifyArraySize(size);
}
if(ExportStructure())
{
if(m_StructureStack.empty())
@@ -948,6 +982,11 @@ public:
m_InternalElement = false;
}
if(IsReading())
{
VerifyArraySize(size);
}
if(ExportStructure())
{
if(m_StructureStack.empty())
@@ -1516,6 +1555,32 @@ private:
}
};
void VerifyArraySize(uint64_t &count)
{
uint64_t size = m_Read->GetSize();
// for streaming, just take 4GB as a 'semi reasonable' upper limit for array sizes
if(m_DataStreaming)
size = 0xFFFFFFFFU;
if(count > size)
{
RDCERR("Reading invalid array or byte buffer - %llu larger than total stream size %llu.",
count, size);
// if we owned the previous stream, delete it
if(m_Ownership == Ownership::Stream)
delete m_Read;
// replace our stream with an invalid one so all subsequent reads fail
m_Read = new StreamReader(StreamReader::InvalidStream);
m_Ownership = Ownership::Stream;
// set the count to 0
count = 0;
}
}
void *m_pUserData = NULL;
StreamWriter *m_Write = NULL;
@@ -1907,3 +1972,12 @@ struct ScopedDeserialiseArray<SerialiserType, byte *>
// a member that is a pointer and could be NULL, so needs a hidden 'present'
// flag serialised out
#define SERIALISE_MEMBER_OPT(obj) ser.SerialiseNullable(#obj, el.obj)
// simple utility function for inside serialise functions, to check if the serialiser has hit an
// error and then bail, without trying to replay anything.
#define SERIALISE_CHECK_READ_ERRORS() \
if(ser.IsReading() && ser.IsErrored()) \
{ \
RDCERR("Serialisation failed in '%s'.", ser.GetCurChunkName().c_str()); \
return false; \
}
+4
View File
@@ -68,6 +68,8 @@ StreamReader::StreamReader(StreamInvalidType)
m_BufferHead = m_BufferBase = NULL;
m_Ownership = Ownership::Nothing;
m_HasError = true;
}
StreamReader::StreamReader(Network::Socket *sock, Ownership own)
@@ -336,6 +338,8 @@ StreamWriter::StreamWriter(StreamInvalidType)
m_Ownership = Ownership::Nothing;
m_InMemory = false;
m_HasError = true;
}
StreamWriter::StreamWriter(Network::Socket *sock, Ownership own)