diff options
| author | kaizhangNV <149626564+kaizhangNV@users.noreply.github.com> | 2024-07-23 10:45:26 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-23 08:45:26 -0700 |
| commit | 986256ffb92ab7c8fc7cf9f2c424919a439a824f (patch) | |
| tree | 260e37bd439275e3398d16fe238b20cd00d08cb7 /source/slang-record-replay/record | |
| parent | c28d8b6aec721fa3350fc52647f1572a353f6151 (diff) | |
Feature/capture (#4625)
* Add decoder
* Add a replay executable to consume the decoded content
Add file-processor.cpp/h where we implement the logic to process
the captured file block by block. Each block is:
function header + parameter buffer + function tailer + function
output[optional].
After reading one block, the block of data is sent to decoder module
to dispatch the corresponding API.
Add slang-decoder.cpp/h where we implement the logic to dispatch
the slang API according to the input block data.
- Rename api_callId.h to capture-format.h
- Renmae capture_utility.cpp to capture-utility.cpp
- Renmae capture_utility.h to capture-utility.h
- Change the #include file name accordingly.
* Reorganize source files structure
Move all the capture logic code into `capture` directory.
- the capture code will be build with slang dll.
Move all the replay logic code into `relay` directoy.
- the replay code is not part of slang dll, it will be built
as a stand alone binary and link against slang dll.
Change the #include file names accordingly.
Add tools/slang-replay/main.cpp for the slang-replay stand alone
binary place holder. Will implement it later.
Update premake5.lua accordingly.
* Update cmake files
Update cmake files to change the build process for
capture and relay system.
- capture component should be build with slang dll, so we
should not include replay component.
- replay component should be a separate executable tool, which
should not include capture component.
- In order to easy use our current cmake infrastructure, move
the shared files to a `util` folder
- change the header include path
* Redesgin the interfaces of consumers
Fix some issues in capture
Finish implementing all slang-decoder functions
* Fix the AppleClang build issue
* Address few comments
- Fix the weird indent issues.
- Correct the function name for CreateGlobalSession()
- Rename file-processor to captureFile-processor to be more specific.
- Use Slang::List instead of std::vector
* record/replay: name refactor change
Refactor the naming.
Change the name "encoder/capture" to "record".
Diffstat (limited to 'source/slang-record-replay/record')
20 files changed, 3566 insertions, 0 deletions
diff --git a/source/slang-record-replay/record/output-stream.cpp b/source/slang-record-replay/record/output-stream.cpp new file mode 100644 index 000000000..8575d19d0 --- /dev/null +++ b/source/slang-record-replay/record/output-stream.cpp @@ -0,0 +1,52 @@ +#include "output-stream.h" +#include "../util/record-utility.h" + +namespace SlangRecord +{ + 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) + { + SlangRecord::slangRecordLog(SlangRecord::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_RECORD_CHECK(m_fileStream.write(data, len)); + } + + MemoryStream::MemoryStream() + : m_memoryStream(Slang::FileAccess::Write) + { } + + void FileOutputStream::flush() + { + SLANG_RECORD_CHECK(m_fileStream.flush()); + } + + void MemoryStream::write(const void* data, size_t len) + { + SLANG_RECORD_CHECK(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-record-replay/record/output-stream.h b/source/slang-record-replay/record/output-stream.h new file mode 100644 index 000000000..770c2aeac --- /dev/null +++ b/source/slang-record-replay/record/output-stream.h @@ -0,0 +1,46 @@ +#ifndef OUTPUT_STREAM_H +#define OUTPUT_STREAM_H + +#include <string> +#include "../../core/slang-stream.h" + +namespace SlangRecord +{ + 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 SlangRecord +#endif // OUTPUT_STREAM_H diff --git a/source/slang-record-replay/record/parameter-recorder.cpp b/source/slang-record-replay/record/parameter-recorder.cpp new file mode 100644 index 000000000..1c2fd9609 --- /dev/null +++ b/source/slang-record-replay/record/parameter-recorder.cpp @@ -0,0 +1,123 @@ +#include "parameter-recorder.h" + +namespace SlangRecord +{ + void ParameterRecorder::recordStruct(slang::SessionDesc const& desc) + { + recordUint64(desc.structureSize); + recordInt64(desc.targetCount); + + for (SlangInt i = 0; i < desc.targetCount; i++) + { + recordStruct(desc.targets[i]); + } + + recordUint32(desc.flags); + recordEnumValue(desc.defaultMatrixLayoutMode); + recordInt64(desc.searchPathCount); + for (SlangInt i = 0; i < desc.searchPathCount; i++) + { + recordString(desc.searchPaths[i]); + } + + recordInt64(desc.preprocessorMacroCount); + for (SlangInt i = 0; i < desc.preprocessorMacroCount; i++) + { + recordStruct(desc.preprocessorMacros[i]); + } + + recordBool(desc.enableEffectAnnotations); + recordBool(desc.allowGLSLSyntax); + + recordUint32(desc.compilerOptionEntryCount); + for (uint32_t i = 0; i < desc.compilerOptionEntryCount; i++) + { + recordStruct(desc.compilerOptionEntries[i]); + } + } + + void ParameterRecorder::recordStruct(slang::PreprocessorMacroDesc const& desc) + { + recordString(desc.name); + recordString(desc.value); + } + + void ParameterRecorder::recordStruct(slang::CompilerOptionEntry const& entry) + { + recordEnumValue(entry.name); + recordStruct(entry.value); + } + + void ParameterRecorder::recordStruct(slang::CompilerOptionValue const& value) + { + recordEnumValue(value.kind); + recordInt32(value.intValue0); + recordString(value.stringValue0); + recordString(value.stringValue1); + } + + void ParameterRecorder::recordStruct(slang::TargetDesc const& targetDesc) + { + recordUint64(targetDesc.structureSize); + recordEnumValue(targetDesc.format); + recordEnumValue(targetDesc.profile); + recordEnumValue(targetDesc.flags); + recordEnumValue(targetDesc.floatingPointMode); + recordEnumValue(targetDesc.lineDirectiveMode); + recordBool(targetDesc.forceGLSLScalarBufferLayout); + recordUint32(targetDesc.compilerOptionEntryCount); + for (uint32_t i = 0; i < targetDesc.compilerOptionEntryCount; i++) + { + recordStruct(targetDesc.compilerOptionEntries[i]); + } + } + + void ParameterRecorder::recordStruct(slang::SpecializationArg const& specializationArg) + { + recordEnumValue(specializationArg.kind); + recordAddress(specializationArg.type); + } + + void ParameterRecorder::recordPointer(const void* value, bool omitData, size_t size) + { + recordAddress(value); + if (omitData) + { + recordUint64(0llu); + return; + } + + recordUint64(size); + if (size) + { + m_stream->write(value, size); + } + } + + void ParameterRecorder::recordPointer(ISlangBlob* blob) + { + recordAddress(static_cast<const void*>(blob)); + + if (blob) + { + size_t size = blob->getBufferSize(); + const void* buffer = blob->getBufferPointer(); + recordPointer(buffer, false, size); + } + } + + // first 4-bytes is the length of the string + void ParameterRecorder::recordString(const char* value) + { + if (value == nullptr) + { + recordUint32(0); + } + else + { + uint32_t size = (uint32_t)strlen(value); + recordUint32(size); + m_stream->write(value, size); + } + } +} diff --git a/source/slang-record-replay/record/parameter-recorder.h b/source/slang-record-replay/record/parameter-recorder.h new file mode 100644 index 000000000..29bf39fb3 --- /dev/null +++ b/source/slang-record-replay/record/parameter-recorder.h @@ -0,0 +1,94 @@ +#ifndef PARAMETER_ENCODER_H +#define PARAMETER_ENCODER_H + +#include <cstdio> +#include <cinttypes> +#include <cstdint> + +#include "output-stream.h" +#include "../util/record-format.h" + +namespace SlangRecord +{ + class ParameterRecorder + { + public: + ParameterRecorder(OutputStream* stream) : m_stream(stream) {}; + void recordInt8(int8_t value) { recordValue(value); } + void recordUint8(uint8_t value) { recordValue(value); } + void recordInt16(int16_t value) { recordValue(value); } + void recordUint16(uint16_t value) { recordValue(value); } + void recordInt32(int32_t value) { recordValue(value); } + void recordUint32(uint32_t value) { recordValue(value); } + void recordInt64(int64_t value) { recordValue(value); } + void recordUint64(uint64_t value) { recordValue(value); } + void recordFloat(float value) { recordValue(value); } + void recordDouble(double value) { recordValue(value); } + void recordBool(bool value) { recordValue(value); } + + template<typename T> + void recordEnumValue(T value) { recordValue(static_cast<uint32_t>(value)); } + + void recordString(const char* value); + void recordPointer(const void* value, bool omitData = false, size_t size = 0); + void recordPointer(ISlangBlob* blob); + void recordAddress(const void* value) { recordValue(reinterpret_cast<SlangRecord::AddressFormat>(value)); } + + void recordStruct(slang::SessionDesc const& desc); + void recordStruct(slang::PreprocessorMacroDesc const& desc); + void recordStruct(slang::CompilerOptionEntry const& entry); + void recordStruct(slang::CompilerOptionValue const& value); + void recordStruct(slang::TargetDesc const& targetDesc); + void recordStruct(slang::SpecializationArg const& specializationArg); + + template <typename T> + void recordValueArray(const T* array, size_t count) + { + recordUint32((uint32_t)count); + for (size_t i = 0; i < count; ++i) + { + recordValue(array[i]); + } + } + + void recordStringArray(const char* const* array, size_t count) + { + recordUint32((uint32_t)count); + for (size_t i = 0; i < count; ++i) + { + recordString(array[i]); + } + } + + template <typename T> + void recordStructArray(T const* array, size_t count) + { + recordUint32((uint32_t)count); + for (size_t i = 0; i < count; ++i) + { + recordStruct(array[i]); + } + } + + template <typename T> + void recordAddressArray(T* const* array, size_t count) + { + recordUint32((uint32_t)count); + for (size_t i = 0; i < count; ++i) + { + recordAddress(array[i]); + } + } + + + private: + template <typename T> + void recordValue(T value) + { + m_stream->write(&value, sizeof(T)); + } + OutputStream* m_stream; + }; +} // namespace SlangRecord + +#endif // PARAMETER_ENCODER_H diff --git a/source/slang-record-replay/record/record-manager.cpp b/source/slang-record-replay/record/record-manager.cpp new file mode 100644 index 000000000..f10d2c704 --- /dev/null +++ b/source/slang-record-replay/record/record-manager.cpp @@ -0,0 +1,97 @@ + +#include <string> +#include <sstream> +#include <thread> +#include "../util/record-utility.h" +#include "record-manager.h" + +namespace SlangRecord +{ + RecordManager::RecordManager(uint64_t globalSessionHandle) + : m_recorder(&m_memoryStream) + { + std::stringstream ss; + ss << "gs-"<< globalSessionHandle <<"-t-"<<std::this_thread::get_id() << ".cap"; + + m_recordFileDirectory = m_recordFileDirectory / "slang-record"; + + if (!std::filesystem::exists(m_recordFileDirectory)) + { + std::error_code ec; + if (!std::filesystem::create_directory(m_recordFileDirectory, ec)) + { + slangRecordLog(LogLevel::Error, "Fail to create directory: %s, error (%d): %s\n", + m_recordFileDirectory.string().c_str(), ec.value(), ec.message().c_str()); + } + } + + std::filesystem::path recordFilePath = m_recordFileDirectory / ss.str(); + m_fileStream = std::make_unique<FileOutputStream>(recordFilePath.string()); + } + + void RecordManager::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)); + } + + void RecordManager::clearWithTailer() + { + m_memoryStream.flush(); + FunctionTailer tailer; + + // write header to memory stream + m_memoryStream.write(&tailer, sizeof(FunctionTailer)); + } + + ParameterRecorder* RecordManager::beginMethodRecord(const ApiCallId& callId, uint64_t handleId) + { + clearWithHeader(callId, handleId); + return &m_recorder; + } + + ParameterRecorder* RecordManager::endMethodRecord() + { + 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 record 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(); + + clearWithTailer(); + return &m_recorder; + } + + void RecordManager::endMethodRecordAppendOutput() + { + FunctionTailer* pTailer = const_cast<FunctionTailer*>( + reinterpret_cast<const FunctionTailer*>(m_memoryStream.getData())); + + pTailer->dataSizeInBytes = (uint32_t)(m_memoryStream.getSizeInBytes() - sizeof(FunctionTailer)); + + // write record 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-record-replay/record/record-manager.h b/source/slang-record-replay/record/record-manager.h new file mode 100644 index 000000000..40992e8ad --- /dev/null +++ b/source/slang-record-replay/record/record-manager.h @@ -0,0 +1,35 @@ +#ifndef RECORD_MANAGER_H +#define RECORD_MANAGER_H + +#include <filesystem> +#include "parameter-recorder.h" +#include "../util/record-format.h" + +namespace SlangRecord +{ + class RecordManager + { + public: + RecordManager(uint64_t globalSessionHandle); + + // Each method record has to start with a FunctionHeader + ParameterRecorder* beginMethodRecord(const ApiCallId& callId, uint64_t handleId); + ParameterRecorder* endMethodRecord(); + + // endMethodRecordAppendOutput is an optional call that can be used to append output to + // the end of the record. It has to start with a FunctionTailer + void endMethodRecordAppendOutput(); + + std::filesystem::path const& getRecordFileDirectory() const { return m_recordFileDirectory; } + + private: + void clearWithHeader(const ApiCallId& callId, uint64_t handleId); + void clearWithTailer(); + + MemoryStream m_memoryStream; + std::unique_ptr<FileOutputStream> m_fileStream; + std::filesystem::path m_recordFileDirectory = std::filesystem::current_path(); + ParameterRecorder m_recorder; + }; +} // namespace SlangRecord +#endif // RECORD_MANAGER_H diff --git a/source/slang-record-replay/record/slang-composite-component-type.cpp b/source/slang-record-replay/record/slang-composite-component-type.cpp new file mode 100644 index 000000000..ef594a893 --- /dev/null +++ b/source/slang-record-replay/record/slang-composite-component-type.cpp @@ -0,0 +1,309 @@ +#include "../util/record-utility.h" +#include "slang-composite-component-type.h" + +namespace SlangRecord +{ + CompositeComponentTypeRecorder::CompositeComponentTypeRecorder( + slang::IComponentType* componentType, RecordManager* recordManager) + : m_actualCompositeComponentType(componentType), + m_recordManager(recordManager) + { + SLANG_RECORD_ASSERT(m_actualCompositeComponentType != nullptr); + SLANG_RECORD_ASSERT(m_recordManager != nullptr); + + m_compositeComponentHandle = reinterpret_cast<uint64_t>(m_actualCompositeComponentType.get()); + slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, componentType); + } + + CompositeComponentTypeRecorder::~CompositeComponentTypeRecorder() + { + m_actualCompositeComponentType->release(); + } + + ISlangUnknown* CompositeComponentTypeRecorder::getInterface(const Guid& guid) + { + if (guid == IComponentType::getTypeGuid()) + { + return static_cast<ISlangUnknown*>(this); + } + return nullptr; + } + + SLANG_NO_THROW slang::ISession* CompositeComponentTypeRecorder::getSession() + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getSession, m_compositeComponentHandle); + recorder = m_recordManager->endMethodRecord(); + } + + slang::ISession* res = m_actualCompositeComponentType->getSession(); + + { + recorder->recordAddress(res); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW slang::ProgramLayout* CompositeComponentTypeRecorder::getLayout( + SlangInt targetIndex, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getLayout, m_compositeComponentHandle); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + slang::ProgramLayout* programLayout = m_actualCompositeComponentType->getLayout(targetIndex, outDiagnostics); + + { + recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(programLayout); + m_recordManager->endMethodRecordAppendOutput(); + } + + return programLayout; + } + + SLANG_NO_THROW SlangInt CompositeComponentTypeRecorder::getSpecializationParamCount() + { + // No need to record this call as it is just a query. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + SlangInt res = m_actualCompositeComponentType->getSpecializationParamCount(); + return res; + } + + SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::getEntryPointCode( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getEntryPointCode, m_compositeComponentHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualCompositeComponentType->getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics); + + { + recorder->recordAddress(*outCode); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::getTargetCode( + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getTargetCode, m_compositeComponentHandle); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualCompositeComponentType->getTargetCode(targetIndex, outCode, outDiagnostics); + + { + recorder->recordAddress(*outCode); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::getResultAsFileSystem( + SlangInt entryPointIndex, + SlangInt targetIndex, + ISlangMutableFileSystem** outFileSystem) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getResultAsFileSystem, m_compositeComponentHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualCompositeComponentType->getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem); + + { + recorder->recordAddress(*outFileSystem); + } + + // TODO: We might need to wrap the file system object. + return res; + } + + SLANG_NO_THROW void CompositeComponentTypeRecorder::getEntryPointHash( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outHash) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getEntryPointHash, m_compositeComponentHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + m_actualCompositeComponentType->getEntryPointHash(entryPointIndex, targetIndex, outHash); + + { + recorder->recordAddress(*outHash); + m_recordManager->endMethodRecordAppendOutput(); + } + } + + SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::specialize( + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + slang::IComponentType** outSpecializedComponentType, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_specialize, m_compositeComponentHandle); + recorder->recordInt64(specializationArgCount); + recorder->recordStructArray(specializationArgs, specializationArgCount); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualCompositeComponentType->specialize(specializationArgs, specializationArgCount, outSpecializedComponentType, outDiagnostics); + + { + recorder->recordAddress(*outSpecializedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::link( + slang::IComponentType** outLinkedComponentType, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_link, m_compositeComponentHandle); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualCompositeComponentType->link(outLinkedComponentType, outDiagnostics); + + { + recorder->recordAddress(*outLinkedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::getEntryPointHostCallable( + int entryPointIndex, + int targetIndex, + ISlangSharedLibrary** outSharedLibrary, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getEntryPointHostCallable, m_compositeComponentHandle); + recorder->recordInt32(entryPointIndex); + recorder->recordInt32(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualCompositeComponentType->getEntryPointHostCallable(entryPointIndex, targetIndex, outSharedLibrary, outDiagnostics); + + { + recorder->recordAddress(*outSharedLibrary); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::renameEntryPoint( + const char* newName, IComponentType** outEntryPoint) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_renameEntryPoint, m_compositeComponentHandle); + recorder->recordString(newName); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualCompositeComponentType->renameEntryPoint(newName, outEntryPoint); + + { + recorder->recordAddress(*outEntryPoint); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult CompositeComponentTypeRecorder::linkWithOptions( + IComponentType** outLinkedComponentType, + uint32_t compilerOptionEntryCount, + slang::CompilerOptionEntry* compilerOptionEntries, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_linkWithOptions, m_compositeComponentHandle); + recorder->recordUint32(compilerOptionEntryCount); + recorder->recordStructArray(compilerOptionEntries, compilerOptionEntryCount); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualCompositeComponentType->linkWithOptions(outLinkedComponentType, compilerOptionEntryCount, compilerOptionEntries, outDiagnostics); + + { + recorder->recordAddress(*outLinkedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } +} diff --git a/source/slang-record-replay/record/slang-composite-component-type.h b/source/slang-record-replay/record/slang-composite-component-type.h new file mode 100644 index 000000000..758a59434 --- /dev/null +++ b/source/slang-record-replay/record/slang-composite-component-type.h @@ -0,0 +1,76 @@ +#ifndef SLANG_COMPOSITE_COMPONENT_TYPE_H +#define SLANG_COMPOSITE_COMPONENT_TYPE_H + +#include "slang-com-ptr.h" +#include "slang.h" +#include "slang-com-helper.h" +#include "../../core/slang-smart-pointer.h" +#include "../../core/slang-dictionary.h" +#include "../../slang/slang-compiler.h" +#include "record-manager.h" + +namespace SlangRecord +{ + using namespace Slang; + class CompositeComponentTypeRecorder: public slang::IComponentType, public RefObject + { + public: + SLANG_REF_OBJECT_IUNKNOWN_ALL + ISlangUnknown* getInterface(const Guid& guid); + + explicit CompositeComponentTypeRecorder(slang::IComponentType* componentType, RecordManager* recordManager); + ~CompositeComponentTypeRecorder(); + + // Interfaces for `IComponentType` + virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override; + virtual SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout( + SlangInt targetIndex = 0, + slang::IBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangInt SLANG_MCALL getSpecializationParamCount() override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem( + SlangInt entryPointIndex, + SlangInt targetIndex, + ISlangMutableFileSystem** outFileSystem) override; + virtual SLANG_NO_THROW void SLANG_MCALL getEntryPointHash( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outHash) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL specialize( + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + slang::IComponentType** outSpecializedComponentType, + ISlangBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL link( + slang::IComponentType** outLinkedComponentType, + ISlangBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable( + int entryPointIndex, + int targetIndex, + ISlangSharedLibrary** outSharedLibrary, + slang::IBlob** outDiagnostics = 0) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL renameEntryPoint( + const char* newName, IComponentType** outEntryPoint) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions( + IComponentType** outLinkedComponentType, + uint32_t compilerOptionEntryCount, + slang::CompilerOptionEntry* compilerOptionEntries, + ISlangBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode( + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics = nullptr) override; + + slang::IComponentType* getActualCompositeComponentType() const { return m_actualCompositeComponentType; } + private: + Slang::ComPtr<slang::IComponentType> m_actualCompositeComponentType; + uint64_t m_compositeComponentHandle = 0; + RecordManager* m_recordManager = nullptr; + + }; +} +#endif // SLANG_COMPOSITE_COMPONENT_TYPE_H diff --git a/source/slang-record-replay/record/slang-entrypoint.cpp b/source/slang-record-replay/record/slang-entrypoint.cpp new file mode 100644 index 000000000..d3bf3b47e --- /dev/null +++ b/source/slang-record-replay/record/slang-entrypoint.cpp @@ -0,0 +1,313 @@ +#include "../util/record-utility.h" +#include "slang-entrypoint.h" + +namespace SlangRecord +{ + EntryPointRecorder::EntryPointRecorder(slang::IEntryPoint* entryPoint, RecordManager* recordManager) + : m_actualEntryPoint(entryPoint), + m_recordManager(recordManager) + { + SLANG_RECORD_ASSERT(m_actualEntryPoint != nullptr); + SLANG_RECORD_ASSERT(m_recordManager != nullptr); + + m_entryPointHandle = reinterpret_cast<uint64_t>(m_actualEntryPoint.get()); + slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, entryPoint); + } + + EntryPointRecorder::~EntryPointRecorder() + { + m_actualEntryPoint->release(); + } + + ISlangUnknown* EntryPointRecorder::getInterface(const Guid& guid) + { + if(guid == EntryPointRecorder::getTypeGuid()) + return static_cast<ISlangUnknown*>(this); + else + return nullptr; + } + + SLANG_NO_THROW slang::ISession* EntryPointRecorder::getSession() + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getSession, m_entryPointHandle); + recorder = m_recordManager->endMethodRecord(); + } + + slang::ISession* session = m_actualEntryPoint->getSession(); + + { + recorder->recordAddress(session); + m_recordManager->endMethodRecordAppendOutput(); + } + + return session; + } + + SLANG_NO_THROW slang::ProgramLayout* EntryPointRecorder::getLayout( + SlangInt targetIndex, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getLayout, m_entryPointHandle); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + slang::ProgramLayout* programLayout = m_actualEntryPoint->getLayout(targetIndex, outDiagnostics); + + { + recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(programLayout); + m_recordManager->endMethodRecordAppendOutput(); + } + + return programLayout; + } + + SLANG_NO_THROW SlangInt EntryPointRecorder::getSpecializationParamCount() + { + // No need to record this call as it is just a query. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + SlangInt res = m_actualEntryPoint->getSpecializationParamCount(); + return res; + } + + SLANG_NO_THROW SlangResult EntryPointRecorder::getEntryPointCode( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getEntryPointCode, m_entryPointHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualEntryPoint->getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics); + + { + recorder->recordAddress(*outCode); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult EntryPointRecorder::getTargetCode( + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getTargetCode, m_entryPointHandle); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualEntryPoint->getTargetCode(targetIndex, outCode, outDiagnostics); + + { + recorder->recordAddress(*outCode); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult EntryPointRecorder::getResultAsFileSystem( + SlangInt entryPointIndex, + SlangInt targetIndex, + ISlangMutableFileSystem** outFileSystem) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getResultAsFileSystem, m_entryPointHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualEntryPoint->getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem); + + { + recorder->recordAddress(*outFileSystem); + } + + // TODO: We might need to wrap the file system object. + return res; + } + + SLANG_NO_THROW void EntryPointRecorder::getEntryPointHash( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outHash) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getEntryPointHash, m_entryPointHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + m_actualEntryPoint->getEntryPointHash(entryPointIndex, targetIndex, outHash); + + { + recorder->recordAddress(*outHash); + m_recordManager->endMethodRecordAppendOutput(); + } + } + + SLANG_NO_THROW SlangResult EntryPointRecorder::specialize( + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + slang::IComponentType** outSpecializedComponentType, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_specialize, m_entryPointHandle); + recorder->recordInt64(specializationArgCount); + recorder->recordStructArray(specializationArgs, specializationArgCount); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualEntryPoint->specialize(specializationArgs, specializationArgCount, outSpecializedComponentType, outDiagnostics); + + { + recorder->recordAddress(*outSpecializedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult EntryPointRecorder::link( + slang::IComponentType** outLinkedComponentType, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_link, m_entryPointHandle); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualEntryPoint->link(outLinkedComponentType, outDiagnostics); + + { + recorder->recordAddress(*outLinkedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult EntryPointRecorder::getEntryPointHostCallable( + int entryPointIndex, + int targetIndex, + ISlangSharedLibrary** outSharedLibrary, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_getEntryPointHostCallable, m_entryPointHandle); + recorder->recordInt32(entryPointIndex); + recorder->recordInt32(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualEntryPoint->getEntryPointHostCallable(entryPointIndex, targetIndex, outSharedLibrary, outDiagnostics); + + { + recorder->recordAddress(*outSharedLibrary); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult EntryPointRecorder::renameEntryPoint( + const char* newName, IComponentType** outEntryPoint) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_renameEntryPoint, m_entryPointHandle); + recorder->recordString(newName); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualEntryPoint->renameEntryPoint(newName, outEntryPoint); + + { + recorder->recordAddress(*outEntryPoint); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult EntryPointRecorder::linkWithOptions( + IComponentType** outLinkedComponentType, + uint32_t compilerOptionEntryCount, + slang::CompilerOptionEntry* compilerOptionEntries, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IEntryPoint_linkWithOptions, m_entryPointHandle); + recorder->recordUint32(compilerOptionEntryCount); + recorder->recordStructArray(compilerOptionEntries, compilerOptionEntryCount); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualEntryPoint->linkWithOptions(outLinkedComponentType, compilerOptionEntryCount, compilerOptionEntries, outDiagnostics); + + { + recorder->recordAddress(*outLinkedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW slang::FunctionReflection* EntryPointRecorder::getFunctionReflection() + { + return m_actualEntryPoint->getFunctionReflection(); + } + +} diff --git a/source/slang-record-replay/record/slang-entrypoint.h b/source/slang-record-replay/record/slang-entrypoint.h new file mode 100644 index 000000000..17c6fab6f --- /dev/null +++ b/source/slang-record-replay/record/slang-entrypoint.h @@ -0,0 +1,77 @@ +#ifndef SLANG_ENTRY_POINT_H +#define SLANG_ENTRY_POINT_H + +#include "slang-com-ptr.h" +#include "slang.h" +#include "slang-com-helper.h" +#include "../../core/slang-smart-pointer.h" +#include "../../core/slang-dictionary.h" +#include "../../slang/slang-compiler.h" +#include "record-manager.h" + +namespace SlangRecord +{ + using namespace Slang; + class EntryPointRecorder : public slang::IEntryPoint, public RefObject + { + public: + SLANG_COM_INTERFACE(0xf4c1e23d, 0xb321, 0x4931, { 0x8f, 0x37, 0xf1, 0x22, 0x6a, 0xf9, 0x20, 0x85 }) + + SLANG_REF_OBJECT_IUNKNOWN_ALL + ISlangUnknown* getInterface(const Guid& guid); + + explicit EntryPointRecorder(slang::IEntryPoint* entryPoint, RecordManager* recordManager); + ~EntryPointRecorder(); + + // Interfaces for `IComponentType` + virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override; + virtual SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout( + SlangInt targetIndex = 0, + slang::IBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangInt SLANG_MCALL getSpecializationParamCount() override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode( + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem( + SlangInt entryPointIndex, + SlangInt targetIndex, + ISlangMutableFileSystem** outFileSystem) override; + virtual SLANG_NO_THROW void SLANG_MCALL getEntryPointHash( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outHash) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL specialize( + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + slang::IComponentType** outSpecializedComponentType, + ISlangBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL link( + slang::IComponentType** outLinkedComponentType, + ISlangBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable( + int entryPointIndex, + int targetIndex, + ISlangSharedLibrary** outSharedLibrary, + slang::IBlob** outDiagnostics = 0) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL renameEntryPoint( + const char* newName, IComponentType** outEntryPoint) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions( + IComponentType** outLinkedComponentType, + uint32_t compilerOptionEntryCount, + slang::CompilerOptionEntry* compilerOptionEntries, + ISlangBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW slang::FunctionReflection* SLANG_MCALL getFunctionReflection() override; + slang::IEntryPoint* getActualEntryPoint() const { return m_actualEntryPoint; } + private: + Slang::ComPtr<slang::IEntryPoint> m_actualEntryPoint; + uint64_t m_entryPointHandle = 0; + RecordManager* m_recordManager = nullptr; + }; +} +#endif // SLANG_ENTRY_POINT_H diff --git a/source/slang-record-replay/record/slang-filesystem.cpp b/source/slang-record-replay/record/slang-filesystem.cpp new file mode 100644 index 000000000..1187a9aa6 --- /dev/null +++ b/source/slang-record-replay/record/slang-filesystem.cpp @@ -0,0 +1,124 @@ +#include "slang-filesystem.h" +#include "../util/record-utility.h" +#include "output-stream.h" + +namespace SlangRecord +{ + // We don't actually need to record the methods of ISlangFileSystemExt, we just want to record the file content + // and save them into disk. + FileSystemRecorder::FileSystemRecorder(ISlangFileSystemExt* fileSystem, RecordManager* recordManager) + : m_actualFileSystem(fileSystem), + m_recordManager(recordManager) + { + SLANG_RECORD_ASSERT(m_actualFileSystem); + SLANG_RECORD_ASSERT(m_recordManager); + slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, m_actualFileSystem.get()); + } + + FileSystemRecorder::~FileSystemRecorder() + { + m_actualFileSystem->release(); + } + + void* FileSystemRecorder::castAs(const Slang::Guid& guid) + { + return getInterface(guid); + } + + ISlangUnknown* FileSystemRecorder::getInterface(const Slang::Guid& guid) + { + if(guid == ISlangUnknown::getTypeGuid() || guid == ISlangFileSystem::getTypeGuid()) + return static_cast<ISlangFileSystem*>(this); + return nullptr; + } + + // TODO: There could be a potential issue that could not be able to dump the generated file content correctly. + // Details: https://github.com/shader-slang/slang/issues/4423. + SLANG_NO_THROW SlangResult FileSystemRecorder::loadFile( + char const* path, + ISlangBlob** outBlob) + { + slangRecordLog(LogLevel::Verbose, "%p: %s, :%s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path); + SlangResult res = m_actualFileSystem->loadFile(path, outBlob); + + // Since the loadFile method could be implemented by client, we can't guarantee the result is always as expected, + // we will check every thing to make sure we won't crash at writing file. + // + // We can only dump the file content after this 'loadFile' call, no matter this call crashes or file is not + // found, we can't save the file anyway, so we don't need to pay special care to the crash recovery. We will + // know something wrong with the loadFile call if we can't find the file in the record directory. + if ((res == SLANG_OK) && (*outBlob != nullptr) && ((*outBlob)->getBufferSize() != 0)) + { + std::filesystem::path filePath = m_recordManager->getRecordFileDirectory(); + filePath = filePath / path; + + FileOutputStream fileStream(filePath.string().c_str()); + + fileStream.write((*outBlob)->getBufferPointer(), (*outBlob)->getBufferSize()); + fileStream.flush(); + } + return res; + } + + SLANG_NO_THROW SlangResult FileSystemRecorder::getFileUniqueIdentity( + const char* path, + ISlangBlob** outUniqueIdentity) + { + slangRecordLog(LogLevel::Verbose, "%p: %s :\"%s\"\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path); + SlangResult res = m_actualFileSystem->getFileUniqueIdentity(path, outUniqueIdentity); + return res; + } + + SLANG_NO_THROW SlangResult FileSystemRecorder::calcCombinedPath( + SlangPathType fromPathType, + const char* fromPath, + const char* path, + ISlangBlob** pathOut) + { + slangRecordLog(LogLevel::Verbose, "%p: %s, :%s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path); + SlangResult res = m_actualFileSystem->calcCombinedPath(fromPathType, fromPath, path, pathOut); + return res; + } + + SLANG_NO_THROW SlangResult FileSystemRecorder::getPathType( + const char* path, + SlangPathType* pathTypeOut) + { + slangRecordLog(LogLevel::Verbose, "%p: %s, :%s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path); + SlangResult res = m_actualFileSystem->getPathType(path, pathTypeOut); + return res; + } + + SLANG_NO_THROW SlangResult FileSystemRecorder::getPath( + PathKind kind, + const char* path, + ISlangBlob** outPath) + { + slangRecordLog(LogLevel::Verbose, "%p: %s, :%s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path); + SlangResult res = m_actualFileSystem->getPath(kind, path, outPath); + return res; + } + + SLANG_NO_THROW void FileSystemRecorder::clearCache() + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__); + m_actualFileSystem->clearCache(); + } + + SLANG_NO_THROW SlangResult FileSystemRecorder::enumeratePathContents( + const char* path, + FileSystemContentsCallBack callback, + void* userData) + { + slangRecordLog(LogLevel::Verbose, "%p: %s, :%s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__, path); + SlangResult res = m_actualFileSystem->enumeratePathContents(path, callback, userData); + return res; + } + + SLANG_NO_THROW OSPathKind FileSystemRecorder::getOSPathKind() + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualFileSystem.get(), __PRETTY_FUNCTION__); + OSPathKind pathKind = m_actualFileSystem->getOSPathKind(); + return pathKind; + } +} diff --git a/source/slang-record-replay/record/slang-filesystem.h b/source/slang-record-replay/record/slang-filesystem.h new file mode 100644 index 000000000..ff7004fa1 --- /dev/null +++ b/source/slang-record-replay/record/slang-filesystem.h @@ -0,0 +1,71 @@ +#ifndef SLANG_FILE_SYSTEM_H +#define SLANG_FILE_SYSTEM_H + +#include "slang-com-helper.h" +#include "slang-com-ptr.h" +#include "../../core/slang-com-object.h" +#include "record-manager.h" + +namespace SlangRecord +{ + + using namespace Slang; + + // slang always requires ISlangFileSystemExt interface, even if user only provides ISlangFileSystem, + // slang will still wrap it with ISlangFileSystemExt. So we have to record ISlangFileSystemExt, even + // though we only need to record loadFile() function. + class FileSystemRecorder : public RefObject, public ISlangFileSystemExt + { + public: + explicit FileSystemRecorder(ISlangFileSystemExt* fileSystem, RecordManager* recordManager); + ~FileSystemRecorder(); + + // ISlangUnknown + SLANG_REF_OBJECT_IUNKNOWN_ALL + + ISlangUnknown* getInterface(const Slang::Guid& guid); + + // ISlangCastable + virtual SLANG_NO_THROW void* SLANG_MCALL castAs(const Slang::Guid& guid) override; + + // ISlangFileSystem + virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadFile( + char const* path, + ISlangBlob** outBlob) override; + + // ISlangFileSystemExt + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getFileUniqueIdentity( + const char* path, + ISlangBlob** outUniqueIdentity) override; + + virtual SLANG_NO_THROW SlangResult SLANG_MCALL calcCombinedPath( + SlangPathType fromPathType, + const char* fromPath, + const char* path, + ISlangBlob** pathOut) override; + + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPathType( + const char* path, + SlangPathType* pathTypeOut) override; + + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPath( + PathKind kind, + const char* path, + ISlangBlob** outPath) override; + + virtual SLANG_NO_THROW void SLANG_MCALL clearCache() override; + + virtual SLANG_NO_THROW SlangResult SLANG_MCALL enumeratePathContents( + const char* path, + FileSystemContentsCallBack callback, + void* userData) override; + + virtual SLANG_NO_THROW OSPathKind SLANG_MCALL getOSPathKind() override; + private: + Slang::ComPtr<ISlangFileSystemExt> m_actualFileSystem; + RecordManager* m_recordManager = nullptr; +}; + +} +#endif + diff --git a/source/slang-record-replay/record/slang-global-session.cpp b/source/slang-record-replay/record/slang-global-session.cpp new file mode 100644 index 000000000..78fa8aaa2 --- /dev/null +++ b/source/slang-record-replay/record/slang-global-session.cpp @@ -0,0 +1,452 @@ +#include "slang-global-session.h" +#include "slang-session.h" +#include "slang-filesystem.h" +#include "../../slang/slang-compiler.h" +#include "../util/record-utility.h" + +namespace SlangRecord +{ + // constructor is called in slang_createGlobalSession + GlobalSessionRecorder::GlobalSessionRecorder(slang::IGlobalSession* session): + m_actualGlobalSession(session) + { + SLANG_RECORD_ASSERT(m_actualGlobalSession != nullptr); + + m_globalSessionHandle = reinterpret_cast<SlangRecord::AddressFormat>(m_actualGlobalSession.get()); + m_recordManager = std::make_unique<RecordManager>(m_globalSessionHandle); + + // We will use the address of the global session as the filename for the record manager + // to make it unique for each global session. + // record slang::createGlobalSession + ParameterRecorder* recorder = m_recordManager->beginMethodRecord(ApiCallId::CreateGlobalSession, g_globalFunctionHandle); + recorder->recordAddress(m_actualGlobalSession); + m_recordManager->endMethodRecord(); + } + + GlobalSessionRecorder::~GlobalSessionRecorder() + { + m_actualGlobalSession->release(); + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::queryInterface(SlangUUID const& uuid, void** outObject) + { + if (uuid == Session::getTypeGuid()) + { + // no add-ref here, the query will cause the inner session to handle the add-ref. + this->m_actualGlobalSession->queryInterface(uuid, outObject); + return SLANG_OK; + } + + if (uuid == ISlangUnknown::getTypeGuid() && uuid == IGlobalSession::getTypeGuid()) + { + addReference(); + *outObject = static_cast<slang::IGlobalSession*>(this); + return SLANG_OK; + } + + return SLANG_E_NO_INTERFACE; + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::createSession(slang::SessionDesc const& desc, slang::ISession** outSession) + { + setLogLevel(); + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + slang::ISession* actualSession = nullptr; + + ParameterRecorder* recorder{}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_createSession, m_globalSessionHandle); + recorder->recordStruct(desc); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualGlobalSession->createSession(desc, &actualSession); + + { // record output + recorder->recordAddress(actualSession); + m_recordManager->endMethodRecordAppendOutput(); + } + + if (actualSession != nullptr) + { + // reset the file system to our record file system. After createSession() call, + // the Linkage will set to user provided file system or slang default file system. + // We need to reset it to our record file system + Slang::Linkage* linkage = static_cast<Linkage*>(actualSession); + FileSystemRecorder* fileSystemRecord = new FileSystemRecorder(linkage->getFileSystemExt(), m_recordManager.get()); + + Slang::ComPtr<FileSystemRecorder> resultFileSystemRecorder(fileSystemRecord); + linkage->setFileSystem(resultFileSystemRecorder.detach()); + + SessionRecorder* sessionRecord = new SessionRecorder(actualSession, m_recordManager.get()); + Slang::ComPtr<SessionRecorder> result(sessionRecord); + *outSession = result.detach(); + } + + return res; + } + + SLANG_NO_THROW SlangProfileID SLANG_MCALL GlobalSessionRecorder::findProfile(char const* name) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_findProfile, m_globalSessionHandle); + recorder->recordString(name); + recorder = m_recordManager->endMethodRecord(); + } + + SlangProfileID profileId = m_actualGlobalSession->findProfile(name); + return profileId; + } + + SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::setDownstreamCompilerPath(SlangPassThrough passThrough, char const* path) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setDownstreamCompilerPath, m_globalSessionHandle); + recorder->recordEnumValue(passThrough); + recorder->recordString(path); + m_recordManager->endMethodRecord(); + } + + m_actualGlobalSession->setDownstreamCompilerPath(passThrough, path); + } + + SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::setDownstreamCompilerPrelude(SlangPassThrough inPassThrough, char const* prelude) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setDownstreamCompilerPrelude, m_globalSessionHandle); + recorder->recordEnumValue(inPassThrough); + recorder->recordString(prelude); + m_recordManager->endMethodRecord(); + } + + m_actualGlobalSession->setDownstreamCompilerPrelude(inPassThrough, prelude); + } + + SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::getDownstreamCompilerPrelude(SlangPassThrough inPassThrough, ISlangBlob** outPrelude) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_getDownstreamCompilerPrelude, m_globalSessionHandle); + recorder->recordEnumValue(inPassThrough); + recorder = m_recordManager->endMethodRecord(); + } + + m_actualGlobalSession->getDownstreamCompilerPrelude(inPassThrough, outPrelude); + + { + recorder->recordAddress(*outPrelude); + m_recordManager->endMethodRecordAppendOutput(); + } + } + + SLANG_NO_THROW const char* SLANG_MCALL GlobalSessionRecorder::getBuildTagString() + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + // No need to record this function. It's just a query function and it won't impact the internal state. + const char* resStr = m_actualGlobalSession->getBuildTagString(); + return resStr; + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::setDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage, SlangPassThrough defaultCompiler) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setDefaultDownstreamCompiler, m_globalSessionHandle); + recorder->recordEnumValue(sourceLanguage); + recorder->recordEnumValue(defaultCompiler); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualGlobalSession->setDefaultDownstreamCompiler(sourceLanguage, defaultCompiler); + return res; + } + + SLANG_NO_THROW SlangPassThrough SLANG_MCALL GlobalSessionRecorder::getDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_getDefaultDownstreamCompiler, m_globalSessionHandle); + recorder->recordEnumValue(sourceLanguage); + recorder = m_recordManager->endMethodRecord(); + } + + SlangPassThrough passThrough = m_actualGlobalSession->getDefaultDownstreamCompiler(sourceLanguage); + return passThrough; + } + + SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::setLanguagePrelude(SlangSourceLanguage inSourceLanguage, char const* prelude) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setLanguagePrelude, m_globalSessionHandle); + recorder->recordEnumValue(inSourceLanguage); + recorder->recordString(prelude); + recorder = m_recordManager->endMethodRecord(); + } + + m_actualGlobalSession->setLanguagePrelude(inSourceLanguage, prelude); + } + + SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::getLanguagePrelude(SlangSourceLanguage inSourceLanguage, ISlangBlob** outPrelude) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_getLanguagePrelude, m_globalSessionHandle); + recorder->recordEnumValue(inSourceLanguage); + recorder = m_recordManager->endMethodRecord(); + } + + m_actualGlobalSession->getLanguagePrelude(inSourceLanguage, outPrelude); + + { + recorder->recordAddress(*outPrelude); + m_recordManager->endMethodRecordAppendOutput(); + } + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::createCompileRequest(slang::ICompileRequest** outCompileRequest) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_createCompileRequest, m_globalSessionHandle); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualGlobalSession->createCompileRequest(outCompileRequest); + + { + recorder->recordAddress(*outCompileRequest); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::addBuiltins(char const* sourcePath, char const* sourceString) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_addBuiltins, m_globalSessionHandle); + recorder->recordString(sourcePath); + recorder->recordString(sourceString); + recorder = m_recordManager->endMethodRecord(); + } + + m_actualGlobalSession->addBuiltins(sourcePath, sourceString); + } + + SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::setSharedLibraryLoader(ISlangSharedLibraryLoader* loader) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + // TODO: Not sure if we need to record this function. Because this functions is something like the file system + // override, it's provided by user code. So capturing it makes no sense. The only way is to wrapper this interface + // by our own implementation, and record it there. + m_actualGlobalSession->setSharedLibraryLoader(loader); + } + + SLANG_NO_THROW ISlangSharedLibraryLoader* SLANG_MCALL GlobalSessionRecorder::getSharedLibraryLoader() + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_getSharedLibraryLoader, m_globalSessionHandle); + recorder = m_recordManager->endMethodRecord(); + } + + ISlangSharedLibraryLoader* loader = m_actualGlobalSession->getSharedLibraryLoader(); + + { + recorder->recordAddress(loader); + m_recordManager->endMethodRecordAppendOutput(); + } + return loader; + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::checkCompileTargetSupport(SlangCompileTarget target) + { + // No need to record this function. It's just a query function and it won't impact the internal state. + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + SlangResult res = m_actualGlobalSession->checkCompileTargetSupport(target); + return res; + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::checkPassThroughSupport(SlangPassThrough passThrough) + { + // No need to record this function. It's just a query function and it won't impact the internal state. + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + SlangResult res = m_actualGlobalSession->checkPassThroughSupport(passThrough); + return res; + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::compileStdLib(slang::CompileStdLibFlags flags) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_compileStdLib, m_globalSessionHandle); + recorder->recordEnumValue(flags); + m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualGlobalSession->compileStdLib(flags); + return res; + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::loadStdLib(const void* stdLib, size_t stdLibSizeInBytes) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_loadStdLib, m_globalSessionHandle); + recorder->recordPointer(stdLib, false, stdLibSizeInBytes); + m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualGlobalSession->loadStdLib(stdLib, stdLibSizeInBytes); + return res; + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::saveStdLib(SlangArchiveType archiveType, ISlangBlob** outBlob) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_saveStdLib, m_globalSessionHandle); + recorder->recordEnumValue(archiveType); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualGlobalSession->saveStdLib(archiveType, outBlob); + + { + recorder->recordAddress(*outBlob); + m_recordManager->endMethodRecordAppendOutput(); + } + return res; + } + + SLANG_NO_THROW SlangCapabilityID SLANG_MCALL GlobalSessionRecorder::findCapability(char const* name) + { + // No need to record this function. It's just a query function and it won't impact the internal state. + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + SlangCapabilityID capId = m_actualGlobalSession->findCapability(name); + return capId; + } + + SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::setDownstreamCompilerForTransition(SlangCompileTarget source, SlangCompileTarget target, SlangPassThrough compiler) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setDownstreamCompilerForTransition, m_globalSessionHandle); + recorder->recordEnumValue(source); + recorder->recordEnumValue(target); + recorder->recordEnumValue(compiler); + m_recordManager->endMethodRecord(); + } + + m_actualGlobalSession->setDownstreamCompilerForTransition(source, target, compiler); + } + + SLANG_NO_THROW SlangPassThrough SLANG_MCALL GlobalSessionRecorder::getDownstreamCompilerForTransition(SlangCompileTarget source, SlangCompileTarget target) + { + // No need to record this function. It's just a query function and it won't impact the internal state. + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + SlangPassThrough passThrough = m_actualGlobalSession->getDownstreamCompilerForTransition(source, target); + return passThrough; + } + + SLANG_NO_THROW void SLANG_MCALL GlobalSessionRecorder::getCompilerElapsedTime(double* outTotalTime, double* outDownstreamTime) + { + // No need to record this function. It's just a query function and it won't impact the internal state. + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + m_actualGlobalSession->getCompilerElapsedTime(outTotalTime, outDownstreamTime); + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::setSPIRVCoreGrammar(char const* jsonPath) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_setSPIRVCoreGrammar, m_globalSessionHandle); + recorder->recordString(jsonPath); + m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualGlobalSession->setSPIRVCoreGrammar(jsonPath); + return res; + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::parseCommandLineArguments( + int argc, const char* const* argv, slang::SessionDesc* outSessionDesc, ISlangUnknown** outAllocation) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_parseCommandLineArguments, m_globalSessionHandle); + recorder->recordInt32(argc); + recorder->recordStringArray(argv, argc); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualGlobalSession->parseCommandLineArguments(argc, argv, outSessionDesc, outAllocation); + + { + recorder->recordAddress(outSessionDesc); + recorder->recordAddress(*outAllocation); + m_recordManager->endMethodRecordAppendOutput(); + } + return res; + } + + SLANG_NO_THROW SlangResult SLANG_MCALL GlobalSessionRecorder::getSessionDescDigest(slang::SessionDesc* sessionDesc, ISlangBlob** outBlob) + { + slangRecordLog(LogLevel::Verbose, "%p: %s\n", m_actualGlobalSession.get(), __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IGlobalSession_getSessionDescDigest, m_globalSessionHandle); + recorder->recordStruct(*sessionDesc); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualGlobalSession->getSessionDescDigest(sessionDesc, outBlob); + + { + recorder->recordAddress(*outBlob); + m_recordManager->endMethodRecordAppendOutput(); + } + return res; + } +} diff --git a/source/slang-record-replay/record/slang-global-session.h b/source/slang-record-replay/record/slang-global-session.h new file mode 100644 index 000000000..3fd584ef4 --- /dev/null +++ b/source/slang-record-replay/record/slang-global-session.h @@ -0,0 +1,85 @@ +#ifndef SLANG_GLOBAL_SESSION_H +#define SLANG_GLOBAL_SESSION_H + +#include "slang-com-ptr.h" +#include "slang.h" +#include "slang-com-helper.h" +#include "../../core/slang-smart-pointer.h" +#include "record-manager.h" + +namespace SlangRecord +{ + using namespace Slang; + + class GlobalSessionRecorder : public RefObject, public slang::IGlobalSession + { + public: + explicit GlobalSessionRecorder(slang::IGlobalSession* session); + virtual ~GlobalSessionRecorder(); + + SLANG_REF_OBJECT_IUNKNOWN_ADD_REF + SLANG_REF_OBJECT_IUNKNOWN_RELEASE + + SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject) SLANG_OVERRIDE; + + // slang::IGlobalSession + SLANG_NO_THROW SlangResult SLANG_MCALL createSession(slang::SessionDesc const& desc, slang::ISession** outSession) override; + SLANG_NO_THROW SlangProfileID SLANG_MCALL findProfile(char const* name) override; + SLANG_NO_THROW void SLANG_MCALL setDownstreamCompilerPath(SlangPassThrough passThrough, char const* path) override; + SLANG_NO_THROW void SLANG_MCALL setDownstreamCompilerPrelude(SlangPassThrough inPassThrough, char const* prelude) override; + SLANG_NO_THROW void SLANG_MCALL getDownstreamCompilerPrelude(SlangPassThrough inPassThrough, ISlangBlob** outPrelude) override; + SLANG_NO_THROW const char* SLANG_MCALL getBuildTagString() override; + SLANG_NO_THROW SlangResult SLANG_MCALL setDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage, SlangPassThrough defaultCompiler) override; + SLANG_NO_THROW SlangPassThrough SLANG_MCALL getDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage) override; + + SLANG_NO_THROW void SLANG_MCALL setLanguagePrelude(SlangSourceLanguage inSourceLanguage, char const* prelude) override; + SLANG_NO_THROW void SLANG_MCALL getLanguagePrelude(SlangSourceLanguage inSourceLanguage, ISlangBlob** outPrelude) override; + + SLANG_NO_THROW SlangResult SLANG_MCALL createCompileRequest(slang::ICompileRequest** outCompileRequest) override; + + SLANG_NO_THROW void SLANG_MCALL addBuiltins(char const* sourcePath, char const* sourceString) override; + SLANG_NO_THROW void SLANG_MCALL setSharedLibraryLoader(ISlangSharedLibraryLoader* loader) override; + SLANG_NO_THROW ISlangSharedLibraryLoader* SLANG_MCALL getSharedLibraryLoader() override; + SLANG_NO_THROW SlangResult SLANG_MCALL checkCompileTargetSupport(SlangCompileTarget target) override; + SLANG_NO_THROW SlangResult SLANG_MCALL checkPassThroughSupport(SlangPassThrough passThrough) override; + + SLANG_NO_THROW SlangResult SLANG_MCALL compileStdLib(slang::CompileStdLibFlags flags) override; + SLANG_NO_THROW SlangResult SLANG_MCALL loadStdLib(const void* stdLib, size_t stdLibSizeInBytes) override; + SLANG_NO_THROW SlangResult SLANG_MCALL saveStdLib(SlangArchiveType archiveType, ISlangBlob** outBlob) override; + + SLANG_NO_THROW SlangCapabilityID SLANG_MCALL findCapability(char const* name) override; + + SLANG_NO_THROW void SLANG_MCALL setDownstreamCompilerForTransition(SlangCompileTarget source, SlangCompileTarget target, SlangPassThrough compiler) override; + SLANG_NO_THROW SlangPassThrough SLANG_MCALL getDownstreamCompilerForTransition(SlangCompileTarget source, SlangCompileTarget target) override; + SLANG_NO_THROW void SLANG_MCALL getCompilerElapsedTime(double* outTotalTime, double* outDownstreamTime) override; + + SLANG_NO_THROW SlangResult SLANG_MCALL setSPIRVCoreGrammar(char const* jsonPath) override; + + SLANG_NO_THROW SlangResult SLANG_MCALL parseCommandLineArguments( + int argc, const char* const* argv, slang::SessionDesc* outSessionDesc, ISlangUnknown** outAllocation) override; + + SLANG_NO_THROW SlangResult SLANG_MCALL getSessionDescDigest(slang::SessionDesc* sessionDesc, ISlangBlob** outBlob) override; + + RecordManager* getRecordManager() { return m_recordManager.get(); } + + private: + SLANG_FORCE_INLINE slang::IGlobalSession* asExternal(GlobalSessionRecorder* session) + { + return static_cast<slang::IGlobalSession*>(session); + } + + Slang::ComPtr<slang::IGlobalSession> m_actualGlobalSession; + + // we will create one record 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 record 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<RecordManager> m_recordManager; + uint64_t m_globalSessionHandle = 0; + }; +} // namespace Slang + +#endif diff --git a/source/slang-record-replay/record/slang-module.cpp b/source/slang-record-replay/record/slang-module.cpp new file mode 100644 index 000000000..1457e7cf1 --- /dev/null +++ b/source/slang-record-replay/record/slang-module.cpp @@ -0,0 +1,496 @@ +#include "../util/record-utility.h" +#include "slang-module.h" + +namespace SlangRecord +{ + ModuleRecorder::ModuleRecorder(slang::IModule* module, RecordManager* recordManager) + : m_actualModule(module), + m_recordManager(recordManager) + { + SLANG_RECORD_ASSERT(m_actualModule != nullptr); + SLANG_RECORD_ASSERT(m_recordManager != nullptr); + + m_moduleHandle = reinterpret_cast<uint64_t>(m_actualModule.get()); + slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, module); + } + + ModuleRecorder::~ModuleRecorder() + { + m_actualModule->release(); + } + + ISlangUnknown* ModuleRecorder::getInterface(const Guid& guid) + { + if(guid == ModuleRecorder::getTypeGuid()) + return static_cast<ISlangUnknown*>(this); + else + return nullptr; + } + + SLANG_NO_THROW slang::DeclReflection* ModuleRecorder::getModuleReflection() + { + // No need to record this call as it is just a query. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + slang::DeclReflection* res = (slang::DeclReflection*)m_actualModule->getModuleReflection(); + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::findEntryPointByName( + char const* name, + slang::IEntryPoint** outEntryPoint) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_findEntryPointByName, m_moduleHandle); + recorder->recordString(name); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->findEntryPointByName(name, outEntryPoint); + + { + recorder->recordAddress(*outEntryPoint); + m_recordManager->endMethodRecordAppendOutput(); + } + + if (SLANG_OK == res) + { + EntryPointRecorder* entryPointRecord = getEntryPointRecorder(*outEntryPoint); + *outEntryPoint = static_cast<slang::IEntryPoint*>(entryPointRecord); + } + return res; + } + + SLANG_NO_THROW SlangInt32 ModuleRecorder::getDefinedEntryPointCount() + { + // No need to record this call as it is just a query. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + SlangInt32 res = m_actualModule->getDefinedEntryPointCount(); + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::getDefinedEntryPoint(SlangInt32 index, slang::IEntryPoint** outEntryPoint) + { + // This call is to find the existing entry point, so it has been created already. Therefore, we don't create a new one + // and assert the error if it is not found in our map. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getDefinedEntryPoint, m_moduleHandle); + recorder->recordInt32(index); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->getDefinedEntryPoint(index, outEntryPoint); + + { + recorder->recordAddress(*outEntryPoint); + m_recordManager->endMethodRecordAppendOutput(); + } + + if (*outEntryPoint) + { + EntryPointRecorder* entryPointRecord = m_mapEntryPointToRecord.tryGetValue(*outEntryPoint); + if (!entryPointRecord) + { + SLANG_RECORD_ASSERT(!"Entrypoint not found in mapEntryPointToRecord"); + } + *outEntryPoint = static_cast<slang::IEntryPoint*>(entryPointRecord); + } + else + *outEntryPoint = nullptr; + + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::serialize(ISlangBlob** outSerializedBlob) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_serialize, m_moduleHandle); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->serialize(outSerializedBlob); + + { + recorder->recordAddress(*outSerializedBlob); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::writeToFile(char const* fileName) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_writeToFile, m_moduleHandle); + recorder->recordString(fileName); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->writeToFile(fileName); + return res; + } + + SLANG_NO_THROW const char* ModuleRecorder::getName() + { + // No need to record this call as it is just a query. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + const char* res = m_actualModule->getName(); + return res; + } + + SLANG_NO_THROW const char* ModuleRecorder::getFilePath() + { + // No need to record this call as it is just a query. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + const char* res = m_actualModule->getFilePath(); + return res; + } + + SLANG_NO_THROW const char* ModuleRecorder::getUniqueIdentity() + { + // No need to record this call as it is just a query. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + const char* res = m_actualModule->getUniqueIdentity(); + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::findAndCheckEntryPoint( + char const* name, + SlangStage stage, + slang::IEntryPoint** outEntryPoint, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_findAndCheckEntryPoint, m_moduleHandle); + recorder->recordString(name); + recorder->recordEnumValue(stage); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->findAndCheckEntryPoint(name, stage, outEntryPoint, outDiagnostics); + + { + recorder->recordAddress(*outEntryPoint); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + if (SLANG_OK == res) + { + EntryPointRecorder* entryPointRecord = getEntryPointRecorder(*outEntryPoint); + *outEntryPoint = static_cast<slang::IEntryPoint*>(entryPointRecord); + } + return res; + } + + SLANG_NO_THROW SlangInt32 ModuleRecorder::getDependencyFileCount() + { + // No need to record this call as it is just a query. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + SlangInt32 res = m_actualModule->getDependencyFileCount(); + return res; + } + + SLANG_NO_THROW char const* ModuleRecorder::getDependencyFilePath(SlangInt32 index) + { + // No need to record this call as it is just a query. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + const char* res = m_actualModule->getDependencyFilePath(index); + return res; + } + + SLANG_NO_THROW slang::ISession* ModuleRecorder::getSession() + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + slang::ISession* session = m_actualModule->getSession(); + + return session; + } + + SLANG_NO_THROW slang::ProgramLayout* ModuleRecorder::getLayout( + SlangInt targetIndex, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getLayout, m_moduleHandle); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + slang::ProgramLayout* programLayout = m_actualModule->getLayout(targetIndex, outDiagnostics); + + { + recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(programLayout); + m_recordManager->endMethodRecordAppendOutput(); + } + + return programLayout; + } + + SLANG_NO_THROW SlangInt ModuleRecorder::getSpecializationParamCount() + { + // No need to record this call as it is just a query. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + SlangInt res = m_actualModule->getSpecializationParamCount(); + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::getEntryPointCode( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getEntryPointCode, m_moduleHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics); + + { + recorder->recordAddress(*outCode); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::getTargetCode( + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getTargetCode, m_moduleHandle); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->getTargetCode(targetIndex, outCode, outDiagnostics); + + { + recorder->recordAddress(*outCode); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::getResultAsFileSystem( + SlangInt entryPointIndex, + SlangInt targetIndex, + ISlangMutableFileSystem** outFileSystem) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getResultAsFileSystem, m_moduleHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem); + + { + recorder->recordAddress(*outFileSystem); + m_recordManager->endMethodRecordAppendOutput(); + } + + // TODO: We might need to wrap the file system object. + return res; + } + + SLANG_NO_THROW void ModuleRecorder::getEntryPointHash( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outHash) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getEntryPointHash, m_moduleHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + m_actualModule->getEntryPointHash(entryPointIndex, targetIndex, outHash); + + { + recorder->recordAddress(*outHash); + m_recordManager->endMethodRecordAppendOutput(); + } + } + + SLANG_NO_THROW SlangResult ModuleRecorder::specialize( + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + slang::IComponentType** outSpecializedComponentType, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_specialize, m_moduleHandle); + recorder->recordInt64(specializationArgCount); + recorder->recordStructArray(specializationArgs, specializationArgCount); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->specialize(specializationArgs, specializationArgCount, outSpecializedComponentType, outDiagnostics); + + { + recorder->recordAddress(*outSpecializedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::link( + IComponentType** outLinkedComponentType, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_link, m_moduleHandle); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->link(outLinkedComponentType, outDiagnostics); + + { + recorder->recordAddress(*outLinkedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::getEntryPointHostCallable( + int entryPointIndex, + int targetIndex, + ISlangSharedLibrary** outSharedLibrary, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_getEntryPointHostCallable, m_moduleHandle); + recorder->recordInt32(entryPointIndex); + recorder->recordInt32(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->getEntryPointHostCallable(entryPointIndex, targetIndex, outSharedLibrary, outDiagnostics); + + { + recorder->recordAddress(*outSharedLibrary); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::renameEntryPoint( + const char* newName, IComponentType** outEntryPoint) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_renameEntryPoint, m_moduleHandle); + recorder->recordString(newName); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->renameEntryPoint(newName, outEntryPoint); + + { + recorder->recordAddress(*outEntryPoint); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult ModuleRecorder::linkWithOptions( + IComponentType** outLinkedComponentType, + uint32_t compilerOptionEntryCount, + slang::CompilerOptionEntry* compilerOptionEntries, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::IModule_linkWithOptions, m_moduleHandle); + recorder->recordUint32(compilerOptionEntryCount); + recorder->recordStructArray(compilerOptionEntries, compilerOptionEntryCount); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualModule->linkWithOptions(outLinkedComponentType, compilerOptionEntryCount, compilerOptionEntries, outDiagnostics); + + { + recorder->recordAddress(*outLinkedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + EntryPointRecorder* ModuleRecorder::getEntryPointRecorder(slang::IEntryPoint* entryPoint) + { + EntryPointRecorder* entryPointRecord = nullptr; + entryPointRecord = m_mapEntryPointToRecord.tryGetValue(entryPoint); + if (!entryPointRecord) + { + entryPointRecord = new EntryPointRecorder(entryPoint, m_recordManager); + Slang::ComPtr<EntryPointRecorder> result(entryPointRecord); + m_mapEntryPointToRecord.add(entryPoint, *result.detach()); + } + return entryPointRecord; + } +} diff --git a/source/slang-record-replay/record/slang-module.h b/source/slang-record-replay/record/slang-module.h new file mode 100644 index 000000000..ca0403c2d --- /dev/null +++ b/source/slang-record-replay/record/slang-module.h @@ -0,0 +1,102 @@ +#ifndef SLANG_MODULE_H +#define SLANG_MODULE_H + +#include "slang-com-ptr.h" +#include "slang.h" +#include "slang-com-helper.h" +#include "../../core/slang-smart-pointer.h" +#include "../../slang/slang-compiler.h" +#include "slang-entrypoint.h" +#include "record-manager.h" + +namespace SlangRecord +{ + using namespace Slang; + class ModuleRecorder : public slang::IModule, public RefObject + { + public: + SLANG_COM_INTERFACE(0xb1802991, 0x185a, 0x4a03, { 0xa7, 0x7e, 0x0c, 0x86, 0xe0, 0x68, 0x2a, 0xab }) + + SLANG_REF_OBJECT_IUNKNOWN_ALL + ISlangUnknown* getInterface(const Guid& guid); + + explicit ModuleRecorder(slang::IModule* module, RecordManager* recordManager); + ~ModuleRecorder(); + + // Interfaces for `IModule` + virtual SLANG_NO_THROW SlangResult SLANG_MCALL findEntryPointByName( + char const* name, slang::IEntryPoint** outEntryPoint) override; + virtual SLANG_NO_THROW SlangInt32 SLANG_MCALL getDefinedEntryPointCount() override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL + getDefinedEntryPoint(SlangInt32 index, slang::IEntryPoint** outEntryPoint) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL serialize(ISlangBlob** outSerializedBlob) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL writeToFile(char const* fileName) override; + virtual SLANG_NO_THROW const char* SLANG_MCALL getName() override; + virtual SLANG_NO_THROW const char* SLANG_MCALL getFilePath() override; + virtual SLANG_NO_THROW const char* SLANG_MCALL getUniqueIdentity() override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL findAndCheckEntryPoint( + char const* name, SlangStage stage, slang::IEntryPoint** outEntryPoint, ISlangBlob** outDiagnostics) override; + virtual SLANG_NO_THROW SlangInt32 SLANG_MCALL getDependencyFileCount() override; + virtual SLANG_NO_THROW char const* SLANG_MCALL getDependencyFilePath( + SlangInt32 index) override; + + // Interfaces for `IComponentType` + virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override; + virtual SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout( + SlangInt targetIndex = 0, + slang::IBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangInt SLANG_MCALL getSpecializationParamCount() override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode( + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem( + SlangInt entryPointIndex, + SlangInt targetIndex, + ISlangMutableFileSystem** outFileSystem) override; + virtual SLANG_NO_THROW void SLANG_MCALL getEntryPointHash( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outHash) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL specialize( + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + slang::IComponentType** outSpecializedComponentType, + ISlangBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL link( + slang::IComponentType** outLinkedComponentType, + ISlangBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable( + int entryPointIndex, + int targetIndex, + ISlangSharedLibrary** outSharedLibrary, + slang::IBlob** outDiagnostics = 0) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL renameEntryPoint( + const char* newName, IComponentType** outEntryPoint) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions( + IComponentType** outLinkedComponentType, + uint32_t compilerOptionEntryCount, + slang::CompilerOptionEntry* compilerOptionEntries, + ISlangBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW slang::DeclReflection* getModuleReflection() override; + + slang::IModule* getActualModule() const { return m_actualModule; } + private: + EntryPointRecorder* getEntryPointRecorder(slang::IEntryPoint* entryPoint); + Slang::ComPtr<slang::IModule> m_actualModule; + uint64_t m_moduleHandle = 0; + RecordManager* m_recordManager = nullptr; + + // `IEntryPoint` can only be created from 'IModule', so we need to record it in + // this class, and create a map such that we don't create new `EntryPointRecorder` + // for the same `IEntryPoint`. + Dictionary<slang::IEntryPoint*, EntryPointRecorder> m_mapEntryPointToRecord; + }; +} // namespace SlangRecord + +#endif // SLANG_MODULE_H diff --git a/source/slang-record-replay/record/slang-session.cpp b/source/slang-record-replay/record/slang-session.cpp new file mode 100644 index 000000000..82c7a7479 --- /dev/null +++ b/source/slang-record-replay/record/slang-session.cpp @@ -0,0 +1,517 @@ +#include "../util/record-utility.h" +#include "slang-session.h" +#include "slang-entrypoint.h" +#include "slang-composite-component-type.h" +#include "slang-type-conformance.h" + +namespace SlangRecord +{ + + SessionRecorder::SessionRecorder(slang::ISession* session, RecordManager* recordManager) + : m_actualSession(session), + m_recordManager(recordManager) + { + SLANG_RECORD_ASSERT(m_actualSession); + SLANG_RECORD_ASSERT(m_recordManager); + m_sessionHandle = reinterpret_cast<uint64_t>(m_actualSession.get()); + slangRecordLog(LogLevel::Verbose, "%s: %p\n", "SessionRecorder create:", session); + } + + SessionRecorder::~SessionRecorder() + { + m_actualSession->release(); + } + + ISlangUnknown* SessionRecorder::getInterface(const Guid& guid) + { + if(guid == ISlangUnknown::getTypeGuid() || guid == ISession::getTypeGuid()) + return asExternal(this); + + return nullptr; + } + + SLANG_NO_THROW slang::IGlobalSession* SessionRecorder::getGlobalSession() + { + // No need to record this function. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + slang::IGlobalSession* pGlobalSession = m_actualSession->getGlobalSession(); + return pGlobalSession; + } + + SLANG_NO_THROW slang::IModule* SessionRecorder::loadModule( + const char* moduleName, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_loadModule, m_sessionHandle); + recorder->recordString(moduleName); + recorder = m_recordManager->endMethodRecord(); + } + + slang::IModule* pModule = m_actualSession->loadModule(moduleName, outDiagnostics); + + { + recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(pModule); + m_recordManager->endMethodRecordAppendOutput(); + } + + ModuleRecorder* pModuleRecorder = getModuleRecorder(pModule); + return static_cast<slang::IModule*>(pModuleRecorder); + } + + SLANG_NO_THROW slang::IModule* SessionRecorder::loadModuleFromIRBlob( + const char* moduleName, + const char* path, + slang::IBlob* source, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_loadModuleFromIRBlob, m_sessionHandle); + recorder->recordString(moduleName); + recorder->recordString(path); + recorder->recordPointer(source); + recorder = m_recordManager->endMethodRecord(); + } + + slang::IModule* pModule = m_actualSession->loadModuleFromIRBlob(moduleName, path, source, outDiagnostics); + + { + recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(pModule); + m_recordManager->endMethodRecordAppendOutput(); + } + + ModuleRecorder* pModuleRecorder = getModuleRecorder(pModule); + return static_cast<slang::IModule*>(pModuleRecorder); + } + + SLANG_NO_THROW slang::IModule* SessionRecorder::loadModuleFromSource( + const char* moduleName, + const char* path, + slang::IBlob* source, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_loadModuleFromSource, m_sessionHandle); + recorder->recordString(moduleName); + recorder->recordString(path); + recorder->recordPointer(source); + recorder = m_recordManager->endMethodRecord(); + } + + slang::IModule* pModule = m_actualSession->loadModuleFromSource(moduleName, path, source, outDiagnostics); + + { + recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(pModule); + m_recordManager->endMethodRecordAppendOutput(); + } + + ModuleRecorder* pModuleRecorder = getModuleRecorder(pModule); + return static_cast<slang::IModule*>(pModuleRecorder); + } + + SLANG_NO_THROW slang::IModule* SessionRecorder::loadModuleFromSourceString( + const char* moduleName, + const char* path, + const char* string, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_loadModuleFromSourceString, m_sessionHandle); + recorder->recordString(moduleName); + recorder->recordString(path); + recorder->recordString(string); + recorder = m_recordManager->endMethodRecord(); + } + + slang::IModule* pModule = m_actualSession->loadModuleFromSourceString(moduleName, path, string, outDiagnostics); + + { + // TODO: Not sure if we need to record the diagnostics blob. + recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(pModule); + m_recordManager->endMethodRecordAppendOutput(); + } + + ModuleRecorder* pModuleRecorder = getModuleRecorder(pModule); + return static_cast<slang::IModule*>(pModuleRecorder); + } + + SLANG_NO_THROW SlangResult SessionRecorder::createCompositeComponentType( + slang::IComponentType* const* componentTypes, + SlangInt componentTypeCount, + slang::IComponentType** outCompositeComponentType, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + Slang::List<slang::IComponentType*> componentTypeList; + + // get the actual component types from our record wrappers + if(SLANG_OK != getActualComponentTypes(componentTypes, componentTypeCount, componentTypeList)) + { + SLANG_RECORD_ASSERT(!"Failed to get actual component types"); + } + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_createCompositeComponentType, m_sessionHandle); + recorder->recordAddressArray(componentTypeList.getBuffer(), componentTypeCount); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult result = m_actualSession->createCompositeComponentType( + componentTypeList.getBuffer(), componentTypeCount, outCompositeComponentType, outDiagnostics); + + { + recorder->recordAddress(*outCompositeComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + if (SLANG_OK == result) + { + CompositeComponentTypeRecorder* compositeComponentTypeRecord = + new CompositeComponentTypeRecorder(*outCompositeComponentType, m_recordManager); + Slang::ComPtr<CompositeComponentTypeRecorder> resultRecord(compositeComponentTypeRecord); + *outCompositeComponentType = resultRecord.detach(); + } + + return result; + } + + SLANG_NO_THROW slang::TypeReflection* SessionRecorder::specializeType( + slang::TypeReflection* type, + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_specializeType, m_sessionHandle); + recorder->recordAddress(type); + recorder->recordStructArray(specializationArgs, specializationArgCount); + recorder = m_recordManager->endMethodRecord(); + } + + slang::TypeReflection* pTypeReflection = m_actualSession->specializeType(type, specializationArgs, specializationArgCount, outDiagnostics); + + { + recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(pTypeReflection); + m_recordManager->endMethodRecordAppendOutput(); + } + + return pTypeReflection; + } + + SLANG_NO_THROW slang::TypeLayoutReflection* SessionRecorder::getTypeLayout( + slang::TypeReflection* type, + SlangInt targetIndex, + slang::LayoutRules rules, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getTypeLayout, m_sessionHandle); + recorder->recordAddress(type); + recorder->recordInt64(targetIndex); + recorder->recordEnumValue(rules); + recorder = m_recordManager->endMethodRecord(); + } + + slang::TypeLayoutReflection* pTypeLayoutReflection = m_actualSession->getTypeLayout(type, targetIndex, rules, outDiagnostics); + + { + recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(pTypeLayoutReflection); + m_recordManager->endMethodRecordAppendOutput(); + } + + return pTypeLayoutReflection; + } + + SLANG_NO_THROW slang::TypeReflection* SessionRecorder::getContainerType( + slang::TypeReflection* elementType, + slang::ContainerType containerType, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getContainerType, m_sessionHandle); + recorder->recordAddress(elementType); + recorder->recordEnumValue(containerType); + recorder = m_recordManager->endMethodRecord(); + } + + slang::TypeReflection* pTypeReflection = m_actualSession->getContainerType(elementType, containerType, outDiagnostics); + + { + recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(pTypeReflection); + m_recordManager->endMethodRecordAppendOutput(); + } + + return pTypeReflection; + } + + SLANG_NO_THROW slang::TypeReflection* SessionRecorder::getDynamicType() + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getDynamicType, m_sessionHandle); + recorder = m_recordManager->endMethodRecord(); + } + + slang::TypeReflection* pTypeReflection = m_actualSession->getDynamicType(); + + { + recorder->recordAddress(pTypeReflection); + m_recordManager->endMethodRecordAppendOutput(); + } + + return pTypeReflection; + } + + SLANG_NO_THROW SlangResult SessionRecorder::getTypeRTTIMangledName( + slang::TypeReflection* type, + ISlangBlob** outNameBlob) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getTypeRTTIMangledName, m_sessionHandle); + recorder->recordAddress(type); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult result = m_actualSession->getTypeRTTIMangledName(type, outNameBlob); + + { + recorder->recordAddress(outNameBlob); + m_recordManager->endMethodRecordAppendOutput(); + } + + return result; + } + + SLANG_NO_THROW SlangResult SessionRecorder::getTypeConformanceWitnessMangledName( + slang::TypeReflection* type, + slang::TypeReflection* interfaceType, + ISlangBlob** outNameBlob) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getTypeConformanceWitnessMangledName, m_sessionHandle); + recorder->recordAddress(type); + recorder->recordAddress(interfaceType); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult result = m_actualSession->getTypeConformanceWitnessMangledName(type, interfaceType, outNameBlob); + + { + recorder->recordAddress(outNameBlob); + m_recordManager->endMethodRecordAppendOutput(); + } + + return result; + } + + SLANG_NO_THROW SlangResult SessionRecorder::getTypeConformanceWitnessSequentialID( + slang::TypeReflection* type, + slang::TypeReflection* interfaceType, + uint32_t* outId) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getTypeConformanceWitnessSequentialID, m_sessionHandle); + recorder->recordAddress(type); + recorder->recordAddress(interfaceType); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult result = m_actualSession->getTypeConformanceWitnessSequentialID(type, interfaceType, outId); + + // No need to record outId, it's not slang allocation + return result; + } + + SLANG_NO_THROW SlangResult SessionRecorder::createTypeConformanceComponentType( + slang::TypeReflection* type, + slang::TypeReflection* interfaceType, + slang::ITypeConformance** outConformance, + SlangInt conformanceIdOverride, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_createTypeConformanceComponentType, m_sessionHandle); + recorder->recordAddress(type); + recorder->recordAddress(interfaceType); + recorder->recordInt64(conformanceIdOverride); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult result = m_actualSession->createTypeConformanceComponentType(type, interfaceType, outConformance, conformanceIdOverride, outDiagnostics); + + { + recorder->recordAddress(*outConformance); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + if (SLANG_OK != result) + { + TypeConformanceRecorder* conformanceRecord = new TypeConformanceRecorder(*outConformance, m_recordManager); + Slang::ComPtr<TypeConformanceRecorder> resultRecord(conformanceRecord); + *outConformance = resultRecord.detach(); + } + + return result; + } + + SLANG_NO_THROW SlangResult SessionRecorder::createCompileRequest( + SlangCompileRequest** outCompileRequest) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_createCompileRequest, m_sessionHandle); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult result = m_actualSession->createCompileRequest(outCompileRequest); + + { + recorder->recordAddress(*outCompileRequest); + m_recordManager->endMethodRecordAppendOutput(); + } + + return result; + } + + SLANG_NO_THROW SlangInt SessionRecorder::getLoadedModuleCount() + { + // No need to record this function, it's just a query. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + SlangInt count = m_actualSession->getLoadedModuleCount(); + return count; + } + + SLANG_NO_THROW slang::IModule* SessionRecorder::getLoadedModule(SlangInt index) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ISession_getLoadedModule, m_sessionHandle); + recorder->recordInt64(index); + recorder = m_recordManager->endMethodRecord(); + } + + slang::IModule* pModule = m_actualSession->getLoadedModule(index); + + { + recorder->recordAddress(pModule); + m_recordManager->endMethodRecordAppendOutput(); + } + + if (pModule) + { + ModuleRecorder* moduleRecord = m_mapModuleToRecord.tryGetValue(pModule); + if (!moduleRecord) + { + SLANG_RECORD_ASSERT(!"Module not found in mapModuleToRecord"); + } + return static_cast<slang::IModule*>(moduleRecord); + } + + return pModule; + } + + SLANG_NO_THROW bool SessionRecorder::isBinaryModuleUpToDate(const char* modulePath, slang::IBlob* binaryModuleBlob) + { + // No need to record this function, it's a query function and doesn't impact slang internal state. + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + bool result = m_actualSession->isBinaryModuleUpToDate(modulePath, binaryModuleBlob); + return result; + } + + ModuleRecorder* SessionRecorder::getModuleRecorder(slang::IModule* module) + { + ModuleRecorder* moduleRecord = nullptr; + moduleRecord = m_mapModuleToRecord.tryGetValue(module); + if (!moduleRecord) + { + moduleRecord = new ModuleRecorder(module, m_recordManager); + Slang::ComPtr<ModuleRecorder> result(moduleRecord); + m_mapModuleToRecord.add(module, *result.detach()); + } + return moduleRecord; + } + + SlangResult SessionRecorder::getActualComponentTypes( + slang::IComponentType* const* componentTypes, + SlangInt componentTypeCount, + List<slang::IComponentType*>& outActualComponentTypes) + { + for (SlangInt i = 0; i < componentTypeCount; i++) + { + slang::IComponentType* const& componentType = componentTypes[i]; + void* outObj = nullptr; + + if (componentType->queryInterface(ModuleRecorder::getTypeGuid(), &outObj) == SLANG_OK) + { + ModuleRecorder* moduleRecord = static_cast<ModuleRecorder*>(outObj); + outActualComponentTypes.add(moduleRecord->getActualModule()); + } + else if (componentType->queryInterface(EntryPointRecorder::getTypeGuid(), &outObj) == SLANG_OK) + { + EntryPointRecorder* entrypointRecord = static_cast<EntryPointRecorder*>(outObj); + outActualComponentTypes.add(entrypointRecord->getActualEntryPoint()); + } + // will fall back to the actual component type, it means that we didn't record this type. + else + { + outActualComponentTypes.add(componentType); + } + } + + if (componentTypeCount == outActualComponentTypes.getCount()) + { + return SLANG_OK; + } + return SLANG_FAIL; + } +} // namespace SlangRecord diff --git a/source/slang-record-replay/record/slang-session.h b/source/slang-record-replay/record/slang-session.h new file mode 100644 index 000000000..ca06e25a0 --- /dev/null +++ b/source/slang-record-replay/record/slang-session.h @@ -0,0 +1,110 @@ +#ifndef SLANG_SESSION_H +#define SLANG_SESSION_H + +#include "slang-com-ptr.h" +#include "slang.h" +#include "slang-com-helper.h" +#include "../../core/slang-smart-pointer.h" +#include "../../core/slang-dictionary.h" +#include "../../slang/slang-compiler.h" +#include "slang-module.h" +#include "record-manager.h" + +namespace SlangRecord +{ + using namespace Slang; + class SessionRecorder: public RefObject, public slang::ISession + { + public: + SLANG_REF_OBJECT_IUNKNOWN_ALL + ISlangUnknown* getInterface(const Guid& guid); + + explicit SessionRecorder(slang::ISession* session, RecordManager* recordManager); + ~SessionRecorder(); + + SLANG_NO_THROW slang::IGlobalSession* SLANG_MCALL getGlobalSession() override; + SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModule( + const char* moduleName, + slang::IBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromIRBlob( + const char* moduleName, + const char* path, + slang::IBlob* source, + slang::IBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromSource( + const char* moduleName, + const char* path, + slang::IBlob* source, + slang::IBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromSourceString( + const char* moduleName, + const char* path, + const char* string, + slang::IBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW SlangResult SLANG_MCALL createCompositeComponentType( + slang::IComponentType* const* componentTypes, + SlangInt componentTypeCount, + slang::IComponentType** outCompositeComponentType, + ISlangBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL specializeType( + slang::TypeReflection* type, + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + ISlangBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL getTypeLayout( + slang::TypeReflection* type, + SlangInt targetIndex = 0, + slang::LayoutRules rules = slang::LayoutRules::Default, + ISlangBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL getContainerType( + slang::TypeReflection* elementType, + slang::ContainerType containerType, + ISlangBlob** outDiagnostics = nullptr) override; + SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL getDynamicType() override; + SLANG_NO_THROW SlangResult SLANG_MCALL getTypeRTTIMangledName( + slang::TypeReflection* type, + ISlangBlob** outNameBlob) override; + SLANG_NO_THROW SlangResult SLANG_MCALL getTypeConformanceWitnessMangledName( + slang::TypeReflection* type, + slang::TypeReflection* interfaceType, + ISlangBlob** outNameBlob) override; + SLANG_NO_THROW SlangResult SLANG_MCALL getTypeConformanceWitnessSequentialID( + slang::TypeReflection* type, + slang::TypeReflection* interfaceType, + uint32_t* outId) override; + SLANG_NO_THROW SlangResult SLANG_MCALL createTypeConformanceComponentType( + slang::TypeReflection* type, + slang::TypeReflection* interfaceType, + slang::ITypeConformance** outConformance, + SlangInt conformanceIdOverride, + ISlangBlob** outDiagnostics) override; + SLANG_NO_THROW SlangResult SLANG_MCALL createCompileRequest( + SlangCompileRequest** outCompileRequest) override; + SLANG_NO_THROW SlangInt SLANG_MCALL getLoadedModuleCount() override; + SLANG_NO_THROW slang::IModule* SLANG_MCALL getLoadedModule(SlangInt index) override; + SLANG_NO_THROW bool SLANG_MCALL isBinaryModuleUpToDate(const char* modulePath, slang::IBlob* binaryModuleBlob) override; + + private: + SLANG_FORCE_INLINE slang::ISession* asExternal(SessionRecorder* session) + { + return static_cast<slang::ISession*>(session); + } + + // The IComponentType object is the record target, therefore `componentTypes` will not be + // the actual component types, we have to use the COM interface to get the actual objects. + SlangResult getActualComponentTypes( + slang::IComponentType* const* componentTypes, + SlangInt componentTypeCount, + List<slang::IComponentType*>& outActualComponentTypes); + + ModuleRecorder* getModuleRecorder(slang::IModule* module); + + Slang::ComPtr<slang::ISession> m_actualSession; + uint64_t m_sessionHandle = 0; + + Dictionary<slang::IModule*, ModuleRecorder> m_mapModuleToRecord; + RecordManager* m_recordManager = nullptr; + }; +} + +#endif // SLANG_SESSION_H diff --git a/source/slang-record-replay/record/slang-type-conformance.cpp b/source/slang-record-replay/record/slang-type-conformance.cpp new file mode 100644 index 000000000..b9b652e9e --- /dev/null +++ b/source/slang-record-replay/record/slang-type-conformance.cpp @@ -0,0 +1,310 @@ +#include "../util/record-utility.h" +#include "slang-type-conformance.h" + +namespace SlangRecord +{ + TypeConformanceRecorder::TypeConformanceRecorder(slang::ITypeConformance* typeConformance, RecordManager* recordManager) + : m_actualTypeConformance(typeConformance), + m_recordManager(recordManager) + { + SLANG_RECORD_ASSERT(m_actualTypeConformance != nullptr); + SLANG_RECORD_ASSERT(m_recordManager != nullptr); + + m_typeConformanceHandle = reinterpret_cast<uint64_t>(m_actualTypeConformance.get()); + slangRecordLog(LogLevel::Verbose, "%s: %p\n", __PRETTY_FUNCTION__, typeConformance); + } + + TypeConformanceRecorder::~TypeConformanceRecorder() + { + m_actualTypeConformance->release(); + } + + ISlangUnknown* TypeConformanceRecorder::getInterface(const Guid& guid) + { + if (guid == TypeConformanceRecorder::getTypeGuid()) + { + return static_cast<ISlangUnknown*>(this); + } + else + { + return nullptr; + } + } + + SLANG_NO_THROW slang::ISession* TypeConformanceRecorder::getSession() + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getSession, m_typeConformanceHandle); + recorder = m_recordManager->endMethodRecord(); + } + + slang::ISession* res = m_actualTypeConformance->getSession(); + + { + recorder->recordAddress(res); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW slang::ProgramLayout* TypeConformanceRecorder::getLayout( + SlangInt targetIndex, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ICompositeComponentType_getLayout, m_typeConformanceHandle); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + slang::ProgramLayout* programLayout = m_actualTypeConformance->getLayout(targetIndex, outDiagnostics); + + { + recorder->recordAddress(*outDiagnostics); + recorder->recordAddress(programLayout); + m_recordManager->endMethodRecordAppendOutput(); + } + + return programLayout; + } + + SLANG_NO_THROW SlangInt TypeConformanceRecorder::getSpecializationParamCount() + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + SlangInt res = m_actualTypeConformance->getSpecializationParamCount(); + return res; + } + + SLANG_NO_THROW SlangResult TypeConformanceRecorder::getEntryPointCode( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getEntryPointCode, m_typeConformanceHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualTypeConformance->getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics); + + { + recorder->recordAddress(*outCode); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult TypeConformanceRecorder::getTargetCode( + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getTargetCode, m_typeConformanceHandle); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualTypeConformance->getTargetCode(targetIndex, outCode, outDiagnostics); + + { + recorder->recordAddress(*outCode); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult TypeConformanceRecorder::getResultAsFileSystem( + SlangInt entryPointIndex, + SlangInt targetIndex, + ISlangMutableFileSystem** outFileSystem) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getResultAsFileSystem, m_typeConformanceHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualTypeConformance->getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem); + + { + recorder->recordAddress(*outFileSystem); + } + + // TODO: We might need to wrap the file system object. + return res; + } + + SLANG_NO_THROW void TypeConformanceRecorder::getEntryPointHash( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outHash) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getEntryPointHash, m_typeConformanceHandle); + recorder->recordInt64(entryPointIndex); + recorder->recordInt64(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + m_actualTypeConformance->getEntryPointHash(entryPointIndex, targetIndex, outHash); + + { + recorder->recordAddress(*outHash); + m_recordManager->endMethodRecordAppendOutput(); + } + } + + SLANG_NO_THROW SlangResult TypeConformanceRecorder::specialize( + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + slang::IComponentType** outSpecializedComponentType, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_specialize, m_typeConformanceHandle); + recorder->recordInt64(specializationArgCount); + recorder->recordStructArray(specializationArgs, specializationArgCount); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualTypeConformance->specialize(specializationArgs, specializationArgCount, outSpecializedComponentType, outDiagnostics); + + { + recorder->recordAddress(*outSpecializedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult TypeConformanceRecorder::link( + slang::IComponentType** outLinkedComponentType, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_link, m_typeConformanceHandle); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualTypeConformance->link(outLinkedComponentType, outDiagnostics); + + { + recorder->recordAddress(*outLinkedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult TypeConformanceRecorder::getEntryPointHostCallable( + int entryPointIndex, + int targetIndex, + ISlangSharedLibrary** outSharedLibrary, + slang::IBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_getEntryPointHostCallable, m_typeConformanceHandle); + recorder->recordInt32(entryPointIndex); + recorder->recordInt32(targetIndex); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualTypeConformance->getEntryPointHostCallable(entryPointIndex, targetIndex, outSharedLibrary, outDiagnostics); + + { + recorder->recordAddress(*outSharedLibrary); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult TypeConformanceRecorder::renameEntryPoint( + const char* newName, IComponentType** outEntryPoint) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_renameEntryPoint, m_typeConformanceHandle); + recorder->recordString(newName); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualTypeConformance->renameEntryPoint(newName, outEntryPoint); + + { + recorder->recordAddress(*outEntryPoint); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } + + SLANG_NO_THROW SlangResult TypeConformanceRecorder::linkWithOptions( + IComponentType** outLinkedComponentType, + uint32_t compilerOptionEntryCount, + slang::CompilerOptionEntry* compilerOptionEntries, + ISlangBlob** outDiagnostics) + { + slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__); + + ParameterRecorder* recorder {}; + { + recorder = m_recordManager->beginMethodRecord(ApiCallId::ITypeConformance_linkWithOptions, m_typeConformanceHandle); + recorder->recordUint32(compilerOptionEntryCount); + recorder->recordStructArray(compilerOptionEntries, compilerOptionEntryCount); + recorder = m_recordManager->endMethodRecord(); + } + + SlangResult res = m_actualTypeConformance->linkWithOptions(outLinkedComponentType, compilerOptionEntryCount, compilerOptionEntries, outDiagnostics); + + { + recorder->recordAddress(*outLinkedComponentType); + recorder->recordAddress(*outDiagnostics); + m_recordManager->endMethodRecordAppendOutput(); + } + + return res; + } +} diff --git a/source/slang-record-replay/record/slang-type-conformance.h b/source/slang-record-replay/record/slang-type-conformance.h new file mode 100644 index 000000000..7bcae0d15 --- /dev/null +++ b/source/slang-record-replay/record/slang-type-conformance.h @@ -0,0 +1,77 @@ +#ifndef SLANG_TYPE_CONFORMANCE_H +#define SLANG_TYPE_CONFORMANCE_H + +#include "slang-com-ptr.h" +#include "slang.h" +#include "slang-com-helper.h" +#include "../../core/slang-smart-pointer.h" +#include "../../core/slang-dictionary.h" +#include "../../slang/slang-compiler.h" +#include "record-manager.h" + +namespace SlangRecord +{ + using namespace Slang; + class TypeConformanceRecorder: public slang::ITypeConformance, public RefObject + { + public: + SLANG_COM_INTERFACE(0x0e67d05d, 0xee0a, 0x41e1, { 0xb5, 0xa3, 0x23, 0xe3, 0xb0, 0xec, 0x33, 0xf1 }) + + SLANG_REF_OBJECT_IUNKNOWN_ALL + ISlangUnknown* getInterface(const Guid& guid); + + explicit TypeConformanceRecorder(slang::ITypeConformance* typeConformance, RecordManager* recordManager); + ~TypeConformanceRecorder(); + + // Interfaces for `IComponentType` + virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override; + virtual SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout( + SlangInt targetIndex = 0, + slang::IBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangInt SLANG_MCALL getSpecializationParamCount() override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode( + SlangInt targetIndex, + slang::IBlob** outCode, + slang::IBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem( + SlangInt entryPointIndex, + SlangInt targetIndex, + ISlangMutableFileSystem** outFileSystem) override; + virtual SLANG_NO_THROW void SLANG_MCALL getEntryPointHash( + SlangInt entryPointIndex, + SlangInt targetIndex, + slang::IBlob** outHash) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL specialize( + slang::SpecializationArg const* specializationArgs, + SlangInt specializationArgCount, + slang::IComponentType** outSpecializedComponentType, + ISlangBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL link( + slang::IComponentType** outLinkedComponentType, + ISlangBlob** outDiagnostics = nullptr) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable( + int entryPointIndex, + int targetIndex, + ISlangSharedLibrary** outSharedLibrary, + slang::IBlob** outDiagnostics = 0) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL renameEntryPoint( + const char* newName, IComponentType** outEntryPoint) override; + virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions( + IComponentType** outLinkedComponentType, + uint32_t compilerOptionEntryCount, + slang::CompilerOptionEntry* compilerOptionEntries, + ISlangBlob** outDiagnostics = nullptr) override; + + slang::ITypeConformance* getActualTypeConformance() const { return m_actualTypeConformance; } + private: + Slang::ComPtr<slang::ITypeConformance> m_actualTypeConformance; + uint64_t m_typeConformanceHandle = 0; + RecordManager* m_recordManager = nullptr; + }; +} +#endif // SLANG_TYPE_CONFORMANCE_H |
