From d31a5fee2c8e1262cf742258d95fc7b954b7c6bb Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 30 Dec 2019 18:18:23 -0800 Subject: [PATCH] Abstract all the runtime specific info and class. Add IRuntime/Runtime classes. The code to get the runtime module address/size info, DAC/DBI file path, create/get the SOS DAC process instance and to create/get the ICorDebug process instance to the Runtime class implementation. It was moved mostly intack with some type and function name changes. Added -desktop and -netcore options to the !sosstatus command to switch between runtimes. Eventually SOSHosting will provide the IRuntime instance when hosted under dotnet-dump. Fix unloading/reloading SOS (under windbg) issue: https://github.com/dotnet/diagnostics/issues/386 Flush process specific global state when process terminates issue: https://github.com/dotnet/diagnostics/issues/479 --- src/SOS/SOS.Hosting/SOSHost.cs | 5 +- src/SOS/Strike/CMakeLists.txt | 2 + src/SOS/Strike/EventCallbacks.cpp | 3 +- src/SOS/Strike/ExpressionNode.cpp | 18 +- src/SOS/Strike/ExpressionNode.h | 2 + src/SOS/Strike/Strike.vcxproj | 4 + src/SOS/Strike/Strike.vcxproj.filters | 4 + src/SOS/Strike/WatchCmd.cpp | 2 - src/SOS/Strike/cordebugdatatarget.h | 251 ++++++++ src/SOS/Strike/cordebuglibraryprovider.h | 170 +++++ src/SOS/Strike/disasm.h | 2 - src/SOS/Strike/exts.h | 21 +- src/SOS/Strike/hostcoreclr.cpp | 238 +------ src/SOS/Strike/hostcoreclr.h | 12 +- src/SOS/Strike/runtime.cpp | 599 ++++++++++++++++++ src/SOS/Strike/runtime.h | 257 ++++++++ src/SOS/Strike/sosdocs.txt | 24 +- src/SOS/Strike/sosdocsunix.txt | 13 +- src/SOS/Strike/strike.cpp | 155 ++--- src/SOS/Strike/util.cpp | 756 +---------------------- src/SOS/Strike/util.h | 100 --- src/Tools/dotnet-dump/Analyzer.cs | 34 +- 22 files changed, 1509 insertions(+), 1163 deletions(-) create mode 100644 src/SOS/Strike/cordebugdatatarget.h create mode 100644 src/SOS/Strike/cordebuglibraryprovider.h create mode 100644 src/SOS/Strike/runtime.cpp create mode 100644 src/SOS/Strike/runtime.h diff --git a/src/SOS/SOS.Hosting/SOSHost.cs b/src/SOS/SOS.Hosting/SOSHost.cs index f30e710e2..115bdeb23 100644 --- a/src/SOS/SOS.Hosting/SOSHost.cs +++ b/src/SOS/SOS.Hosting/SOSHost.cs @@ -38,6 +38,7 @@ namespace SOS [In, MarshalAs(UnmanagedType.Struct)] ref SOSNetCoreCallbacks callbacks, int callbacksSize, [In, MarshalAs(UnmanagedType.LPStr)] string tempDirectory, + bool isDesktop, [In, MarshalAs(UnmanagedType.LPStr)] string dacFilePath, [In, MarshalAs(UnmanagedType.LPStr)] string dbiFilePath, bool symbolStoreEnabled); @@ -224,9 +225,10 @@ namespace SOS /// Loads and initializes the SOS module. /// /// Temporary directory created to download DAC module + /// if true, desktop runtime, else .NET Core runtime /// The path to DAC that CLRMD loaded or downloaded or null /// The path to DBI (for future use) or null - public void InitializeSOSHost(string tempDirectory, string dacFilePath, string dbiFilePath) + public void InitializeSOSHost(string tempDirectory, bool isDesktop, string dacFilePath, string dbiFilePath) { if (_sosLibrary == IntPtr.Zero) { @@ -268,6 +270,7 @@ namespace SOS ref s_callbacks, Marshal.SizeOf(), tempDirectory, + isDesktop, dacFilePath, dbiFilePath, SymbolReader.IsSymbolStoreEnabled()); diff --git a/src/SOS/Strike/CMakeLists.txt b/src/SOS/Strike/CMakeLists.txt index ae4ff471f..207ec5a99 100644 --- a/src/SOS/Strike/CMakeLists.txt +++ b/src/SOS/Strike/CMakeLists.txt @@ -69,6 +69,7 @@ if(WIN32) gcroot.cpp hostcoreclr.cpp metadata.cpp + runtime.cpp sigparser.cpp sildasm.cpp sos.cpp @@ -125,6 +126,7 @@ else(WIN32) gcroot.cpp hostcoreclr.cpp metadata.cpp + runtime.cpp sigparser.cpp sildasm.cpp stressLogDump.cpp diff --git a/src/SOS/Strike/EventCallbacks.cpp b/src/SOS/Strike/EventCallbacks.cpp index 947d913be..bdef2f9e6 100644 --- a/src/SOS/Strike/EventCallbacks.cpp +++ b/src/SOS/Strike/EventCallbacks.cpp @@ -105,7 +105,8 @@ HRESULT __stdcall EventCallbacks::Exception(PEXCEPTION_RECORD64 Exception, ULONG HRESULT __stdcall EventCallbacks::ExitProcess(ULONG ExitCode) { - UninitCorDebugInterface(); + Runtime::CleanupRuntimes(); + CleanupTempDirectory(); return DEBUG_STATUS_NO_CHANGE; } diff --git a/src/SOS/Strike/ExpressionNode.cpp b/src/SOS/Strike/ExpressionNode.cpp index b61dcad48..45a2f5c19 100644 --- a/src/SOS/Strike/ExpressionNode.cpp +++ b/src/SOS/Strike/ExpressionNode.cpp @@ -4,11 +4,12 @@ #include "ExpressionNode.h" - #ifndef IfFailRet #define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0) #endif +ICorDebugProcess* ExpressionNode::s_pCorDebugProcess = nullptr; + // Returns the complete expression being evaluated to get the value for this node // The returned pointer is a string interior to this object - once you release // all references to this object the string is invalid. @@ -45,8 +46,15 @@ WCHAR* ExpressionNode::GetErrorMessage() { return pErrorMessage; } // Factory function for creating the expression node at the root of a tree HRESULT ExpressionNode::CreateExpressionNode(__in_z WCHAR* pExpression, ExpressionNode** ppExpressionNode) { + _ASSERTE(g_pRuntime != nullptr); *ppExpressionNode = NULL; - HRESULT Status = CreateExpressionNodeHelper(pExpression, + + HRESULT Status = g_pRuntime->GetCorDebugInterface(&s_pCorDebugProcess); + if (FAILED(Status)) + { + return Status; + } + Status = CreateExpressionNodeHelper(pExpression, pExpression, 0, NULL, @@ -1760,7 +1768,8 @@ HRESULT ExpressionNode::EnumerateFrames(FrameEnumCallback pCallback, VOID* pUser ULONG ulThreadID = 0; g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID); - IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread)); + _ASSERTE(s_pCorDebugProcess != nullptr); + IfFailRet(s_pCorDebugProcess->GetThread(ulThreadID, &pThread)); IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3)); IfFailRet(pThread3->CreateStackWalk(&pStackWalk)); @@ -1980,7 +1989,8 @@ HRESULT ExpressionNode::FindTypeByName(__in_z const WCHAR* pTypeName, ICorDebugT { HRESULT Status = S_OK; ToRelease pAppDomainEnum; - IfFailRet(g_pCorDebugProcess->EnumerateAppDomains(&pAppDomainEnum)); + _ASSERTE(s_pCorDebugProcess != nullptr); + IfFailRet(s_pCorDebugProcess->EnumerateAppDomains(&pAppDomainEnum)); DWORD count; IfFailRet(pAppDomainEnum->GetCount(&count)); for(DWORD i = 0; i < count; i++) diff --git a/src/SOS/Strike/ExpressionNode.h b/src/SOS/Strike/ExpressionNode.h index 48cc03672..b845c8a33 100644 --- a/src/SOS/Strike/ExpressionNode.h +++ b/src/SOS/Strike/ExpressionNode.h @@ -88,6 +88,8 @@ public: VOID DFSVisit(ExpressionNodeVisitorCallback pFunc, VOID* pUserData, int depth=0); private: + static ICorDebugProcess* s_pCorDebugProcess; + // for nodes that evaluate to a type, this is that type // for nodes that evaluate to a debuggee value, this is the type of that // value or one of its base types. It represents the type the value should diff --git a/src/SOS/Strike/Strike.vcxproj b/src/SOS/Strike/Strike.vcxproj index 9f5e15cb1..510008f66 100644 --- a/src/SOS/Strike/Strike.vcxproj +++ b/src/SOS/Strike/Strike.vcxproj @@ -391,6 +391,7 @@ + @@ -405,6 +406,8 @@ + + @@ -417,6 +420,7 @@ + diff --git a/src/SOS/Strike/Strike.vcxproj.filters b/src/SOS/Strike/Strike.vcxproj.filters index cd5bb759b..1f4f91078 100644 --- a/src/SOS/Strike/Strike.vcxproj.filters +++ b/src/SOS/Strike/Strike.vcxproj.filters @@ -21,6 +21,7 @@ + @@ -57,6 +58,9 @@ inc + + + diff --git a/src/SOS/Strike/WatchCmd.cpp b/src/SOS/Strike/WatchCmd.cpp index 443f1dd6e..ca548eea2 100644 --- a/src/SOS/Strike/WatchCmd.cpp +++ b/src/SOS/Strike/WatchCmd.cpp @@ -95,7 +95,6 @@ HRESULT WatchCmd::Print(int expansionIndex, __in_z WCHAR* expansionPath, __in_z INIT_API_EE(); INIT_API_DAC(); EnableDMLHolder dmlHolder(TRUE); - IfFailRet(InitCorDebugInterface()); PersistList* pFilterList = NULL; if(pFilterName != NULL) @@ -208,7 +207,6 @@ HRESULT WatchCmd::SaveList(__in_z WCHAR* pSaveName) HRESULT Status = S_OK; INIT_API_EE(); INIT_API_DAC(); - IfFailRet(InitCorDebugInterface()); RemoveList(pSaveName); PersistList* pList = new PersistList(); diff --git a/src/SOS/Strike/cordebugdatatarget.h b/src/SOS/Strike/cordebugdatatarget.h new file mode 100644 index 000000000..fe5bece3a --- /dev/null +++ b/src/SOS/Strike/cordebugdatatarget.h @@ -0,0 +1,251 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef __cordebugdatatarget_h__ +#define __cordebugdatatarget_h__ + +/**********************************************************************\ +* Data target for the debugged process. Provided to OpenVirtualProcess +* in order to get an ICorDebugProcess back. +\**********************************************************************/ +class CorDebugDataTarget : public ICorDebugMutableDataTarget, public ICorDebugMetaDataLocator, public ICorDebugDataTarget4 +{ +public: + CorDebugDataTarget() : m_ref(0) + { + } + + virtual ~CorDebugDataTarget() {} + + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID InterfaceId, + PVOID* pInterface) + { + if (InterfaceId == IID_IUnknown) + { + *pInterface = static_cast(static_cast(this)); + } + else if (InterfaceId == IID_ICorDebugDataTarget) + { + *pInterface = static_cast(this); + } + else if (InterfaceId == IID_ICorDebugMutableDataTarget) + { + *pInterface = static_cast(this); + } + else if (InterfaceId == IID_ICorDebugMetaDataLocator) + { + *pInterface = static_cast(this); + } + else if (InterfaceId == IID_ICorDebugDataTarget4) + { + *pInterface = static_cast(this); + } + else + { + *pInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE AddRef() + { + return InterlockedIncrement(&m_ref); + } + + virtual ULONG STDMETHODCALLTYPE Release() + { + LONG ref = InterlockedDecrement(&m_ref); + if (ref == 0) + { + delete this; + } + return ref; + } + + // + // ICorDebugDataTarget. + // + + virtual HRESULT STDMETHODCALLTYPE GetPlatform(CorDebugPlatform * pPlatform) + { + ULONG platformKind = g_targetMachine->GetPlatform(); +#ifdef FEATURE_PAL + if(platformKind == IMAGE_FILE_MACHINE_I386) + *pPlatform = CORDB_PLATFORM_POSIX_X86; + else if(platformKind == IMAGE_FILE_MACHINE_AMD64) + *pPlatform = CORDB_PLATFORM_POSIX_AMD64; + else if(platformKind == IMAGE_FILE_MACHINE_ARMNT) + *pPlatform = CORDB_PLATFORM_POSIX_ARM; + else + return E_FAIL; +#else + if(platformKind == IMAGE_FILE_MACHINE_I386) + *pPlatform = CORDB_PLATFORM_WINDOWS_X86; + else if(platformKind == IMAGE_FILE_MACHINE_AMD64) + *pPlatform = CORDB_PLATFORM_WINDOWS_AMD64; + else if(platformKind == IMAGE_FILE_MACHINE_ARMNT) + *pPlatform = CORDB_PLATFORM_WINDOWS_ARM; + else if(platformKind == IMAGE_FILE_MACHINE_ARM64) + *pPlatform = CORDB_PLATFORM_WINDOWS_ARM64; + else + return E_FAIL; +#endif + + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE ReadVirtual( + CORDB_ADDRESS address, + BYTE * pBuffer, + ULONG32 request, + ULONG32 * pcbRead) + { + if (g_ExtData == NULL) + { + return E_UNEXPECTED; + } +#ifdef FEATURE_PAL + if (g_sos != nullptr) + { + // LLDB synthesizes memory (returns 0's) for missing pages (in this case the missing metadata pages) + // in core dumps. This functions creates a list of the metadata regions and caches the metadata if + // available from the local or downloaded assembly. If the read would be in the metadata of a loaded + // assembly, the metadata from the this cache will be returned. + HRESULT hr = GetMetadataMemory(address, request, pBuffer); + if (SUCCEEDED(hr)) { + if (pcbRead != nullptr) { + *pcbRead = request; + } + return hr; + } + } +#endif + HRESULT hr = g_ExtData->ReadVirtual(address, pBuffer, request, (PULONG) pcbRead); + if (FAILED(hr)) + { + ExtDbgOut("CorDebugDataTarget::ReadVirtual FAILED %08x address %p size %08x\n", hr, address, request); + } + return hr; + } + + virtual HRESULT STDMETHODCALLTYPE GetThreadContext( + DWORD dwThreadOSID, + ULONG32 contextFlags, + ULONG32 contextSize, + BYTE * context) + { +#ifdef FEATURE_PAL + if (g_ExtServices == NULL) + { + return E_UNEXPECTED; + } + return g_ExtServices->GetThreadContextById(dwThreadOSID, contextFlags, contextSize, context); +#else + ULONG ulThreadIDOrig; + ULONG ulThreadIDRequested; + HRESULT hr; + + hr = g_ExtSystem->GetCurrentThreadId(&ulThreadIDOrig); + if (FAILED(hr)) + { + return hr; + } + + hr = g_ExtSystem->GetThreadIdBySystemId(dwThreadOSID, &ulThreadIDRequested); + if (FAILED(hr)) + { + return hr; + } + + hr = g_ExtSystem->SetCurrentThreadId(ulThreadIDRequested); + if (FAILED(hr)) + { + return hr; + } + + // Prepare context structure + ZeroMemory(context, contextSize); + ((CONTEXT*) context)->ContextFlags = contextFlags; + + // Ok, do it! + hr = g_ExtAdvanced->GetThreadContext((LPVOID) context, contextSize); + + // This is cleanup; failure here doesn't mean GetThreadContext should fail + // (that's determined by hr). + g_ExtSystem->SetCurrentThreadId(ulThreadIDOrig); + + return hr; +#endif // FEATURE_PAL + } + + // + // ICorDebugMutableDataTarget. + // + + virtual HRESULT STDMETHODCALLTYPE WriteVirtual(CORDB_ADDRESS address, + const BYTE * pBuffer, + ULONG32 bytesRequested) + { + if (g_ExtData == NULL) + { + return E_UNEXPECTED; + } + return g_ExtData->WriteVirtual(address, (PVOID)pBuffer, bytesRequested, NULL); + } + + virtual HRESULT STDMETHODCALLTYPE SetThreadContext(DWORD dwThreadID, + ULONG32 contextSize, + const BYTE * pContext) + { + return E_NOTIMPL; + } + + virtual HRESULT STDMETHODCALLTYPE ContinueStatusChanged(DWORD dwThreadId, + CORDB_CONTINUE_STATUS continueStatus) + { + return E_NOTIMPL; + } + + // + // ICorDebugMetaDataLocator. + // + + virtual HRESULT STDMETHODCALLTYPE GetMetaData( + /* [in] */ LPCWSTR wszImagePath, + /* [in] */ DWORD dwImageTimeStamp, + /* [in] */ DWORD dwImageSize, + /* [in] */ ULONG32 cchPathBuffer, + /* [annotation][out] */ + _Out_ ULONG32 *pcchPathBuffer, + /* [annotation][length_is][size_is][out] */ + _Out_writes_to_(cchPathBuffer, *pcchPathBuffer) WCHAR wszPathBuffer[]) + { + return E_NOTIMPL; + } + + // + // ICorDebugDataTarget4 + // + virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context) + { +#ifdef FEATURE_PAL + if (g_ExtServices == NULL) + { + return E_UNEXPECTED; + } + return g_ExtServices->VirtualUnwind(threadId, contextSize, context); +#else + return E_NOTIMPL; +#endif + } + +protected: + LONG m_ref; +}; + +#endif // __cordebugdatatarget_h__ diff --git a/src/SOS/Strike/cordebuglibraryprovider.h b/src/SOS/Strike/cordebuglibraryprovider.h new file mode 100644 index 000000000..f5eac41b5 --- /dev/null +++ b/src/SOS/Strike/cordebuglibraryprovider.h @@ -0,0 +1,170 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef __cordebuglibraryprovider_h__ +#define __cordebuglibraryprovider_h__ + +#ifndef FEATURE_PAL +extern HMODULE LoadLibraryAndCheck(PCWSTR filename, DWORD timestamp, DWORD filesize); +#endif + +/**********************************************************************\ + * Provides a way for the public CLR debugging interface to find the + * appropriate mscordbi.dll, DAC, etc. +\**********************************************************************/ +class CorDebugLibraryProvider : public ICLRDebuggingLibraryProvider, ICLRDebuggingLibraryProvider2 +{ +public: + CorDebugLibraryProvider(Runtime* pRuntime) : + m_ref(0), + m_pRuntime(pRuntime) + { + } + + virtual ~CorDebugLibraryProvider() {} + + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID InterfaceId, + PVOID* pInterface) + { + if (InterfaceId == IID_IUnknown) + { + *pInterface = static_cast(static_cast(this)); + } +#ifndef FEATURE_PAL + else if (InterfaceId == IID_ICLRDebuggingLibraryProvider) + { + *pInterface = static_cast(this); + } +#endif + else if (InterfaceId == IID_ICLRDebuggingLibraryProvider2) + { + *pInterface = static_cast(this); + } + else + { + *pInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE AddRef() + { + return InterlockedIncrement(&m_ref); + } + + virtual ULONG STDMETHODCALLTYPE Release() + { + LONG ref = InterlockedDecrement(&m_ref); + if (ref == 0) + { + delete this; + } + return ref; + } + + HRESULT ProvideLibraryInternal( + const WCHAR* pwszFileName, + DWORD dwTimestamp, + DWORD dwSizeOfImage, + HMODULE* phModule, + LPWSTR* ppResolvedModulePath) + { + const char* filePath = nullptr; + + if (_wcsncmp(pwszFileName, m_pRuntime->GetDacDllNameW(), _wcslen(m_pRuntime->GetDacDllNameW())) == 0) + { + filePath = m_pRuntime->GetDacFilePath(); + } + else if (_wcsncmp(pwszFileName, NET_DBI_DLL_NAME_W, _wcslen(NET_DBI_DLL_NAME_W)) == 0) + { + filePath = m_pRuntime->GetDbiFilePath(); + } + + ArrayHolder modulePath = new WCHAR[MAX_LONGPATH + 1]; + if (filePath != nullptr) + { + int length = MultiByteToWideChar(CP_ACP, 0, filePath, -1, modulePath, MAX_LONGPATH); + if (0 >= length) + { + ExtErr("MultiByteToWideChar(filePath) failed. Last error = 0x%x\n", GetLastError()); + return HRESULT_FROM_WIN32(GetLastError()); + } + } + else + { + LPCSTR runtimeDirectory = m_pRuntime->GetRuntimeDirectory(); + if (runtimeDirectory == nullptr) + { + ExtErr("Runtime not loaded\n"); + return E_FAIL; + } + int length = MultiByteToWideChar(CP_ACP, 0, runtimeDirectory, -1, modulePath, MAX_LONGPATH); + if (0 >= length) + { + ExtErr("MultiByteToWideChar(runtimeDirectory) failed. Last error = 0x%x\n", GetLastError()); + return HRESULT_FROM_WIN32(GetLastError()); + } + wcscat_s(modulePath, MAX_LONGPATH, pwszFileName); + } + + ExtOut("Loaded %S\n", modulePath.GetPtr()); + +#ifndef FEATURE_PAL + if (phModule != NULL) + { + *phModule = LoadLibraryAndCheck(modulePath.GetPtr(), dwTimestamp, dwSizeOfImage); + } +#endif + if (ppResolvedModulePath != NULL) + { + *ppResolvedModulePath = modulePath.Detach(); + } + return S_OK; + } + + // Called by the shim to locate and load dac and dbi + // Parameters: + // pwszFileName - the name of the file to load + // dwTimestamp - the expected timestamp of the file + // dwSizeOfImage - the expected SizeOfImage (a PE header data value) + // phModule - a handle to loaded module + // + // Return Value + // S_OK if the file was loaded, or any error if not + virtual HRESULT STDMETHODCALLTYPE ProvideLibrary( + const WCHAR * pwszFileName, + DWORD dwTimestamp, + DWORD dwSizeOfImage, + HMODULE* phModule) + { + if ((phModule == NULL) || (pwszFileName == NULL)) + { + return E_INVALIDARG; + } + return ProvideLibraryInternal(pwszFileName, dwTimestamp, dwSizeOfImage, phModule, NULL); + } + + virtual HRESULT STDMETHODCALLTYPE ProvideLibrary2( + const WCHAR* pwszFileName, + DWORD dwTimestamp, + DWORD dwSizeOfImage, + LPWSTR* ppResolvedModulePath) + { + if ((pwszFileName == NULL) || (ppResolvedModulePath == NULL)) + { + return E_INVALIDARG; + } + return ProvideLibraryInternal(pwszFileName, dwTimestamp, dwSizeOfImage, NULL, ppResolvedModulePath); + } + +protected: + LONG m_ref; + Runtime* m_pRuntime; +}; + +#endif // __cordebuglibraryprovider_h__ diff --git a/src/SOS/Strike/disasm.h b/src/SOS/Strike/disasm.h index cec59b5a6..1eb3920cb 100644 --- a/src/SOS/Strike/disasm.h +++ b/src/SOS/Strike/disasm.h @@ -90,8 +90,6 @@ void DumpStackWorker (DumpStackFlag &DSFlag); void UnassemblyUnmanaged (DWORD_PTR IP, BOOL bSuppressLines); -HRESULT CheckEEDll (); - BOOL GetCalleeSite (DWORD_PTR IP, DWORD_PTR &IPCallee); void DisasmAndClean (DWORD_PTR &IP, __out_ecount_opt(length) char *line, ULONG length); diff --git a/src/SOS/Strike/exts.h b/src/SOS/Strike/exts.h index e15e88776..cefec80d9 100644 --- a/src/SOS/Strike/exts.h +++ b/src/SOS/Strike/exts.h @@ -79,6 +79,7 @@ typedef struct _TADDR_SEGINFO } TADDR_SEGINFO; #include "util.h" +#include "runtime.h" #ifdef __cplusplus extern "C" { @@ -175,7 +176,6 @@ inline BOOL IsInterrupt() ExtOut("Command canceled at the user's request.\n"); ControlC = TRUE; } - return ControlC; } @@ -196,7 +196,11 @@ public: inline void EENotLoadedMessage(HRESULT Status) { - ExtOut("Failed to find runtime module (%s), 0x%08x\n", GetRuntimeDllName(), Status); +#ifdef FEATURE_PAL + ExtOut("Failed to find runtime module (%s), 0x%08x\n", NETCORE_RUNTIME_DLL_NAME_A, Status); +#else + ExtOut("Failed to find runtime module (%s or %s), 0x%08x\n", NETCORE_RUNTIME_DLL_NAME_A, DESKTOP_RUNTIME_DLL_NAME_A, Status); +#endif ExtOut("Extension commands need it in order to have something to do.\n"); } @@ -204,7 +208,7 @@ inline void DACMessage(HRESULT Status) { ExtOut("Failed to load data access module, 0x%08x\n", Status); #ifndef FEATURE_PAL - ExtOut("Verify that 1) you have a recent build of the debugger (6.2.14 or newer)\n"); + ExtOut("Verify that 1) you have a recent build of the debugger (10.0.18317.1001 or newer)\n"); ExtOut(" 2) the file %s that matches your version of %s is\n", GetDacDllName(), GetRuntimeDllName()); ExtOut(" in the version directory or on the symbol path\n"); ExtOut(" 3) or, if you are debugging a dump file, verify that the file \n"); @@ -251,7 +255,7 @@ HRESULT CheckEEDll(); if ((Status = ArchQuery()) != S_OK) return Status; #define INIT_API_EE() \ - if ((Status = CheckEEDll()) != S_OK) \ + if ((Status = CheckEEDll()) != S_OK) \ { \ EENotLoadedMessage(Status); \ return Status; \ @@ -285,13 +289,8 @@ HRESULT CheckEEDll(); // and functions they call should test g_bDacBroken before calling any DAC enabled // feature. #define INIT_API_NO_RET_ON_FAILURE() \ - INIT_API_NOEE() \ - if ((Status = CheckEEDll()) != S_OK) \ - { \ - ExtOut("Failed to find runtime module (%s), 0x%08x\n", GetRuntimeDllName(), Status); \ - ExtOut("Some functionality may be impaired\n"); \ - } \ - else if ((Status = LoadClrDebugDll()) != S_OK) \ + INIT_API_NODAC() \ + if ((Status = LoadClrDebugDll()) != S_OK) \ { \ ExtOut("Failed to load data access module (%s), 0x%08x\n", GetDacDllName(), Status); \ ExtOut("Some functionality may be impaired\n"); \ diff --git a/src/SOS/Strike/hostcoreclr.cpp b/src/SOS/Strike/hostcoreclr.cpp index e362a1b84..62e4dc194 100644 --- a/src/SOS/Strike/hostcoreclr.cpp +++ b/src/SOS/Strike/hostcoreclr.cpp @@ -9,6 +9,7 @@ // ==--== #include "sos.h" #include "disasm.h" +#include "runtime.h" #include #include "corhdr.h" @@ -40,10 +41,8 @@ #endif static bool g_hostingInitialized = false; -static bool g_symbolStoreInitialized = false; +bool g_symbolStoreInitialized = false; LPCSTR g_hostRuntimeDirectory = nullptr; -LPCSTR g_dacFilePath = nullptr; -LPCSTR g_dbiFilePath = nullptr; LPCSTR g_tmpPath = nullptr; SOSNetCoreCallbacks g_SOSNetCoreCallbacks; #ifndef FEATURE_PAL @@ -226,78 +225,6 @@ static bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutabl #endif // FEATURE_PAL -/**********************************************************************\ - * Returns the runtime module/runtime directory of the target. -\**********************************************************************/ -HRESULT GetRuntimeDirectory(std::string& runtimeDirectory) -{ -#ifdef FEATURE_PAL - LPCSTR directory = g_ExtServices->GetCoreClrDirectory(); - if (directory == NULL) - { - ExtErr("Error: Runtime module (%s) not loaded yet\n", GetRuntimeDllName()); - return E_FAIL; - } - if (!GetAbsolutePath(directory, runtimeDirectory)) - { - ExtDbgOut("Error: Runtime directory %s doesn't exist\n", directory); - return E_FAIL; - } -#else - ULONG index; - HRESULT Status = GetRuntimeModuleInfo(&index, NULL); - if (FAILED(Status)) - { - ExtErr("Error: Runtime module (%s) not loaded yet\n", GetRuntimeDllName()); - return Status; - } - ArrayHolder szModuleName = new char[MAX_LONGPATH + 1]; - Status = g_ExtSymbols->GetModuleNames(index, 0, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL, NULL, 0, NULL); - if (FAILED(Status)) - { - ExtErr("Error: Failed to get runtime module name\n"); - return Status; - } - if (GetFileAttributesA(szModuleName) == INVALID_FILE_ATTRIBUTES) - { - Status = HRESULT_FROM_WIN32(GetLastError()); - ExtDbgOut("Error: Runtime module %s doesn't exist %08x\n", szModuleName, Status); - return Status; - } - runtimeDirectory = szModuleName; - - // Parse off the module name to get just the path - size_t lastSlash = runtimeDirectory.rfind(DIRECTORY_SEPARATOR_CHAR_A); - if (lastSlash == std::string::npos) - { - ExtDbgOut("Error: Runtime module %s has no directory separator\n", szModuleName); - return E_FAIL; - } - runtimeDirectory.assign(runtimeDirectory, 0, lastSlash); -#endif - return S_OK; -} - -/**********************************************************************\ - * Returns the runtime module/runtime directory of the target. -\**********************************************************************/ -HRESULT GetRuntimeDirectory(LPWSTR modulePath, int modulePathSize) -{ - std::string runtimeDirectory; - HRESULT hr = GetRuntimeDirectory(runtimeDirectory); - if (FAILED(hr)) - { - return hr; - } - int length = MultiByteToWideChar(CP_ACP, 0, runtimeDirectory.c_str(), -1, modulePath, modulePathSize); - if (0 >= length) - { - ExtErr("MultiByteToWideChar(runtimeDirectory) failed. Last error = 0x%x\n", GetLastError()); - return HRESULT_FROM_WIN32(GetLastError()); - } - return S_OK; -} - // // Searches the runtime directory for a .NET Core runtime version // @@ -408,17 +335,22 @@ static HRESULT GetHostRuntime(std::string& coreClrPath, std::string& hostRuntime // Find highest 3.1.x version if (!FindDotNetVersion(3, 1, hostRuntimeDirectory)) { + HRESULT hr = CheckEEDll(); + if (FAILED(hr)) { + return hr; + } // Don't use the desktop runtime to host - if (g_isDesktopRuntime) + if (g_pRuntime->IsDesktop()) { return E_FAIL; } // If an installed runtime can not be found, use the target coreclr version - HRESULT hr = GetRuntimeDirectory(hostRuntimeDirectory); - if (FAILED(hr)) + LPCSTR runtimeDirectory = g_pRuntime->GetRuntimeDirectory(); + if (runtimeDirectory == nullptr) { - return hr; + return E_FAIL; } + hostRuntimeDirectory = runtimeDirectory; } } } @@ -494,96 +426,10 @@ void CleanupTempDirectory() } } -/**********************************************************************\ - * Returns the DAC module path to the rest of SOS. -\**********************************************************************/ -LPCSTR GetDacFilePath() -{ - // If the DAC path hasn't been set by the symbol download support, use the one in the runtime directory. - if (g_dacFilePath == nullptr) - { - std::string dacModulePath; - HRESULT hr = GetRuntimeDirectory(dacModulePath); - if (SUCCEEDED(hr)) - { - dacModulePath.append(DIRECTORY_SEPARATOR_STR_A); - dacModulePath.append(GetDacDllName()); -#ifdef FEATURE_PAL - // If DAC file exists in the runtime directory - if (access(dacModulePath.c_str(), F_OK) == 0) -#endif - { -#if defined(__linux__) - // We are creating a symlink to the DAC in a temp directory - // where libcoreclrtraceptprovider.so doesn't exist so it - // doesn't get loaded by the DAC causing a LTTng-UST exception. - // - // Issue #https://github.com/dotnet/coreclr/issues/20205 - LPCSTR tmpPath = GetTempDirectory(); - if (tmpPath != nullptr) - { - std::string dacSymLink(tmpPath); - dacSymLink.append(NETCORE_DAC_DLL_NAME_A); - - int error = symlink(dacModulePath.c_str(), dacSymLink.c_str()); - if (error == 0) - { - dacModulePath.assign(dacSymLink); - } - else - { - ExtErr("symlink(%s, %s) FAILED %s\n", dacModulePath.c_str(), dacSymLink.c_str(), strerror(errno)); - } - } -#endif - g_dacFilePath = _strdup(dacModulePath.c_str()); - } - } - - if (g_dacFilePath == nullptr) - { - // Attempt to only load the DAC/DBI modules - LoadNativeSymbols(true); - } - } - return g_dacFilePath; -} - -/**********************************************************************\ - * Returns the DBI module path to the rest of SOS. -\**********************************************************************/ -LPCSTR GetDbiFilePath() -{ - if (g_dbiFilePath == nullptr) - { - std::string dbiModulePath; - HRESULT hr = GetRuntimeDirectory(dbiModulePath); - if (SUCCEEDED(hr)) - { - dbiModulePath.append(DIRECTORY_SEPARATOR_STR_A); - dbiModulePath.append(NET_DBI_DLL_NAME_A); -#ifdef FEATURE_PAL - // If DBI file exists in the runtime directory - if (access(dbiModulePath.c_str(), F_OK) == 0) -#endif - { - g_dbiFilePath = _strdup(dbiModulePath.c_str()); - } - } - - if (g_dbiFilePath == nullptr) - { - // Attempt to only load the DAC/DBI modules - LoadNativeSymbols(true); - } - } - return g_dbiFilePath; -} - /**********************************************************************\ * Called when the managed SOS Host loads/initializes SOS. \**********************************************************************/ -extern "C" HRESULT SOSInitializeByHost(SOSNetCoreCallbacks* callbacks, int callbacksSize, LPCSTR tempDirectory, LPCSTR dacFilePath, LPCSTR dbiFilePath, bool symbolStoreEnabled) +extern "C" HRESULT SOSInitializeByHost(SOSNetCoreCallbacks* callbacks, int callbacksSize, LPCSTR tempDirectory, bool isDesktop, LPCSTR dacFilePath, LPCSTR dbiFilePath, bool symbolStoreEnabled) { if (memcpy_s(&g_SOSNetCoreCallbacks, sizeof(g_SOSNetCoreCallbacks), callbacks, callbacksSize) != 0) { @@ -593,14 +439,7 @@ extern "C" HRESULT SOSInitializeByHost(SOSNetCoreCallbacks* callbacks, int callb { g_tmpPath = _strdup(tempDirectory); } - if (dacFilePath != nullptr) - { - g_dacFilePath = _strdup(dacFilePath); - } - if (dbiFilePath != nullptr) - { - g_dbiFilePath = _strdup(dbiFilePath); - } + Runtime::SetDacDbiPath(isDesktop, dacFilePath, dbiFilePath); #ifndef FEATURE_PAL // When SOS is hosted on dotnet-dump, the ExtensionApis are not set so // the expression evaluation function needs to be supplied. @@ -756,7 +595,7 @@ HRESULT InitializeHosting() // Pass to managed helper code to read in-memory PEs/PDBs. // Returns the number of bytes read. // -static int ReadMemoryForSymbols(ULONG64 address, uint8_t *buffer, int cb) +int ReadMemoryForSymbols(ULONG64 address, uint8_t *buffer, int cb) { ULONG read; if (SafeReadMemory(TO_TADDR(address), (PVOID)buffer, cb, &read)) @@ -857,32 +696,23 @@ void InitializeSymbolStoreFromSymPath() } #endif // FEATURE_PAL +#ifdef FEATURE_PAL + // // Symbol downloader callback // static void SymbolFileCallback(void* param, const char* moduleFileName, const char* symbolFilePath) { - if (strcmp(moduleFileName, GetRuntimeDllName()) == 0) { + if (strcmp(moduleFileName, NETCORE_RUNTIME_DLL_NAME_A) == 0) { return; } - if (strcmp(moduleFileName, GetDacDllName()) == 0) { - if (g_dacFilePath == nullptr) { - g_dacFilePath = _strdup(symbolFilePath); - } + if (strcmp(moduleFileName, NETCORE_DAC_DLL_NAME_A) == 0) { return; } if (strcmp(moduleFileName, NET_DBI_DLL_NAME_A) == 0) { - if (g_dbiFilePath == nullptr) { - g_dbiFilePath = _strdup(symbolFilePath); - } return; } -#ifdef FEATURE_PAL - if (g_ExtServices2 != nullptr) - { - g_ExtServices2->AddModuleSymbol(param, symbolFilePath); - } -#endif + g_ExtServices2->AddModuleSymbol(param, symbolFilePath); } // @@ -903,40 +733,14 @@ static void LoadNativeSymbolsCallback(void* param, const char* moduleFilePath, U HRESULT LoadNativeSymbols(bool runtimeOnly) { HRESULT hr = S_OK; -#ifdef FEATURE_PAL if (g_symbolStoreInitialized) { - hr = g_ExtServices2 ? g_ExtServices2->LoadNativeSymbols(runtimeOnly, LoadNativeSymbolsCallback) : E_NOINTERFACE; + hr = g_ExtServices2->LoadNativeSymbols(runtimeOnly, LoadNativeSymbolsCallback); } -#else - if (runtimeOnly) - { - ULONG index; - ULONG64 moduleAddress; - HRESULT hr = GetRuntimeModuleInfo(&index, &moduleAddress); - if (SUCCEEDED(hr)) - { - ArrayHolder moduleFilePath = new char[MAX_LONGPATH + 1]; - hr = g_ExtSymbols->GetModuleNames(index, 0, moduleFilePath, MAX_LONGPATH, NULL, NULL, 0, NULL, NULL, 0, NULL); - if (SUCCEEDED(hr)) - { - DEBUG_MODULE_PARAMETERS moduleParams; - hr = g_ExtSymbols->GetModuleParameters(1, &moduleAddress, 0, &moduleParams); - if (SUCCEEDED(hr)) - { - hr = InitializeSymbolStore(); - if (SUCCEEDED(hr) && g_symbolStoreInitialized) - { - LoadNativeSymbolsCallback(nullptr, moduleFilePath, moduleAddress, moduleParams.Size); - } - } - } - } - } -#endif return hr; } +#endif /**********************************************************************\ * Displays the symbol server and cache status. diff --git a/src/SOS/Strike/hostcoreclr.h b/src/SOS/Strike/hostcoreclr.h index f4848c6d2..e1bc8d7f4 100644 --- a/src/SOS/Strike/hostcoreclr.h +++ b/src/SOS/Strike/hostcoreclr.h @@ -70,15 +70,14 @@ static const char *MetadataHelperClassName = "SOS.MetadataHelper"; extern HMODULE g_hInstance; extern LPCSTR g_hostRuntimeDirectory; -extern LPCSTR g_dacFilePath; -extern LPCSTR g_dbiFilePath; extern LPCSTR g_tmpPath; extern SOSNetCoreCallbacks g_SOSNetCoreCallbacks; -extern HRESULT GetRuntimeDirectory(std::string& runtimeDirectory); -extern HRESULT GetRuntimeDirectory(LPWSTR modulePath, int modulePathSize); -extern LPCSTR GetDacFilePath(); -extern LPCSTR GetDbiFilePath(); +#ifdef FEATURE_PAL +extern bool GetAbsolutePath(const char* path, std::string& absolutePath); +extern HRESULT LoadNativeSymbols(bool runtimeOnly = false); +#endif + extern LPCSTR GetTempDirectory(); extern void CleanupTempDirectory(); extern BOOL IsHostingInitialized(); @@ -98,7 +97,6 @@ extern HRESULT InitializeSymbolStore( extern void InitializeSymbolStoreFromSymPath(); #endif -extern HRESULT LoadNativeSymbols(bool runtimeOnly = false); extern void DisplaySymbolStore(); extern void DisableSymbolStore(); diff --git a/src/SOS/Strike/runtime.cpp b/src/SOS/Strike/runtime.cpp new file mode 100644 index 000000000..cafe21bd1 --- /dev/null +++ b/src/SOS/Strike/runtime.cpp @@ -0,0 +1,599 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "strike.h" +#include "util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "runtime.h" +#include "datatarget.h" +#include "cordebugdatatarget.h" +#include "cordebuglibraryprovider.h" + +#ifdef FEATURE_PAL +#include +#include +#include +#endif // !FEATURE_PAL + +Runtime* Runtime::s_netcore = nullptr; +#ifndef FEATURE_PAL +Runtime* Runtime::s_desktop = nullptr; +#endif + +// Used to initialize the runtime instance with values from the host when under dotnet-dump +bool Runtime::s_isDesktop = false; +LPCSTR Runtime::s_dacFilePath = nullptr; +LPCSTR Runtime::s_dbiFilePath = nullptr; + +// Current runtime instance +IRuntime* g_pRuntime = nullptr; + +/**********************************************************************\ + * Creates a desktop or .NET Core instance of the runtime class +\**********************************************************************/ +HRESULT Runtime::CreateInstance(bool isDesktop, Runtime **ppRuntime) +{ + PCSTR runtimeModuleName = isDesktop ? DESKTOP_RUNTIME_MODULE_NAME_A : NETCORE_RUNTIME_MODULE_NAME_A; + ULONG moduleIndex = 0; + ULONG64 moduleAddress = 0; + ULONG64 moduleSize = 0; + HRESULT hr = S_OK; + + if (*ppRuntime == nullptr) + { + hr = g_ExtSymbols->GetModuleByModuleName(runtimeModuleName, 0, &moduleIndex, &moduleAddress); + if (SUCCEEDED(hr)) + { +#ifdef FEATURE_PAL + hr = g_ExtServices2->GetModuleInfo(moduleIndex, nullptr, &moduleSize); +#else + _ASSERTE(moduleAddress != 0); + DEBUG_MODULE_PARAMETERS params; + hr = g_ExtSymbols->GetModuleParameters(1, &moduleAddress, 0, ¶ms); + if (SUCCEEDED(hr)) + { + moduleSize = params.Size; + + if (params.SymbolType == SymDeferred) + { + std::string reloadCommand; + reloadCommand.append("/f "); + reloadCommand.append(runtimeModuleName); + reloadCommand.append(".dll"); + g_ExtSymbols->Reload(reloadCommand.c_str()); + g_ExtSymbols->GetModuleParameters(1, &moduleAddress, 0, ¶ms); + + if (params.SymbolType != SymPdb && params.SymbolType != SymDia) + { + ExtOut("PDB symbol for %s not loaded\n", runtimeModuleName); + } + } + } +#endif + } + if (SUCCEEDED(hr)) + { + *ppRuntime = new Runtime(isDesktop, moduleIndex, moduleAddress, moduleSize); + OnUnloadTask::Register(CleanupRuntimes); + } + } + return hr; +} + +/**********************************************************************\ + * Creates an instance of the runtime class. First it attempts to create + * the .NET Core instance and if that fails, it will try to create the + * desktop CLR instance. If both runtimes exists in the process or dump + * this runtime only creates the .NET Core version and leaves creating + * the desktop instance on demand in SwitchRuntime. +\**********************************************************************/ +HRESULT Runtime::CreateInstance() +{ + HRESULT hr = S_OK; + if (g_pRuntime == nullptr) + { + hr = CreateInstance(false, &s_netcore); +#ifdef FEATURE_PAL + g_pRuntime = s_netcore; +#else + if (FAILED(hr)) + { + hr = CreateInstance(true, &s_desktop); + } + g_pRuntime = s_netcore != nullptr ? s_netcore : s_desktop; +#endif + } + return hr; +} + +/**********************************************************************\ + * Switches between the .NET Core and desktop runtimes (if both + * loaded). Creates the desktop CLR runtime instance on demand. +\**********************************************************************/ +#ifndef FEATURE_PAL +bool Runtime::SwitchRuntime(bool desktop) +{ + if (desktop) { + CreateInstance(true, &s_desktop); + } + IRuntime* runtime = desktop ? s_desktop : s_netcore; + if (runtime == nullptr) { + return false; + } + g_pRuntime = runtime; + return true; +} +#endif + +/**********************************************************************\ + * Cleans up the runtime instances +\**********************************************************************/ +void Runtime::CleanupRuntimes() +{ + if (s_netcore != nullptr) + { + delete s_netcore; + s_netcore = nullptr; + } +#ifndef FEATURE_PAL + if (s_desktop != nullptr) + { + delete s_desktop; + s_desktop = nullptr; + } +#endif + g_pRuntime = nullptr; +} + +/**********************************************************************\ + * Destroys the runtime instance +\**********************************************************************/ +Runtime::~Runtime() +{ + if (m_runtimeDirectory != nullptr) + { + free((void*)m_runtimeDirectory); + m_runtimeDirectory = nullptr; + } + if (m_dacFilePath != nullptr) + { + free((void*)m_dacFilePath); + m_dacFilePath = nullptr; + } + if (m_dbiFilePath != nullptr) + { + free((void*)m_dbiFilePath); + m_dbiFilePath = nullptr; + } + if (m_pCorDebugProcess != NULL) + { + m_pCorDebugProcess->Detach(); + m_pCorDebugProcess->Release(); + m_pCorDebugProcess = nullptr; + } + if (m_clrDataProcess != nullptr) + { + m_clrDataProcess->Release(); + m_clrDataProcess = nullptr; + } +} + +/**********************************************************************\ + * Flushes DAC caches +\**********************************************************************/ +void Runtime::Flush() +{ + if (s_netcore != nullptr && s_netcore->m_clrDataProcess != nullptr) + { + s_netcore->m_clrDataProcess->Flush(); + } +#ifndef FEATURE_PAL + if (s_desktop != nullptr && s_desktop->m_clrDataProcess != nullptr) + { + s_desktop->m_clrDataProcess->Flush(); + } +#endif +} + +/**********************************************************************\ + * Returns the runtime directory of the target +\**********************************************************************/ +HRESULT Runtime::GetRuntimeDirectory(std::string& runtimeDirectory) +{ +#ifdef FEATURE_PAL + LPCSTR directory = g_ExtServices->GetCoreClrDirectory(); + if (directory == NULL) + { + ExtErr("Error: Runtime module (%s) not loaded yet\n", GetRuntimeDllName()); + return E_FAIL; + } + if (!GetAbsolutePath(directory, runtimeDirectory)) + { + ExtDbgOut("Error: Runtime directory %s doesn't exist\n", directory); + return E_FAIL; + } +#else + ArrayHolder szModuleName = new char[MAX_LONGPATH + 1]; + HRESULT hr = g_ExtSymbols->GetModuleNames(m_index, 0, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL, NULL, 0, NULL); + if (FAILED(hr)) + { + ExtErr("Error: Failed to get runtime module name\n"); + return hr; + } + if (GetFileAttributesA(szModuleName) == INVALID_FILE_ATTRIBUTES) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + ExtDbgOut("Error: Runtime module %s doesn't exist %08x\n", szModuleName, hr); + return hr; + } + runtimeDirectory = szModuleName; + + // Parse off the module name to get just the path + size_t lastSlash = runtimeDirectory.rfind(DIRECTORY_SEPARATOR_CHAR_A); + if (lastSlash == std::string::npos) + { + ExtDbgOut("Error: Runtime module %s has no directory separator\n", szModuleName); + return E_FAIL; + } + runtimeDirectory.assign(runtimeDirectory, 0, lastSlash); +#endif + return S_OK; +} + +/**********************************************************************\ + * Returns the runtime directory of the target +\**********************************************************************/ +LPCSTR Runtime::GetRuntimeDirectory() +{ + if (m_runtimeDirectory == nullptr) + { + std::string runtimeDirectory; + if (SUCCEEDED(GetRuntimeDirectory(runtimeDirectory))) + { + m_runtimeDirectory = _strdup(runtimeDirectory.c_str()); + } + } + return m_runtimeDirectory; +} + +/**********************************************************************\ + * Returns the DAC module path to the rest of SOS. +\**********************************************************************/ +LPCSTR Runtime::GetDacFilePath() +{ + // If the DAC path hasn't been set by the symbol download support, use the one in the runtime directory. + if (m_dacFilePath == nullptr) + { + LPCSTR directory = GetRuntimeDirectory(); + if (directory != nullptr) + { + std::string dacModulePath(directory); + dacModulePath.append(DIRECTORY_SEPARATOR_STR_A); + dacModulePath.append(GetDacDllName()); +#ifdef FEATURE_PAL + // If DAC file exists in the runtime directory + if (access(dacModulePath.c_str(), F_OK) == 0) +#endif + { +#if defined(__linux__) + // We are creating a symlink to the DAC in a temp directory + // where libcoreclrtraceptprovider.so doesn't exist so it + // doesn't get loaded by the DAC causing a LTTng-UST exception. + // + // Issue #https://github.com/dotnet/coreclr/issues/20205 + LPCSTR tmpPath = GetTempDirectory(); + if (tmpPath != nullptr) + { + std::string dacSymLink(tmpPath); + dacSymLink.append(NETCORE_DAC_DLL_NAME_A); + + int error = symlink(dacModulePath.c_str(), dacSymLink.c_str()); + if (error == 0) + { + dacModulePath.assign(dacSymLink); + } + else + { + ExtErr("symlink(%s, %s) FAILED %s\n", dacModulePath.c_str(), dacSymLink.c_str(), strerror(errno)); + } + } +#endif + m_dacFilePath = _strdup(dacModulePath.c_str()); + } + } + + if (m_dacFilePath == nullptr) + { + // Attempt to only load the DAC/DBI modules + LoadRuntimeModules(); + } + } + return m_dacFilePath; +} + +/**********************************************************************\ + * Returns the DBI module path to the rest of SOS +\**********************************************************************/ +LPCSTR Runtime::GetDbiFilePath() +{ + if (m_dbiFilePath == nullptr) + { + LPCSTR directory = GetRuntimeDirectory(); + if (directory != nullptr) + { + std::string dbiModulePath(directory); + dbiModulePath.append(DIRECTORY_SEPARATOR_STR_A); + dbiModulePath.append(NET_DBI_DLL_NAME_A); +#ifdef FEATURE_PAL + // If DBI file exists in the runtime directory + if (access(dbiModulePath.c_str(), F_OK) == 0) +#endif + { + m_dbiFilePath = _strdup(dbiModulePath.c_str()); + } + } + + if (m_dbiFilePath == nullptr) + { + // Attempt to only load the DAC/DBI modules + LoadRuntimeModules(); + } + } + return m_dbiFilePath; +} + +/**********************************************************************\ + * Creates an instance of the DAC clr data process +\**********************************************************************/ +HRESULT Runtime::GetClrDataProcess(IXCLRDataProcess** ppClrDataProcess) +{ + if (m_clrDataProcess == nullptr) + { + *ppClrDataProcess = nullptr; + + LPCSTR dacFilePath = GetDacFilePath(); + if (dacFilePath == nullptr) + { + return CORDBG_E_NO_IMAGE_AVAILABLE; + } + HMODULE hdac = LoadLibraryA(dacFilePath); + if (hdac == NULL) + { + return CORDBG_E_MISSING_DEBUGGER_EXPORTS; + } + PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance"); + if (pfnCLRDataCreateInstance == nullptr) + { + FreeLibrary(hdac); + return CORDBG_E_MISSING_DEBUGGER_EXPORTS; + } + ICLRDataTarget *target = new DataTarget(); + HRESULT hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&m_clrDataProcess); + if (FAILED(hr)) + { + m_clrDataProcess = nullptr; + return hr; + } + ULONG32 flags = 0; + m_clrDataProcess->GetOtherNotificationFlags(&flags); + flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD | CLRDATA_NOTIFY_ON_EXCEPTION); + m_clrDataProcess->SetOtherNotificationFlags(flags); + } + *ppClrDataProcess = m_clrDataProcess; + return S_OK; +} + +/**********************************************************************\ + * Loads and initializes the public ICorDebug interfaces. This should be + * called at least once per debugger stop state to ensure that the + * interface is available and that it doesn't hold stale data. Calling + * it more than once isn't an error, but does have perf overhead from + * needlessly flushing memory caches. +\**********************************************************************/ +HRESULT Runtime::GetCorDebugInterface(ICorDebugProcess** ppCorDebugProcess) +{ + HMODULE hModule = NULL; + HRESULT hr; + ToRelease pClrDebugging; + + // We may already have an ICorDebug instance we can use + if (m_pCorDebugProcess != nullptr) + { + // ICorDebugProcess4 is currently considered a private experimental interface on ICorDebug, it might go away so + // we need to be sure to handle its absence gracefully + ToRelease pProcess4 = NULL; + if (SUCCEEDED(m_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess4), (void**)&pProcess4))) + { + // FLUSH_ALL is more expensive than PROCESS_RUNNING, but this allows us to be safe even if things + // like IDNA are in use where we might be looking at non-sequential snapshots of process state + if (SUCCEEDED(pProcess4->ProcessStateChanged(FLUSH_ALL))) + { + // We already have an ICorDebug instance loaded and flushed, nothing more to do + *ppCorDebugProcess = m_pCorDebugProcess; + return S_OK; + } + } + + // This is a very heavy handed way of reseting + m_pCorDebugProcess->Detach(); + m_pCorDebugProcess->Release(); + m_pCorDebugProcess = nullptr; + } + + // SOS now has a statically linked version of the loader code that is normally found in mscoree/mscoreei.dll + // Its not much code and takes a big step towards 0 install dependencies + // Need to pick the appropriate SKU of CLR to detect +#if defined(FEATURE_CORESYSTEM) + GUID skuId = CLR_ID_ONECORE_CLR; +#else + GUID skuId = CLR_ID_CORECLR; +#endif +#ifndef FEATURE_PAL + if (IsDesktop()) + { + skuId = CLR_ID_V4_DESKTOP; + } +#endif + CLRDebuggingImpl* pDebuggingImpl = new CLRDebuggingImpl(skuId); + hr = pDebuggingImpl->QueryInterface(IID_ICLRDebugging, (LPVOID *)&pClrDebugging); + if (FAILED(hr)) + { + delete pDebuggingImpl; + return hr; + } + + ToRelease pCorDebugDataTarget = new CorDebugDataTarget; + pCorDebugDataTarget->AddRef(); + + ToRelease pCorDebugLibraryProvider = new CorDebugLibraryProvider(this); + pCorDebugLibraryProvider->AddRef(); + + CLR_DEBUGGING_VERSION clrDebuggingVersionRequested = {0}; + clrDebuggingVersionRequested.wMajor = 4; + + CLR_DEBUGGING_VERSION clrDebuggingVersionActual = {0}; + + CLR_DEBUGGING_PROCESS_FLAGS clrDebuggingFlags = (CLR_DEBUGGING_PROCESS_FLAGS)0; + + ToRelease pUnkProcess; + hr = pClrDebugging->OpenVirtualProcess( + GetModuleAddress(), + pCorDebugDataTarget, + pCorDebugLibraryProvider, + &clrDebuggingVersionRequested, + IID_ICorDebugProcess, + &pUnkProcess, + &clrDebuggingVersionActual, + &clrDebuggingFlags); + + if (FAILED(hr)) { + return hr; + } + hr = pUnkProcess->QueryInterface(IID_ICorDebugProcess, (PVOID*)&m_pCorDebugProcess); + if (FAILED(hr)) { + return hr; + } + *ppCorDebugProcess = m_pCorDebugProcess; + return hr; +} + +/**********************************************************************\ + * Displays the runtime internal status +\**********************************************************************/ +void Runtime::DisplayStatus() +{ + ExtOut("%s runtime at %p (%08x)\n", m_isDesktop ? "Desktop" : ".NET Core", m_address, m_size); + if (m_runtimeDirectory != nullptr) { + ExtOut("Runtime directory: %s\n", m_runtimeDirectory); + } + if (m_dacFilePath != nullptr) { + ExtOut("DAC file path: %s\n", m_dacFilePath); + } + if (m_dbiFilePath != nullptr) { + ExtOut("DBI file path: %s\n", m_dbiFilePath); + } +} + +extern bool g_symbolStoreInitialized; +extern HRESULT InitializeSymbolStore(); +extern int ReadMemoryForSymbols(ULONG64 address, uint8_t* buffer, int cb); + +/**********************************************************************\ + * Attempt to download the runtime modules (runtime, DAC and DBI) +\**********************************************************************/ +void Runtime::LoadRuntimeModules() +{ + ArrayHolder moduleFilePath = new char[MAX_LONGPATH + 1]; + HRESULT hr = g_ExtSymbols->GetModuleNames(m_index, 0, moduleFilePath, MAX_LONGPATH, NULL, NULL, 0, NULL, NULL, 0, NULL); + if (SUCCEEDED(hr)) + { + hr = InitializeSymbolStore(); + if (SUCCEEDED(hr) && g_symbolStoreInitialized) + { + _ASSERTE(g_SOSNetCoreCallbacks.LoadNativeSymbolsDelegate != nullptr); + g_SOSNetCoreCallbacks.LoadNativeSymbolsDelegate(SymbolFileCallback, this, moduleFilePath, m_address, (int)m_size, ReadMemoryForSymbols); + } + } +} + +/**********************************************************************\ + * Called by LoadRuntimeModules to set the DAC and DBI file paths +\**********************************************************************/ +void Runtime::SymbolFileCallback(const char* moduleFileName, const char* symbolFilePath) +{ + if (strcmp(moduleFileName, GetRuntimeDllName()) == 0) { + return; + } + if (strcmp(moduleFileName, GetDacDllName()) == 0) { + SetDacFilePath(symbolFilePath); + return; + } + if (strcmp(moduleFileName, NET_DBI_DLL_NAME_A) == 0) { + SetDbiFilePath(symbolFilePath); + return; + } +} + +#ifndef FEATURE_PAL + +/**********************************************************************\ + * Internal function to load and check the version of the module +\**********************************************************************/ +HMODULE LoadLibraryAndCheck( + PCWSTR filename, + DWORD timestamp, + DWORD filesize) +{ + HMODULE hModule = LoadLibraryExW( + filename, + NULL, // __reserved + LOAD_WITH_ALTERED_SEARCH_PATH); // Ensure we check the dir in wszFullPath first + + if (hModule == NULL) + { + ExtOut("Unable to load '%S'. hr = 0x%x.\n", filename, HRESULT_FROM_WIN32(GetLastError())); + return NULL; + } + + // Did we load the right one? + MODULEINFO modInfo = {0}; + if (!GetModuleInformation( + GetCurrentProcess(), + hModule, + &modInfo, + sizeof(modInfo))) + { + ExtOut("Failed to read module information for '%S'. hr = 0x%x.\n", filename, HRESULT_FROM_WIN32(GetLastError())); + FreeLibrary(hModule); + return NULL; + } + + IMAGE_DOS_HEADER * pDOSHeader = (IMAGE_DOS_HEADER *) modInfo.lpBaseOfDll; + IMAGE_NT_HEADERS * pNTHeaders = (IMAGE_NT_HEADERS *) (((LPBYTE) modInfo.lpBaseOfDll) + pDOSHeader->e_lfanew); + DWORD dwSizeActual = pNTHeaders->OptionalHeader.SizeOfImage; + DWORD dwTimeStampActual = pNTHeaders->FileHeader.TimeDateStamp; + if ((dwSizeActual != filesize) || (dwTimeStampActual != timestamp)) + { + ExtOut("Found '%S', but it does not match the CLR being debugged.\n", filename); + ExtOut("Size: Expected '0x%x', Actual '0x%x'\n", filesize, dwSizeActual); + ExtOut("Time stamp: Expected '0x%x', Actual '0x%x'\n", timestamp, dwTimeStampActual); + FreeLibrary(hModule); + return NULL; + } + + return hModule; +} + +#endif // FEATURE_PAL diff --git a/src/SOS/Strike/runtime.h b/src/SOS/Strike/runtime.h new file mode 100644 index 000000000..eef489352 --- /dev/null +++ b/src/SOS/Strike/runtime.h @@ -0,0 +1,257 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef __runtime_h__ +#define __runtime_h__ + +#ifdef FEATURE_PAL + +#define NETCORE_RUNTIME_MODULE_NAME_W MAKEDLLNAME_W(W("coreclr")) +#define NETCORE_RUNTIME_MODULE_NAME_A MAKEDLLNAME_A("coreclr") +#define NETCORE_RUNTIME_DLL_NAME_W NETCORE_RUNTIME_MODULE_NAME_W +#define NETCORE_RUNTIME_DLL_NAME_A NETCORE_RUNTIME_MODULE_NAME_A + +#define NETCORE_DAC_MODULE_NAME_W MAKEDLLNAME_W(W("mscordaccore")) +#define NETCORE_DAC_MODULE_NAME_A MAKEDLLNAME_A("mscordaccore") +#define NETCORE_DAC_DLL_NAME_W NETCORE_DAC_MODULE_NAME_W +#define NETCORE_DAC_DLL_NAME_A NETCORE_DAC_MODULE_NAME_A + +#define NET_DBI_MODULE_NAME_W MAKEDLLNAME_W(W("mscordbi")) +#define NET_DBI_MODULE_NAME_A MAKEDLLNAME_A("mscordbi") +#define NET_DBI_DLL_NAME_W NET_DBI_MODULE_NAME_W +#define NET_DBI_DLL_NAME_A NET_DBI_MODULE_NAME_A + +#else + +#define NETCORE_RUNTIME_MODULE_NAME_W W("coreclr") +#define NETCORE_RUNTIME_MODULE_NAME_A "coreclr" +#define NETCORE_RUNTIME_DLL_NAME_W MAKEDLLNAME_W(NETCORE_RUNTIME_MODULE_NAME_W) +#define NETCORE_RUNTIME_DLL_NAME_A MAKEDLLNAME_A(NETCORE_RUNTIME_MODULE_NAME_A) + +#define NETCORE_DAC_MODULE_NAME_W W("mscordaccore") +#define NETCORE_DAC_MODULE_NAME_A "mscordaccore" +#define NETCORE_DAC_DLL_NAME_W MAKEDLLNAME_W(NETCORE_DAC_MODULE_NAME_W) +#define NETCORE_DAC_DLL_NAME_A MAKEDLLNAME_A(NETCORE_DAC_MODULE_NAME_A) + +#define NET_DBI_MODULE_NAME_W W("mscordbi") +#define NET_DBI_MODULE_NAME_A "mscordbi" +#define NET_DBI_DLL_NAME_W MAKEDLLNAME_W(W("mscordbi")) +#define NET_DBI_DLL_NAME_A MAKEDLLNAME_A("mscordbi") + +#endif // FEATURE_PAL + +#define DESKTOP_RUNTIME_MODULE_NAME_W W("clr") +#define DESKTOP_RUNTIME_MODULE_NAME_A "clr" +#define DESKTOP_RUNTIME_DLL_NAME_W MAKEDLLNAME_W(DESKTOP_RUNTIME_MODULE_NAME_W) +#define DESKTOP_RUNTIME_DLL_NAME_A MAKEDLLNAME_A(DESKTOP_RUNTIME_MODULE_NAME_A) + +#define DESKTOP_DAC_MODULE_NAME_W W("mscordacwks") +#define DESKTOP_DAC_MODULE_NAME_A "mscordacwks" +#define DESKTOP_DAC_DLL_NAME_W MAKEDLLNAME_W(W("mscordacwks")) +#define DESKTOP_DAC_DLL_NAME_A MAKEDLLNAME_A("mscordacwks") + +/**********************************************************************\ + * Runtime interface +\**********************************************************************/ +class IRuntime +{ +public: + // Returns true if desktop CLR; false if .NET Core + virtual bool IsDesktop() const = 0; + + // Returns the runtime module index + virtual ULONG GetModuleIndex() const = 0; + + // Returns the runtime module base address + virtual ULONG64 GetModuleAddress() const = 0; + + // Returns the runtime module size + virtual ULONG64 GetModuleSize() const = 0; + + // Returns the directory of the runtime file + virtual LPCSTR GetRuntimeDirectory() = 0; + + // Returns the DAC module path to the rest of SOS + virtual LPCSTR GetDacFilePath() = 0; + + // Returns the DBI module path to the rest of SOS + virtual LPCSTR GetDbiFilePath() = 0; + + // Returns the DAC data process instance + virtual HRESULT GetClrDataProcess(IXCLRDataProcess** ppClrDataProcess) = 0; + + // Initializes and returns the DBI debugging interface instance + virtual HRESULT GetCorDebugInterface(ICorDebugProcess** ppCorDebugProcess) = 0; + + // Displays the runtime internal status + virtual void DisplayStatus() = 0; +}; + +extern IRuntime* g_pRuntime; + +/**********************************************************************\ + * Local Runtime interface implementation +\**********************************************************************/ +class Runtime : public IRuntime +{ +private: + bool m_isDesktop; + ULONG m_index; + ULONG64 m_address; + ULONG64 m_size; + LPCSTR m_runtimeDirectory; + LPCSTR m_dacFilePath; + LPCSTR m_dbiFilePath; + IXCLRDataProcess* m_clrDataProcess; + ICorDebugProcess* m_pCorDebugProcess; + + static Runtime* s_netcore; +#ifndef FEATURE_PAL + static Runtime* s_desktop; +#endif + static bool s_isDesktop; + static LPCSTR s_dacFilePath; + static LPCSTR s_dbiFilePath; + + Runtime(bool isDesktop, ULONG index, ULONG64 address, ULONG64 size) : + m_isDesktop(isDesktop), + m_index(index), + m_address(address), + m_size(size), + m_runtimeDirectory(nullptr), + m_dacFilePath(nullptr), + m_dbiFilePath(nullptr), + m_clrDataProcess(nullptr), + m_pCorDebugProcess(nullptr) + { + _ASSERTE(index != -1); + _ASSERTE(address != 0); + _ASSERTE(size != 0); + if (isDesktop == s_isDesktop) { + SetDacFilePath(s_dacFilePath); + SetDbiFilePath(s_dbiFilePath); + } + } + + virtual Runtime::~Runtime(); + + static HRESULT CreateInstance(bool isDesktop, Runtime** ppRuntime); + + HRESULT GetRuntimeDirectory(std::string& runtimeDirectory); + + void LoadRuntimeModules(); + + void SymbolFileCallback(const char* moduleFileName, const char* symbolFilePath); + + static void SymbolFileCallback(void* param, const char* moduleFileName, const char* symbolFilePath) + { + ((Runtime*)param)->SymbolFileCallback(moduleFileName, symbolFilePath); + } + + void SetDacFilePath(LPCSTR dacFilePath) + { + if (m_dacFilePath == nullptr && dacFilePath != nullptr) { + m_dacFilePath = _strdup(dacFilePath); + } + } + + void SetDbiFilePath(LPCSTR dbiFilePath) + { + if (m_dbiFilePath == nullptr && dbiFilePath != nullptr) { + m_dbiFilePath = _strdup(dbiFilePath); + } + } + +public: + static HRESULT CreateInstance(); + + static void CleanupRuntimes(); + +#ifndef FEATURE_PAL + static bool SwitchRuntime(bool desktop); +#endif + + static void SetDacDbiPath(bool isDesktop, LPCSTR dacFilePath, LPCSTR dbiFilePath) + { + s_isDesktop = isDesktop; + if (dacFilePath != nullptr) { + s_dacFilePath = _strdup(dacFilePath); + } + if (dbiFilePath != nullptr) { + s_dbiFilePath = _strdup(dbiFilePath); + } + } + + static void Flush(); + + virtual bool IsDesktop() const { return m_isDesktop; } + + virtual ULONG GetModuleIndex() const { return m_index; } + + virtual ULONG64 GetModuleAddress() const { return m_address; } + + virtual ULONG64 GetModuleSize() const { return m_size; } + + LPCSTR GetRuntimeDirectory(); + + LPCSTR GetDacFilePath(); + + LPCSTR GetDbiFilePath(); + + HRESULT GetClrDataProcess(IXCLRDataProcess** ppClrDataProcess); + + HRESULT GetCorDebugInterface(ICorDebugProcess** ppCorDebugProcess); + + void DisplayStatus(); + + // Returns the runtime module DLL name (clr.dll, coreclr.dll, libcoreclr.so, libcoreclr.dylib) + inline const char* GetRuntimeDllName() const + { + return IsDesktop() ? DESKTOP_RUNTIME_DLL_NAME_A : NETCORE_RUNTIME_DLL_NAME_A; + } + + // Returns the DAC module name (mscordacwks.dll, mscordaccore.dll, libmscordaccore.so, libmscordaccore.dylib) + inline const char* GetDacDllName() const + { + return IsDesktop() ? DESKTOP_DAC_DLL_NAME_A : NETCORE_DAC_DLL_NAME_A; + } + + // Returns the DAC module name (mscordacwks, mscordaccore, libmscordaccore.so, libmscordaccore.dylib) + inline const WCHAR* GetDacModuleNameW() const + { + return IsDesktop() ? DESKTOP_DAC_MODULE_NAME_W : NETCORE_DAC_MODULE_NAME_W; + } + + // Returns the DAC module name (mscordacwks.dll, mscordaccore.dll, libmscordaccore.so, libmscordaccore.dylib) + inline const WCHAR* GetDacDllNameW() const + { + return IsDesktop() ? DESKTOP_DAC_DLL_NAME_W : NETCORE_DAC_DLL_NAME_W; + } +}; + +// Returns the runtime module name (clr, coreclr, libcoreclr.so, libcoreclr.dylib). +inline const char* GetRuntimeModuleName() +{ + return g_pRuntime->IsDesktop() ? DESKTOP_RUNTIME_MODULE_NAME_A : NETCORE_RUNTIME_MODULE_NAME_A; +} + +// Returns the runtime module DLL name (clr.dll, coreclr.dll, libcoreclr.so, libcoreclr.dylib) +inline const char* GetRuntimeDllName() +{ + return g_pRuntime->IsDesktop() ? DESKTOP_RUNTIME_DLL_NAME_A : NETCORE_RUNTIME_DLL_NAME_A; +} + +// Returns the DAC module name (mscordacwks, mscordaccore, libmscordaccore.so, libmscordaccore.dylib) +inline const char* GetDacModuleName() +{ + return g_pRuntime->IsDesktop() ? DESKTOP_DAC_MODULE_NAME_A : NETCORE_DAC_MODULE_NAME_A; +} + +// Returns the DAC module name (mscordacwks.dll, mscordaccore.dll, libmscordaccore.so, libmscordaccore.dylib) +inline const char* GetDacDllName() +{ + return g_pRuntime->IsDesktop() ? DESKTOP_DAC_DLL_NAME_A : NETCORE_DAC_DLL_NAME_A; +} + +#endif // __runtime_h__ diff --git a/src/SOS/Strike/sosdocs.txt b/src/SOS/Strike/sosdocs.txt index 655b39094..fcd4cfad0 100644 --- a/src/SOS/Strike/sosdocs.txt +++ b/src/SOS/Strike/sosdocs.txt @@ -64,7 +64,8 @@ HistInit SetHostRuntime (sethostruntime) HistRoot SetSymbolServer (setsymbolserver) HistObj FAQ HistObjFind SOSFlush -HistClear Help (soshelp) +HistClear SOSStatus (sosstatus) + Help (soshelp) \\ COMMAND: faq. @@ -2609,7 +2610,7 @@ You can use the "dotnet --info" in a command shell to find the path of an instal \\ COMMAND: setsymbolserver. -SetSymbolServer [-ms] [-mi] [-disable] [-log] [-cache ] [-directory ] [] +!SetSymbolServer [-ms] [-mi] [-disable] [-log] [-cache ] [-directory ] [] -ms - Use the public Microsoft symbol server. -mi - Use the internal symweb symbol server. @@ -2625,3 +2626,22 @@ symbol server support is automatically enabled. To disable downloading or clear the current SOS symbol settings allowing new symbol paths to be set: 0:000> !setsymbolserver -disable +\\ + +COMMAND: sosstatus. +!SOSStatus [-desktop] [-netcore] [-reset] + +-desktop - switch to the desktop runtime if loaded. +-netcore - switch to the .NET Core runtime if loaded. +-reset - reset all the cached internal SOS state. + +Display internal SOS status, reset the internal cached state, or change between desktop or +netcore runtimes when both are loaded in the process or dump. + +0:000> !sosstatus +Target platform: 8664 Context size 04d0 +.NET Core runtime at 00007FFEE7230000 (005c2000) +DAC file path: C:\Users\mikem\AppData\Local\Temp\SymbolCache\mscordaccore.dll/5d0707425c2000/mscordaccore.dll +DBI file path: C:\Users\mikem\AppData\Local\Temp\SymbolCache\mscordbi.dll/5d0707425c2000/mscordbi.dll +Cache: C:\Users\mikem\AppData\Local\Temp\SymbolCache +Server: http://msdl.microsoft.com/download/symbols/ \ No newline at end of file diff --git a/src/SOS/Strike/sosdocsunix.txt b/src/SOS/Strike/sosdocsunix.txt index 3aa6df27b..e2200387d 100644 --- a/src/SOS/Strike/sosdocsunix.txt +++ b/src/SOS/Strike/sosdocsunix.txt @@ -56,8 +56,9 @@ Examining the GC history Other HistInit (histinit) SetHostRuntime (sethostruntime) HistRoot (histroot) SetSymbolServer (setsymbolserver, loadsymbols) HistObj (histobj) FAQ -HistObjFind (histobjfind) SOSFlush -HistClear (histclear) Help (soshelp) +HistObjFind (histobjfind) SOSFlush (sosflush) +HistClear (histclear) SOSStatus (sosstatus) + Help (soshelp) \\ COMMAND: faq. @@ -1954,3 +1955,11 @@ stack frames). (lldb) loadsymbols (lldb) bt +\\ + +COMMAND: sosstatus. +SOSStatus [-reset] + +-reset - reset all the cached internal SOS state. + +Display internal SOS status or reset the internal cached state. diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index acd6d3d63..27814e649 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -1405,8 +1405,10 @@ DECLARE_API(DumpMT) if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK) { // Is it an fcall? - if ((TO_TADDR(MethodDescData.NativeCodeAddr) >= TO_TADDR(g_moduleInfo[MSCORWKS].baseAddr)) && - ((TO_TADDR(MethodDescData.NativeCodeAddr) < TO_TADDR(g_moduleInfo[MSCORWKS].baseAddr + g_moduleInfo[MSCORWKS].size)))) + ULONG64 baseAddress = g_pRuntime->GetModuleAddress(); + ULONG64 size = g_pRuntime->GetModuleSize(); + if ((TO_TADDR(MethodDescData.NativeCodeAddr) >= TO_TADDR(baseAddress)) && + ((TO_TADDR(MethodDescData.NativeCodeAddr) < TO_TADDR(baseAddress + size)))) { pszJitType = "FCALL"; } @@ -7578,7 +7580,6 @@ public: } return m_count; } - /* * New code was generated or discarded for a method.: @@ -7586,9 +7587,11 @@ public: STDMETHODIMP OnCodeGenerated(IXCLRDataMethodInstance* method) { #ifndef FEATURE_PAL + _ASSERTE(g_pRuntime != nullptr); + // This is only needed for desktop runtime because OnCodeGenerated2 // isn't supported by the desktop DAC. - if (g_isDesktopRuntime) + if (g_pRuntime->IsDesktop()) { // Some method has been generated, make a breakpoint and remove it. ULONG32 len = mdNameLen; @@ -9831,8 +9834,10 @@ DECLARE_API(DumpLog) MINIDUMP_NOT_SUPPORTED(); + _ASSERTE(g_pRuntime != nullptr); + // Not supported on desktop runtime - if (g_isDesktopRuntime) + if (g_pRuntime->IsDesktop()) { ExtErr("DumpLog not supported on desktop runtime\n"); return E_FAIL; @@ -9915,12 +9920,6 @@ DECLARE_API (DumpGCLog) INIT_API_NODAC(); MINIDUMP_NOT_SUPPORTED(); - if (GetEEFlavor() == UNKNOWNEE) - { - ExtOut("CLR not loaded\n"); - return Status; - } - const char* fileName = "GCLog.txt"; while (isspace (*args)) @@ -10021,12 +10020,6 @@ DECLARE_API (DumpGCConfigLog) #ifdef GC_CONFIG_DRIVEN MINIDUMP_NOT_SUPPORTED(); - if (GetEEFlavor() == UNKNOWNEE) - { - ExtOut("CLR not loaded\n"); - return Status; - } - const char* fileName = "GCConfigLog.txt"; while (isspace (*args)) @@ -10317,11 +10310,6 @@ DECLARE_API(EEVersion) { INIT_API(); - EEFLAVOR eef = GetEEFlavor(); - if (eef == UNKNOWNEE) { - ExtOut("CLR not loaded\n"); - return Status; - } static const int fileVersionBufferSize = 1024; ArrayHolder fileVersionBuffer = new char[fileVersionBufferSize]; VS_FIXEDFILEINFO version; @@ -10347,7 +10335,7 @@ DECLARE_API(EEVersion) } else { - BOOL fRet = IsRetailBuild((size_t)g_moduleInfo[eef].baseAddr); + BOOL fRet = IsRetailBuild((size_t)g_pRuntime->GetModuleAddress()); if (fRet) ExtOut(" retail"); else @@ -10407,27 +10395,55 @@ DECLARE_API(EEVersion) \**********************************************************************/ DECLARE_API(SOSStatus) { - INIT_API_NOEE(); + INIT_API_EXT(); + BOOL bDesktop = FALSE; + BOOL bNetCore = FALSE; + BOOL bReset = FALSE; + CMDOption option[] = + { // name, vptr, type, hasValue +#ifndef FEATURE_PAL + {"-desktop", &bDesktop, COBOOL, FALSE}, + {"-netcore", &bNetCore, COBOOL, FALSE}, +#endif + {"-reset", &bReset, COBOOL, FALSE}, + }; + if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) + { + return Status; + } +#ifndef FEATURE_PAL + if (bNetCore || bDesktop) + { + PCSTR name = bDesktop ? "desktop CLR" : ".NET Core";; + if (!Runtime::SwitchRuntime(bDesktop)) + { + ExtErr("The %s runtime is not loaded\n", name); + return E_FAIL; + } + ExtOut("Switched to %s runtime successfully\n", name); + return S_OK; + } +#endif + if (bReset) + { + Runtime::CleanupRuntimes(); + CleanupTempDirectory(); + ExtOut("SOS state reset\n"); + return S_OK; + } if (g_targetMachine != nullptr) { ExtOut("Target platform: %04x Context size %04x\n", g_targetMachine->GetPlatform(), g_targetMachine->GetContextSize()); } + if (g_pRuntime != nullptr) { + g_pRuntime->DisplayStatus(); + } if (g_tmpPath != nullptr) { ExtOut("Temp path: %s\n", g_tmpPath); } - if (g_dacFilePath != nullptr) { - ExtOut("DAC file path: %s\n", g_dacFilePath); - } - if (g_dbiFilePath != nullptr) { - ExtOut("DBI file path: %s\n", g_dbiFilePath); - } if (g_hostRuntimeDirectory != nullptr) { ExtOut("Host runtime path: %s\n", g_hostRuntimeDirectory); } - std::string runtimeDirectory; - if (SUCCEEDED(GetRuntimeDirectory(runtimeDirectory))) { - ExtOut("Runtime path: %s\n", runtimeDirectory.c_str()); - } DisplaySymbolStore(); return Status; @@ -11683,10 +11699,8 @@ DECLARE_API(GCHandles) #ifndef FEATURE_PAL DECLARE_API(TraceToCode) { - INIT_API_NOEE(); - - static ULONG64 g_clrBaseAddr = 0; - + INIT_API_NODAC(); + _ASSERTE(g_pRuntime != nullptr); while(true) { @@ -11703,13 +11717,10 @@ DECLARE_API(TraceToCode) ULONG64 base = 0; CLRDATA_ADDRESS cdaStart = TO_CDADDR(Offset); DacpMethodDescData MethodDescData; - if(g_ExtSymbols->GetModuleByOffset(Offset, 0, NULL, &base) == S_OK) + if (g_ExtSymbols->GetModuleByOffset(Offset, 0, NULL, &base) == S_OK) { - if(g_clrBaseAddr == 0) - { - GetRuntimeModuleInfo(NULL, &g_clrBaseAddr); - } - if(g_clrBaseAddr == base) + ULONG64 clrBaseAddr = g_pRuntime->GetModuleAddress(); + if(clrBaseAddr == base) { ExtOut("Compiled code in CLR\n"); codeType = 4; @@ -11774,14 +11785,14 @@ DECLARE_API(TraceToCode) // This is an experimental and undocumented API that sets a debugger pseudo-register based // on the type of code at the given IP. It can be used in scripts to keep stepping until certain -// kinds of code have been reached. Presumbably its slower than !TraceToCode but at least it +// kinds of code have been reached. Presumably its slower than !TraceToCode but at least it // cancels much better #ifndef FEATURE_PAL DECLARE_API(GetCodeTypeFlags) { INIT_API(); + _ASSERTE(g_pRuntime != nullptr); - char buffer[100+mdNameLen]; size_t ip; StringHolder PReg; @@ -11822,7 +11833,7 @@ DECLARE_API(GetCodeTypeFlags) CLRDATA_ADDRESS cdaStart = TO_CDADDR(ip); DWORD codeType = 0; CLRDATA_ADDRESS addr; - if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK) + if (g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK) { WCHAR wszNameBuffer[1024]; // should be large enough @@ -11841,8 +11852,8 @@ DECLARE_API(GetCodeTypeFlags) } else if(g_ExtSymbols->GetModuleByOffset (ip, 0, NULL, &base) == S_OK) { - ULONG64 clrBaseAddr = 0; - if(SUCCEEDED(GetRuntimeModuleInfo(NULL, &clrBaseAddr)) && base==clrBaseAddr) + ULONG64 clrBaseAddr = g_pRuntime->GetModuleAddress(); + if (base == clrBaseAddr) { ExtOut("Compiled code in CLR"); codeType = 4; @@ -13175,7 +13186,8 @@ public: { HRESULT Status; - IfFailRet(InitCorDebugInterface()); + ICorDebugProcess* pCorDebugProcess; + IfFailRet(g_pRuntime->GetCorDebugInterface(&pCorDebugProcess)); ExtOut("\n\n\nDumping managed stack and managed variables using ICorDebug.\n"); ExtOut("=============================================================================\n"); @@ -13186,7 +13198,7 @@ public: ULONG ulThreadID = 0; g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID); - IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread)); + IfFailRet(pCorDebugProcess->GetThread(ulThreadID, &pThread)); IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3)); IfFailRet(pThread3->CreateStackWalk(&pStackWalk)); @@ -13346,10 +13358,6 @@ public: } ExtOut("=============================================================================\n"); -#ifdef FEATURE_PAL - // Temporary until we get a process exit notification plumbed from lldb - UninitCorDebugInterface(); -#endif return S_OK; } }; @@ -14265,17 +14273,17 @@ DECLARE_API( VMMap ) #endif // FEATURE_PAL -DECLARE_API( SOSFlush ) +DECLARE_API(SOSFlush) { - INIT_API(); + INIT_API_EXT(); - g_clrData->Flush(); + Runtime::Flush(); #ifdef FEATURE_PAL FlushMetadataRegions(); #endif return Status; -} // DECLARE_API( SOSFlush ) +} #ifndef FEATURE_PAL @@ -15465,7 +15473,7 @@ DECLARE_API(VerifyStackTrace) // This is an internal-only Apollo extension to de-optimize the code DECLARE_API(SuppressJitOptimization) { - INIT_API_NOEE(); + INIT_API_NODAC(); MINIDUMP_NOT_SUPPORTED(); StringHolder onOff; @@ -15503,12 +15511,12 @@ DECLARE_API(SuppressJitOptimization) else if(nArg == 1 && (_stricmp(onOff.data, "Off") == 0)) { // if CLR is already loaded, try to change the flags now - if(CheckEEDll() == S_OK) + if (CheckEEDll() == S_OK) { SetNGENCompilerFlags(CORDEBUG_JIT_DEFAULT); } - if(g_fAllowJitOptimization) + if (g_fAllowJitOptimization) ExtOut("JIT optimization is already permitted\n"); else { @@ -15531,13 +15539,14 @@ HRESULT SetNGENCompilerFlags(DWORD flags) HRESULT hr; ToRelease proc2; - if(FAILED(hr = InitCorDebugInterface())) + ICorDebugProcess* pCorDebugProcess; + if (FAILED(hr = g_pRuntime->GetCorDebugInterface(&pCorDebugProcess))) { ExtOut("SOS: warning, prejitted code optimizations could not be changed. Failed to load ICorDebug HR = 0x%x\n", hr); } - else if(FAILED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess2), (void**) &proc2))) + else if (FAILED(pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess2), (void**) &proc2))) { - if(flags != CORDEBUG_JIT_DEFAULT) + if (flags != CORDEBUG_JIT_DEFAULT) { ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n"); } @@ -15546,7 +15555,7 @@ HRESULT SetNGENCompilerFlags(DWORD flags) hr = S_OK; } } - else if(FAILED(hr = proc2->SetDesiredNGENCompilerFlags(flags))) + else if (FAILED(hr = proc2->SetDesiredNGENCompilerFlags(flags))) { // Versions of CLR that don't have SetDesiredNGENCompilerFlags DAC-ized will return E_FAIL. // This was first supported in the clr_triton branch around 4/1/12, Apollo release @@ -15562,9 +15571,9 @@ HRESULT SetNGENCompilerFlags(DWORD flags) hr = S_OK; } } - else if(hr == CORDBG_E_NGEN_NOT_SUPPORTED) + else if (hr == CORDBG_E_NGEN_NOT_SUPPORTED) { - if(flags != CORDEBUG_JIT_DEFAULT) + if (flags != CORDEBUG_JIT_DEFAULT) { ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support NGEN\n"); } @@ -15573,14 +15582,14 @@ HRESULT SetNGENCompilerFlags(DWORD flags) hr = S_OK; } } - else if(hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS) + else if (hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS) { DWORD currentFlags = 0; - if(FAILED(hr = proc2->GetDesiredNGENCompilerFlags(¤tFlags))) + if (FAILED(hr = proc2->GetDesiredNGENCompilerFlags(¤tFlags))) { ExtOut("SOS: warning, prejitted code optimizations could not be changed. GetDesiredNGENCompilerFlags failed hr=0x%x\n", hr); } - else if(currentFlags != flags) + else if (currentFlags != flags) { ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. This setting is fixed once CLR starts\n"); } @@ -15927,8 +15936,8 @@ DECLARE_API(SetSymbolServer) {"-timeout", &timeoutInMinutes, COSIZE_T, TRUE}, {"-ms", &msdl, COBOOL, FALSE}, {"-log", &logging, COBOOL, FALSE}, - {"-loadsymbols", &loadNative, COBOOL, FALSE}, #ifdef FEATURE_PAL + {"-loadsymbols", &loadNative, COBOOL, FALSE}, {"-sympath", &windowsSymbolPath.data, COSTRING, TRUE}, #else {"-mi", &symweb, COBOOL, FALSE}, @@ -15997,10 +16006,12 @@ DECLARE_API(SetSymbolServer) ExtOut("Symbol download logging enabled\n"); } } +#ifdef FEATURE_PAL else if (loadNative) { Status = LoadNativeSymbols(); } +#endif else { DisplaySymbolStore(); diff --git a/src/SOS/Strike/util.cpp b/src/SOS/Strike/util.cpp index 9b9332ab4..4bde52925 100644 --- a/src/SOS/Strike/util.cpp +++ b/src/SOS/Strike/util.cpp @@ -26,6 +26,7 @@ #include #include "debugshim.h" #include "datatarget.h" +#include "runtime.h" #include "gcinfo.h" #ifndef STRESS_LOG @@ -63,8 +64,6 @@ const char * const CorElementTypeNamespace[ELEMENT_TYPE_MAX]= IXCLRDataProcess *g_clrData = NULL; ISOSDacInterface *g_sos = NULL; -IXCLRDataProcess *g_clrDataProcess = NULL; -ICorDebugProcess *g_pCorDebugProcess = NULL; #ifndef IfFailRet #define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0) @@ -97,6 +96,7 @@ void __cdecl operator delete[](void* pObj) throw() \**********************************************************************/ DWORD_PTR GetValueFromExpression(___in __in_z const char *const instr) { + _ASSERTE(g_pRuntime != nullptr); std::string symbol; symbol.append(GetRuntimeModuleName()); symbol.append("!"); @@ -156,86 +156,14 @@ DWORD_PTR GetValueFromExpression(___in __in_z const char *const instr) #endif // FEATURE_PAL -ModuleInfo g_moduleInfo[MSCOREND] = {{0, 0, DEBUG_ANY_ID, FALSE}, {0, 0, DEBUG_ANY_ID, FALSE}, {0, 0, DEBUG_ANY_ID, FALSE}}; - void ReportOOM() { ExtOut("SOS Error: Out of memory\n"); } -// This is set as a side-effect of CheckEEDll()/GetRuntimeModuleInfo(). -bool g_isDesktopRuntime = false; - -HRESULT GetRuntimeModuleInfo(PULONG moduleIndex, PULONG64 moduleBase) -{ - g_isDesktopRuntime = false; - HRESULT hr = g_ExtSymbols->GetModuleByModuleName(NETCORE_RUNTIME_MODULE_NAME_A, 0, moduleIndex, moduleBase); -#ifndef FEATURE_PAL - if (FAILED(hr)) { - hr = g_ExtSymbols->GetModuleByModuleName(DESKTOP_RUNTIME_MODULE_NAME_A, 0, moduleIndex, moduleBase); - g_isDesktopRuntime = SUCCEEDED(hr); - } -#endif - return hr; -} - HRESULT CheckEEDll() { - HRESULT hr = S_OK; - - // Do we have runtime module info? - if (g_moduleInfo[MSCORWKS].baseAddr == 0) - { - hr = GetRuntimeModuleInfo(&g_moduleInfo[MSCORWKS].index, &g_moduleInfo[MSCORWKS].baseAddr); -#ifdef FEATURE_PAL - if (SUCCEEDED(hr)) - { - if (g_ExtServices2 != nullptr) - { - g_ExtServices2->GetModuleInfo(g_moduleInfo[MSCORWKS].index, nullptr, &g_moduleInfo[MSCORWKS].size); - } - } -#else - if (g_moduleInfo[MSCORWKS].baseAddr != 0 && g_moduleInfo[MSCORWKS].hasPdb == FALSE) - { - DEBUG_MODULE_PARAMETERS params; - if (SUCCEEDED(g_ExtSymbols->GetModuleParameters(1, &g_moduleInfo[MSCORWKS].baseAddr, 0, ¶ms))) - { - if (params.SymbolType == SymDeferred) - { - std::string reloadCommand; - reloadCommand.append("/f "); - reloadCommand.append(GetRuntimeDllName()); - g_ExtSymbols->Reload(reloadCommand.c_str()); - g_ExtSymbols->GetModuleParameters(1, &g_moduleInfo[MSCORWKS].baseAddr, 0, ¶ms); - } - if (params.SymbolType == SymPdb || params.SymbolType == SymDia) - { - g_moduleInfo[MSCORWKS].hasPdb = TRUE; - } - g_moduleInfo[MSCORWKS].size = params.Size; - } - } - if (g_moduleInfo[MSCORWKS].baseAddr != 0 && g_moduleInfo[MSCORWKS].hasPdb == FALSE) - { - ExtOut("PDB symbol for %s not loaded\n", GetRuntimeDllName()); - } -#endif // FEATURE_PAL - } - return hr; -} - -EEFLAVOR GetEEFlavor() -{ -#ifdef FEATURE_PAL - return MSCORWKS; -#else // FEATUER_PAL - EEFLAVOR flavor = UNKNOWNEE; - if (SUCCEEDED(GetRuntimeModuleInfo(NULL, NULL))) { - flavor = MSCORWKS; - } - return flavor; -#endif // FEATURE_PAL else + return Runtime::CreateInstance(); } BOOL IsDumpFile() @@ -3280,6 +3208,7 @@ CLRDATA_ADDRESS GetCurrentManagedThread () void ReloadSymbolWithLineInfo() { + _ASSERTE(g_pRuntime != nullptr); #ifndef FEATURE_PAL static BOOL bLoadSymbol = FALSE; if (!bLoadSymbol) @@ -3293,14 +3222,10 @@ void ReloadSymbolWithLineInfo() { g_ExtSymbols->Reload("/f" MSCOREE_SHIM_A); } - EEFLAVOR flavor = GetEEFlavor(); - if (flavor == MSCORWKS) - { - std::string reloadCommand; - reloadCommand.append("/f "); - reloadCommand.append(GetRuntimeDllName()); - g_ExtSymbols->Reload(reloadCommand.c_str()); - } + std::string reloadCommand; + reloadCommand.append("/f "); + reloadCommand.append(GetRuntimeDllName()); + g_ExtSymbols->Reload(reloadCommand.c_str()); } // reload mscoree.pdb and clrjit.pdb to get line info @@ -3365,9 +3290,7 @@ BOOL GetEEVersion(VS_FIXEDFILEINFO* pFileInfo, char* fileVersionBuffer, int file { _ASSERTE(pFileInfo); _ASSERTE(g_ExtSymbols2 != nullptr); - - ModuleInfo moduleInfo = g_moduleInfo[GetEEFlavor()]; - _ASSERTE(moduleInfo.index != DEBUG_ANY_ID); + _ASSERTE(g_pRuntime != nullptr); #ifdef FEATURE_PAL // Load the symbols for runtime. On Linux we are looking for the "sccsid" @@ -3375,7 +3298,7 @@ BOOL GetEEVersion(VS_FIXEDFILEINFO* pFileInfo, char* fileVersionBuffer, int file LoadNativeSymbols(true); #endif - HRESULT hr = g_ExtSymbols2->GetModuleVersionInformation(moduleInfo.index, 0, "\\", pFileInfo, sizeof(VS_FIXEDFILEINFO), NULL); + HRESULT hr = g_ExtSymbols2->GetModuleVersionInformation(g_pRuntime->GetModuleIndex(), 0, "\\", pFileInfo, sizeof(VS_FIXEDFILEINFO), NULL); // Attempt to get the the FileVersion string that contains version and the "built by" and commit id info if (fileVersionBuffer != nullptr) @@ -3385,7 +3308,7 @@ BOOL GetEEVersion(VS_FIXEDFILEINFO* pFileInfo, char* fileVersionBuffer, int file } // We can assume the English/CP_UNICODE lang/code page for the runtime modules g_ExtSymbols2->GetModuleVersionInformation( - moduleInfo.index, 0, "\\StringFileInfo\\040904B0\\FileVersion", fileVersionBuffer, fileVersionBufferSizeInBytes, NULL); + g_pRuntime->GetModuleIndex(), 0, "\\StringFileInfo\\040904B0\\FileVersion", fileVersionBuffer, fileVersionBufferSizeInBytes, NULL); } return SUCCEEDED(hr); @@ -3959,47 +3882,6 @@ void ResetGlobals(void) Output::ResetIndent(); } -static HRESULT GetClrDataProcess() -{ - HRESULT hr = S_OK; - - if (g_clrDataProcess == NULL) - { - LPCSTR dacFilePath = GetDacFilePath(); - if (dacFilePath == nullptr) - { - return E_FAIL; - } - HMODULE hdac = LoadLibraryA(dacFilePath); - if (hdac == NULL) - { - return CORDBG_E_MISSING_DEBUGGER_EXPORTS; - } - PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance"); - if (pfnCLRDataCreateInstance == NULL) - { - FreeLibrary(hdac); - return CORDBG_E_MISSING_DEBUGGER_EXPORTS; - } - ICLRDataTarget *target = new DataTarget(); - hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&g_clrDataProcess); - if (FAILED(hr)) - { - g_clrDataProcess = NULL; - return hr; - } - ULONG32 flags = 0; - g_clrDataProcess->GetOtherNotificationFlags(&flags); - flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD | CLRDATA_NOTIFY_ON_EXCEPTION); - g_clrDataProcess->SetOtherNotificationFlags(flags); - } - g_clrData = g_clrDataProcess; - g_clrData->AddRef(); - g_clrData->Flush(); - - return S_OK; -} - //--------------------------------------------------------------------------------------- // // Loads private DAC interface, and points g_clrData to it. @@ -4009,8 +3891,9 @@ static HRESULT GetClrDataProcess() // HRESULT LoadClrDebugDll(void) { - HRESULT hr = GetClrDataProcess(); - if (FAILED(hr)) + _ASSERTE(g_pRuntime != nullptr); + HRESULT hr = g_pRuntime->GetClrDataProcess(&g_clrData); + if (FAILED(hr)) { #ifdef FEATURE_PAL return hr; @@ -4030,621 +3913,20 @@ HRESULT LoadClrDebugDll(void) g_clrData->Flush(); #endif } - hr = g_clrData->QueryInterface(__uuidof(ISOSDacInterface), (void**)&g_sos); - if (FAILED(hr)) - { - g_sos = NULL; - return hr; - } - return S_OK; -} - -#ifndef FEATURE_PAL - -HMODULE -LoadLibraryAndCheck( - PCWSTR filename, - DWORD timestamp, - DWORD filesize) -{ - HMODULE hModule = LoadLibraryExW( - filename, - NULL, // __reserved - LOAD_WITH_ALTERED_SEARCH_PATH); // Ensure we check the dir in wszFullPath first - - if (hModule == NULL) - { - ExtOut("Unable to load '%S'. hr = 0x%x.\n", filename, HRESULT_FROM_WIN32(GetLastError())); - return NULL; - } - - // Did we load the right one? - MODULEINFO modInfo = {0}; - if (!GetModuleInformation( - GetCurrentProcess(), - hModule, - &modInfo, - sizeof(modInfo))) - { - ExtOut("Failed to read module information for '%S'. hr = 0x%x.\n", filename, HRESULT_FROM_WIN32(GetLastError())); - FreeLibrary(hModule); - return NULL; - } - - IMAGE_DOS_HEADER * pDOSHeader = (IMAGE_DOS_HEADER *) modInfo.lpBaseOfDll; - IMAGE_NT_HEADERS * pNTHeaders = (IMAGE_NT_HEADERS *) (((LPBYTE) modInfo.lpBaseOfDll) + pDOSHeader->e_lfanew); - DWORD dwSizeActual = pNTHeaders->OptionalHeader.SizeOfImage; - DWORD dwTimeStampActual = pNTHeaders->FileHeader.TimeDateStamp; - if ((dwSizeActual != filesize) || (dwTimeStampActual != timestamp)) - { - ExtOut("Found '%S', but it does not match the CLR being debugged.\n", filename); - ExtOut("Size: Expected '0x%x', Actual '0x%x'\n", filesize, dwSizeActual); - ExtOut("Time stamp: Expected '0x%x', Actual '0x%x'\n", timestamp, dwTimeStampActual); - FreeLibrary(hModule); - return NULL; - } - - return hModule; -} - -#endif // FEATURE_PAL - -//--------------------------------------------------------------------------------------- -// Provides a way for the public CLR debugging interface to find the appropriate mscordbi.dll, DAC, etc. -class SOSLibraryProvider : public ICLRDebuggingLibraryProvider, ICLRDebuggingLibraryProvider2 -{ -public: - SOSLibraryProvider() : m_ref(0) - { - } - - virtual ~SOSLibraryProvider() {} - - virtual HRESULT STDMETHODCALLTYPE QueryInterface( - REFIID InterfaceId, - PVOID* pInterface) - { - if (InterfaceId == IID_IUnknown) - { - *pInterface = static_cast(static_cast(this)); - } -#ifndef FEATURE_PAL - else if (InterfaceId == IID_ICLRDebuggingLibraryProvider) - { - *pInterface = static_cast(this); - } -#endif - else if (InterfaceId == IID_ICLRDebuggingLibraryProvider2) - { - *pInterface = static_cast(this); - } - else - { - *pInterface = NULL; - return E_NOINTERFACE; - } - - AddRef(); - return S_OK; - } - - virtual ULONG STDMETHODCALLTYPE AddRef() - { - return InterlockedIncrement(&m_ref); - } - - virtual ULONG STDMETHODCALLTYPE Release() - { - LONG ref = InterlockedDecrement(&m_ref); - if (ref == 0) - { - delete this; - } - return ref; - } - - HRESULT ProvideLibraryInternal( - const WCHAR* pwszFileName, - DWORD dwTimestamp, - DWORD dwSizeOfImage, - HMODULE* phModule, - LPWSTR* ppResolvedModulePath) - { - const char* filePath = nullptr; - - if (_wcsncmp(pwszFileName, GetDacDllNameW(), _wcslen(GetDacDllNameW())) == 0) - { - filePath = GetDacFilePath(); - } - else if (_wcsncmp(pwszFileName, NET_DBI_DLL_NAME_W, _wcslen(NET_DBI_DLL_NAME_W)) == 0) - { - filePath = GetDbiFilePath(); - } - - ArrayHolder modulePath = new WCHAR[MAX_LONGPATH + 1]; - if (filePath != nullptr) - { - int length = MultiByteToWideChar(CP_ACP, 0, filePath, -1, modulePath, MAX_LONGPATH); - if (0 >= length) - { - ExtErr("MultiByteToWideChar(filePath) failed. Last error = 0x%x\n", GetLastError()); - return HRESULT_FROM_WIN32(GetLastError()); - } - } - else - { - HRESULT hr = GetRuntimeDirectory(modulePath, MAX_LONGPATH); - if (FAILED(hr)) - { - return hr; - } - wcscat_s(modulePath, MAX_LONGPATH, pwszFileName); - } - - ExtOut("Loaded %S\n", modulePath.GetPtr()); - -#ifndef FEATURE_PAL - if (phModule != NULL) - { - *phModule = LoadLibraryAndCheck(modulePath.GetPtr(), dwTimestamp, dwSizeOfImage); - } -#endif - if (ppResolvedModulePath != NULL) - { - *ppResolvedModulePath = modulePath.Detach(); - } - return S_OK; - } - - // Called by the shim to locate and load dac and dbi - // Parameters: - // pwszFileName - the name of the file to load - // dwTimestamp - the expected timestamp of the file - // dwSizeOfImage - the expected SizeOfImage (a PE header data value) - // phModule - a handle to loaded module - // - // Return Value - // S_OK if the file was loaded, or any error if not - virtual HRESULT STDMETHODCALLTYPE ProvideLibrary( - const WCHAR * pwszFileName, - DWORD dwTimestamp, - DWORD dwSizeOfImage, - HMODULE* phModule) - { - if ((phModule == NULL) || (pwszFileName == NULL)) - { - return E_INVALIDARG; - } - return ProvideLibraryInternal(pwszFileName, dwTimestamp, dwSizeOfImage, phModule, NULL); - } - - virtual HRESULT STDMETHODCALLTYPE ProvideLibrary2( - const WCHAR* pwszFileName, - DWORD dwTimestamp, - DWORD dwSizeOfImage, - LPWSTR* ppResolvedModulePath) - { - if ((pwszFileName == NULL) || (ppResolvedModulePath == NULL)) - { - return E_INVALIDARG; - } - return ProvideLibraryInternal(pwszFileName, dwTimestamp, dwSizeOfImage, NULL, ppResolvedModulePath); - } - -protected: - LONG m_ref; -}; - -//--------------------------------------------------------------------------------------- -// Data target for the debugged process. Provided to OpenVirtualProcess in order to -// get an ICorDebugProcess back -// -class SOSDataTarget : public ICorDebugMutableDataTarget, public ICorDebugMetaDataLocator, public ICorDebugDataTarget4 -{ -public: - SOSDataTarget() : m_ref(0) - { - } - - virtual ~SOSDataTarget() {} - - virtual HRESULT STDMETHODCALLTYPE QueryInterface( - REFIID InterfaceId, - PVOID* pInterface) - { - if (InterfaceId == IID_IUnknown) - { - *pInterface = static_cast(static_cast(this)); - } - else if (InterfaceId == IID_ICorDebugDataTarget) - { - *pInterface = static_cast(this); - } - else if (InterfaceId == IID_ICorDebugMutableDataTarget) - { - *pInterface = static_cast(this); - } - else if (InterfaceId == IID_ICorDebugMetaDataLocator) - { - *pInterface = static_cast(this); - } - else if (InterfaceId == IID_ICorDebugDataTarget4) - { - *pInterface = static_cast(this); - } - else - { - *pInterface = NULL; - return E_NOINTERFACE; - } - - AddRef(); - return S_OK; - } - - virtual ULONG STDMETHODCALLTYPE AddRef() - { - return InterlockedIncrement(&m_ref); - } - - virtual ULONG STDMETHODCALLTYPE Release() - { - LONG ref = InterlockedDecrement(&m_ref); - if (ref == 0) - { - delete this; - } - return ref; - } - - // - // ICorDebugDataTarget. - // - - virtual HRESULT STDMETHODCALLTYPE GetPlatform(CorDebugPlatform * pPlatform) - { - ULONG platformKind = g_targetMachine->GetPlatform(); -#ifdef FEATURE_PAL - if(platformKind == IMAGE_FILE_MACHINE_I386) - *pPlatform = CORDB_PLATFORM_POSIX_X86; - else if(platformKind == IMAGE_FILE_MACHINE_AMD64) - *pPlatform = CORDB_PLATFORM_POSIX_AMD64; - else if(platformKind == IMAGE_FILE_MACHINE_ARMNT) - *pPlatform = CORDB_PLATFORM_POSIX_ARM; - else - return E_FAIL; -#else - if(platformKind == IMAGE_FILE_MACHINE_I386) - *pPlatform = CORDB_PLATFORM_WINDOWS_X86; - else if(platformKind == IMAGE_FILE_MACHINE_AMD64) - *pPlatform = CORDB_PLATFORM_WINDOWS_AMD64; - else if(platformKind == IMAGE_FILE_MACHINE_ARMNT) - *pPlatform = CORDB_PLATFORM_WINDOWS_ARM; - else if(platformKind == IMAGE_FILE_MACHINE_ARM64) - *pPlatform = CORDB_PLATFORM_WINDOWS_ARM64; - else - return E_FAIL; -#endif - - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE ReadVirtual( - CORDB_ADDRESS address, - BYTE * pBuffer, - ULONG32 request, - ULONG32 * pcbRead) - { - if (g_ExtData == NULL) - { - return E_UNEXPECTED; - } -#ifdef FEATURE_PAL - if (g_sos != nullptr) - { - // LLDB synthesizes memory (returns 0's) for missing pages (in this case the missing metadata pages) - // in core dumps. This functions creates a list of the metadata regions and caches the metadata if - // available from the local or downloaded assembly. If the read would be in the metadata of a loaded - // assembly, the metadata from the this cache will be returned. - HRESULT hr = GetMetadataMemory(address, request, pBuffer); - if (SUCCEEDED(hr)) { - if (pcbRead != nullptr) { - *pcbRead = request; - } - return hr; - } - } -#endif - HRESULT hr = g_ExtData->ReadVirtual(address, pBuffer, request, (PULONG) pcbRead); - if (FAILED(hr)) - { - ExtDbgOut("SOSDataTarget::ReadVirtual FAILED %08x address %p size %08x\n", hr, address, request); - } - return hr; - } - - virtual HRESULT STDMETHODCALLTYPE GetThreadContext( - DWORD dwThreadOSID, - ULONG32 contextFlags, - ULONG32 contextSize, - BYTE * context) - { -#ifdef FEATURE_PAL - if (g_ExtServices == NULL) - { - return E_UNEXPECTED; - } - return g_ExtServices->GetThreadContextById(dwThreadOSID, contextFlags, contextSize, context); -#else - ULONG ulThreadIDOrig; - ULONG ulThreadIDRequested; - HRESULT hr; - - hr = g_ExtSystem->GetCurrentThreadId(&ulThreadIDOrig); - if (FAILED(hr)) - { - return hr; - } - - hr = g_ExtSystem->GetThreadIdBySystemId(dwThreadOSID, &ulThreadIDRequested); - if (FAILED(hr)) - { - return hr; - } - - hr = g_ExtSystem->SetCurrentThreadId(ulThreadIDRequested); - if (FAILED(hr)) - { - return hr; - } - - // Prepare context structure - ZeroMemory(context, contextSize); - ((CONTEXT*) context)->ContextFlags = contextFlags; - - // Ok, do it! - hr = g_ExtAdvanced->GetThreadContext((LPVOID) context, contextSize); - - // This is cleanup; failure here doesn't mean GetThreadContext should fail - // (that's determined by hr). - g_ExtSystem->SetCurrentThreadId(ulThreadIDOrig); - - return hr; -#endif // FEATURE_PAL - } - - // - // ICorDebugMutableDataTarget. - // - - virtual HRESULT STDMETHODCALLTYPE WriteVirtual(CORDB_ADDRESS address, - const BYTE * pBuffer, - ULONG32 bytesRequested) - { - if (g_ExtData == NULL) - { - return E_UNEXPECTED; - } - return g_ExtData->WriteVirtual(address, (PVOID)pBuffer, bytesRequested, NULL); - } - - virtual HRESULT STDMETHODCALLTYPE SetThreadContext(DWORD dwThreadID, - ULONG32 contextSize, - const BYTE * pContext) - { - return E_NOTIMPL; - } - - virtual HRESULT STDMETHODCALLTYPE ContinueStatusChanged(DWORD dwThreadId, - CORDB_CONTINUE_STATUS continueStatus) - { - return E_NOTIMPL; - } - - // - // ICorDebugMetaDataLocator. - // - - virtual HRESULT STDMETHODCALLTYPE GetMetaData( - /* [in] */ LPCWSTR wszImagePath, - /* [in] */ DWORD dwImageTimeStamp, - /* [in] */ DWORD dwImageSize, - /* [in] */ ULONG32 cchPathBuffer, - /* [annotation][out] */ - _Out_ ULONG32 *pcchPathBuffer, - /* [annotation][length_is][size_is][out] */ - _Out_writes_to_(cchPathBuffer, *pcchPathBuffer) WCHAR wszPathBuffer[]) - { - return E_NOTIMPL; - } - - // - // ICorDebugDataTarget4 - // - virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context) - { -#ifdef FEATURE_PAL - if (g_ExtServices == NULL) - { - return E_UNEXPECTED; - } - return g_ExtServices->VirtualUnwind(threadId, contextSize, context); -#else - return E_NOTIMPL; -#endif - } - -protected: - LONG m_ref; -}; - -HRESULT InitCorDebugInterfaceFromModule(ULONG64 ulBase, ICLRDebugging * pClrDebugging) -{ - HRESULT hr; - - ToRelease pSOSDataTarget = new SOSDataTarget; - pSOSDataTarget->AddRef(); - - ToRelease pSOSLibraryProvider = new SOSLibraryProvider; - pSOSLibraryProvider->AddRef(); - - CLR_DEBUGGING_VERSION clrDebuggingVersionRequested = {0}; - clrDebuggingVersionRequested.wMajor = 4; - - CLR_DEBUGGING_VERSION clrDebuggingVersionActual = {0}; - - CLR_DEBUGGING_PROCESS_FLAGS clrDebuggingFlags = (CLR_DEBUGGING_PROCESS_FLAGS)0; - - ToRelease pUnkProcess; - - hr = pClrDebugging->OpenVirtualProcess( - ulBase, - pSOSDataTarget, - pSOSLibraryProvider, - &clrDebuggingVersionRequested, - IID_ICorDebugProcess, - &pUnkProcess, - &clrDebuggingVersionActual, - &clrDebuggingFlags); - if (FAILED(hr)) + else { - return hr; + g_clrData->AddRef(); + g_clrData->Flush(); } - - ICorDebugProcess * pCorDebugProcess = NULL; - hr = pUnkProcess->QueryInterface(IID_ICorDebugProcess, (PVOID*) &pCorDebugProcess); + hr = g_clrData->QueryInterface(__uuidof(ISOSDacInterface), (void**)&g_sos); if (FAILED(hr)) { + g_sos = NULL; return hr; } - - // Transfer memory ownership of refcount to global - g_pCorDebugProcess = pCorDebugProcess; return S_OK; } -//--------------------------------------------------------------------------------------- -// -// Unloads public ICorDebug interfaces, and clears g_pCorDebugProcess -// This is only needed once after CLR unloads, not after every InitCorDebugInterface call -// -VOID UninitCorDebugInterface() -{ - if(g_pCorDebugProcess != NULL) - { - g_pCorDebugProcess->Detach(); - g_pCorDebugProcess->Release(); - g_pCorDebugProcess = NULL; - } -} - -//--------------------------------------------------------------------------------------- -// -// Loads public ICorDebug interfaces, and points g_pCorDebugProcess to them -// This should be called at least once per windbg stop state to ensure that -// the interface is available and that it doesn't hold stale data. Calling it -// more than once isn't an error, but does have perf overhead from needlessly -// flushing memory caches. -// -// Return Value: -// HRESULT indicating success or failure -// - -HRESULT InitCorDebugInterface() -{ - HMODULE hModule = NULL; - HRESULT hr; - ToRelease pClrDebugging; - - // we may already have an ICorDebug instance we can use - if(g_pCorDebugProcess != NULL) - { - // ICorDebugProcess4 is currently considered a private experimental interface on ICorDebug, it might go away so - // we need to be sure to handle its absence gracefully - ToRelease pProcess4 = NULL; - if(SUCCEEDED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess4), (void**)&pProcess4))) - { - // FLUSH_ALL is more expensive than PROCESS_RUNNING, but this allows us to be safe even if things - // like IDNA are in use where we might be looking at non-sequential snapshots of process state - if(SUCCEEDED(pProcess4->ProcessStateChanged(FLUSH_ALL))) - { - // we already have an ICorDebug instance loaded and flushed, nothing more to do - return S_OK; - } - } - - // this is a very heavy handed way of reseting - UninitCorDebugInterface(); - } - - // SOS now has a statically linked version of the loader code that is normally found in mscoree/mscoreei.dll - // Its not much code and takes a big step towards 0 install dependencies - // Need to pick the appropriate SKU of CLR to detect -#if defined(FEATURE_CORESYSTEM) - GUID skuId = CLR_ID_ONECORE_CLR; -#else - GUID skuId = CLR_ID_CORECLR; -#endif -#ifndef FEATURE_PAL - if (g_isDesktopRuntime) - { - skuId = CLR_ID_V4_DESKTOP; - } -#endif - CLRDebuggingImpl* pDebuggingImpl = new CLRDebuggingImpl(skuId); - hr = pDebuggingImpl->QueryInterface(IID_ICLRDebugging, (LPVOID *)&pClrDebugging); - if (FAILED(hr)) - { - delete pDebuggingImpl; - return hr; - } - -#ifndef FEATURE_PAL - ULONG cLoadedModules; - ULONG cUnloadedModules; - hr = g_ExtSymbols->GetNumberModules(&cLoadedModules, &cUnloadedModules); - if (FAILED(hr)) - { - return hr; - } - - ULONG64 ulBase; - for (ULONG i = 0; i < cLoadedModules; i++) - { - hr = g_ExtSymbols->GetModuleByIndex(i, &ulBase); - if (FAILED(hr)) - { - return hr; - } - - // Dunno if this is a CLR module or not (or even if it's the particular one the - // user cares about during inproc SxS scenarios). For now, just try to use it - // to grab an ICorDebugProcess. If it works, great. Else, continue the loop - // until we find the first one that works. - hr = InitCorDebugInterfaceFromModule(ulBase, pClrDebugging); - if (SUCCEEDED(hr)) - { - return hr; - } - - // On failure, just iterate to the next module and try again... - } - - // Still here? Didn't find the right module. - // TODO: Anything useful to return or log here? - return E_FAIL; -#else - ULONG64 ulBase; - hr = GetRuntimeModuleInfo(NULL, &ulBase); - if (SUCCEEDED(hr)) - { - hr = InitCorDebugInterfaceFromModule(ulBase, pClrDebugging); - } - return hr; -#endif // FEATURE_PAL -} - - typedef enum { GC_HEAP_INVALID = 0, diff --git a/src/SOS/Strike/util.h b/src/SOS/Strike/util.h index febfdda08..92666b8a1 100644 --- a/src/SOS/Strike/util.h +++ b/src/SOS/Strike/util.h @@ -202,9 +202,6 @@ extern ISOSDacInterface *g_sos; #include "dacprivate.h" -interface ICorDebugProcess; -extern ICorDebugProcess * g_pCorDebugProcess; - // This class is templated for easy modification. We may need to update the CachedString // or related classes to use WCHAR instead of char in the future. template @@ -1539,99 +1536,6 @@ private: T* m_ptr; }; -// SOS's runtime, dac and dbi module name defines. *MODULE* is just the -// module name on Windows, *DLL* has the .dll extension. On Linux/MacOS, -// *MODULE* and *DLL* are the same. - -#ifdef FEATURE_PAL - -#define NETCORE_RUNTIME_MODULE_NAME_W MAKEDLLNAME_W(W("coreclr")) -#define NETCORE_RUNTIME_MODULE_NAME_A MAKEDLLNAME_A("coreclr") -#define NETCORE_RUNTIME_DLL_NAME_W NETCORE_RUNTIME_MODULE_NAME_W -#define NETCORE_RUNTIME_DLL_NAME_A NETCORE_RUNTIME_MODULE_NAME_A - -#define NETCORE_DAC_MODULE_NAME_W MAKEDLLNAME_W(W("mscordaccore")) -#define NETCORE_DAC_MODULE_NAME_A MAKEDLLNAME_A("mscordaccore") -#define NETCORE_DAC_DLL_NAME_W NETCORE_DAC_MODULE_NAME_W -#define NETCORE_DAC_DLL_NAME_A NETCORE_DAC_MODULE_NAME_A - -#define NET_DBI_MODULE_NAME_W MAKEDLLNAME_W(W("mscordbi")) -#define NET_DBI_MODULE_NAME_A MAKEDLLNAME_A("mscordbi") -#define NET_DBI_DLL_NAME_W NET_DBI_MODULE_NAME_W -#define NET_DBI_DLL_NAME_A NET_DBI_MODULE_NAME_A - -#else - -#define NETCORE_RUNTIME_MODULE_NAME_W W("coreclr") -#define NETCORE_RUNTIME_MODULE_NAME_A "coreclr" -#define NETCORE_RUNTIME_DLL_NAME_W MAKEDLLNAME_W(NETCORE_RUNTIME_MODULE_NAME_W) -#define NETCORE_RUNTIME_DLL_NAME_A MAKEDLLNAME_A(NETCORE_RUNTIME_MODULE_NAME_A) - -#define NETCORE_DAC_MODULE_NAME_W W("mscordaccore") -#define NETCORE_DAC_MODULE_NAME_A "mscordaccore" -#define NETCORE_DAC_DLL_NAME_W MAKEDLLNAME_W(NETCORE_DAC_MODULE_NAME_W) -#define NETCORE_DAC_DLL_NAME_A MAKEDLLNAME_A(NETCORE_DAC_MODULE_NAME_A) - -#define NET_DBI_MODULE_NAME_W W("mscordbi") -#define NET_DBI_MODULE_NAME_A "mscordbi" -#define NET_DBI_DLL_NAME_W MAKEDLLNAME_W(W("mscordbi")) -#define NET_DBI_DLL_NAME_A MAKEDLLNAME_A("mscordbi") - -#endif // FEATURE_PAL - -#define DESKTOP_RUNTIME_MODULE_NAME_W W("clr") -#define DESKTOP_RUNTIME_MODULE_NAME_A "clr" -#define DESKTOP_RUNTIME_DLL_NAME_W MAKEDLLNAME_W(DESKTOP_RUNTIME_MODULE_NAME_W) -#define DESKTOP_RUNTIME_DLL_NAME_A MAKEDLLNAME_A(DESKTOP_RUNTIME_MODULE_NAME_A) - -#define DESKTOP_DAC_MODULE_NAME_W W("mscordacwks") -#define DESKTOP_DAC_MODULE_NAME_A "mscordacwks" -#define DESKTOP_DAC_DLL_NAME_W MAKEDLLNAME_W(W("mscordacwks")) -#define DESKTOP_DAC_DLL_NAME_A MAKEDLLNAME_A("mscordacwks") - -// This is set as a side-effect of CheckEEDll()/GetRuntimeModuleInfo(). -extern bool g_isDesktopRuntime; - -inline const char* GetRuntimeModuleName() -{ - return g_isDesktopRuntime ? DESKTOP_RUNTIME_MODULE_NAME_A : NETCORE_RUNTIME_MODULE_NAME_A; -} - -inline const char* GetRuntimeDllName() -{ - return g_isDesktopRuntime ? DESKTOP_RUNTIME_DLL_NAME_A : NETCORE_RUNTIME_DLL_NAME_A; -} - -inline const char* GetDacModuleName() -{ - return g_isDesktopRuntime ? DESKTOP_DAC_MODULE_NAME_A : NETCORE_DAC_MODULE_NAME_A; -} - -inline const char* GetDacDllName() -{ - return g_isDesktopRuntime ? DESKTOP_DAC_DLL_NAME_A : NETCORE_DAC_DLL_NAME_A; -} - -inline const WCHAR* GetDacModuleNameW() -{ - return g_isDesktopRuntime ? DESKTOP_DAC_MODULE_NAME_W : NETCORE_DAC_MODULE_NAME_W; -} - -inline const WCHAR* GetDacDllNameW() -{ - return g_isDesktopRuntime ? DESKTOP_DAC_DLL_NAME_W : NETCORE_DAC_DLL_NAME_W; -} - -struct ModuleInfo -{ - ULONG64 baseAddr; - ULONG64 size; - ULONG index; - BOOL hasPdb; -}; - -extern ModuleInfo g_moduleInfo[]; - BOOL InitializeHeapData(); BOOL IsServerBuild (); UINT GetMaxGeneration(); @@ -1645,10 +1549,6 @@ void DecodeIL(IMetaDataImport *pImport, BYTE *buffer, ULONG bufSize); void DecodeDynamicIL(BYTE *data, ULONG Size, DacpObjectData& tokenArray); ULONG DisplayILOperation(const UINT indentCount, BYTE* pBuffer, ULONG position, std::function& func); -HRESULT GetRuntimeModuleInfo(PULONG moduleIndex, PULONG64 moduleBase); -EEFLAVOR GetEEFlavor (); -HRESULT InitCorDebugInterface(); -VOID UninitCorDebugInterface(); BOOL GetEEVersion(VS_FIXEDFILEINFO* pFileInfo, char* fileVersionBuffer, int fileVersionBufferSizeInBytes); bool IsRuntimeVersion(DWORD major); bool IsRuntimeVersion(VS_FIXEDFILEINFO& fileInfo, DWORD major); diff --git a/src/Tools/dotnet-dump/Analyzer.cs b/src/Tools/dotnet-dump/Analyzer.cs index 77c33c341..b48a75810 100644 --- a/src/Tools/dotnet-dump/Analyzer.cs +++ b/src/Tools/dotnet-dump/Analyzer.cs @@ -26,6 +26,7 @@ namespace Microsoft.Diagnostics.Tools.Dump private readonly ServiceProvider _serviceProvider; private readonly ConsoleProvider _consoleProvider; private readonly CommandProcessor _commandProcessor; + private bool _isDesktop; private string _dacFilePath; /// @@ -142,7 +143,7 @@ namespace Microsoft.Diagnostics.Tools.Dump _serviceProvider.AddServiceFactory(typeof(SOSHost), () => { var sosHost = new SOSHost(_serviceProvider); - sosHost.InitializeSOSHost(SymbolReader.TempDirectory, _dacFilePath, dbiFilePath: null); + sosHost.InitializeSOSHost(SymbolReader.TempDirectory, _isDesktop, _dacFilePath, dbiFilePath: null); return sosHost; }); } @@ -152,11 +153,33 @@ namespace Microsoft.Diagnostics.Tools.Dump /// private ClrRuntime CreateRuntime(DataTarget target) { - ClrRuntime runtime; - if (target.ClrVersions.Count != 1) { - throw new InvalidOperationException("More or less than 1 CLR version is present"); + ClrInfo clrInfo = null; + + // First check if there is a .NET Core runtime loaded + foreach (ClrInfo clr in target.ClrVersions) + { + if (clr.Flavor == ClrFlavor.Core) + { + clrInfo = clr; + break; + } } - ClrInfo clrInfo = target.ClrVersions[0]; + // If no .NET Core runtime, then check for desktop runtime + if (clrInfo == null) + { + foreach (ClrInfo clr in target.ClrVersions) + { + if (clr.Flavor == ClrFlavor.Desktop) + { + clrInfo = clr; + break; + } + } + } + if (clrInfo == null) { + throw new InvalidOperationException("No CLR runtime is present"); + } + ClrRuntime runtime; string dacFilePath = GetDacFile(clrInfo); try { @@ -219,6 +242,7 @@ namespace Microsoft.Diagnostics.Tools.Dump { throw new FileNotFoundException("Could not find matching DAC for this runtime: {0}", clrInfo.ModuleInfo.FileName); } + _isDesktop = clrInfo.Flavor == ClrFlavor.Desktop; } return _dacFilePath; } -- 2.34.1