diff --git a/docs/design/datacontracts/StressLog.md b/docs/design/datacontracts/StressLog.md index abb7824a837f0c..f4630b391a2d21 100644 --- a/docs/design/datacontracts/StressLog.md +++ b/docs/design/datacontracts/StressLog.md @@ -53,6 +53,7 @@ Data descriptors used: | StressLog | TickFrequency | Number of ticks per second for stresslog timestamps | | StressLog | StartTimestamp | Timestamp when the stress log was started | | StressLog | ModuleOffset | Offset of the module in the stress log | +| StressLog | Modules | Offset of the stress log's module table (if StressLogHasModuleTable is `1`) | | StressLog | Logs | Pointer to the thread-specific logs | | StressLogModuleDesc | BaseAddress | Base address of the module | | StressLogModuleDesc | Size | Size of the module | @@ -80,7 +81,6 @@ Global variables used: | StressLogChunkSize | uint | Size of a stress log chunk | | StressLogMaxMessageSize | ulong | Maximum size of a stress log message | | StressLogHasModuleTable | byte | Whether the stress log module table is present | -| StressLogModuleTable | pointer | Pointer to the stress log's module table (if StressLogHasModuleTable is `1`) | ```csharp bool HasStressLog() @@ -216,12 +216,21 @@ protected TargetPointer GetFormatPointer(ulong formatOffset) return new TargetPointer(stressLog.ModuleOffset + formatOffset); } - TargetPointer moduleTable = target.ReadGlobalPointer(Constants.Globals.StressLogModuleTable); + TargetPointer? moduleTable; + if (!target.TryReadGlobalPointer(Constants.Globals.StressLogModuleTable, out moduleTable)) + { + if (!target.TryReadGlobalPointer(Constants.Globals.StressLog, out TargetPointer? pStressLog)) + { + throw new InvalidOperationException("StressLogModuleTable is not set and StressLog is not available, but StressLogHasModuleTable is set to 1."); + } + Data.StressLog stressLog = target.ProcessedData.GetOrAdd(pStressLog.Value); + moduleTable = stressLog.Modules ?? throw new InvalidOperationException("StressLogModuleTable is not set and StressLog does not contain a ModuleTable offset, but StressLogHasModuleTable is set to 1."); + } uint moduleEntrySize = target.GetTypeInfo(DataType.StressLogModuleDesc).Size!.Value; uint maxModules = target.ReadGlobal(Constants.Globals.StressLogMaxModules); for (uint i = 0; i < maxModules; ++i) { - StressLogModuleDesc module = new(Target, moduleTable + i * moduleEntrySize); + StressLogModuleDesc module = new(Target, moduleTable.Value + i * moduleEntrySize); ulong relativeOffset = formatOffset - cumulativeOffset; if (relativeOffset < module.Size.Value) { diff --git a/src/coreclr/clrdatadescriptors.cmake b/src/coreclr/clrdatadescriptors.cmake index 21f7b1600e830b..bd3d13836b4f08 100644 --- a/src/coreclr/clrdatadescriptors.cmake +++ b/src/coreclr/clrdatadescriptors.cmake @@ -78,5 +78,13 @@ function(generate_data_descriptors) # inherit definitions, include directories, and dependencies from the INTERFACE target target_link_libraries(${LIBRARY} PRIVATE ${DATA_DESCRIPTORS_INTERFACE_TARGET}) + + if(MSVC) + # /Zc:externConstexpr is required to export constexpr variables + # from the object file, otherwise the linker will not export them. + # See https://learn.microsoft.com/en-us/cpp/build/reference/zc-externconstexpr + # for more details. + target_compile_options(${LIBRARY} PRIVATE /Zc:externConstexpr) + endif(MSVC) endif() endfunction(generate_data_descriptors) diff --git a/src/coreclr/debug/datadescriptor-shared/contract-descriptor.c.in b/src/coreclr/debug/datadescriptor-shared/contract-descriptor.c.in index a728afc10dcb33..8da6f70498461f 100644 --- a/src/coreclr/debug/datadescriptor-shared/contract-descriptor.c.in +++ b/src/coreclr/debug/datadescriptor-shared/contract-descriptor.c.in @@ -18,12 +18,12 @@ struct ContractDescriptor const char *descriptor; const uint32_t pointer_data_count; uint32_t pad0; - const uintptr_t *pointer_data; + const void** pointer_data; }; // POINTER_DATA_NAME and CONTRACT_NAME are macros provided by // contractconfiguration.h which is configured by CMake -extern const uintptr_t POINTER_DATA_NAME[]; +extern const void* POINTER_DATA_NAME[]; #if EXPORT_CONTRACT DLLEXPORT diff --git a/src/coreclr/debug/datadescriptor-shared/contractdescriptorstub.c b/src/coreclr/debug/datadescriptor-shared/contractdescriptorstub.c index 6bcabd2c829618..e403b4711eb61d 100644 --- a/src/coreclr/debug/datadescriptor-shared/contractdescriptorstub.c +++ b/src/coreclr/debug/datadescriptor-shared/contractdescriptorstub.c @@ -18,15 +18,15 @@ struct ContractDescriptor const char *descriptor; const uint32_t pointer_data_count; uint32_t pad0; - const uintptr_t *pointer_data; + const void** pointer_data; }; // POINTER_DATA_NAME and CONTRACT_NAME are macros provided by // contractconfiguration.h which is configured by CMake -extern const uintptr_t POINTER_DATA_NAME[]; +extern const void* POINTER_DATA_NAME[]; // just the placeholder pointer -const uintptr_t POINTER_DATA_NAME[] = { (uintptr_t)0 }; +const void* POINTER_DATA_NAME[] = { (void*)0 }; DLLEXPORT struct ContractDescriptor CONTRACT_NAME; diff --git a/src/coreclr/debug/datadescriptor-shared/contractpointerdata.cpp b/src/coreclr/debug/datadescriptor-shared/contractpointerdata.cpp index 9e1c9929aa41df..443c7f182c001f 100644 --- a/src/coreclr/debug/datadescriptor-shared/contractpointerdata.cpp +++ b/src/coreclr/debug/datadescriptor-shared/contractpointerdata.cpp @@ -6,13 +6,12 @@ extern "C" { -// without an extern declaration, clang does not emit this global into the object file -extern const uintptr_t POINTER_DATA_NAME[]; - -const uintptr_t POINTER_DATA_NAME[] = { - (uintptr_t)0, // placeholder -#define CDAC_GLOBAL_POINTER(name,value) (uintptr_t)(value), -#define CDAC_GLOBAL_SUB_DESCRIPTOR(name,value) (uintptr_t)(value), -#include "wrappeddatadescriptor.inc" + // without an extern declaration, clang does not emit this global into the object file + extern constexpr const void* POINTER_DATA_NAME[] = { + (void*)0, // placeholder + #define CDAC_GLOBAL_POINTER(name,value) (void*)(value), + #define CDAC_GLOBAL_SUB_DESCRIPTOR(name,value) (void*)(value), + #include "wrappeddatadescriptor.inc" + }; }; -} + diff --git a/src/coreclr/inc/stresslog.h b/src/coreclr/inc/stresslog.h index 236e559f4f88c5..9219a4b32fa9eb 100644 --- a/src/coreclr/inc/stresslog.h +++ b/src/coreclr/inc/stresslog.h @@ -396,23 +396,24 @@ inline BOOL StressLog::LogOn(unsigned facility, unsigned level) template<> struct cdac_offsets { - static const size_t facilitiesToLog = offsetof(StressLog, facilitiesToLog); - static const size_t levelToLog = offsetof(StressLog, levelToLog); - static const size_t MaxSizePerThread = offsetof(StressLog, MaxSizePerThread); - static const size_t MaxSizeTotal = offsetof(StressLog, MaxSizeTotal); - static const size_t totalChunk = offsetof(StressLog, totalChunk); - static const size_t logs = offsetof(StressLog, logs); - static const size_t tickFrequency = offsetof(StressLog, tickFrequency); - static const size_t startTimeStamp = offsetof(StressLog, startTimeStamp); - static const size_t startTime = offsetof(StressLog, startTime); - static const size_t moduleOffset = offsetof(StressLog, moduleOffset); - static constexpr uint64_t MAX_MODULES = StressLog::MAX_MODULES; + static constexpr size_t facilitiesToLog = offsetof(StressLog, facilitiesToLog); + static constexpr size_t levelToLog = offsetof(StressLog, levelToLog); + static constexpr size_t MaxSizePerThread = offsetof(StressLog, MaxSizePerThread); + static constexpr size_t MaxSizeTotal = offsetof(StressLog, MaxSizeTotal); + static constexpr size_t totalChunk = offsetof(StressLog, totalChunk); + static constexpr size_t logs = offsetof(StressLog, logs); + static constexpr size_t tickFrequency = offsetof(StressLog, tickFrequency); + static constexpr size_t startTimeStamp = offsetof(StressLog, startTimeStamp); + static constexpr size_t startTime = offsetof(StressLog, startTime); + static constexpr size_t moduleOffset = offsetof(StressLog, moduleOffset); + static constexpr size_t MAX_MODULES = StressLog::MAX_MODULES; + static constexpr size_t modules = offsetof(StressLog, modules); struct ModuleDesc { static constexpr size_t type_size = sizeof(StressLog::ModuleDesc); - static const size_t baseAddress = offsetof(StressLog::ModuleDesc, baseAddress); - static const size_t size = offsetof(StressLog::ModuleDesc, size); + static constexpr size_t baseAddress = offsetof(StressLog::ModuleDesc, baseAddress); + static constexpr size_t size = offsetof(StressLog::ModuleDesc, size); }; }; diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 167c581cf9e2d3..3d8559f5f3e92d 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -317,6 +317,7 @@ CDAC_TYPE_FIELD(StressLog, /* pointer */, Logs, cdac_offsets::logs) CDAC_TYPE_FIELD(StressLog, /* uint64 */, TickFrequency, cdac_offsets::tickFrequency) CDAC_TYPE_FIELD(StressLog, /* uint64 */, StartTimestamp, cdac_offsets::startTimeStamp) CDAC_TYPE_FIELD(StressLog, /* nuint */, ModuleOffset, cdac_offsets::moduleOffset) +CDAC_TYPE_FIELD(StressLog, /* StressLogModuleDesc[] */, Modules, cdac_offsets::modules) CDAC_TYPE_END(StressLog) CDAC_TYPE_BEGIN(StressLogModuleDesc) @@ -908,7 +909,6 @@ CDAC_GLOBAL_POINTER(TlsIndexBase, &::_tls_index) CDAC_GLOBAL(StressLogEnabled, uint8, 1) CDAC_GLOBAL_POINTER(StressLog, &g_pStressLog) CDAC_GLOBAL(StressLogHasModuleTable, uint8, 1) -CDAC_GLOBAL_POINTER(StressLogModuleTable, &g_pStressLog->modules) CDAC_GLOBAL(StressLogMaxModules, uint64, cdac_offsets::MAX_MODULES) CDAC_GLOBAL(StressLogChunkSize, uint32, STRESSLOG_CHUNK_SIZE) CDAC_GLOBAL(StressLogValidChunkSig, uint32, StressLogChunk::ValidChunkSig) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StressLog.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StressLog.cs index 5d4885dca2fc79..7940f14b7faa7f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StressLog.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StressLog.cs @@ -112,13 +112,22 @@ private TargetPointer GetFormatPointer(ulong formatOffset) return new TargetPointer(stressLog.ModuleOffset.Value + formatOffset); } - TargetPointer moduleTable = target.ReadGlobalPointer(Constants.Globals.StressLogModuleTable); + TargetPointer? moduleTable; + if (!target.TryReadGlobalPointer(Constants.Globals.StressLogModuleTable, out moduleTable)) + { + if (!target.TryReadGlobalPointer(Constants.Globals.StressLog, out TargetPointer? pStressLog)) + { + throw new InvalidOperationException("StressLogModuleTable is not set and StressLog is not available, but StressLogHasModuleTable is set to 1."); + } + Data.StressLog stressLog = target.ProcessedData.GetOrAdd(pStressLog.Value); + moduleTable = stressLog.Modules ?? throw new InvalidOperationException("StressLogModuleTable is not set and StressLog does not contain a ModuleTable offset, but StressLogHasModuleTable is set to 1."); + } uint moduleEntrySize = target.GetTypeInfo(DataType.StressLogModuleDesc).Size!.Value; uint maxModules = target.ReadGlobal(Constants.Globals.StressLogMaxModules); ulong cumulativeOffset = 0; for (uint i = 0; i < maxModules; ++i) { - Data.StressLogModuleDesc module = target.ProcessedData.GetOrAdd(moduleTable + i * moduleEntrySize); + Data.StressLogModuleDesc module = target.ProcessedData.GetOrAdd(moduleTable.Value + i * moduleEntrySize); ulong relativeOffset = formatOffset - cumulativeOffset; if (relativeOffset < module.Size.Value) { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StressLog.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StressLog.cs index 71f4c3ca20f5ac..b23eff5acda316 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StressLog.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StressLog.cs @@ -23,6 +23,9 @@ public StressLog(Target target, TargetPointer address) StartTimestamp = target.Read(address + (ulong)type.Fields[nameof(StartTimestamp)].Offset); ModuleOffset = target.ReadNUInt(address + (ulong)type.Fields[nameof(ModuleOffset)].Offset); + if (type.Fields.ContainsKey(nameof(Modules))) + Modules = target.ReadPointer(address + (ulong)type.Fields[nameof(Modules)].Offset); + Logs = target.ReadPointer(address + (ulong)type.Fields[nameof(Logs)].Offset); } @@ -42,5 +45,7 @@ public StressLog(Target target, TargetPointer address) public TargetNUInt ModuleOffset { get; init; } + public TargetPointer? Modules { get; init; } + public TargetPointer Logs { get; init; } } diff --git a/src/tools/StressLogAnalyzer/src/Program.cs b/src/tools/StressLogAnalyzer/src/Program.cs index 69b3a55584b4f9..4012f137e97673 100644 --- a/src/tools/StressLogAnalyzer/src/Program.cs +++ b/src/tools/StressLogAnalyzer/src/Program.cs @@ -520,7 +520,7 @@ private static ContractDescriptorParser.ContractDescriptor GetDescriptor(int str { Baseline = BaseContractDescriptor.Baseline, Version = BaseContractDescriptor.Version, - Contracts = new(){ { "StressLog", stressLogVersion } }, + Contracts = new() { { "StressLog", stressLogVersion } }, Types = BaseContractDescriptor.Types, Globals = BaseContractDescriptor.Globals, }; @@ -542,7 +542,8 @@ private static ContractDescriptorParser.ContractDescriptor GetDescriptor(int str "Logs": 24, "TickFrequency": 48, "StartTimestamp": 56, - "ModuleOffset": 72 + "ModuleOffset": 72, + "Modules": 80 }, "StressLogModuleDesc": { "!": 16,