summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkaizhangNV <149626564+kaizhangNV@users.noreply.github.com>2024-06-13 13:02:12 -0700
committerGitHub <noreply@github.com>2024-06-13 13:02:12 -0700
commitf0d40ad5e1d0a0dec39fe8a141d3f81d88fc576a (patch)
tree210a0a9b5bb2aea0e64776527c4ce04266709a43
parentecc6ecb3a25a28eb5e85cfdb2bf170448ab9a4e7 (diff)
capture/replay: implement infrastructure for capture (#4372)
* Define api call ID for each being captured methods * Add parameter encoder interface * Add outputStream and capture manager Add infrastructure for output stream This is the interface to record the method and parameter, and also provide functionality to write all the serialized data into file. Add capture manager: Capture manager is associated to global session object, it will provide the functionality to actual record all the APIs. Implement some of parameter encoder functions. * Fix some Windows & cmake build error * remove unused headers
-rw-r--r--build/visual-studio/slang/slang.vcxproj7
-rw-r--r--build/visual-studio/slang/slang.vcxproj.filters21
-rw-r--r--source/core/slang-stream.h5
-rw-r--r--source/slang-capture-replay/api_callId.h147
-rw-r--r--source/slang-capture-replay/capture-manager.cpp53
-rw-r--r--source/slang-capture-replay/capture-manager.h30
-rw-r--r--source/slang-capture-replay/output-stream.cpp52
-rw-r--r--source/slang-capture-replay/output-stream.h46
-rw-r--r--source/slang-capture-replay/parameter-encoder.cpp72
-rw-r--r--source/slang-capture-replay/parameter-encoder.h51
-rw-r--r--source/slang-capture-replay/slang-global-session.cpp13
-rw-r--r--source/slang-capture-replay/slang-global-session.h13
12 files changed, 507 insertions, 3 deletions
diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj
index 826ac3e38..c0f61ffb5 100644
--- a/build/visual-studio/slang/slang.vcxproj
+++ b/build/visual-studio/slang/slang.vcxproj
@@ -308,7 +308,11 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\..\..\slang.h" />
+ <ClInclude Include="..\..\..\source\slang-capture-replay\api_callId.h" />
+ <ClInclude Include="..\..\..\source\slang-capture-replay\capture-manager.h" />
<ClInclude Include="..\..\..\source\slang-capture-replay\capture_utility.h" />
+ <ClInclude Include="..\..\..\source\slang-capture-replay\output-stream.h" />
+ <ClInclude Include="..\..\..\source\slang-capture-replay\parameter-encoder.h" />
<ClInclude Include="..\..\..\source\slang-capture-replay\slang-composite-component-type.h" />
<ClInclude Include="..\..\..\source\slang-capture-replay\slang-entrypoint.h" />
<ClInclude Include="..\..\..\source\slang-capture-replay\slang-filesystem.h" />
@@ -546,7 +550,10 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
<ClCompile Include="..\..\..\prelude\slang-cuda-prelude.h.cpp" />
<ClCompile Include="..\..\..\prelude\slang-hlsl-prelude.h.cpp" />
<ClCompile Include="..\..\..\prelude\slang-torch-prelude.h.cpp" />
+ <ClCompile Include="..\..\..\source\slang-capture-replay\capture-manager.cpp" />
<ClCompile Include="..\..\..\source\slang-capture-replay\capture_utility.cpp" />
+ <ClCompile Include="..\..\..\source\slang-capture-replay\output-stream.cpp" />
+ <ClCompile Include="..\..\..\source\slang-capture-replay\parameter-encoder.cpp" />
<ClCompile Include="..\..\..\source\slang-capture-replay\slang-composite-component-type.cpp" />
<ClCompile Include="..\..\..\source\slang-capture-replay\slang-entrypoint.cpp" />
<ClCompile Include="..\..\..\source\slang-capture-replay\slang-filesystem.cpp" />
diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters
index 59fdc4048..601de8b63 100644
--- a/build/visual-studio/slang/slang.vcxproj.filters
+++ b/build/visual-studio/slang/slang.vcxproj.filters
@@ -12,9 +12,21 @@
<ClInclude Include="..\..\..\slang.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\slang-capture-replay\api_callId.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\source\slang-capture-replay\capture-manager.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\slang-capture-replay\capture_utility.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\slang-capture-replay\output-stream.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\source\slang-capture-replay\parameter-encoder.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\slang-capture-replay\slang-composite-component-type.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -722,9 +734,18 @@
<ClCompile Include="..\..\..\prelude\slang-torch-prelude.h.cpp">
<Filter>Header Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\slang-capture-replay\capture-manager.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\slang-capture-replay\capture_utility.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\slang-capture-replay\output-stream.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\source\slang-capture-replay\parameter-encoder.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\slang-capture-replay\slang-composite-component-type.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/source/core/slang-stream.h b/source/core/slang-stream.h
index 9f405dbb0..a62c18838 100644
--- a/source/core/slang-stream.h
+++ b/source/core/slang-stream.h
@@ -138,7 +138,10 @@ public:
void setContent(const void* contents, size_t contentsSize)
{
m_ownedContents.setCount(contentsSize);
- ::memcpy(m_ownedContents.getBuffer(), contents, contentsSize);
+ if (contentsSize > 0)
+ {
+ ::memcpy(m_ownedContents.getBuffer(), contents, contentsSize);
+ }
_setContents(m_ownedContents.getBuffer(), m_ownedContents.getCount());
}
diff --git a/source/slang-capture-replay/api_callId.h b/source/slang-capture-replay/api_callId.h
new file mode 100644
index 000000000..0a96ef4a7
--- /dev/null
+++ b/source/slang-capture-replay/api_callId.h
@@ -0,0 +1,147 @@
+#ifndef API_CALL_ID_H
+#define API_CALL_ID_H
+
+#include <cstdint>
+
+namespace SlangCapture
+{
+ constexpr uint32_t makeApiCallId(uint16_t classId, uint16_t memberFunctionId)
+ {
+ return ((static_cast<uint32_t>(classId) << 16) & 0xffff0000) | (static_cast<uint32_t>(memberFunctionId) & 0x0000ffff);
+ }
+
+ constexpr uint16_t getClassId(uint32_t callId)
+ {
+ return static_cast<uint16_t>((callId >> 16) & 0x0000ffff);
+ }
+
+ constexpr uint16_t getMemberFunctionId(uint32_t callId)
+ {
+ return static_cast<uint16_t>(callId & 0x0000ffff);
+ }
+
+ enum ApiClassId : uint16_t
+ {
+ GlobalFunction = 1,
+ Class_IGlobalSession = 2,
+ Class_ISession = 3,
+ Class_IModule = 4,
+ Class_IEntryPoint = 5,
+ Class_ICompositeComponentType = 6,
+ Class_ITypeConformance = 7,
+ };
+
+ typedef uint64_t AddressFormat;
+
+ constexpr uint64_t g_globalFunctionHandle = 0;
+
+ enum ApiCallId : uint32_t
+ {
+ InvalidCallId = 0x00000000,
+ ICreateGlobalSession = makeApiCallId(GlobalFunction, 0x0000),
+ IGlobalSession_createSession = makeApiCallId(Class_IGlobalSession, 0x0001),
+ IGlobalSession_findProfile = makeApiCallId(Class_IGlobalSession, 0x0002),
+ IGlobalSession_setDownstreamCompilerPath = makeApiCallId(Class_IGlobalSession, 0x0003),
+ IGlobalSession_setDownstreamCompilerPrelude = makeApiCallId(Class_IGlobalSession, 0x0004),
+ IGlobalSession_getDownstreamCompilerPrelude = makeApiCallId(Class_IGlobalSession, 0x0005),
+ IGlobalSession_getBuildTagString = makeApiCallId(Class_IGlobalSession, 0x0006),
+ IGlobalSession_setDefaultDownstreamCompiler = makeApiCallId(Class_IGlobalSession, 0x0007),
+ IGlobalSession_getDefaultDownstreamCompiler = makeApiCallId(Class_IGlobalSession, 0x0008),
+ IGlobalSession_setLanguagePrelude = makeApiCallId(Class_IGlobalSession, 0x0009),
+ IGlobalSession_getLanguagePrelude = makeApiCallId(Class_IGlobalSession, 0x000A),
+ IGlobalSession_createCompileRequest = makeApiCallId(Class_IGlobalSession, 0x000B),
+ IGlobalSession_addBuiltins = makeApiCallId(Class_IGlobalSession, 0x000C),
+ IGlobalSession_setSharedLibraryLoader = makeApiCallId(Class_IGlobalSession, 0x000D),
+ IGlobalSession_getSharedLibraryLoader = makeApiCallId(Class_IGlobalSession, 0x000E),
+ IGlobalSession_checkCompileTargetSupport = makeApiCallId(Class_IGlobalSession, 0x000F),
+ IGlobalSession_checkPassThroughSupport = makeApiCallId(Class_IGlobalSession, 0x0010),
+ IGlobalSession_compileStdLib = makeApiCallId(Class_IGlobalSession, 0x0011),
+ IGlobalSession_loadStdLib = makeApiCallId(Class_IGlobalSession, 0x0012),
+ IGlobalSession_saveStdLib = makeApiCallId(Class_IGlobalSession, 0x0013),
+ IGlobalSession_findCapability = makeApiCallId(Class_IGlobalSession, 0x0014),
+ IGlobalSession_setDownstreamCompilerForTransition = makeApiCallId(Class_IGlobalSession, 0x0015),
+ IGlobalSession_getDownstreamCompilerForTransition = makeApiCallId(Class_IGlobalSession, 0x0016),
+ IGlobalSession_getCompilerElapsedTime = makeApiCallId(Class_IGlobalSession, 0x0017),
+ IGlobalSession_setSPIRVCoreGrammar = makeApiCallId(Class_IGlobalSession, 0x0018),
+ IGlobalSession_parseCommandLineArguments = makeApiCallId(Class_IGlobalSession, 0x0019),
+ IGlobalSession_getSessionDescDigest = makeApiCallId(Class_IGlobalSession, 0x001A),
+
+ ISession_getGlobalSession = makeApiCallId(Class_ISession, 0x0001),
+ ISession_loadModule = makeApiCallId(Class_ISession, 0x0002),
+ ISession_loadModuleFromBlob = makeApiCallId(Class_ISession, 0x0003),
+ ISession_loadModuleFromIRBlob = makeApiCallId(Class_ISession, 0x0004),
+ ISession_loadModuleFromSource = makeApiCallId(Class_ISession, 0x0005),
+ ISession_loadModuleFromSourceString = makeApiCallId(Class_ISession, 0x0006),
+ ISession_createCompositeComponentType = makeApiCallId(Class_ISession, 0x0007),
+ ISession_specializeType = makeApiCallId(Class_ISession, 0x0008),
+ ISession_getTypeLayout = makeApiCallId(Class_ISession, 0x0009),
+ ISession_getContainerType = makeApiCallId(Class_ISession, 0x000A),
+ ISession_getDynamicType = makeApiCallId(Class_ISession, 0x000B),
+ ISession_getTypeRTTIMangledName = makeApiCallId(Class_ISession, 0x000C),
+ ISession_getTypeConformanceWitnessMangledName = makeApiCallId(Class_ISession, 0x000D),
+ ISession_getTypeConformanceWitnessSequentialID = makeApiCallId(Class_ISession, 0x000E),
+ ISession_createTypeConformanceComponentType = makeApiCallId(Class_ISession, 0x000F),
+ ISession_createCompileRequest = makeApiCallId(Class_ISession, 0x0010),
+ ISession_getLoadedModuleCount = makeApiCallId(Class_ISession, 0x0011),
+ ISession_getLoadedModule = makeApiCallId(Class_ISession, 0x0012),
+ ISession_isBinaryModuleUpToDate = makeApiCallId(Class_ISession, 0x0013),
+
+ IModule_findEntryPointByName = makeApiCallId(Class_IModule, 0x0001),
+ IModule_getDefinedEntryPointCount = makeApiCallId(Class_IModule, 0x0002),
+ IModule_getDefinedEntryPoint = makeApiCallId(Class_IModule, 0x0003),
+ IModule_serialize = makeApiCallId(Class_IModule, 0x0004),
+ IModule_writeToFile = makeApiCallId(Class_IModule, 0x0005),
+ IModule_getName = makeApiCallId(Class_IModule, 0x0006),
+ IModule_getFilePath = makeApiCallId(Class_IModule, 0x0007),
+ IModule_getUniqueIdentity = makeApiCallId(Class_IModule, 0x0008),
+ IModule_findAndCheckEntryPoint = makeApiCallId(Class_IModule, 0x0009),
+ IModule_getSession = makeApiCallId(Class_IModule, 0x000A),
+ IModule_getLayout = makeApiCallId(Class_IModule, 0x000B),
+ IModule_getSpecializationParamCount = makeApiCallId(Class_IModule, 0x000C),
+ IModule_getEntryPointCode = makeApiCallId(Class_IModule, 0x000D),
+ IModule_getResultAsFileSystem = makeApiCallId(Class_IModule, 0x000E),
+ IModule_getEntryPointHash = makeApiCallId(Class_IModule, 0x000F),
+ IModule_specialize = makeApiCallId(Class_IModule, 0x0010),
+ IModule_link = makeApiCallId(Class_IModule, 0x0011),
+ IModule_getEntryPointHostCallable = makeApiCallId(Class_IModule, 0x0012),
+ IModule_renameEntryPoint = makeApiCallId(Class_IModule, 0x0013),
+ IModule_linkWithOptions = makeApiCallId(Class_IModule, 0x0014),
+
+ IEntryPoint_getSession = makeApiCallId(Class_IEntryPoint, 0x0001),
+ IEntryPoint_getLayout = makeApiCallId(Class_IEntryPoint, 0x0002),
+ IEntryPoint_getSpecializationParamCount = makeApiCallId(Class_IEntryPoint, 0x0003),
+ IEntryPoint_getEntryPointCode = makeApiCallId(Class_IEntryPoint, 0x0004),
+ IEntryPoint_getResultAsFileSystem = makeApiCallId(Class_IEntryPoint, 0x0005),
+ IEntryPoint_getEntryPointHash = makeApiCallId(Class_IEntryPoint, 0x0006),
+ IEntryPoint_specialize = makeApiCallId(Class_IEntryPoint, 0x0007),
+ IEntryPoint_link = makeApiCallId(Class_IEntryPoint, 0x0008),
+ IEntryPoint_getEntryPointHostCallable = makeApiCallId(Class_IEntryPoint, 0x0009),
+ IEntryPoint_renameEntryPoint = makeApiCallId(Class_IEntryPoint, 0x000A),
+ IEntryPoint_linkWithOptions = makeApiCallId(Class_IEntryPoint, 0x000B),
+
+ ICompositeComponentType_getSession = makeApiCallId(Class_ICompositeComponentType, 0x0001),
+ ICompositeComponentType_getLayout = makeApiCallId(Class_ICompositeComponentType, 0x0002),
+ ICompositeComponentType_getSpecializationParamCount = makeApiCallId(Class_ICompositeComponentType, 0x0003),
+ ICompositeComponentType_getEntryPointCode = makeApiCallId(Class_ICompositeComponentType, 0x0004),
+ ICompositeComponentType_getResultAsFileSystem = makeApiCallId(Class_ICompositeComponentType, 0x0005),
+ ICompositeComponentType_getEntryPointHash = makeApiCallId(Class_ICompositeComponentType, 0x0006),
+ ICompositeComponentType_specialize = makeApiCallId(Class_ICompositeComponentType, 0x0007),
+ ICompositeComponentType_link = makeApiCallId(Class_ICompositeComponentType, 0x0008),
+ ICompositeComponentType_getEntryPointHostCallable = makeApiCallId(Class_ICompositeComponentType, 0x0009),
+ ICompositeComponentType_renameEntryPoint = makeApiCallId(Class_ICompositeComponentType, 0x000A),
+ ICompositeComponentType_linkWithOptions = makeApiCallId(Class_ICompositeComponentType, 0x000B),
+
+ ITypeConformance_getSession = makeApiCallId(Class_ITypeConformance, 0x0001),
+ ITypeConformance_getLayout = makeApiCallId(Class_ITypeConformance, 0x0002),
+ ITypeConformance_getSpecializationParamCount = makeApiCallId(Class_ITypeConformance, 0x0003),
+ ITypeConformance_getEntryPointCode = makeApiCallId(Class_ITypeConformance, 0x0004),
+ ITypeConformance_getResultAsFileSystem = makeApiCallId(Class_ITypeConformance, 0x0005),
+ ITypeConformance_getEntryPointHash = makeApiCallId(Class_ITypeConformance, 0x0006),
+ ITypeConformance_specialize = makeApiCallId(Class_ITypeConformance, 0x0007),
+ ITypeConformance_link = makeApiCallId(Class_ITypeConformance, 0x0008),
+ ITypeConformance_getEntryPointHostCallable = makeApiCallId(Class_ITypeConformance, 0x0009),
+ ITypeConformance_renameEntryPoint = makeApiCallId(Class_ITypeConformance, 0x000A),
+ ITypeConformance_linkWithOptions = makeApiCallId(Class_ITypeConformance, 0x000B)
+ };
+}
+#endif
diff --git a/source/slang-capture-replay/capture-manager.cpp b/source/slang-capture-replay/capture-manager.cpp
new file mode 100644
index 000000000..be5615e00
--- /dev/null
+++ b/source/slang-capture-replay/capture-manager.cpp
@@ -0,0 +1,53 @@
+
+#include <string>
+#include <sstream>
+#include <thread>
+#include "capture-manager.h"
+
+namespace SlangCapture
+{
+ CaptureManager::CaptureManager(uint64_t globalSessionHandle)
+ : m_encoder(&m_memoryStream)
+ {
+ std::stringstream ss;
+ ss << "gs-"<< globalSessionHandle <<"t-"<<std::this_thread::get_id() << ".cap";
+ m_fileStream = std::make_unique<FileOutputStream>(ss.str());
+ }
+
+ void CaptureManager::clearWithHeader(const ApiCallId& callId, uint64_t handleId)
+ {
+ m_memoryStream.flush();
+ FunctionHeader header {};
+ header.callId = callId;
+ header.handleId = handleId;
+
+ // write header to memory stream
+ m_memoryStream.write(&header, sizeof(FunctionHeader));
+ }
+
+ ParameterEncoder* CaptureManager::beginMethodCapture(const ApiCallId& callId, uint64_t handleId)
+ {
+ clearWithHeader(callId, handleId);
+ return &m_encoder;
+ }
+
+ void CaptureManager::endMethodCapture()
+ {
+ FunctionHeader* pHeader = const_cast<FunctionHeader*>(
+ reinterpret_cast<const FunctionHeader*>(m_memoryStream.getData()));
+
+ pHeader->dataSizeInBytes = m_memoryStream.getSizeInBytes() - sizeof(FunctionHeader);
+
+ std::hash<std::thread::id> hasher;
+ pHeader->threadId = hasher(std::this_thread::get_id());
+
+ // write capture data to file
+ m_fileStream->write(m_memoryStream.getData(), m_memoryStream.getSizeInBytes());
+
+ // take effect of the write
+ m_fileStream->flush();
+
+ // clear the memory stream
+ m_memoryStream.flush();
+ }
+}
diff --git a/source/slang-capture-replay/capture-manager.h b/source/slang-capture-replay/capture-manager.h
new file mode 100644
index 000000000..55d42c458
--- /dev/null
+++ b/source/slang-capture-replay/capture-manager.h
@@ -0,0 +1,30 @@
+#ifndef CAPTURE_MANAGER_H
+#define CAPTURE_MANAGER_H
+
+#include "parameter-encoder.h"
+#include "api_callId.h"
+namespace SlangCapture
+{
+ class CaptureManager
+ {
+ public:
+ CaptureManager(uint64_t globalSessionHandle);
+ ParameterEncoder* beginMethodCapture(const ApiCallId& callId, uint64_t handleId);
+ void endMethodCapture();
+ private:
+ void clearWithHeader(const ApiCallId& callId, uint64_t handleId);
+
+ struct FunctionHeader
+ {
+ ApiCallId callId {InvalidCallId};
+ uint64_t handleId {0};
+ uint64_t dataSizeInBytes {0};
+ uint64_t threadId {0};
+ };
+
+ MemoryStream m_memoryStream;
+ std::unique_ptr<FileOutputStream> m_fileStream;
+ ParameterEncoder m_encoder;
+ };
+} // namespace SlangCapture
+#endif // CAPTURE_MANAGER_H
diff --git a/source/slang-capture-replay/output-stream.cpp b/source/slang-capture-replay/output-stream.cpp
new file mode 100644
index 000000000..acde6fbf1
--- /dev/null
+++ b/source/slang-capture-replay/output-stream.cpp
@@ -0,0 +1,52 @@
+#include "output-stream.h"
+#include "capture_utility.h"
+
+namespace SlangCapture
+{
+ FileOutputStream::FileOutputStream(const std::string& filename, bool append)
+ {
+ Slang::String path(filename.c_str());
+ Slang::FileMode fileMode = append ? Slang::FileMode::Append : Slang::FileMode::Create;
+ Slang::FileAccess fileAccess = Slang::FileAccess::Write;
+ Slang::FileShare fileShare = Slang::FileShare::None;
+
+ SlangResult res = m_fileStream.init(path, fileMode, fileAccess, fileShare);
+
+ if (res != SLANG_OK)
+ {
+ SlangCapture::slangCaptureLog(SlangCapture::LogLevel::Error, "Failed to open file %s\n", filename.c_str());
+ std::abort();
+ }
+ }
+
+ FileOutputStream::~FileOutputStream()
+ {
+ m_fileStream.close();
+ }
+
+ void FileOutputStream::write(const void* data, size_t len)
+ {
+ SLANG_CAPTURE_ASSERT(m_fileStream.write(data, len));
+ }
+
+ MemoryStream::MemoryStream()
+ : m_memoryStream(Slang::FileAccess::Write)
+ { }
+
+ void FileOutputStream::flush()
+ {
+ SLANG_CAPTURE_ASSERT(m_fileStream.flush());
+ }
+
+ void MemoryStream::write(const void* data, size_t len)
+ {
+ SLANG_CAPTURE_ASSERT(m_memoryStream.write(data, len));
+ }
+
+ void MemoryStream::flush()
+ {
+ // This call will reset the underlying buffer to size 0,
+ // and reset the write position to 0.
+ m_memoryStream.setContent(nullptr, 0);
+ }
+}
diff --git a/source/slang-capture-replay/output-stream.h b/source/slang-capture-replay/output-stream.h
new file mode 100644
index 000000000..1f2c882b0
--- /dev/null
+++ b/source/slang-capture-replay/output-stream.h
@@ -0,0 +1,46 @@
+#ifndef OUTPUT_STREAM_H
+#define OUTPUT_STREAM_H
+
+#include <string>
+#include "../core/slang-stream.h"
+
+namespace SlangCapture
+{
+ class OutputStream
+ {
+ public:
+ virtual ~OutputStream() {}
+ virtual void write(const void* data, size_t len) = 0;
+ virtual void flush() {}
+ };
+
+ class FileOutputStream : public OutputStream
+ {
+ public:
+ FileOutputStream(const std::string& filename, bool append = false);
+ virtual ~FileOutputStream() override;
+ virtual void write(const void* data, size_t len) override;
+ virtual void flush() override;
+
+ private:
+ Slang::FileStream m_fileStream;
+ };
+
+ // The reason we inherit from OwnedMemoryStream instead of declaring it
+ // as a member is because OwnedMemoryStream lacks some of the functionality
+ // of operating on the underlying buffer directly.
+ class MemoryStream : public OutputStream
+ {
+ public:
+ MemoryStream();
+ virtual ~MemoryStream() { }
+ virtual void write(const void* data, size_t len) override;
+ virtual void flush() override;
+ const void* getData() { return m_memoryStream.getContents().getBuffer(); }
+ size_t getSizeInBytes() { return m_memoryStream.getContents().getCount(); }
+
+ private:
+ Slang::OwnedMemoryStream m_memoryStream;
+ };
+} // namespace SlangCapture
+#endif // OUTPUT_STREAM_H
diff --git a/source/slang-capture-replay/parameter-encoder.cpp b/source/slang-capture-replay/parameter-encoder.cpp
new file mode 100644
index 000000000..5167cc2ab
--- /dev/null
+++ b/source/slang-capture-replay/parameter-encoder.cpp
@@ -0,0 +1,72 @@
+#include "parameter-encoder.h"
+
+namespace SlangCapture
+{
+ void ParameterEncoder::encodeStruct(slang::SessionDesc const& desc)
+ {
+ encodeUint64(desc.structureSize);
+ encodeInt64(desc.targetCount);
+
+ for (SlangInt i = 0; i < desc.targetCount; i++)
+ {
+ encodeStruct(desc.targets[i]);
+ }
+
+ encodeUint32(desc.flags);
+ encodeUint32(desc.defaultMatrixLayoutMode);
+ encodeInt64(desc.searchPathCount);
+ for (SlangInt i = 0; i < desc.searchPathCount; i++)
+ {
+ encodeString(desc.searchPaths[i]);
+ }
+
+ encodeInt64(desc.preprocessorMacroCount);
+ for (SlangInt i = 0; i < desc.preprocessorMacroCount; i++)
+ {
+ encodeStruct(desc.preprocessorMacros[i]);
+ }
+
+ encodeBool(desc.enableEffectAnnotations);
+ encodeBool(desc.allowGLSLSyntax);
+
+ encodeUint32(desc.compilerOptionEntryCount);
+ for (uint32_t i = 0; i < desc.compilerOptionEntryCount; i++)
+ {
+ encodeStruct(desc.compilerOptionEntries[i]);
+ }
+ }
+
+ void ParameterEncoder::encodeStruct(slang::PreprocessorMacroDesc const& desc)
+ {
+ encodeString(desc.name);
+ encodeString(desc.value);
+ }
+
+ void ParameterEncoder::encodeStruct(slang::CompilerOptionEntry const& entry)
+ {
+ encodeInt32((int32_t)(entry.name));
+ encodeStruct(entry.value);
+ }
+
+ void ParameterEncoder::encodeStruct(slang::CompilerOptionValue const& value)
+ {
+ (void)value;
+ }
+
+ void ParameterEncoder::encodeStruct(slang::TargetDesc const& targetDesc)
+ {
+ (void)targetDesc;
+ }
+
+ void ParameterEncoder::encodePointer(const void* value, bool omitData, size_t size)
+ {
+ (void)value;
+ (void)omitData;
+ (void)size;
+ }
+ // first 4-bytes is the length of the string
+ void ParameterEncoder::encodeString(const char* value)
+ {
+ (void)value;
+ }
+}
diff --git a/source/slang-capture-replay/parameter-encoder.h b/source/slang-capture-replay/parameter-encoder.h
new file mode 100644
index 000000000..c7c2d959a
--- /dev/null
+++ b/source/slang-capture-replay/parameter-encoder.h
@@ -0,0 +1,51 @@
+#ifndef PARAMETER_ENCODER_H
+#define PARAMETER_ENCODER_H
+
+#include <cstdio>
+#include <cinttypes>
+#include <cstdint>
+
+#include "output-stream.h"
+
+namespace SlangCapture
+{
+ class ParameterEncoder
+ {
+ public:
+ ParameterEncoder(OutputStream* stream) : m_stream(stream) {};
+ void encodeInt8(int8_t value) { encodeValue(value); }
+ void encodeUint8(uint8_t value) { encodeValue(value); }
+ void encodeInt16(int16_t value) { encodeValue(value); }
+ void encodeUint16(uint16_t value) { encodeValue(value); }
+ void encodeInt32(int32_t value) { encodeValue(value); }
+ void encodeUint32(uint32_t value) { encodeValue(value); }
+ void encodeInt64(int64_t value) { encodeValue(value); }
+ void encodeUint64(uint64_t value) { encodeValue(value); }
+ void encodeFloat(float value) { encodeValue(value); }
+ void encodeDouble(double value) { encodeValue(value); }
+ void encodeBool(bool value) { encodeValue(value); }
+
+ template<typename T>
+ void encodeEnumValue(T value) { encodeValue(static_cast<uint32_t>(value)); }
+
+ void encodeString(const char* value);
+ void encodePointer(const void* value, bool omitData = false, size_t size = 0);
+ void encodeAddress(const void* value) { encodeValue(reinterpret_cast<uint64_t>(value)); }
+
+ void encodeStruct(slang::SessionDesc const& desc);
+ void encodeStruct(slang::PreprocessorMacroDesc const& desc);
+ void encodeStruct(slang::CompilerOptionEntry const& entry);
+ void encodeStruct(slang::CompilerOptionValue const& value);
+ void encodeStruct(slang::TargetDesc const& targetDesc);
+
+ private:
+ template <typename T>
+ void encodeValue(T value)
+ {
+ m_stream->write(&value, sizeof(T));
+ }
+ OutputStream* m_stream;
+ };
+} // namespace SlangCapture
+
+#endif // PARAMETER_ENCODER_H
diff --git a/source/slang-capture-replay/slang-global-session.cpp b/source/slang-capture-replay/slang-global-session.cpp
index 05a2b93ec..45ea0b58d 100644
--- a/source/slang-capture-replay/slang-global-session.cpp
+++ b/source/slang-capture-replay/slang-global-session.cpp
@@ -1,5 +1,3 @@
-
-#include <vector>
#include "slang-global-session.h"
#include "slang-session.h"
#include "slang-filesystem.h"
@@ -8,10 +6,21 @@
namespace SlangCapture
{
+ // constructor is called in slang_createGlobalSession
GlobalSessionCapture::GlobalSessionCapture(slang::IGlobalSession* session):
m_actualGlobalSession(session)
{
SLANG_CAPTURE_ASSERT(m_actualGlobalSession != nullptr);
+
+ m_thisHandle = reinterpret_cast<SlangCapture::AddressFormat>(this);
+ m_captureManager = std::make_unique<CaptureManager>(m_thisHandle);
+
+ // We will use the address of the global session as the filename for the capture manager
+ // to make it unique for each global session.
+ // capture slang::createGlobalSession
+ ParameterEncoder* encoder = m_captureManager->beginMethodCapture(ApiCallId::ICreateGlobalSession, g_globalFunctionHandle);
+ encoder->encodeAddress(m_actualGlobalSession);
+ m_captureManager->endMethodCapture();
}
GlobalSessionCapture::~GlobalSessionCapture()
diff --git a/source/slang-capture-replay/slang-global-session.h b/source/slang-capture-replay/slang-global-session.h
index 52260ba21..6923ddf11 100644
--- a/source/slang-capture-replay/slang-global-session.h
+++ b/source/slang-capture-replay/slang-global-session.h
@@ -5,6 +5,7 @@
#include "../../slang.h"
#include "../../slang-com-helper.h"
#include "../core/slang-smart-pointer.h"
+#include "capture-manager.h"
namespace SlangCapture
{
@@ -58,6 +59,8 @@ namespace SlangCapture
SLANG_NO_THROW SlangResult SLANG_MCALL getSessionDescDigest(slang::SessionDesc* sessionDesc, ISlangBlob** outBlob) override;
+ CaptureManager* getCaptureManager() { return m_captureManager.get(); }
+
private:
SLANG_FORCE_INLINE slang::IGlobalSession* asExternal(GlobalSessionCapture* session)
{
@@ -65,6 +68,16 @@ namespace SlangCapture
}
Slang::ComPtr<slang::IGlobalSession> m_actualGlobalSession;
+
+ // we will create one capture file per IGlobalSession.
+ // We don't try to reproduce the user application's threading model, because it requires lots of effort and it's not necessary.
+ // Instead, we record all the compilation jobs associated with the session in the same capture file, so that during replay,
+ // those jobs will be executed sequentially. This might violate the user application's threading model, because those jobs might
+ // be executed in different threads. But it's not a big problem, because slang doesn't allow multiple threads to access the same
+ // session at the same time. So even if there is one session used by multiple threads, those threads will execute the compile jobs
+ // sequentially.
+ std::unique_ptr<CaptureManager> m_captureManager;
+ uint64_t m_thisHandle = 0;
};
} // namespace Slang