From 5c793ecbf1cd1afce007bd50f70cdb914b8f6df0 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 16 Jun 2026 16:56:23 -0400 Subject: [PATCH] Use ISOSDacInterface17 for DumpLog when available Add StressLog::DumpViaInterface17 which queries for ISOSDacInterface17 to enumerate stress log threads and messages via the cDAC contract path. DumpLog tries this first, falling back to the legacy raw-read path if the interface is unavailable. A -legacy flag opts out explicitly. - Add ISOSDacInterface17, ISOSStressLogThreadEnum, ISOSStressLogMsgEnum and supporting SOS* data structs to the prebuilt sospriv headers - Fix legacy StressLog::Dump incorrectly returning a failure HRESULT when a format string ReadVirtual failed mid-loop, even though all entries were successfully processed - Gate stress log enablement in tests to only OtherCommands (DumpLog) - Add DumpLog CI test coverage with VERIFY Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/SOS/Strike/stressLogDump.cpp | 237 +++++++++++++- src/SOS/Strike/strike.cpp | 35 ++- src/shared/inc/stresslog.h | 4 + src/shared/pal/prebuilt/idl/sospriv_i.cpp | 9 + src/shared/pal/prebuilt/inc/sospriv.h | 291 ++++++++++++++++++ src/tests/SOS.UnitTests/SOS.cs | 1 + src/tests/SOS.UnitTests/SOSRunner.cs | 24 ++ .../Scripts/OtherCommands.script | 3 + 8 files changed, 594 insertions(+), 10 deletions(-) diff --git a/src/SOS/Strike/stressLogDump.cpp b/src/SOS/Strike/stressLogDump.cpp index 3b851555d8..18d6e802f7 100644 --- a/src/SOS/Strike/stressLogDump.cpp +++ b/src/SOS/Strike/stressLogDump.cpp @@ -11,6 +11,7 @@ #include "util.h" #include #include +#include #ifndef STRESS_LOG #define STRESS_LOG @@ -361,6 +362,236 @@ StressMsg* GetStressMsgInLatestVersion(StressMsg* rawMsg, int version) return rawMsg; } +/*********************************************************************************/ +// Try to dump the stress log using ISOSDacInterface17. +// Returns S_OK on success, S_FALSE if no messages, or E_NOINTERFACE if the +// interface is not available (caller should fall back to legacy path). +HRESULT StressLog::DumpViaInterface17(const char* fileName, struct IDebugDataSpaces* memCallBack) +{ + // Try to get ISOSDacInterface17 + if (g_clrData == NULL) + { + return E_NOINTERFACE; + } + ReleaseHolder pSos17; + HRESULT hr = g_clrData->QueryInterface(__uuidof(ISOSDacInterface17), (void**)&pSos17); + if (FAILED(hr) || pSos17 == NULL) + { + return E_NOINTERFACE; + } + + // Get stress log header data + SOSStressLogData logData; + hr = pSos17->GetStressLogData(&logData); + if (hr != S_OK) + { + return FAILED(hr) ? hr : S_FALSE; + } + + FILE* file = NULL; + + // Get thread enumerator + ReleaseHolder pThreadEnum; + hr = pSos17->GetStressLogThreadEnumerator(pThreadEnum.GetAddr()); + if (FAILED(hr) || pThreadEnum == NULL) + { + return FAILED(hr) ? hr : E_FAIL; + } + + // Collect all threads + struct ThreadInfo + { + SOSThreadStressLogData data; + uint64_t latestTimestamp; + }; + + std::vector threads; + SOSThreadStressLogData threadData; + unsigned int fetched = 0; + for (;;) + { + fetched = 0; + hr = pThreadEnum->Next(1, &threadData, &fetched); + if (FAILED(hr) || fetched == 0) + break; + threads.push_back({ threadData, 0 }); + } + pThreadEnum.Release(); + + if (threads.empty()) + { + ExtOut("----- No thread logs in the image -----\n"); + return S_FALSE; + } + + // Create message enumerators for each thread and find the latest timestamp + struct ThreadMsgState + { + ReleaseHolder pEnum; + SOSStressMsgData currentMsg; + bool hasMsg; + uint64_t threadId; + }; + + unsigned int threadCount = (unsigned int)threads.size(); + ArrayHolder msgStates = new (std::nothrow) ThreadMsgState[threadCount]; + if (msgStates == NULL) + { + return E_OUTOFMEMORY; + } + unsigned int msgStateCount = 0; + uint64_t lastTimeStamp = 0; + + for (auto& t : threads) + { + ISOSStressLogMsgEnum* pMsgEnum = NULL; + hr = pSos17->GetStressLogMessageEnumerator(t.data.ThreadLogAddress, &pMsgEnum); + if (FAILED(hr) || pMsgEnum == NULL) + continue; + + ThreadMsgState& state = msgStates[msgStateCount]; + state.pEnum = pMsgEnum; + state.threadId = t.data.ThreadId; + state.hasMsg = false; + + // Prime the enumerator with the first message + fetched = 0; + hr = pMsgEnum->Next(1, &state.currentMsg, &fetched); + if (SUCCEEDED(hr) && fetched > 0) + { + state.hasMsg = true; + if (state.currentMsg.Timestamp > lastTimeStamp) + lastTimeStamp = state.currentMsg.Timestamp; + } + + msgStateCount++; + } + + // Open the output file + if ((file = fopen(fileName, "w")) == NULL) + { + return GetLastError(); + } + + // Print header -- match legacy format exactly + FILETIME startTime; + memcpy(&startTime, &logData.StartTime, sizeof(FILETIME)); + double totalSecs = 0; + if (lastTimeStamp > logData.StartTimestamp && logData.TickFrequency > 0) + { + totalSecs = ((double)(lastTimeStamp - logData.StartTimestamp)) / logData.TickFrequency; + } + FILETIME endTime; + INT64 endTimeVal = *((INT64*)&startTime) + ((INT64)(totalSecs * 1.0E7)); + memcpy(&endTime, &endTimeVal, sizeof(FILETIME)); + + WCHAR timeBuff[64]; + fprintf(file, "STRESS LOG:\n" + " facilitiesToLog = 0x%x\n" + " levelToLog = %d\n" + " MaxLogSizePerThread = 0x%x (%d)\n" + " MaxTotalLogSize = 0x%x (%d)\n" + " CurrentTotalLogChunk = %d\n" + " ThreadsWithLogs = %d\n", + logData.LoggedFacilities, logData.Level, + logData.MaxSizePerThread, logData.MaxSizePerThread, + logData.MaxSizeTotal, logData.MaxSizeTotal, + logData.TotalChunks, (int)threads.size()); + + fprintf(file, " Clock frequency = %5.3f GHz\n", logData.TickFrequency / 1.0E9); + fprintf(file, " Start time %S\n", getTime(&startTime, timeBuff, 64)); + fprintf(file, " Last message time %S\n", getTime(&endTime, timeBuff, 64)); + fprintf(file, " Total elapsed time %5.3f sec\n", totalSecs); + + fprintf(file, "\nTHREAD TIMESTAMP FACILITY MESSAGE\n"); + fprintf(file, " ID (sec from start)\n"); + fprintf(file, "--------------------------------------------------------------------------------------\n"); + + // Merge messages across all threads by timestamp (newest first) + char format[257]; + format[256] = format[0] = 0; + unsigned msgCtr = 0; + + for (;;) + { + // Find the thread with the newest message + int newestIdx = -1; + uint64_t newestTimestamp = 0; + + for (unsigned int i = 0; i < msgStateCount; i++) + { + if (msgStates[i].hasMsg && msgStates[i].currentMsg.Timestamp > newestTimestamp) + { + newestTimestamp = msgStates[i].currentMsg.Timestamp; + newestIdx = i; + } + } + + if (newestIdx < 0) + break; + + if (IsInterrupt()) + { + fprintf(file, "----- Interrupted by user -----\n"); + break; + } + + ThreadMsgState& state = msgStates[newestIdx]; + SOSStressMsgData& msg = state.currentMsg; + + if (msg.FormatString != 0) + { + // Read the format string from target memory + hr = memCallBack->ReadVirtual(msg.FormatString, format, 256, 0); + if (hr != S_OK) + strcpy_s(format, ARRAY_SIZE(format), "Could not read address of format string"); + + double deltaTime = ((double)(msg.Timestamp - logData.StartTimestamp)) / logData.TickFrequency; + + // Read arguments + void* args[StressMsg::maxArgCnt] = {}; + if (msg.ArgumentCount > 0) + { + CLRDATA_ADDRESS argAddrs[StressMsg::maxArgCnt] = {}; + unsigned int argsFetched = 0; + unsigned int argsToFetch = msg.ArgumentCount; + if (argsToFetch > StressMsg::maxArgCnt) + argsToFetch = StressMsg::maxArgCnt; + state.pEnum->GetArguments(0, argsToFetch, argAddrs, &argsFetched); + for (unsigned int i = 0; i < argsFetched; i++) + args[i] = (void*)(size_t)argAddrs[i]; + } + + // Handle TaskSwitch marker the same way as the legacy path + if (strcmp(format, ThreadStressLog::TaskSwitchMsg()) == 0) + { + fprintf(file, "Task was switched from %x\n", (unsigned)(size_t)args[0]); + state.threadId = (unsigned)(size_t)args[0]; + } + else + { + formatOutput(memCallBack, file, format, (unsigned)state.threadId, deltaTime, msg.Facility, args); + } + msgCtr++; + } + + // Advance to the next message for this thread + fetched = 0; + hr = state.pEnum->Next(1, &state.currentMsg, &fetched); + if (fetched == 0 || FAILED(hr)) + { + state.hasMsg = false; + fprintf(file, "------------ Last message from thread %x -----------\n", (unsigned)state.threadId); + } + } + + // Match legacy footer format + fprintf(file, "---------------------------- %d total entries ------------------------------------\n", msgCtr); + fclose(file); + + return msgCtr > 0 ? S_OK : S_FALSE; +} + /*********************************************************************************/ HRESULT StressLog::Dump(ULONG64 outProcLog, const char* fileName, struct IDebugDataSpaces* memCallBack) { @@ -482,7 +713,6 @@ HRESULT StressLog::Dump(ULONG64 outProcLog, const char* fileName, struct IDebugD hr = GetLastError(); goto FREE_MEM; } - hr = S_FALSE; // return false if there are no message to print to the log vDoOut(bDoGcHist, file, "STRESS LOG:\n" " facilitiesToLog = 0x%x\n" @@ -540,8 +770,7 @@ HRESULT StressLog::Dump(ULONG64 outProcLog, const char* fileName, struct IDebugD if (latestMsg->GetFormatOffset() != 0 && !latestLog->CompletedDump()) { TADDR taFmt = GetFormatAddr(inProcLog, latestMsg->GetFormatOffset(), bHasModuleTable); - hr = memCallBack->ReadVirtual(TO_CDADDR(taFmt), format, 256, 0); - if (hr != S_OK) + if (memCallBack->ReadVirtual(TO_CDADDR(taFmt), format, 256, 0) != S_OK) strcpy_s(format, ARRAY_SIZE(format), "Could not read address of format string"); double deltaTime = ((double) (latestMsg->GetTimeStamp() - inProcLog.startTimeStamp)) / inProcLog.tickFrequency; @@ -588,6 +817,8 @@ HRESULT StressLog::Dump(ULONG64 outProcLog, const char* fileName, struct IDebugD } ExtOut("\n"); + hr = (msgCtr > 0) ? S_OK : S_FALSE; + vDoOut(bDoGcHist, file, "---------------------------- %d total entries ------------------------------------\n", msgCtr); if (!bDoGcHist) { diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index 573cfd98d4..7fe412a43b 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -7573,6 +7573,9 @@ HRESULT GetIntermediateLangMap(BOOL bIL, const DacpCodeHeaderData& codeHeaderDat * will dump the stress log associated with any DLL linked * * against utilcode.lib, most commonly mscordbi.dll * * (e.g. !DumpLog -addr mscordbi!StressLog::theLog) * +* !DumpLog -legacy [filename] * +* will force the legacy raw-read path instead of * +* ISOSDacInterface17 * * * \**********************************************************************/ DECLARE_API(DumpLog) @@ -7593,15 +7596,15 @@ DECLARE_API(DumpLog) return E_FAIL; } - LoadRuntimeSymbols(); - const char* fileName = "StressLog.txt"; CLRDATA_ADDRESS StressLogAddress = (TADDR)0; + BOOL useLegacy = FALSE; StringHolder sFileName, sLogAddr; CMDOption option[] = { // name, vptr, type, hasValue - {"-addr", &sLogAddr.data, COSTRING, TRUE} + {"-addr", &sLogAddr.data, COSTRING, TRUE}, + {"-legacy", &useLegacy, COBOOL, FALSE} }; CMDValue arg[] = { // vptr, type @@ -7617,6 +7620,28 @@ DECLARE_API(DumpLog) fileName = sFileName.data; } + ExtOut("Attempting to dump Stress log to file '%s'\n", fileName); + + // Try the cDAC-based path (ISOSDacInterface17) first unless -legacy is specified. + if (!useLegacy) + { + HRESULT hrInterface17 = StressLog::DumpViaInterface17(fileName, g_ExtData); + if (hrInterface17 != E_NOINTERFACE) + { + Status = hrInterface17; + if (Status == S_OK) + ExtOut("SUCCESS: Stress log dumped\n"); + else if (Status == S_FALSE) + ExtOut("No Stress log in the image, no file written\n"); + else + ExtOut("FAILURE: Stress log not dumped\n"); + return Status; + } + } + + // Fall back to the legacy raw-read path. + LoadRuntimeSymbols(); + // allow users to specify -addr mscordbdi!StressLog::theLog, for example. if (sLogAddr.data != NULL) { @@ -7654,10 +7679,6 @@ DECLARE_API(DumpLog) return E_FAIL; } - ExtOut("Attempting to dump Stress log to file '%s'\n", fileName); - - - Status = StressLog::Dump(StressLogAddress, fileName, g_ExtData); if (Status == S_OK) diff --git a/src/shared/inc/stresslog.h b/src/shared/inc/stresslog.h index 58fbfdba97..c229080122 100644 --- a/src/shared/inc/stresslog.h +++ b/src/shared/inc/stresslog.h @@ -211,6 +211,10 @@ class StressLog { // This function is defined in the tools\strike\stressLogDump.cpp file static HRESULT Dump(ULONG64 logAddr, const char* fileName, struct IDebugDataSpaces* memCallBack); + // Try to dump via ISOSDacInterface17. + // Returns S_OK on success, S_FALSE if no messages, or E_NOINTERFACE if unavailable. + static HRESULT DumpViaInterface17(const char* fileName, struct IDebugDataSpaces* memCallBack); + static BOOL StressLogOn(unsigned facility, unsigned level); static BOOL ETWLogOn(unsigned facility, unsigned level); static BOOL LogOn(unsigned facility, unsigned level); diff --git a/src/shared/pal/prebuilt/idl/sospriv_i.cpp b/src/shared/pal/prebuilt/idl/sospriv_i.cpp index f3c4867a3a..9e78203b96 100644 --- a/src/shared/pal/prebuilt/idl/sospriv_i.cpp +++ b/src/shared/pal/prebuilt/idl/sospriv_i.cpp @@ -130,6 +130,15 @@ MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface15,0x7ed81261,0x52a9,0x4a23,0xa3,0x58, MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface16,0x4ba12ff8,0xdaac,0x4e43,0xac,0x56,0x98,0xcf,0x8d,0x5c,0x59,0x5d); + +MIDL_DEFINE_GUID(IID, IID_ISOSStressLogThreadEnum,0x94a2bd3d,0xab3d,0x43bf,0x81,0xd8,0x3a,0xe9,0x6b,0x8e,0x33,0xcd); + + +MIDL_DEFINE_GUID(IID, IID_ISOSStressLogMsgEnum,0x437cb033,0xafe7,0x4c0f,0xa4,0xa7,0x82,0xc8,0x91,0xbc,0x04,0x9e); + + +MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface17,0x2f4bb585,0xed50,0x479e,0xbb,0xe0,0x10,0xa9,0x5a,0x5d,0xa3,0xbb); + #undef MIDL_DEFINE_GUID #ifdef __cplusplus diff --git a/src/shared/pal/prebuilt/inc/sospriv.h b/src/shared/pal/prebuilt/inc/sospriv.h index 4ff12d95b7..61c13f6895 100644 --- a/src/shared/pal/prebuilt/inc/sospriv.h +++ b/src/shared/pal/prebuilt/inc/sospriv.h @@ -3765,6 +3765,297 @@ EXTERN_C const IID IID_ISOSDacInterface16; #endif /* __ISOSDacInterface16_INTERFACE_DEFINED__ */ +#ifndef _SOS_StressLogData +#define _SOS_StressLogData + +typedef struct _SOSStressLogData +{ + unsigned int LoggedFacilities; + unsigned int Level; + unsigned int MaxSizePerThread; + unsigned int MaxSizeTotal; + int TotalChunks; + UINT64 TickFrequency; + UINT64 StartTimestamp; + UINT64 StartTime; +} SOSStressLogData; + +#endif //_SOS_StressLogData + +#ifndef _SOS_ThreadStressLogData +#define _SOS_ThreadStressLogData + +typedef struct _SOSThreadStressLogData +{ + CLRDATA_ADDRESS ThreadLogAddress; + UINT64 ThreadId; +} SOSThreadStressLogData; + +#endif //_SOS_ThreadStressLogData + +#ifndef _SOS_StressMsgData +#define _SOS_StressMsgData + +typedef struct _SOSStressMsgData +{ + unsigned int Facility; + CLRDATA_ADDRESS FormatString; + UINT64 Timestamp; + unsigned int ArgumentCount; +} SOSStressMsgData; + +#endif //_SOS_StressMsgData + +#ifndef __ISOSStressLogThreadEnum_INTERFACE_DEFINED__ +#define __ISOSStressLogThreadEnum_INTERFACE_DEFINED__ + +/* interface ISOSStressLogThreadEnum */ +/* [uuid][local][object] */ + + +EXTERN_C const IID IID_ISOSStressLogThreadEnum; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("94a2bd3d-ab3d-43bf-81d8-3ae96b8e33cd") + ISOSStressLogThreadEnum : public ISOSEnum + { + public: + virtual HRESULT STDMETHODCALLTYPE Next( + unsigned int count, + SOSThreadStressLogData values[], + unsigned int *pFetched) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ISOSStressLogThreadEnumVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISOSStressLogThreadEnum * This, + REFIID riid, + void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISOSStressLogThreadEnum * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISOSStressLogThreadEnum * This); + + HRESULT ( STDMETHODCALLTYPE *Skip )( + ISOSStressLogThreadEnum * This, + unsigned int count); + + HRESULT ( STDMETHODCALLTYPE *Reset )( + ISOSStressLogThreadEnum * This); + + HRESULT ( STDMETHODCALLTYPE *GetCount )( + ISOSStressLogThreadEnum * This, + unsigned int *pCount); + + HRESULT ( STDMETHODCALLTYPE *Next )( + ISOSStressLogThreadEnum * This, + unsigned int count, + SOSThreadStressLogData values[], + unsigned int *pFetched); + + END_INTERFACE + } ISOSStressLogThreadEnumVtbl; + + interface ISOSStressLogThreadEnum + { + CONST_VTBL struct ISOSStressLogThreadEnumVtbl *lpVtbl; + }; + +#endif /* C style interface */ + +#endif /* __ISOSStressLogThreadEnum_INTERFACE_DEFINED__ */ + + +#ifndef __ISOSStressLogMsgEnum_INTERFACE_DEFINED__ +#define __ISOSStressLogMsgEnum_INTERFACE_DEFINED__ + +/* interface ISOSStressLogMsgEnum */ +/* [uuid][local][object] */ + + +EXTERN_C const IID IID_ISOSStressLogMsgEnum; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("437cb033-afe7-4c0f-a4a7-82c891bc049e") + ISOSStressLogMsgEnum : public ISOSEnum + { + public: + virtual HRESULT STDMETHODCALLTYPE Next( + unsigned int count, + SOSStressMsgData values[], + unsigned int *pFetched) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetArguments( + unsigned int messageIndex, + unsigned int argCount, + CLRDATA_ADDRESS args[], + unsigned int *pFetched) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ISOSStressLogMsgEnumVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISOSStressLogMsgEnum * This, + REFIID riid, + void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISOSStressLogMsgEnum * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISOSStressLogMsgEnum * This); + + HRESULT ( STDMETHODCALLTYPE *Skip )( + ISOSStressLogMsgEnum * This, + unsigned int count); + + HRESULT ( STDMETHODCALLTYPE *Reset )( + ISOSStressLogMsgEnum * This); + + HRESULT ( STDMETHODCALLTYPE *GetCount )( + ISOSStressLogMsgEnum * This, + unsigned int *pCount); + + HRESULT ( STDMETHODCALLTYPE *Next )( + ISOSStressLogMsgEnum * This, + unsigned int count, + SOSStressMsgData values[], + unsigned int *pFetched); + + HRESULT ( STDMETHODCALLTYPE *GetArguments )( + ISOSStressLogMsgEnum * This, + unsigned int messageIndex, + unsigned int argCount, + CLRDATA_ADDRESS args[], + unsigned int *pFetched); + + END_INTERFACE + } ISOSStressLogMsgEnumVtbl; + + interface ISOSStressLogMsgEnum + { + CONST_VTBL struct ISOSStressLogMsgEnumVtbl *lpVtbl; + }; + +#endif /* C style interface */ + +#endif /* __ISOSStressLogMsgEnum_INTERFACE_DEFINED__ */ + + +#ifndef __ISOSDacInterface17_INTERFACE_DEFINED__ +#define __ISOSDacInterface17_INTERFACE_DEFINED__ + +/* interface ISOSDacInterface17 */ +/* [uuid][local][object] */ + + +EXTERN_C const IID IID_ISOSDacInterface17; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("2f4bb585-ed50-479e-bbe0-10a95a5da3bb") + ISOSDacInterface17 : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetStressLogData( + SOSStressLogData *data) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStressLogThreadEnumerator( + ISOSStressLogThreadEnum **ppEnum) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStressLogMessageEnumerator( + CLRDATA_ADDRESS threadStressLogAddress, + ISOSStressLogMsgEnum **ppEnum) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ISOSDacInterface17Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISOSDacInterface17 * This, + REFIID riid, + void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISOSDacInterface17 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISOSDacInterface17 * This); + + HRESULT ( STDMETHODCALLTYPE *GetStressLogData )( + ISOSDacInterface17 * This, + SOSStressLogData *data); + + HRESULT ( STDMETHODCALLTYPE *GetStressLogThreadEnumerator )( + ISOSDacInterface17 * This, + ISOSStressLogThreadEnum **ppEnum); + + HRESULT ( STDMETHODCALLTYPE *GetStressLogMessageEnumerator )( + ISOSDacInterface17 * This, + CLRDATA_ADDRESS threadStressLogAddress, + ISOSStressLogMsgEnum **ppEnum); + + END_INTERFACE + } ISOSDacInterface17Vtbl; + + interface ISOSDacInterface17 + { + CONST_VTBL struct ISOSDacInterface17Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + +#define ISOSDacInterface17_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ISOSDacInterface17_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ISOSDacInterface17_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + +#define ISOSDacInterface17_GetStressLogData(This,data) \ + ( (This)->lpVtbl -> GetStressLogData(This,data) ) + +#define ISOSDacInterface17_GetStressLogThreadEnumerator(This,ppEnum) \ + ( (This)->lpVtbl -> GetStressLogThreadEnumerator(This,ppEnum) ) + +#define ISOSDacInterface17_GetStressLogMessageEnumerator(This,threadStressLogAddress,ppEnum) \ + ( (This)->lpVtbl -> GetStressLogMessageEnumerator(This,threadStressLogAddress,ppEnum) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + +#endif /* __ISOSDacInterface17_INTERFACE_DEFINED__ */ + + /* Additional Prototypes for ALL interfaces */ /* end of Additional Prototypes */ diff --git a/src/tests/SOS.UnitTests/SOS.cs b/src/tests/SOS.UnitTests/SOS.cs index 110c8ab27f..5d365a55ec 100644 --- a/src/tests/SOS.UnitTests/SOS.cs +++ b/src/tests/SOS.UnitTests/SOS.cs @@ -664,6 +664,7 @@ await SOSTestHelpers.RunTest( TestConfiguration = config, TestName = "SOS.OtherCommands", DebuggeeName = "SymbolTestApp", + EnableStressLog = true, // Assumes that SymbolTestDll.dll that is dynamically loaded is the parent directory of the single file app DebuggeeArguments = config.PublishSingleFile ? Path.Combine("%DEBUG_ROOT%", "..") : "%DEBUG_ROOT%" }, diff --git a/src/tests/SOS.UnitTests/SOSRunner.cs b/src/tests/SOS.UnitTests/SOSRunner.cs index e8a6c83760..5555a42916 100644 --- a/src/tests/SOS.UnitTests/SOSRunner.cs +++ b/src/tests/SOS.UnitTests/SOSRunner.cs @@ -147,6 +147,8 @@ public DumpType DumpType public bool EnableSOSLogging { get; set; } = true; + public bool EnableStressLog { get; set; } + public bool TestCrashReport { get { return _testCrashReport && DumpGenerator == DumpGenerator.CreateDump && OS.Kind != OSKind.Windows; } @@ -333,6 +335,18 @@ public static async Task CreateDump(TestInformation information) processRunner.WithEnvironmentVariable("DOTNET_Interpreter", "InterpTestMethod*"); } + // Enable stress logging so DumpLog tests have data to read. + // Must be set before any dump generation path so the stress log + // is captured regardless of how the dump is generated. + if (information.EnableStressLog) + { + processRunner. + WithRuntimeConfiguration("StressLog", "1"). + WithRuntimeConfiguration("LogFacility", "0xffffffbf"). + WithRuntimeConfiguration("LogLevel", "6"). + WithRuntimeConfiguration("StressLogSize", "65536"); + } + if (dumpGeneration == DumpGenerator.CreateDump) { // Run the debuggee with the createdump environment variables set to generate a coredump on unhandled exception @@ -715,6 +729,16 @@ public static async Task StartDebugger(TestInformation information, D processRunner.WithEnvironmentVariable("DOTNET_ENABLE_CDAC", "1"); } + // Enable stress logging for both live and dump paths when requested + if (information.EnableStressLog) + { + processRunner. + WithEnvironmentVariable("DOTNET_StressLog", "1"). + WithEnvironmentVariable("DOTNET_LogFacility", "0xffffffbf"). + WithEnvironmentVariable("DOTNET_LogLevel", "6"). + WithEnvironmentVariable("DOTNET_StressLogSize", "65536"); + } + // Exit codes on Windows should always be 0, but not on Linux/OSX for the faulting debuggees. if (OS.Kind == OSKind.Windows) { diff --git a/src/tests/SOS.UnitTests/Scripts/OtherCommands.script b/src/tests/SOS.UnitTests/Scripts/OtherCommands.script index 8263579590..eb7d49983c 100644 --- a/src/tests/SOS.UnitTests/Scripts/OtherCommands.script +++ b/src/tests/SOS.UnitTests/Scripts/OtherCommands.script @@ -142,5 +142,8 @@ SOSCOMMAND:DumpGCData SOSCOMMAND:DumpRuntimeTypes +SOSCOMMAND:DumpLog +VERIFY:SUCCESS: Stress log dumped + EXTCOMMAND:logclose EXTCOMMAND:logging --disable