1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
16 #include "dacprivate.h"
23 #include <xcordebug.h>
27 #include "debugshim.h"
30 #include "datatarget.h"
37 #define STRESS_LOG_READONLY
38 #include "stresslog.h"
41 #define MAX_SYMBOL_LEN 4096
42 #define SYM_BUFFER_SIZE (sizeof(IMAGEHLP_SYMBOL) + MAX_SYMBOL_LEN)
43 char symBuffer[SYM_BUFFER_SIZE];
44 PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) symBuffer;
47 #include <coreruncommon.h>
49 #endif // !FEATURE_PAL
51 #include <coreclrhost.h>
54 LoadSymbolsForModuleDelegate SymbolReader::loadSymbolsForModuleDelegate;
55 DisposeDelegate SymbolReader::disposeDelegate;
56 ResolveSequencePointDelegate SymbolReader::resolveSequencePointDelegate;
57 GetLocalVariableName SymbolReader::getLocalVariableNameDelegate;
58 GetLineByILOffsetDelegate SymbolReader::getLineByILOffsetDelegate;
60 const char * const CorElementTypeName[ELEMENT_TYPE_MAX]=
62 #define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) c,
63 #include "cortypeinfo.h"
67 const char * const CorElementTypeNamespace[ELEMENT_TYPE_MAX]=
69 #define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) ns,
70 #include "cortypeinfo.h"
74 IXCLRDataProcess *g_clrData = NULL;
75 ISOSDacInterface *g_sos = NULL;
76 ICorDebugProcess *g_pCorDebugProcess = NULL;
79 #define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
83 #define IfFailGoto(EXPR, label) do { Status = (EXPR); if(FAILED(Status)) { goto label; } } while (0)
87 #define IfFailGo(EXPR) IfFailGoto(EXPR, Error)
90 // Max number of reverted rejit versions that !dumpmd and !ip2md will print
91 const UINT kcMaxRevertedRejitData = 10;
92 const UINT kcMaxTieredVersions = 10;
95 // ensure we always allocate on the process heap
96 void* __cdecl operator new(size_t size) throw()
97 { return HeapAlloc(GetProcessHeap(), 0, size); }
98 void __cdecl operator delete(void* pObj) throw()
99 { HeapFree(GetProcessHeap(), 0, pObj); }
101 void* __cdecl operator new[](size_t size) throw()
102 { return HeapAlloc(GetProcessHeap(), 0, size); }
103 void __cdecl operator delete[](void* pObj) throw()
104 { HeapFree(GetProcessHeap(), 0, pObj); }
106 /**********************************************************************\
107 * Here we define types and functions that support custom COM *
108 * activation rules, as defined by the CIOptions enum. *
110 \**********************************************************************/
112 typedef unsigned __int64 QWORD;
114 namespace com_activation
117 // Forward declarations for the implementation methods
120 HRESULT CreateInstanceCustomImpl(
124 CIOptions cciOptions,
126 HRESULT ClrCreateInstance(
130 CIOptions cciOptions,
132 HRESULT CreateInstanceFromPath(
137 BOOL GetPathFromModule(
139 __in_ecount(cFqPath) LPWSTR fqPath,
141 HRESULT PickClrRuntimeInfo(
142 ICLRMetaHost *pMetaHost,
143 CIOptions cciOptions,
144 ICLRRuntimeInfo** ppClr);
145 QWORD VerString2Qword(LPCWSTR vStr);
146 void CleanupClsidHmodMap();
148 // Helper structures for defining the CLSID -> HMODULE hash table we
149 // use for caching already activated objects
150 class hash_compareGUID
153 static const size_t bucket_size = 4;
154 static const size_t min_buckets = 8;
158 size_t operator( )(const GUID& _Key) const
160 DWORD *pdw = (DWORD*)&_Key;
161 return (size_t)(pdw[0] ^ pdw[1] ^ pdw[2] ^ pdw[3]);
164 bool operator( )(const GUID& _Key1, const GUID& _Key2) const
165 { return memcmp(&_Key1, &_Key2, sizeof(GUID)) == -1; }
168 static std::unordered_map<GUID, HMODULE, hash_compareGUID> *g_pClsidHmodMap = NULL;
172 /**********************************************************************\
173 * Routine Description: *
175 * CreateInstanceCustomImpl() provides a way to activate a COM object *
176 * w/o triggering the FeatureOnDemand dialog. In order to do this we *
177 * must avoid using the CoCreateInstance() API, which, on a machine *
178 * with v4+ installed and w/o v2, would trigger this. *
179 * CreateInstanceCustom() activates the requested COM object according *
180 * to the specified passed in CIOptions, in the following order *
181 * (skipping the steps not enabled in the CIOptions flags passed in): *
182 * 1. Attempt to activate the COM object using a framework install: *
183 * a. If the debugger machine has a V4+ shell shim use the shim *
184 * to activate the object *
185 * b. Otherwise simply call CoCreateInstance *
186 * 2. If unsuccessful attempt to activate looking for the dllName in *
187 * the same folder as the DAC was loaded from *
188 * 3. If unsuccessful attempt to activate the COM object looking in *
189 * every path specified in the debugger's .exepath and .sympath *
190 \**********************************************************************/
191 HRESULT CreateInstanceCustomImpl(
195 CIOptions cciOptions,
198 _ASSERTE(ppItf != NULL);
203 WCHAR wszClsid[64] = W("<CLSID>");
205 // Step 1: Attempt activation using an installed runtime
206 if ((cciOptions & cciFxMask) != 0)
208 CIOptions opt = cciOptions & cciFxMask;
209 if (SUCCEEDED(ClrCreateInstance(clsid, iid, dllName, opt, ppItf)))
212 ExtDbgOut("Failed to instantiate {%ls} from installed .NET framework locations.\n", wszClsid);
215 if ((cciOptions & cciDbiColocated) != 0)
217 // if we institute a way to retrieve the module for the current DBI we
218 // can perform the same steps as for the DAC.
221 // Step 2: attempt activation using the folder the DAC was loaded from
222 if ((cciOptions & cciDacColocated) != 0)
224 _ASSERTE(dllName != NULL);
226 WCHAR path[MAX_LONGPATH];
228 if (SUCCEEDED(g_sos->GetDacModuleHandle(&hDac))
229 && GetPathFromModule(hDac, path, _countof(path)))
231 // build the fully qualified file name and attempt instantiation
232 if (wcscat_s(path, dllName) == 0
233 && SUCCEEDED(CreateInstanceFromPath(clsid, iid, path, ppItf)))
239 ExtDbgOut("Failed to instantiate {%ls} from DAC location.\n", wszClsid);
242 // Step 3: attempt activation using the debugger's .exepath and .sympath
243 if ((cciOptions & cciDbgPath) != 0)
245 _ASSERTE(dllName != NULL);
247 ToRelease<IDebugSymbols3> spSym3(NULL);
248 HRESULT hr = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
251 ExtDbgOut("Unable to query IDebugSymbol3 HRESULT=0x%x.\n", hr);
255 typedef HRESULT (__stdcall IDebugSymbols3::*GetPathFunc)(LPWSTR , ULONG, ULONG*);
258 // Handle both the image path and the symbol path
259 GetPathFunc rgGetPathFuncs[] =
260 { &IDebugSymbols3::GetImagePathWide, &IDebugSymbols3::GetSymbolPathWide };
262 for (int i = 0; i < _countof(rgGetPathFuncs); ++i)
266 // get the path buffer size
267 if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(NULL, 0, &pathSize) != S_OK)
272 ArrayHolder<WCHAR> imgPath = new WCHAR[pathSize+MAX_LONGPATH+1];
278 // actually get the path
279 if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(imgPath, pathSize, NULL) != S_OK)
285 LPCWSTR pathElem = wcstok_s(imgPath, W(";"), &ctx);
286 while (pathElem != NULL)
288 WCHAR fullName[MAX_LONGPATH];
289 wcscpy_s(fullName, _countof(fullName), pathElem);
290 if (wcscat_s(fullName, W("\\")) == 0 && wcscat_s(fullName, dllName) == 0)
292 if (SUCCEEDED(CreateInstanceFromPath(clsid, iid, fullName, ppItf)))
298 pathElem = wcstok_s(NULL, W(";"), &ctx);
304 ExtDbgOut("Failed to instantiate {%ls} from debugger's image path.\n", wszClsid);
307 return REGDB_E_CLASSNOTREG;
312 // SOS is essentially single-threaded. ignore "construction of local static object is not thread-safe"
313 #pragma warning(push)
314 #pragma warning(disable:4640)
318 /**********************************************************************\
319 * Routine Description: *
321 * ClrCreateInstance() attempts to activate a COM object using an *
322 * installed framework: *
323 * a. If the debugger machine has a V4+ shell shim use the shim to *
324 * activate the object *
325 * b. Otherwise simply call CoCreateInstance *
326 \**********************************************************************/
327 HRESULT ClrCreateInstance(
331 CIOptions cciOptions,
334 _ASSERTE((cciOptions & ~cciFxMask) == 0 && (cciOptions & cciFxMask) != 0);
335 HRESULT Status = S_OK;
337 static CIOptions prevOpt = 0;
338 static HRESULT prevHr = S_OK;
340 // if we already tried to use NetFx install and failed don't try it again
341 if (prevOpt == cciOptions && FAILED(prevHr))
346 prevOpt = cciOptions;
348 // first try usig the metahost API:
349 HRESULT (__stdcall *pfnCLRCreateInstance)(REFCLSID clsid, REFIID riid, LPVOID * ppInterface) = NULL;
350 HMODULE hMscoree = NULL;
352 // if there's a v4+ shim on the debugger machine
353 if (GetProcAddressT("CLRCreateInstance", W("mscoree.dll"), &pfnCLRCreateInstance, &hMscoree))
355 // attempt to create an ICLRMetaHost instance
356 ToRelease<ICLRMetaHost> spMH;
357 Status = pfnCLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (void**)&spMH);
358 if (Status == E_NOTIMPL)
360 // E_NOTIMPL means we have a v4 aware mscoree but no v4+ framework
361 IfFailGo( CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, ppItf) );
367 // pick a runtime according to cciOptions
368 ToRelease<ICLRRuntimeInfo> spClr;
369 IfFailGo( PickClrRuntimeInfo(spMH, cciOptions, &spClr) );
371 // activate the COM object
372 Status = spClr->GetInterface(clsid, iid, ppItf);
374 if (FAILED(Status) && dllName)
376 // if we have a v4+ runtime that does not have the fix to activate the requested CLSID
377 // try activating with the path
378 WCHAR clrDir[MAX_LONGPATH];
379 DWORD cchClrDir = _countof(clrDir);
380 IfFailGo( spClr->GetRuntimeDirectory(clrDir, &cchClrDir) );
381 IfFailGo( wcscat_s(clrDir, dllName) == 0 ? S_OK : E_FAIL );
382 IfFailGo( CreateInstanceFromPath(clsid, iid, clrDir, ppItf) );
388 // otherwise fallback to regular COM activation
389 IfFailGo( CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, ppItf) );
393 if (hMscoree != NULL)
395 FreeLibrary(hMscoree);
398 // remember if we succeeded or failed
409 /**********************************************************************\
410 * Routine Description: *
412 * CreateInstanceFromPath() instantiates a COM object using a passed in *
413 * fully-qualified path and a CLSID. *
417 * It uses a unordered_map to cache the mapping between a CLSID and the *
418 * HMODULE that is successfully used to activate the CLSID from. When *
419 * SOS is unloaded (in DebugExtensionUninitialize()) we call *
420 * FreeLibrary() for all cached HMODULEs. *
421 \**********************************************************************/
422 HRESULT CreateInstanceFromPath(
428 HRESULT Status = S_OK;
429 HRESULT (__stdcall *pfnDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv) = NULL;
433 if (g_pClsidHmodMap == NULL)
435 g_pClsidHmodMap = new std::unordered_map<GUID, HMODULE, hash_compareGUID>();
436 OnUnloadTask::Register(CleanupClsidHmodMap);
439 auto it = g_pClsidHmodMap->find(clsid);
440 if (it != g_pClsidHmodMap->end())
443 if (!GetProcAddressT("DllGetClassObject", path, &pfnDllGetClassObject, &hmod))
444 return REGDB_E_CLASSNOTREG;
446 ToRelease<IClassFactory> pFactory;
447 IfFailGo(pfnDllGetClassObject(clsid, IID_IClassFactory, (void**)&pFactory));
449 IfFailGo(pFactory->CreateInstance(NULL, iid, ppItf));
451 // only cache the HMODULE if we successfully created the COM object
452 (*g_pClsidHmodMap)[clsid] = hmod;
464 /**********************************************************************\
465 * Routine Description: *
467 * CleanupClsidHmodMap() cleans up the CLSID -> HMODULE map used to *
468 * cache successful activations from specific paths. This is registered *
469 * as an OnUnloadTask in CreateInstanceFromPath(), and executes when *
470 * SOS is unloaded, in DebugExtensionUninitialize(). *
471 \**********************************************************************/
472 void CleanupClsidHmodMap()
474 if (g_pClsidHmodMap != NULL)
476 for (auto it = g_pClsidHmodMap->begin(); it != g_pClsidHmodMap->end(); ++it)
478 _ASSERTE(it->second != NULL);
479 FreeLibrary(it->second);
482 delete g_pClsidHmodMap;
483 g_pClsidHmodMap = NULL;
487 /**********************************************************************\
488 * Routine Description: *
490 * PickClrRuntimeInfo() selects on CLR runtime from the ones installed *
491 * on the debugger machine. If cciFxAny is specified in cciOptions it *
492 * simply returns the first runtime enumerated by the metahost *
493 * interface. If cciLatestFx is specified we pick the runtime with the *
494 * highest version (parsing the string returned from *
495 * ICLRRuntimeInfo::GetVersionString(). *
496 \**********************************************************************/
497 HRESULT PickClrRuntimeInfo(
498 ICLRMetaHost *pMetaHost,
499 CIOptions cciOptions,
500 ICLRRuntimeInfo** ppClr)
505 // only support "Any framework" and "latest framework"
506 if (cciOptions != cciAnyFx && cciOptions != cciLatestFx)
509 HRESULT Status = S_OK;
512 // get the CLRRuntime enumerator
513 ToRelease<IEnumUnknown> spClrsEnum;
514 IfFailRet(pMetaHost->EnumerateInstalledRuntimes(&spClrsEnum));
516 ToRelease<ICLRRuntimeInfo> spChosenClr;
522 // retrieve the next ICLRRuntimeInfo
524 ToRelease<IUnknown> spClrUnk;
525 if (spClrsEnum->Next(1, &spClrUnk, &cnt) != S_OK || cnt != 1)
528 ToRelease<ICLRRuntimeInfo> spClr;
529 BOOL bLoadable = FALSE;
530 // ignore un-loadable runtimes
531 if (FAILED(spClrUnk->QueryInterface(IID_ICLRRuntimeInfo, (void**)&spClr))
532 || FAILED(spClr->IsLoadable(&bLoadable))
539 DWORD cStr = _countof(vStr);
540 if (FAILED(spClr->GetVersionString(vStr, &cStr)))
545 if ((cciOptions & cciAnyFx) != 0)
547 spChosenClr = spClr.Detach();
551 QWORD ver = VerString2Qword(vStr);
552 if ((cciOptions & cciLatestFx) != 0)
557 spChosenClr = spClr.Detach();
562 if (cntClr == 0 || spChosenClr == NULL)
565 return E_NOINTERFACE;
569 *ppClr = spChosenClr.Detach();
575 /**********************************************************************\
576 * Routine Description: *
578 * VerString2Qword() parses a string as returned from *
579 * ICLRRuntimeInfo::GetVersionString() into a QWORD, assuming every *
580 * numeric element is a WORD portion in the QWORD. *
581 \**********************************************************************/
582 QWORD VerString2Qword(LPCWSTR vStr)
584 _ASSERTE(vStr[0] == L'v' || vStr[0] == L'V');
588 if (swscanf_s(vStr+1, W("%d.%d.%d"), &v1, &v2, &v3) == 3)
590 result = ((QWORD)v1 << 48) | ((QWORD)v2 << 32) | ((QWORD)v3 << 16);
592 else if (swscanf_s(vStr+1, W("%d.%d"), &v1, &v2) == 2)
594 result = ((QWORD)v1 << 48) | ((QWORD)v2 << 32);
596 else if (swscanf_s(vStr+1, W("%d"), &v1) == 1)
598 result = ((QWORD)v1 << 48);
605 /**********************************************************************\
606 * Routine Description: *
608 * GetPathFromModule() returns the name of the folder containing the *
609 * file associated with hModule. *
610 \**********************************************************************/
611 BOOL GetPathFromModule(
613 __in_ecount(cFqPath) LPWSTR fqPath,
616 int len = GetModuleFileNameW(hModule, fqPath, cFqPath);
617 if (len == 0 || len == cFqPath)
620 WCHAR *pLastSep = _wcsrchr(fqPath, DIRECTORY_SEPARATOR_CHAR_W);
621 if (pLastSep == NULL || pLastSep+1 >= fqPath+cFqPath)
624 *(pLastSep+1) = L'\0';
631 /**********************************************************************\
632 * Routine Description: *
634 * CreateInstanceCustom() provides a way to activate a COM object w/o *
635 * triggering the FeatureOnDemand dialog. In order to do this we *
636 * must avoid using the CoCreateInstance() API, which, on a machine *
637 * with v4+ installed and w/o v2, would trigger this. *
638 * CreateInstanceCustom() activates the requested COM object according *
639 * to the specified passed in CIOptions, in the following order *
640 * (skipping the steps not enabled in the CIOptions flags passed in): *
641 * 1. Attempt to activate the COM object using a framework install: *
642 * a. If the debugger machine has a V4+ shell shim use the shim *
643 * to activate the object *
644 * b. Otherwise simply call CoCreateInstance *
645 * 2. If unsuccessful attempt to activate looking for the dllName in *
646 * the same folder as the DAC was loaded from *
647 * 3. If unsuccessful attempt to activate the COM object looking in *
648 * every path specified in the debugger's .exepath and .sympath *
649 \**********************************************************************/
650 HRESULT CreateInstanceCustom(
654 CIOptions cciOptions,
657 return com_activation::CreateInstanceCustomImpl(clsid, iid, dllName, cciOptions, ppItf);
663 /**********************************************************************\
664 * Routine Description: *
666 * This function is called to get the memory address given a symbol *
667 * name. It handles difference in symbol name between ntsd and *
670 \**********************************************************************/
671 DWORD_PTR GetValueFromExpression (___in __in_z const char *const instr)
674 const char *str = instr;
678 HRESULT hr = g_ExtSymbols->GetOffsetByName (str, &dwAddr);
680 return (DWORD_PTR)dwAddr;
681 else if (hr == S_FALSE && dwAddr)
682 return (DWORD_PTR)dwAddr;
684 strcpy_s (name, _countof(name), str);
686 if ((ptr = strstr (name, "__")) != NULL)
691 while ((ptr = strstr(ptr, "__")) != NULL)
698 hr = g_ExtSymbols->GetOffsetByName (name, &dwAddr);
700 return (DWORD_PTR)dwAddr;
701 else if (hr == S_FALSE && dwAddr)
702 return (DWORD_PTR)dwAddr;
704 else if ((ptr = strstr (name, "::")) != NULL)
709 while ((ptr = strstr(ptr, "::")) != NULL)
716 hr = g_ExtSymbols->GetOffsetByName (name, &dwAddr);
718 return (DWORD_PTR)dwAddr;
719 else if (hr == S_FALSE && dwAddr)
720 return (DWORD_PTR)dwAddr;
725 #endif // FEATURE_PAL
727 ModuleInfo moduleInfo[MSCOREND] = {{0,FALSE,0},{0,FALSE,0},{0,FALSE,0}};
731 ExtOut("SOS Error: Out of memory\n");
737 VS_FIXEDFILEINFO ee = {};
739 static VS_FIXEDFILEINFO sos = {};
740 static BOOL sosDataInit = FALSE;
742 BOOL result = GetEEVersion(&ee);
743 if (result && !sosDataInit)
745 result = GetSOSVersion(&sos);
751 // We will ignore errors because it's possible sos is being loaded before CLR.
754 if ((ee.dwFileVersionMS != sos.dwFileVersionMS) || (ee.dwFileVersionLS != sos.dwFileVersionLS))
756 ExtOut("The version of SOS does not match the version of CLR you are debugging. Please\n");
757 ExtOut("load the matching version of SOS for the version of CLR you are debugging.\n");
758 ExtOut("CLR Version: %u.%u.%u.%u\n",
759 HIWORD(ee.dwFileVersionMS),
760 LOWORD(ee.dwFileVersionMS),
761 HIWORD(ee.dwFileVersionLS),
762 LOWORD(ee.dwFileVersionLS));
764 ExtOut("SOS Version: %u.%u.%u.%u\n",
765 HIWORD(sos.dwFileVersionMS),
766 LOWORD(sos.dwFileVersionMS),
767 HIWORD(sos.dwFileVersionLS),
768 LOWORD(sos.dwFileVersionLS));
772 DEBUG_MODULE_PARAMETERS Params;
774 // Do we have clr.dll
775 if (moduleInfo[MSCORWKS].baseAddr == 0)
777 g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL,
778 &moduleInfo[MSCORWKS].baseAddr);
779 if (moduleInfo[MSCORWKS].baseAddr != 0 && moduleInfo[MSCORWKS].hasPdb == FALSE)
781 g_ExtSymbols->GetModuleParameters (1, &moduleInfo[MSCORWKS].baseAddr, 0, &Params);
782 if (Params.SymbolType == SymDeferred)
784 g_ExtSymbols->Reload("/f " MAIN_CLR_DLL_NAME_A);
785 g_ExtSymbols->GetModuleParameters (1, &moduleInfo[MSCORWKS].baseAddr, 0, &Params);
788 if (Params.SymbolType == SymPdb || Params.SymbolType == SymDia)
790 moduleInfo[MSCORWKS].hasPdb = TRUE;
793 moduleInfo[MSCORWKS].size = Params.Size;
795 if (moduleInfo[MSCORWKS].baseAddr != 0 && moduleInfo[MSCORWKS].hasPdb == FALSE)
796 ExtOut("PDB symbol for clr.dll not loaded\n");
799 return (moduleInfo[MSCORWKS].baseAddr != 0) ? S_OK : E_FAIL;
802 #endif // FEATURE_PAL
805 EEFLAVOR GetEEFlavor ()
810 EEFLAVOR flavor = UNKNOWNEE;
812 if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A,0,NULL,NULL))) {
816 #endif // FEATURE_PAL else
821 static int g_fDumpFile = -1;
822 if (g_fDumpFile == -1) {
825 g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
826 if (Qualifier >= DEBUG_DUMP_SMALL)
831 return g_fDumpFile != 0;
834 BOOL g_InMinidumpSafeMode = FALSE;
836 BOOL IsMiniDumpFileNODAC ()
841 g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
842 if (Qualifier == DEBUG_DUMP_SMALL)
844 g_ExtControl->GetDumpFormatFlags(&Qualifier);
845 if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0)
851 #endif // FEATURE_PAL
856 // We use this predicate to mean the smallest, most restrictive kind of
857 // minidump file. There is no heap dump, only that set of information
858 // gathered to make !clrstack, !threads, !help, !eeversion and !pe work.
859 BOOL IsMiniDumpFile ()
862 // It is okay for this to be static, because although the debugger may debug multiple
863 // managed processes at once, I don't believe multiple dumpfiles of different
864 // types is a scenario to worry about.
865 if (IsMiniDumpFileNODAC())
867 // Beyond recognizing the dump type above, all we can rely on for this
868 // is a flag set by the user indicating they want a safe mode minidump
869 // experience. This is primarily for testing.
870 return g_InMinidumpSafeMode;
873 #endif // FEATURE_PAL
879 static ULONG Class = DEBUG_CLASS_UNINITIALIZED;
880 if (Class == DEBUG_CLASS_UNINITIALIZED) {
882 g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
889 // Check if a file exist
890 BOOL FileExist (const char *filename)
892 WIN32_FIND_DATA FindFileData;
893 HANDLE handle = FindFirstFile (filename, &FindFileData);
894 if (handle != INVALID_HANDLE_VALUE) {
903 BOOL FileExist (const WCHAR *filename)
905 WIN32_FIND_DATAW FindFileData;
906 HANDLE handle = FindFirstFileW (filename, &FindFileData);
907 if (handle != INVALID_HANDLE_VALUE) {
915 /**********************************************************************\
916 * Routine Description: *
918 * This function is called to find out if a dll is bbt-ized *
920 \**********************************************************************/
921 BOOL IsRetailBuild (size_t base)
923 IMAGE_DOS_HEADER DosHeader;
924 if (g_ExtData->ReadVirtual(TO_CDADDR(base), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
926 IMAGE_NT_HEADERS32 Header32;
927 if (g_ExtData->ReadVirtual(TO_CDADDR(base + DosHeader.e_lfanew), &Header32, sizeof(Header32), NULL) != S_OK)
929 // If there is no COMHeader, this can not be managed code.
930 if (Header32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress == 0)
933 size_t debugDirAddr = base + Header32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
934 size_t nSize = Header32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
935 IMAGE_DEBUG_DIRECTORY debugDir;
937 while (nbytes < nSize) {
938 if (g_ExtData->ReadVirtual(TO_CDADDR(debugDirAddr+nbytes), &debugDir, sizeof(debugDir), NULL) != S_OK)
940 if (debugDir.Type == 0xA) {
943 nbytes += sizeof(debugDir);
948 #endif // !FEATURE_PAL
950 /**********************************************************************\
951 * Routine Description: *
953 * This function is called to read memory from the debugee's *
954 * address space. If the initial read fails, it attempts to read *
955 * only up to the edge of the page containing "offset". *
957 \**********************************************************************/
958 BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb,
959 PULONG lpcbBytesRead)
963 bRet = SUCCEEDED(g_ExtData->ReadVirtual(TO_CDADDR(offset), lpBuffer, cb,
968 cb = (ULONG)(NextOSPageAddress(offset) - offset);
969 bRet = SUCCEEDED(g_ExtData->ReadVirtual(TO_CDADDR(offset), lpBuffer, cb,
977 static ULONG pageSize = 0;
979 g_ExtControl->GetPageSize(&pageSize);
984 size_t NextOSPageAddress (size_t addr)
986 size_t pageSize = OSPageSize();
987 return (addr+pageSize)&(~(pageSize-1));
991 /**********************************************************************\
992 * Routine Description: *
994 * This function is called to get the address of MethodDesc *
995 * given an ip address *
997 \**********************************************************************/
998 void IP2MethodDesc (DWORD_PTR IP, DWORD_PTR &methodDesc, JITTypes &jitType,
999 DWORD_PTR &gcinfoAddr)
1002 CLRDATA_ADDRESS EIP = TO_CDADDR(IP);
1003 DacpCodeHeaderData codeHeaderData;
1008 if (codeHeaderData.Request(g_sos, EIP) != S_OK)
1013 methodDesc = (DWORD_PTR) codeHeaderData.MethodDescPtr;
1014 jitType = (JITTypes) codeHeaderData.JITType;
1015 gcinfoAddr = (DWORD_PTR) codeHeaderData.GCInfo;
1018 BOOL IsValueField (DacpFieldDescData *pFD)
1020 return (pFD->Type == ELEMENT_TYPE_VALUETYPE);
1023 void DisplayDataMember (DacpFieldDescData* pFD, DWORD_PTR dwAddr, BOOL fAlign=TRUE)
1027 // we must have called this function for a "real" (non-zero size) data type
1028 PREFIX_ASSUME(gElementTypeInfo[pFD->Type] != 0);
1030 DWORD_PTR dwTmp = dwAddr;
1031 bool bVTStatic = (pFD->bIsStatic && pFD->Type == ELEMENT_TYPE_VALUETYPE);
1033 if (gElementTypeInfo[pFD->Type] != NO_SIZE || bVTStatic)
1043 unsigned __int64 UInt64;
1048 ZeroMemory(&value, sizeof(value));
1051 // static VTypes are boxed
1052 moveBlock (value, dwTmp, gElementTypeInfo[ELEMENT_TYPE_CLASS]);
1056 moveBlock (value, dwTmp, gElementTypeInfo[pFD->Type]);
1061 case ELEMENT_TYPE_I1:
1062 // there's no ANSI conformant type specifier for
1063 // signed char, so use the next best thing,
1064 // signed short (sign extending)
1066 ExtOut("%" POINTERSIZE "hd", (short)value.ch);
1068 ExtOut("%d", value.ch);
1070 case ELEMENT_TYPE_I2:
1072 ExtOut("%" POINTERSIZE "hd", value.Short);
1074 ExtOut("%d", value.Short);
1076 case ELEMENT_TYPE_I4:
1078 ExtOut("%" POINTERSIZE "d", value.Int);
1080 ExtOut("%d", value.Int);
1082 case ELEMENT_TYPE_I8:
1083 ExtOut("%I64d", value.Int64);
1085 case ELEMENT_TYPE_U1:
1086 case ELEMENT_TYPE_BOOLEAN:
1088 // there's no ANSI conformant type specifier for
1089 // unsigned char, so use the next best thing,
1090 // unsigned short, not extending the sign
1091 ExtOut("%" POINTERSIZE "hu", (USHORT)value.Short);
1093 ExtOut("%u", value.ch);
1095 case ELEMENT_TYPE_U2:
1097 ExtOut("%" POINTERSIZE "hu", value.Short);
1099 ExtOut("%u", value.Short);
1101 case ELEMENT_TYPE_U4:
1103 ExtOut("%" POINTERSIZE "u", value.UInt);
1105 ExtOut("%u", value.UInt);
1107 case ELEMENT_TYPE_U8:
1108 ExtOut("%I64u", value.UInt64);
1110 case ELEMENT_TYPE_I:
1111 case ELEMENT_TYPE_U:
1113 ExtOut("%" POINTERSIZE "p", SOS_PTR(value.ptr));
1115 ExtOut("%p", SOS_PTR(value.ptr));
1117 case ELEMENT_TYPE_R4:
1118 ExtOut("%f", value.Float);
1120 case ELEMENT_TYPE_R8:
1121 ExtOut("%f", value.Double);
1123 case ELEMENT_TYPE_CHAR:
1125 ExtOut("%" POINTERSIZE "hx", value.Short);
1127 ExtOut("%x", value.Short);
1129 case ELEMENT_TYPE_VALUETYPE:
1131 DMLOut(DMLValueClass(pFD->MTOfType, dwTmp));
1133 ExtOut("%p", SOS_PTR(0));
1137 DMLOut(DMLObject(value.ptr));
1139 ExtOut("%p", SOS_PTR(0));
1145 if (pFD->Type == ELEMENT_TYPE_VALUETYPE)
1146 DMLOut(DMLValueClass(pFD->MTOfType, dwTmp));
1148 ExtOut("%p", SOS_PTR(0));
1153 ExtOut("%" POINTERSIZE "s", " ");
1157 void GetStaticFieldPTR(DWORD_PTR* pOutPtr, DacpDomainLocalModuleData* pDLMD, DacpMethodTableData* pMTD, DacpFieldDescData* pFDD, BYTE* pFlags = 0)
1161 if (pFDD->Type == ELEMENT_TYPE_VALUETYPE
1162 || pFDD->Type == ELEMENT_TYPE_CLASS)
1164 dwTmp = (DWORD_PTR) pDLMD->pGCStaticDataStart + pFDD->dwOffset;
1168 dwTmp = (DWORD_PTR) pDLMD->pNonGCStaticDataStart + pFDD->dwOffset;
1173 if (pMTD->bIsDynamic)
1175 ExtOut("dynamic statics NYI");
1180 if (pFlags && pMTD->bIsShared)
1183 DWORD_PTR pTargetFlags = (DWORD_PTR) pDLMD->pClassData + RidFromToken(pMTD->cl) - 1;
1184 move_xp (flags, pTargetFlags);
1195 void GetDLMFlags(DacpDomainLocalModuleData* pDLMD, DacpMethodTableData* pMTD, BYTE* pFlags)
1197 if (pMTD->bIsDynamic)
1199 ExtOut("dynamic statics NYI");
1207 DWORD_PTR pTargetFlags = (DWORD_PTR) pDLMD->pClassData + RidFromToken(pMTD->cl) - 1;
1208 move_xp (flags, pTargetFlags);
1216 void GetThreadStaticFieldPTR(DWORD_PTR* pOutPtr, DacpThreadLocalModuleData* pTLMD, DacpMethodTableData* pMTD, DacpFieldDescData* pFDD, BYTE* pFlags = 0)
1220 if (pFDD->Type == ELEMENT_TYPE_VALUETYPE
1221 || pFDD->Type == ELEMENT_TYPE_CLASS)
1223 dwTmp = (DWORD_PTR) pTLMD->pGCStaticDataStart + pFDD->dwOffset;
1227 dwTmp = (DWORD_PTR) pTLMD->pNonGCStaticDataStart + pFDD->dwOffset;
1232 if (pMTD->bIsDynamic)
1234 ExtOut("dynamic thread statics NYI");
1242 DWORD_PTR pTargetFlags = (DWORD_PTR) pTLMD->pClassData + RidFromToken(pMTD->cl) - 1;
1243 move_xp (flags, pTargetFlags);
1253 void DisplaySharedStatic(ULONG64 dwModuleDomainID, DacpMethodTableData* pMT, DacpFieldDescData *pFD)
1255 DacpAppDomainStoreData adsData;
1256 if (adsData.Request(g_sos)!=S_OK)
1258 ExtOut("Unable to get AppDomain information\n");
1261 ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adsData.DomainCount];
1268 if (g_sos->GetAppDomainList(adsData.DomainCount,pArray, NULL)!=S_OK)
1270 ExtOut("Unable to get array of AppDomains\n");
1274 #if defined(_TARGET_WIN64_)
1275 ExtOut(" >> Domain:Value ");
1277 ExtOut(" >> Domain:Value ");
1279 // Skip the SystemDomain and SharedDomain
1280 for (int i = 0; i < adsData.DomainCount ; i ++)
1282 DacpAppDomainData appdomainData;
1283 if (appdomainData.Request(g_sos,pArray[i])!=S_OK)
1285 ExtOut("Unable to get AppDomain %lx\n",pArray[i]);
1289 DacpDomainLocalModuleData vDomainLocalModule;
1290 if (g_sos->GetDomainLocalModuleDataFromAppDomain(appdomainData.AppDomainPtr, (int)dwModuleDomainID, &vDomainLocalModule) != S_OK)
1292 DMLOut(" %s:NotInit ", DMLDomain(pArray[i]));
1298 GetStaticFieldPTR(&dwTmp, &vDomainLocalModule , pMT, pFD, &Flags);
1300 if ((Flags&1) == 0) {
1301 // We have not initialized this yet.
1302 DMLOut(" %s:NotInit ", DMLDomain(pArray[i]));
1305 else if (Flags & 2) {
1306 // We have not initialized this yet.
1307 DMLOut(" %s:FailInit", DMLDomain(pArray[i]));
1311 DMLOut(" %s:", DMLDomain(appdomainData.AppDomainPtr));
1312 DisplayDataMember(pFD, dwTmp, FALSE);
1317 void DisplayThreadStatic (DacpModuleData* pModule, DacpMethodTableData* pMT, DacpFieldDescData *pFD, BOOL fIsShared)
1319 SIZE_T dwModuleIndex = (SIZE_T)pModule->dwModuleIndex;
1320 SIZE_T dwModuleDomainID = (SIZE_T)pModule->dwModuleID;
1322 DacpThreadStoreData ThreadStore;
1323 ThreadStore.Request(g_sos);
1325 ExtOut(" >> Thread:Value");
1326 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
1329 DacpThreadData vThread;
1330 if (vThread.Request(g_sos, CurThread) != S_OK)
1332 ExtOut(" error getting thread %p, aborting this field\n", SOS_PTR(CurThread));
1336 if (vThread.osThreadId != 0)
1338 CLRDATA_ADDRESS appDomainAddr = vThread.domain;
1340 // Get the DLM (we need this to check the ClassInit flags).
1341 // It's annoying that we have to issue one request for
1342 // domain-neutral modules and domain-specific modules.
1343 DacpDomainLocalModuleData vDomainLocalModule;
1346 if (g_sos->GetDomainLocalModuleDataFromAppDomain(appDomainAddr, (int)dwModuleDomainID, &vDomainLocalModule) != S_OK)
1348 // Not initialized, go to next thread
1349 // and continue looping
1350 CurThread = vThread.nextThread;
1356 if (g_sos->GetDomainLocalModuleDataFromModule(pMT->Module, &vDomainLocalModule) != S_OK)
1358 // Not initialized, go to next thread
1359 // and continue looping
1360 CurThread = vThread.nextThread;
1366 DacpThreadLocalModuleData vThreadLocalModule;
1367 if (g_sos->GetThreadLocalModuleData(CurThread, (int)dwModuleIndex, &vThreadLocalModule) != S_OK)
1369 // Not initialized, go to next thread
1370 // and continue looping
1371 CurThread = vThread.nextThread;
1377 GetThreadStaticFieldPTR(&dwTmp, &vThreadLocalModule, pMT, pFD, &Flags);
1381 // Not allocated, go to next thread
1382 // and continue looping
1383 CurThread = vThread.nextThread;
1388 GetDLMFlags(&vDomainLocalModule, pMT, &Flags);
1392 // Not initialized, go to next thread
1393 // and continue looping
1394 CurThread = vThread.nextThread;
1398 ExtOut(" %x:", vThread.osThreadId);
1399 DisplayDataMember(pFD, dwTmp, FALSE);
1402 // Go to next thread
1403 CurThread = vThread.nextThread;
1408 void DisplayContextStatic (DacpFieldDescData *pFD, size_t offset, BOOL fIsShared)
1410 ExtOut("\nDisplay of context static variables is not implemented yet\n");
1413 DWORD_PTR *domainList = NULL;
1414 GetDomainList (domainList, numDomain);
1415 ToDestroy des0 ((void**)&domainList);
1416 AppDomain vAppDomain;
1419 ExtOut(" >> Domain:Value");
1420 for (int i = 0; i < numDomain; i ++)
1422 DWORD_PTR dwAddr = domainList[i];
1426 vAppDomain.Fill (dwAddr);
1427 if (vAppDomain.m_pDefaultContext == 0)
1429 dwAddr = (DWORD_PTR)vAppDomain.m_pDefaultContext;
1430 vContext.Fill (dwAddr);
1433 dwAddr = (DWORD_PTR)vContext.m_pSharedStaticData;
1435 dwAddr = (DWORD_PTR)vContext.m_pUnsharedStaticData;
1438 dwAddr += offsetof(STATIC_DATA, dataPtr);
1440 if (safemove (dwAddr, dwAddr) == 0)
1443 // We have not initialized this yet.
1446 dwAddr += pFD->dwOffset;
1447 if (pFD->Type == ELEMENT_TYPE_CLASS
1448 || pFD->Type == ELEMENT_TYPE_VALUETYPE)
1450 if (safemove (dwAddr, dwAddr) == 0)
1454 // We have not initialized this yet.
1456 ExtOut(" %p:", (ULONG64)domainList[i]);
1457 DisplayDataMember (pFD, dwAddr, FALSE);
1463 const char * ElementTypeName(unsigned type)
1466 case ELEMENT_TYPE_PTR:
1469 case ELEMENT_TYPE_BYREF:
1472 case ELEMENT_TYPE_VALUETYPE:
1475 case ELEMENT_TYPE_CLASS:
1478 case ELEMENT_TYPE_VAR:
1481 case ELEMENT_TYPE_ARRAY:
1484 case ELEMENT_TYPE_FNPTR:
1487 case ELEMENT_TYPE_SZARRAY:
1490 case ELEMENT_TYPE_MVAR:
1494 if ((type >= _countof(CorElementTypeName)) || (CorElementTypeName[type] == NULL))
1498 return CorElementTypeName[type];
1501 } // ElementTypeName
1503 const char * ElementTypeNamespace(unsigned type)
1505 if ((type >= _countof(CorElementTypeName)) || (CorElementTypeNamespace[type] == NULL))
1509 return CorElementTypeNamespace[type];
1512 void ComposeName_s(CorElementType Type, __out_ecount(capacity_buffer) LPSTR buffer, size_t capacity_buffer)
1514 const char *p = ElementTypeNamespace(Type);
1515 if ((p) && (*p != '\0'))
1517 strcpy_s(buffer,capacity_buffer,p);
1518 strcat_s(buffer,capacity_buffer,".");
1519 strcat_s(buffer,capacity_buffer,ElementTypeName(Type));
1523 strcpy_s(buffer,capacity_buffer,ElementTypeName(Type));
1527 // NOTE: pszName is changed
1528 // INPUT MAXCHARS RETURN
1529 // HelloThere 5 ...re
1530 // HelloThere 8 ...There
1531 LPWSTR FormatTypeName (__out_ecount (maxChars) LPWSTR pszName, UINT maxChars)
1534 UINT iLen = (int) _wcslen(pszName);
1535 if (iLen > maxChars)
1537 iStart = iLen - maxChars;
1538 UINT numDots = (maxChars < 3) ? maxChars : 3;
1539 for (UINT i=0; i < numDots; i++)
1540 pszName[iStart+i] = '.';
1542 return pszName + iStart;
1545 /**********************************************************************\
1546 * Routine Description: *
1548 * This function is called to dump all fields of a managed object. *
1549 * dwStartAddr specifies the beginning memory address. *
1550 * bFirst is used to avoid printing header everytime. *
1552 \**********************************************************************/
1553 void DisplayFields(CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodTableFieldData *pMTFD, DWORD_PTR dwStartAddr, BOOL bFirst, BOOL bValueClass)
1555 static DWORD numInstanceFields = 0;
1559 ExtOut("%" POINTERSIZE "s %8s %8s %20s %2s %8s %" POINTERSIZE "s %s\n",
1560 "MT", "Field", "Offset", "Type", "VT", "Attr", "Value", "Name");
1561 numInstanceFields = 0;
1564 BOOL fIsShared = pMTD->bIsShared;
1566 if (pMTD->ParentMethodTable)
1568 DacpMethodTableData vParentMethTable;
1569 if (vParentMethTable.Request(g_sos,pMTD->ParentMethodTable) != S_OK)
1571 ExtOut("Invalid parent MethodTable\n");
1575 DacpMethodTableFieldData vParentMethTableFields;
1576 if (vParentMethTableFields.Request(g_sos,pMTD->ParentMethodTable) != S_OK)
1578 ExtOut("Invalid parent EEClass\n");
1582 DisplayFields(pMTD->ParentMethodTable, &vParentMethTable, &vParentMethTableFields, dwStartAddr, FALSE, bValueClass);
1585 DWORD numStaticFields = 0;
1586 CLRDATA_ADDRESS dwAddr = pMTFD->FirstField;
1587 DacpFieldDescData vFieldDesc;
1589 // Get the module name
1590 DacpModuleData module;
1591 if (module.Request(g_sos, pMTD->Module)!=S_OK)
1594 ToRelease<IMetaDataImport> pImport = MDImportForModule(&module);
1596 while (numInstanceFields < pMTFD->wNumInstanceFields
1597 || numStaticFields < pMTFD->wNumStaticFields)
1604 if ((vFieldDesc.Request(g_sos, dwAddr)!=S_OK) ||
1605 (vFieldDesc.Type >= ELEMENT_TYPE_MAX))
1607 ExtOut("Unable to display fields\n");
1610 dwAddr = vFieldDesc.NextField;
1612 DWORD offset = vFieldDesc.dwOffset;
1613 if(!((vFieldDesc.bIsThreadLocal || vFieldDesc.bIsContextLocal || fIsShared) && vFieldDesc.bIsStatic))
1617 offset += sizeof(BaseObject);
1621 DMLOut("%s %8x %8x ", DMLMethodTable(vFieldDesc.MTOfType),
1622 TokenFromRid(vFieldDesc.mb, mdtFieldDef),
1625 char ElementName[mdNameLen];
1626 if ((vFieldDesc.Type == ELEMENT_TYPE_VALUETYPE ||
1627 vFieldDesc.Type == ELEMENT_TYPE_CLASS) && vFieldDesc.MTOfType)
1629 NameForMT_s((DWORD_PTR)vFieldDesc.MTOfType, g_mdName, mdNameLen);
1630 ExtOut("%20.20S ", FormatTypeName(g_mdName, 20));
1634 if (vFieldDesc.Type == ELEMENT_TYPE_CLASS && vFieldDesc.TokenOfType != mdTypeDefNil)
1636 // Get the name from Metadata!!!
1637 NameForToken_s(TokenFromRid(vFieldDesc.TokenOfType, mdtTypeDef), pImport, g_mdName, mdNameLen, false);
1638 ExtOut("%20.20S ", FormatTypeName(g_mdName, 20));
1642 // If ET type from signature is different from fielddesc, then the signature one is more descriptive.
1643 // For example, E_T_STRING in field desc will be E_T_CLASS. In minidump's case, we won't have
1644 // the method table for it.
1645 ComposeName_s(vFieldDesc.Type != vFieldDesc.sigType ? vFieldDesc.sigType : vFieldDesc.Type, ElementName, sizeof(ElementName)/sizeof(ElementName[0]));
1646 ExtOut("%20.20s ", ElementName);
1650 ExtOut("%2s ", (IsElementValueType(vFieldDesc.Type)) ? "1" : "0");
1652 if (vFieldDesc.bIsStatic && (vFieldDesc.bIsThreadLocal || vFieldDesc.bIsContextLocal))
1656 ExtOut("%8s %" POINTERSIZE "s", "shared", vFieldDesc.bIsThreadLocal ? "TLstatic" : "CLstatic");
1658 ExtOut("%8s ", vFieldDesc.bIsThreadLocal ? "TLstatic" : "CLstatic");
1660 NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
1661 ExtOut(" %S\n", g_mdName);
1663 if (IsMiniDumpFile())
1665 ExtOut(" <no information>\n");
1669 if (vFieldDesc.bIsThreadLocal)
1671 DacpModuleData vModule;
1672 if (vModule.Request(g_sos,pMTD->Module) == S_OK)
1674 DisplayThreadStatic(&vModule, pMTD, &vFieldDesc, fIsShared);
1677 else if (vFieldDesc.bIsContextLocal)
1679 DisplayContextStatic(&vFieldDesc,
1680 pMTFD->wContextStaticOffset,
1686 else if (vFieldDesc.bIsStatic)
1692 ExtOut("%8s %" POINTERSIZE "s", "shared", "static");
1694 NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
1695 ExtOut(" %S\n", g_mdName);
1697 if (IsMiniDumpFile())
1699 ExtOut(" <no information>\n");
1703 DacpModuleData vModule;
1704 if (vModule.Request(g_sos,pMTD->Module) == S_OK)
1706 DisplaySharedStatic(vModule.dwModuleID, pMTD, &vFieldDesc);
1712 ExtOut("%8s ", "static");
1714 DacpDomainLocalModuleData vDomainLocalModule;
1716 // The MethodTable isn't shared, so the module must not be loaded domain neutral. We can
1717 // get the specific DomainLocalModule instance without needing to know the AppDomain in advance.
1718 if (g_sos->GetDomainLocalModuleDataFromModule(pMTD->Module, &vDomainLocalModule) != S_OK)
1720 ExtOut(" <no information>\n");
1725 GetStaticFieldPTR(&dwTmp, &vDomainLocalModule, pMTD, &vFieldDesc);
1726 DisplayDataMember(&vFieldDesc, dwTmp);
1728 NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
1729 ExtOut(" %S\n", g_mdName);
1735 numInstanceFields ++;
1737 ExtOut("%8s ", "instance");
1739 if (dwStartAddr > 0)
1741 DWORD_PTR dwTmp = dwStartAddr + vFieldDesc.dwOffset + (bValueClass ? 0 : sizeof(BaseObject));
1742 DisplayDataMember(&vFieldDesc, dwTmp);
1746 ExtOut(" %8s", " ");
1750 NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
1751 ExtOut(" %S\n", g_mdName);
1759 // Return value: -1 = error,
1760 // 0 = field not found,
1761 // > 0 = offset to field from objAddr
1762 int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, __in_z LPCWSTR wszFieldName, BOOL bFirst)
1765 if FAILED(GetMTOfObject(TO_TADDR(cdaObj), &mt))
1768 return GetObjFieldOffset(cdaObj, TO_CDADDR(mt), wszFieldName, bFirst);
1771 // Return value: -1 = error,
1772 // 0 = field not found,
1773 // > 0 = offset to field from objAddr
1774 int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName,
1775 BOOL bFirst/*=TRUE*/)
1778 #define EXITPOINT(EXPR) do { if(!(EXPR)) { return -1; } } while (0)
1780 DacpObjectData objData;
1781 DacpMethodTableData dmtd;
1782 DacpMethodTableFieldData vMethodTableFields;
1783 DacpFieldDescData vFieldDesc;
1784 DacpModuleData module;
1785 static DWORD numInstanceFields = 0; // Static due to recursion visiting parents
1789 numInstanceFields = 0;
1792 EXITPOINT(objData.Request(g_sos, cdaObj) == S_OK);
1793 EXITPOINT(dmtd.Request(g_sos, cdaMT) == S_OK);
1795 if (dmtd.ParentMethodTable)
1797 DWORD retVal = GetObjFieldOffset (cdaObj, dmtd.ParentMethodTable,
1798 wszFieldName, FALSE);
1801 // return in case of error or success.
1802 // Fall through for field-not-found.
1807 EXITPOINT (vMethodTableFields.Request(g_sos,cdaMT) == S_OK);
1808 EXITPOINT (module.Request(g_sos,dmtd.Module) == S_OK);
1810 CLRDATA_ADDRESS dwAddr = vMethodTableFields.FirstField;
1811 ToRelease<IMetaDataImport> pImport = MDImportForModule(&module);
1813 while (numInstanceFields < vMethodTableFields.wNumInstanceFields)
1815 EXITPOINT (vFieldDesc.Request(g_sos, dwAddr) == S_OK);
1817 if (!vFieldDesc.bIsStatic)
1819 DWORD offset = vFieldDesc.dwOffset + sizeof(BaseObject);
1820 NameForToken_s (TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
1821 if (_wcscmp (wszFieldName, g_mdName) == 0)
1825 numInstanceFields ++;
1828 dwAddr = vFieldDesc.NextField;
1831 // Field name not found...
1837 // Returns an AppDomain address if AssemblyPtr is loaded into that domain only. Otherwise
1839 CLRDATA_ADDRESS IsInOneDomainOnly(CLRDATA_ADDRESS AssemblyPtr)
1841 CLRDATA_ADDRESS appDomain = NULL;
1843 DacpAppDomainStoreData adstore;
1844 if (adstore.Request(g_sos) != S_OK)
1846 ExtOut("Unable to get appdomain store\n");
1851 if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), adstore.DomainCount, AllocSize))
1857 ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adstore.DomainCount];
1864 if (g_sos->GetAppDomainList(adstore.DomainCount, pArray, NULL)!=S_OK)
1866 ExtOut ("Failed to get appdomain list\n");
1870 for (int i = 0; i < adstore.DomainCount; i++)
1875 DacpAppDomainData dadd;
1876 if (dadd.Request(g_sos, pArray[i]) != S_OK)
1878 ExtOut ("Unable to get AppDomain %p\n", SOS_PTR(pArray[i]));
1882 if (dadd.AssemblyCount)
1884 size_t AssemblyAllocSize;
1885 if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), dadd.AssemblyCount, AssemblyAllocSize))
1891 ArrayHolder<CLRDATA_ADDRESS> pAsmArray = new CLRDATA_ADDRESS[dadd.AssemblyCount];
1892 if (pAsmArray==NULL)
1898 if (g_sos->GetAssemblyList(dadd.AppDomainPtr,dadd.AssemblyCount,pAsmArray, NULL)!=S_OK)
1900 ExtOut("Unable to get array of Assemblies\n");
1904 for (LONG n = 0; n < dadd.AssemblyCount; n ++)
1909 if (AssemblyPtr == pAsmArray[n])
1911 if (appDomain != NULL)
1913 // We have found more than one AppDomain that loaded this
1914 // assembly, we must return NULL.
1917 appDomain = dadd.AppDomainPtr;
1927 CLRDATA_ADDRESS GetAppDomainForMT(CLRDATA_ADDRESS mtPtr)
1929 DacpMethodTableData mt;
1930 if (mt.Request(g_sos, mtPtr) != S_OK)
1935 DacpModuleData module;
1936 if (module.Request(g_sos, mt.Module) != S_OK)
1941 DacpAssemblyData assembly;
1942 if (assembly.Request(g_sos, module.Assembly) != S_OK)
1947 DacpAppDomainStoreData adstore;
1948 if (adstore.Request(g_sos) != S_OK)
1953 return (assembly.ParentDomain == adstore.sharedDomain) ?
1954 IsInOneDomainOnly(assembly.AssemblyPtr) :
1955 assembly.ParentDomain;
1958 CLRDATA_ADDRESS GetAppDomain(CLRDATA_ADDRESS objPtr)
1960 CLRDATA_ADDRESS appDomain = NULL;
1962 DacpObjectData objData;
1963 if (objData.Request(g_sos,objPtr) != S_OK)
1968 // First check eeclass->module->assembly->domain.
1969 // Then check the object flags word
1970 // finally, search threads for a reference to the object, and look at the thread context.
1972 DacpMethodTableData mt;
1973 if (mt.Request(g_sos,objData.MethodTable) != S_OK)
1978 DacpModuleData module;
1979 if (module.Request(g_sos,mt.Module) != S_OK)
1984 DacpAssemblyData assembly;
1985 if (assembly.Request(g_sos,module.Assembly) != S_OK)
1990 DacpAppDomainStoreData adstore;
1991 if (adstore.Request(g_sos) != S_OK)
1996 if (assembly.ParentDomain == adstore.sharedDomain)
1998 sos::Object obj(TO_TADDR(objPtr));
2000 if (!obj.TryGetHeader(value))
2005 DWORD adIndex = (value >> SBLK_APPDOMAIN_SHIFT) & SBLK_MASK_APPDOMAININDEX;
2006 if ( ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0) || adIndex==0)
2008 // No AppDomainID information. We'll make use of a heuristic.
2009 // If the assembly is in the shared domain, we can report it as
2010 // being in domain X if the only other domain that has the assembly
2011 // loaded is domain X.
2012 appDomain = IsInOneDomainOnly(assembly.AssemblyPtr);
2013 if (appDomain == NULL && ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0))
2015 if ((value & BIT_SBLK_IS_HASHCODE) == 0)
2017 UINT index = value & MASK_SYNCBLOCKINDEX;
2018 // We have a syncblock, the appdomain ID may be in there.
2019 DacpSyncBlockData syncBlockData;
2020 if (syncBlockData.Request(g_sos,index) == S_OK)
2022 appDomain = syncBlockData.appDomainPtr;
2027 else if ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) == 0)
2030 if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), adstore.DomainCount, AllocSize))
2034 // we know we have a non-zero adIndex. Find the appdomain.
2035 ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adstore.DomainCount];
2041 if (g_sos->GetAppDomainList(adstore.DomainCount, pArray, NULL)!=S_OK)
2046 for (int i = 0; i < adstore.DomainCount; i++)
2048 DacpAppDomainData dadd;
2049 if (dadd.Request(g_sos, pArray[i]) != S_OK)
2053 if (dadd.dwId == adIndex)
2055 appDomain = pArray[i];
2063 appDomain = assembly.ParentDomain;
2069 HRESULT FileNameForModule (DWORD_PTR pModuleAddr, __out_ecount (MAX_LONGPATH) WCHAR *fileName)
2071 DacpModuleData ModuleData;
2072 fileName[0] = L'\0';
2074 HRESULT hr = ModuleData.Request(g_sos, TO_CDADDR(pModuleAddr));
2077 hr = FileNameForModule(&ModuleData,fileName);
2083 /**********************************************************************\
2084 * Routine Description: *
2086 * This function is called to find the file name given a Module. *
2088 \**********************************************************************/
2089 // fileName should be at least MAX_LONGPATH
2090 HRESULT FileNameForModule (DacpModuleData *pModule, __out_ecount (MAX_LONGPATH) WCHAR *fileName)
2092 fileName[0] = L'\0';
2095 CLRDATA_ADDRESS dwAddr = pModule->File;
2098 // TODO: We have dynamic module
2102 CLRDATA_ADDRESS base = 0;
2103 hr = g_sos->GetPEFileBase(dwAddr, &base);
2106 hr = g_sos->GetPEFileName(dwAddr, MAX_LONGPATH, fileName, NULL);
2109 if (fileName[0] != W('\0'))
2116 hr = DllsName((ULONG_PTR) base, fileName);
2118 #endif // !FEATURE_PAL
2121 // If we got here, either DllsName worked, or we couldn't find a name
2125 void AssemblyInfo(DacpAssemblyData *pAssembly)
2127 ExtOut("ClassLoader: %p\n", SOS_PTR(pAssembly->ClassLoader));
2128 if ((ULONG64)pAssembly->AssemblySecDesc != NULL)
2129 ExtOut("SecurityDescriptor: %p\n", SOS_PTR(pAssembly->AssemblySecDesc));
2130 ExtOut(" Module Name\n");
2132 ArrayHolder<CLRDATA_ADDRESS> Modules = new CLRDATA_ADDRESS[pAssembly->ModuleCount];
2134 || g_sos->GetAssemblyModuleList(pAssembly->AssemblyPtr, pAssembly->ModuleCount, Modules, NULL) != S_OK)
2140 for (UINT n=0;n<pAssembly->ModuleCount;n++)
2147 CLRDATA_ADDRESS ModuleAddr = Modules[n];
2148 DMLOut("%s " WIN86_8SPACES, DMLModule(ModuleAddr));
2149 DacpModuleData moduleData;
2150 if (moduleData.Request(g_sos,ModuleAddr)==S_OK)
2152 WCHAR fileName[MAX_LONGPATH];
2153 FileNameForModule (&moduleData, fileName);
2156 ExtOut("%S\n", fileName);
2160 ExtOut("%S\n", (moduleData.bIsReflection) ? W("Dynamic Module") : W("Unknown Module"));
2166 const char *GetStageText(DacpAppDomainDataStage stage)
2170 case STAGE_CREATING:
2172 case STAGE_READYFORMANAGEDCODE:
2173 return "READYFORMANAGEDCODE";
2178 case STAGE_UNLOAD_REQUESTED:
2179 return "UNLOAD_REQUESTED";
2184 case STAGE_FINALIZING:
2185 return "FINALIZING";
2186 case STAGE_FINALIZED:
2188 case STAGE_HANDLETABLE_NOACCESS:
2189 return "HANDLETABLE_NOACCESS";
2192 case STAGE_COLLECTED:
2200 /**********************************************************************\
2201 * Routine Description: *
2203 * This function is called to dump the contents of a domain. *
2205 \**********************************************************************/
2206 void DomainInfo (DacpAppDomainData *pDomain)
2208 ExtOut("LowFrequencyHeap: %p\n", SOS_PTR(pDomain->pLowFrequencyHeap));
2209 ExtOut("HighFrequencyHeap: %p\n", SOS_PTR(pDomain->pHighFrequencyHeap));
2210 ExtOut("StubHeap: %p\n", SOS_PTR(pDomain->pStubHeap));
2211 ExtOut("Stage: %s\n", GetStageText(pDomain->appDomainStage));
2212 if ((ULONG64)pDomain->AppSecDesc != NULL)
2213 ExtOut("SecurityDescriptor: %p\n", SOS_PTR(pDomain->AppSecDesc));
2216 if (g_sos->GetAppDomainName(pDomain->AppDomainPtr, mdNameLen, g_mdName, NULL)!=S_OK)
2218 ExtOut("Error getting AppDomain friendly name\n");
2222 ExtOut("%S\n", (g_mdName[0] != L'\0') ? g_mdName : W("None"));
2225 if (pDomain->AssemblyCount == 0)
2228 ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[pDomain->AssemblyCount];
2235 if (g_sos->GetAssemblyList(pDomain->AppDomainPtr,pDomain->AssemblyCount,pArray, NULL)!=S_OK)
2237 ExtOut("Unable to get array of Assemblies\n");
2242 // Assembly vAssembly;
2243 for (n = 0; n < pDomain->AssemblyCount; n ++)
2251 DMLOut("Assembly: %s", DMLAssembly(pArray[n]));
2252 DacpAssemblyData assemblyData;
2253 if (assemblyData.Request(g_sos, pArray[n], pDomain->AppDomainPtr) == S_OK)
2255 if (assemblyData.isDynamic)
2256 ExtOut(" (Dynamic)");
2259 if (g_sos->GetAssemblyName(pArray[n], mdNameLen, g_mdName, NULL) == S_OK)
2260 ExtOut("%S", g_mdName);
2263 AssemblyInfo(&assemblyData);
2270 /**********************************************************************\
2271 * Routine Description: *
2273 * This function is called to find the name of a MethodDesc using *
2276 \**********************************************************************/
2277 BOOL NameForMD_s (DWORD_PTR pMD, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName)
2280 CLRDATA_ADDRESS StartAddr = TO_CDADDR(pMD);
2281 DacpMethodDescData MethodDescData;
2283 // don't need to check for minidump file as all commands are seals
2284 // We also do not have EEJitManager to validate anyway.
2286 if (!IsMiniDumpFile() && MethodDescData.Request(g_sos,StartAddr) != S_OK)
2288 ExtOut("%p is not a MethodDesc\n", SOS_PTR(StartAddr));
2292 if (g_sos->GetMethodDescName(StartAddr, mdNameLen, mdName, NULL) != S_OK)
2294 wcscpy_s(mdName, capacity_mdName, W("UNKNOWN"));
2300 /**********************************************************************\
2301 * Routine Description: *
2303 * This function is called to find the name of a MethodTable using *
2306 \**********************************************************************/
2307 BOOL NameForMT_s(DWORD_PTR MTAddr, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName)
2309 HRESULT hr = g_sos->GetMethodTableName(TO_CDADDR(MTAddr), (ULONG32)capacity_mdName, mdName, NULL);
2310 return SUCCEEDED(hr);
2313 WCHAR *CreateMethodTableName(TADDR mt, TADDR cmt)
2318 if (mt == sos::MethodTable::GetFreeMT())
2321 wcscpy_s(res, 5, W("Free"));
2325 if (mt == sos::MethodTable::GetArrayMT() && cmt != NULL)
2331 unsigned int needed = 0;
2332 HRESULT hr = g_sos->GetMethodTableName(mt, 0, NULL, &needed);
2334 // If failed, we will return null.
2337 // +2 for [], if we need it.
2338 res = new WCHAR[needed+2];
2339 hr = g_sos->GetMethodTableName(mt, needed, res, NULL);
2348 res[needed-1] = '[';
2357 /**********************************************************************\
2358 * Routine Description: *
2360 * Return TRUE if str2 is a substring of str1 and str1 and str2 *
2361 * share the same file path.
2363 \**********************************************************************/
2364 BOOL IsSameModuleName (const char *str1, const char *str2)
2366 if (strlen (str1) < strlen (str2))
2368 const char *ptr1 = str1 + strlen(str1)-1;
2369 const char *ptr2 = str2 + strlen(str2)-1;
2370 while (ptr2 >= str2)
2373 if (tolower(*ptr1) != tolower(*ptr2))
2383 if (ptr1 >= str1 && *ptr1 != DIRECTORY_SEPARATOR_CHAR_A && *ptr1 != ':')
2390 /**********************************************************************\
2391 * Routine Description: *
2393 * Return TRUE if moduleAddr is the address of a module. *
2395 \**********************************************************************/
2396 BOOL IsModule (DWORD_PTR moduleAddr)
2398 DacpModuleData module;
2399 return (module.Request(g_sos, TO_CDADDR(moduleAddr))==S_OK);
2402 /**********************************************************************\
2403 * Routine Description: *
2405 * Return TRUE if value is the address of a MethodTable. *
2406 * We verify that MethodTable and EEClass are right.
2408 \**********************************************************************/
2409 BOOL IsMethodTable (DWORD_PTR value)
2411 DacpMethodTableData mtabledata;
2412 if (mtabledata.Request(g_sos, TO_CDADDR(value))!=S_OK)
2420 /**********************************************************************\
2421 * Routine Description: *
2423 * Return TRUE if value is the address of a MethodDesc. *
2424 * We verify that MethodTable and EEClass are right.
2426 \**********************************************************************/
2427 BOOL IsMethodDesc (DWORD_PTR value)
2429 // Just by retrieving one successfully from the DAC, we know we have a MethodDesc.
2430 DacpMethodDescData MethodDescData;
2431 if (MethodDescData.Request(g_sos, TO_CDADDR(value)) != S_OK)
2439 DacpUsefulGlobalsData g_special_usefulGlobals;
2441 BOOL IsObjectArray (DacpObjectData *pData)
2443 if (pData->ObjectType == OBJ_ARRAY)
2444 return g_special_usefulGlobals.ArrayMethodTable == pData->MethodTable;
2449 BOOL IsObjectArray (DWORD_PTR obj)
2451 DWORD_PTR mtAddr = NULL;
2452 if (SUCCEEDED(GetMTOfObject(obj, &mtAddr)))
2453 return TO_TADDR(g_special_usefulGlobals.ArrayMethodTable) == mtAddr;
2458 BOOL IsStringObject (size_t obj)
2460 DWORD_PTR mtAddr = NULL;
2462 if (SUCCEEDED(GetMTOfObject(obj, &mtAddr)))
2463 return TO_TADDR(g_special_usefulGlobals.StringMethodTable) == mtAddr;
2468 void DumpStackObjectsOutput(const char *location, DWORD_PTR objAddr, BOOL verifyFields)
2470 // rule out pointers that are outside of the gc heap.
2471 if (g_snapshot.GetHeap(objAddr) == NULL)
2474 DacpObjectData objectData;
2475 if (objectData.Request(g_sos, TO_CDADDR(objAddr)) != S_OK)
2478 if (sos::IsObject(objAddr, verifyFields != FALSE)
2479 && !sos::MethodTable::IsFreeMT(TO_TADDR(objectData.MethodTable)))
2481 DMLOut("%-" POINTERSIZE "s %s ", location, DMLObject(objAddr));
2482 if (g_sos->GetObjectClassName(TO_CDADDR(objAddr), mdNameLen, g_mdName, NULL)==S_OK)
2484 ExtOut("%S", g_mdName);
2486 if (IsStringObject(objAddr))
2489 StringObjectContent(objAddr, FALSE, 40);
2491 else if (IsObjectArray(objAddr) &&
2492 (g_sos->GetMethodTableName(objectData.ElementTypeHandle, mdNameLen, g_mdName, NULL) == S_OK))
2495 ExtOut("(%S[])", g_mdName);
2500 ExtOut("<unknown type>");
2506 void DumpStackObjectsOutput(DWORD_PTR ptr, DWORD_PTR objAddr, BOOL verifyFields)
2509 sprintf_s(location, 64, "%p", (DWORD_PTR *)ptr);
2511 DumpStackObjectsOutput(location, objAddr, verifyFields);
2514 void DumpStackObjectsInternal(size_t StackTop, size_t StackBottom, BOOL verifyFields)
2516 for (DWORD_PTR ptr = StackTop; ptr <= StackBottom; ptr += sizeof(DWORD_PTR))
2522 move_xp(objAddr, ptr);
2524 DumpStackObjectsOutput(ptr, objAddr, verifyFields);
2528 void DumpRegObjectHelper(const char *regName, BOOL verifyFields)
2532 if (FAILED(g_ExtRegisters->GetValueByName(regName, ®)))
2537 if (FAILED(g_ExtRegisters->GetIndexByName(regName, &IREG)) ||
2538 FAILED(g_ExtRegisters->GetValue(IREG, &value)))
2541 #if defined(SOS_TARGET_X86) || defined(SOS_TARGET_ARM)
2542 reg = (DWORD_PTR) value.I32;
2543 #elif defined(SOS_TARGET_AMD64) || defined(SOS_TARGET_ARM64)
2544 reg = (DWORD_PTR) value.I64;
2546 #error Unsupported target
2548 #endif // FEATURE_PAL
2550 DumpStackObjectsOutput(regName, reg, verifyFields);
2553 void DumpStackObjectsHelper (
2558 ExtOut(g_targetMachine->GetDumpStackObjectsHeading());
2562 g_targetMachine->GetGCRegisters(®s, &cnt);
2564 for (size_t i = 0; i < cnt; ++i)
2565 DumpRegObjectHelper(regs[i], verifyFields);
2567 // Make certain StackTop is dword aligned:
2568 DumpStackObjectsInternal(StackTop & ~ALIGNCONST, StackBottom, verifyFields);
2571 void AddToModuleList(DWORD_PTR * &moduleList, int &numModule, int &maxList,
2572 DWORD_PTR dwModuleAddr)
2575 for (i = 0; i < numModule; i ++)
2577 if (moduleList[i] == dwModuleAddr)
2582 moduleList[numModule] = dwModuleAddr;
2584 if (numModule == maxList)
2587 if (!ClrSafeInt<int>::multiply(maxList, 2, listLength))
2589 ExtOut("<integer overflow>\n");
2594 DWORD_PTR *list = new DWORD_PTR [listLength];
2602 memcpy (list, moduleList, maxList * sizeof(PVOID));
2603 delete[] moduleList;
2610 BOOL IsFusionLoadedModule (LPCSTR fusionName, LPCSTR mName)
2612 // The fusion name will be in this format:
2613 // <module name>, Version=<version>, Culture=<culture>, PublicKeyToken=<token>
2614 // If fusionName up to the comma matches mName (case insensitive),
2615 // we consider that a match was found.
2616 LPCSTR commaPos = strchr (fusionName, ',');
2619 // verify that fusionName and mName match up to a comma.
2620 while (*fusionName != ',')
2628 if (tolower(*fusionName) != tolower(*mName))
2630 if (*fusionName != *mName)
2643 BOOL DebuggerModuleNamesMatch (CLRDATA_ADDRESS PEFileAddr, ___in __in_z LPSTR mName)
2645 // Another way to see if a module is the same is
2646 // to accept that mName may be the debugger's name for
2647 // a loaded module. We can get the debugger's name for
2648 // the module we are looking at right now, and compare
2649 // it with mName, if they match exactly, we can add
2650 // the module to the list.
2653 CLRDATA_ADDRESS pebase = 0;
2654 if (g_sos->GetPEFileBase(PEFileAddr, &pebase) == S_OK)
2660 if (g_ExtSymbols->GetModuleByOffset(pebase, 0, &Index, &base) == S_OK)
2662 CHAR ModuleName[MAX_LONGPATH+1];
2664 if (g_ExtSymbols->GetModuleNames(Index, base, NULL, 0, NULL, ModuleName,
2665 MAX_LONGPATH, NULL, NULL, 0, NULL) == S_OK)
2667 if (_stricmp (ModuleName, mName) == 0)
2679 DWORD_PTR *ModuleFromName(__in_opt LPSTR mName, int *numModule)
2681 if (numModule == NULL)
2684 DWORD_PTR *moduleList = NULL;
2687 DacpAppDomainStoreData adsData;
2688 if (adsData.Request(g_sos)!=S_OK)
2691 ArrayHolder<CLRDATA_ADDRESS> pAssemblyArray = NULL;
2692 ArrayHolder<CLRDATA_ADDRESS> pModules = NULL;
2693 int arrayLength = 0;
2694 if (!ClrSafeInt<int>::addition(adsData.DomainCount, 2, arrayLength))
2696 ExtOut("<integer overflow>\n");
2699 ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[arrayLength];
2707 pArray[0] = adsData.systemDomain;
2708 pArray[1] = adsData.sharedDomain;
2709 if (g_sos->GetAppDomainList(adsData.DomainCount, pArray.GetPtr()+2, NULL)!=S_OK)
2711 ExtOut("Unable to get array of AppDomains\n");
2717 int maxList = arrayLength; // account for system and shared domains
2718 if (maxList <= 0 || !ClrSafeInt<size_t>::multiply(maxList, sizeof(PVOID), AllocSize))
2720 ExtOut("Integer overflow error.\n");
2724 moduleList = new DWORD_PTR[maxList];
2725 if (moduleList == NULL)
2731 WCHAR StringData[MAX_LONGPATH];
2732 char fileName[sizeof(StringData)/2];
2734 // Search all domains to find a module
2735 for (int n = 0; n < adsData.DomainCount+2; n++)
2739 ExtOut("<interrupted>\n");
2743 DacpAppDomainData appDomain;
2744 if (FAILED(appDomain.Request(g_sos,pArray[n])))
2746 // Don't print a failure message here, there is a very normal case when checking
2747 // for modules after clr is loaded but before any AppDomains or assemblies are created
2753 // >!bpmd Foo.dll Foo.Bar
2755 // we will correctly give the answer that whatever module you were looking for, it isn't loaded yet
2759 if (appDomain.AssemblyCount)
2761 pAssemblyArray = new CLRDATA_ADDRESS[appDomain.AssemblyCount];
2762 if (pAssemblyArray==NULL)
2768 if (FAILED(g_sos->GetAssemblyList(appDomain.AppDomainPtr, appDomain.AssemblyCount, pAssemblyArray, NULL)))
2770 ExtOut("Unable to get array of Assemblies for the given AppDomain..\n");
2774 for (int nAssem = 0; nAssem < appDomain.AssemblyCount; nAssem ++)
2778 ExtOut("<interrupted>\n");
2782 DacpAssemblyData assemblyData;
2783 if (FAILED(assemblyData.Request(g_sos, pAssemblyArray[nAssem])))
2785 ExtOut("Failed to request assembly.\n");
2789 pModules = new CLRDATA_ADDRESS[assemblyData.ModuleCount];
2790 if (FAILED(g_sos->GetAssemblyModuleList(assemblyData.AssemblyPtr, assemblyData.ModuleCount, pModules, NULL)))
2792 ExtOut("Failed to get the modules for the given assembly.\n");
2796 for (UINT nModule = 0; nModule < assemblyData.ModuleCount; nModule++)
2800 ExtOut("<interrupted>\n");
2804 CLRDATA_ADDRESS ModuleAddr = pModules[nModule];
2805 DacpModuleData ModuleData;
2806 if (FAILED(ModuleData.Request(g_sos,ModuleAddr)))
2808 ExtOut("Failed to request Module data from assembly.\n");
2812 FileNameForModule ((DWORD_PTR)ModuleAddr, StringData);
2814 for (m = 0; StringData[m] != L'\0'; m++)
2816 fileName[m] = (char)StringData[m];
2820 if ((mName == NULL) ||
2821 IsSameModuleName(fileName, mName) ||
2822 DebuggerModuleNamesMatch(ModuleData.File, mName) ||
2823 IsFusionLoadedModule(fileName, mName))
2825 AddToModuleList(moduleList, *numModule, maxList, (DWORD_PTR)ModuleAddr);
2831 pAssemblyArray = NULL;
2837 // We do not want to return a half-constructed list. Instead, we return NULL on a failure.
2839 delete [] moduleList;
2843 /**********************************************************************\
2844 * Routine Description: *
2846 * Find the EE data given a name. *
2848 \**********************************************************************/
2849 void GetInfoFromName(DWORD_PTR ModulePtr, const char* name)
2851 ToRelease<IMetaDataImport> pImport = MDImportForModule (ModulePtr);
2855 static WCHAR wszName[MAX_CLASSNAME_LENGTH];
2857 size_t length = strlen (name);
2858 for (n = 0; n <= length; n ++)
2859 wszName[n] = name[n];
2861 // First enumerate methods. We're taking advantage of the DAC's
2862 // CLRDataModule::EnumMethodDefinitionByName which can parse
2863 // method names (whether in nested classes, or explicit interface
2864 // method implementations).
2865 ToRelease<IXCLRDataModule> ModuleDefinition;
2866 if (g_sos->GetModule(ModulePtr, &ModuleDefinition) == S_OK)
2869 if (ModuleDefinition->StartEnumMethodDefinitionsByName(wszName, 0, &h) == S_OK)
2871 IXCLRDataMethodDefinition *pMeth = NULL;
2872 BOOL fStatus = FALSE;
2873 while (ModuleDefinition->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
2876 ExtOut("-----------------------\n");
2879 if (pMeth->GetTokenAndScope(&token, NULL) == S_OK)
2881 GetInfoFromModule(ModulePtr, token);
2886 ModuleDefinition->EndEnumMethodDefinitionsByName(h);
2892 // Now look for types, type members and fields
2894 mdToken tkEnclose = mdTokenNil;
2896 WCHAR *pHead = wszName;
2897 while ( ((pName = _wcschr (pHead,L'+')) != NULL) ||
2898 ((pName = _wcschr (pHead,L'/')) != NULL)) {
2900 if (FAILED(pImport->FindTypeDefByName(pHead,tkEnclose,&tkEnclose)))
2907 // @todo: Handle Nested classes correctly.
2908 if (SUCCEEDED (pImport->FindTypeDefByName (pName, tkEnclose, &cl)))
2910 GetInfoFromModule(ModulePtr, cl);
2914 // See if it is a method
2916 if ((pwzMethod = _wcsrchr(pName, L'.')) == NULL)
2919 if (pwzMethod[-1] == L'.')
2921 pwzMethod[0] = L'\0';
2924 // @todo: Handle Nested classes correctly.
2925 if (SUCCEEDED(pImport->FindTypeDefByName (pName, tkEnclose, &cl)))
2929 HCORENUM henum = NULL;
2933 if (SUCCEEDED (pImport->EnumMembersWithName (&henum, cl, pwzMethod,
2934 &token, 1, &cTokens))
2937 ExtOut("Member (mdToken token) of\n");
2938 GetInfoFromModule(ModulePtr, cl);
2944 if (SUCCEEDED (pImport->EnumFieldsWithName (&henum, cl, pwzMethod,
2945 &token, 1, &cTokens))
2948 ExtOut("Field (mdToken token) of\n");
2949 GetInfoFromModule(ModulePtr, cl);
2955 /**********************************************************************\
2956 * Routine Description: *
2958 * Find the EE data given a token. *
2960 \**********************************************************************/
2961 DWORD_PTR GetMethodDescFromModule(DWORD_PTR ModuleAddr, ULONG token)
2963 if (TypeFromToken(token) != mdtMethodDef)
2966 CLRDATA_ADDRESS md = 0;
2967 if (FAILED(g_sos->GetMethodDescFromToken(ModuleAddr, token, &md)))
2973 // a NULL ReturnValue means the method desc is not loaded yet
2974 return MD_NOT_YET_LOADED;
2976 else if ( !IsMethodDesc((DWORD_PTR)md))
2981 return (DWORD_PTR)md;
2984 /**********************************************************************\
2985 * Routine Description: *
2987 * Find the MethodDefinitions given a name. *
2989 \**********************************************************************/
2990 HRESULT GetMethodDefinitionsFromName(TADDR ModulePtr, IXCLRDataModule* mod, const char *name, IXCLRDataMethodDefinition **ppOut, int numMethods, int *numMethodsNeeded)
2996 size_t length = strlen (name);
2997 for (n = 0; n <= length; n ++)
2998 g_mdName[n] = name[n];
3001 int methodCount = 0;
3002 if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
3004 IXCLRDataMethodDefinition *pMeth = NULL;
3005 while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
3010 mod->EndEnumMethodDefinitionsByName(h);
3013 if(numMethodsNeeded != NULL)
3014 *numMethodsNeeded = methodCount;
3017 if(numMethods > methodCount)
3018 numMethods = methodCount;
3020 if (methodCount > 0)
3022 if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
3024 IXCLRDataMethodDefinition *pMeth = NULL;
3025 for (int i = 0; i < numMethods && mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK; i++)
3029 mod->EndEnumMethodDefinitionsByName(h);
3036 /**********************************************************************\
3037 * Routine Description: *
3039 * Find the EE data given a name. *
3041 \**********************************************************************/
3042 HRESULT GetMethodDescsFromName(TADDR ModulePtr, IXCLRDataModule* mod, const char *name, DWORD_PTR **pOut,int *numMethods)
3044 if (name == NULL || pOut == NULL || numMethods == NULL)
3051 size_t length = strlen (name);
3052 for (n = 0; n <= length; n ++)
3053 g_mdName[n] = name[n];
3056 int methodCount = 0;
3057 if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
3059 IXCLRDataMethodDefinition *pMeth = NULL;
3060 while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
3065 mod->EndEnumMethodDefinitionsByName(h);
3068 if (methodCount > 0)
3070 *pOut = new TADDR[methodCount];
3074 return E_OUTOFMEMORY;
3077 *numMethods = methodCount;
3079 if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
3082 IXCLRDataMethodDefinition *pMeth = NULL;
3083 while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
3086 if (pMeth->GetTokenAndScope(&token, NULL) != S_OK)
3088 (*pOut)[i] = GetMethodDescFromModule(ModulePtr, token);
3089 if ((*pOut)[i] == NULL)
3097 mod->EndEnumMethodDefinitionsByName(h);
3104 /**********************************************************************\
3105 * Routine Description: *
3107 * Find the EE data given a token. *
3109 \**********************************************************************/
3110 void GetInfoFromModule (DWORD_PTR ModuleAddr, ULONG token, DWORD_PTR *ret)
3112 switch (TypeFromToken(token))
3123 ExtOut("This token type is not supported\n");
3128 CLRDATA_ADDRESS md = 0;
3129 if (FAILED(g_sos->GetMethodDescFromToken(ModuleAddr, token, &md)) || !IsValidToken (ModuleAddr, token))
3131 ExtOut("<invalid module token>\n");
3137 *ret = (DWORD_PTR)md;
3141 ExtOut("Token: %p\n", SOS_PTR(token));
3143 switch (TypeFromToken(token))
3147 NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
3148 ExtOut("Field name: %S\n", g_mdName);
3155 DMLOut("MethodDesc: %s\n", DMLMethodDesc(md));
3157 // Easiest to get full parameterized method name from ..::GetMethodName
3158 if (g_sos->GetMethodDescName(md, mdNameLen, g_mdName, NULL) != S_OK)
3160 // Fall back to just method name without parameters..
3161 NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
3166 ExtOut("MethodDesc: <not loaded yet>\n");
3167 NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
3170 ExtOut("Name: %S\n", g_mdName);
3171 // Nice to have a little more data
3174 DacpMethodDescData MethodDescData;
3175 if (MethodDescData.Request(g_sos, md) == S_OK)
3177 if (MethodDescData.bHasNativeCode)
3179 DMLOut("JITTED Code Address: %s\n", DMLIP(MethodDescData.NativeCodeAddr));
3185 DMLOut("Not JITTED yet. Use <exec cmd=\"!bpmd -md %p\">!bpmd -md %p</exec> to break on run.\n",
3186 SOS_PTR(md), SOS_PTR(md));
3188 ExtOut("Not JITTED yet. Use !bpmd -md %p to break on run.\n", SOS_PTR(md));
3190 ExtOut("Not JITTED yet. Use 'bpmd -md %p' to break on run.\n", SOS_PTR(md));
3196 ExtOut ("<Error getting MethodDesc information>\n");
3201 ExtOut("Not JITTED yet.\n");
3210 DMLOut("MethodTable: %s\n", DMLMethodTable(md));
3211 DacpMethodTableData mtabledata;
3212 if (mtabledata.Request(g_sos, md) == S_OK)
3214 DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
3218 ExtOut("EEClass: <error getting EEClass>\n");
3223 ExtOut("MethodTable: <not loaded yet>\n");
3224 ExtOut("EEClass: <not loaded yet>\n");
3226 NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
3227 ExtOut("Name: %S\n", g_mdName);
3236 BOOL IsMTForFreeObj(DWORD_PTR pMT)
3238 return (pMT == g_special_usefulGlobals.FreeMethodTable);
3241 const char *EHTypeName(EHClauseType et)
3245 else if (et == EHFinally)
3247 else if (et == EHFilter)
3249 else if (et == EHTyped)
3255 void DumpTieredNativeCodeAddressInfo(struct DacpTieredVersionData * pTieredVersionData, const UINT cTieredVersionData)
3257 ExtOut("Code Version History:\n");
3259 for(int i = cTieredVersionData - 1; i >= 0; --i)
3261 const char *descriptor = NULL;
3262 switch(pTieredVersionData[i].TieredInfo)
3264 case DacpTieredVersionData::TIERED_UNKNOWN:
3266 _ASSERTE(!"Update SOS to understand the new tier");
3267 descriptor = "Unknown Tier";
3269 case DacpTieredVersionData::NON_TIERED:
3270 descriptor = "Non-Tiered";
3272 case DacpTieredVersionData::TIERED_0:
3273 descriptor = "Tier 0";
3275 case DacpTieredVersionData::TIERED_1:
3276 descriptor = "Tier 1";
3280 DMLOut(" CodeAddr: %s (%s)\n", DMLIP(pTieredVersionData[i].NativeCodeAddr), descriptor);
3281 ExtOut(" NativeCodeVersion: %p\n", SOS_PTR(pTieredVersionData[i].NativeCodeVersionNodePtr));
3285 void DumpRejitData(CLRDATA_ADDRESS pMethodDesc, DacpReJitData * pReJitData)
3287 ExtOut(" ReJITID %p: ", SOS_PTR(pReJitData->rejitID));
3289 struct DacpTieredVersionData codeAddrs[kcMaxTieredVersions];
3292 ReleaseHolder<ISOSDacInterface5> sos5;
3293 if (SUCCEEDED(g_sos->QueryInterface(__uuidof(ISOSDacInterface5), &sos5)) &&
3294 SUCCEEDED(sos5->GetTieredVersions(pMethodDesc,
3295 (int)pReJitData->rejitID,
3297 kcMaxTieredVersions,
3300 DumpTieredNativeCodeAddressInfo(codeAddrs, cCodeAddrs);
3304 switch (pReJitData->flags)
3307 case DacpReJitData::kUnknown:
3311 case DacpReJitData::kRequested:
3312 szFlags = " (READY to jit on next call)";
3315 case DacpReJitData::kActive:
3316 szFlags = " (CURRENT)";
3319 case DacpReJitData::kReverted:
3320 szFlags = " (reverted)";
3324 ExtOut("%s\n", szFlags);
3327 // For !ip2md requests, this function helps us ensure that rejitted version corresponding
3328 // to the specified IP always gets dumped. It may have already been dumped if it was the
3329 // current rejit version (which is always dumped) or one of the reverted versions that we
3330 // happened to dump before we clipped their number down to kcRejitDataRevertedMax.
3331 BOOL ShouldDumpRejitDataRequested(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData)
3333 if (pMethodDescData->rejitDataRequested.rejitID == 0)
3336 if (pMethodDescData->rejitDataRequested.rejitID == pMethodDescData->rejitDataCurrent.rejitID)
3339 for (ULONG i=0; i < cRevertedRejitData; i++)
3341 if (pMethodDescData->rejitDataRequested.rejitID == pRevertedRejitData[i].rejitID)
3349 void DumpAllRejitDataIfNecessary(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData)
3351 // If there's no rejit info to output, then skip
3352 if ((pMethodDescData->rejitDataCurrent.rejitID == 0) &&
3353 (pMethodDescData->rejitDataRequested.rejitID == 0) &&
3354 (cRevertedRejitData == 0))
3358 ExtOut("ReJITed versions:\n");
3360 // Dump CURRENT rejit info
3361 DumpRejitData(pMethodDescData->MethodDescPtr, &pMethodDescData->rejitDataCurrent);
3363 // Dump reverted rejit infos
3364 for (ULONG i=0; i < cRevertedRejitData; i++)
3366 DumpRejitData(pMethodDescData->MethodDescPtr, &pRevertedRejitData[i]);
3369 // For !ip2md, ensure we dump the rejit version corresponding to the specified IP
3370 // (if not already dumped)
3371 if (ShouldDumpRejitDataRequested(pMethodDescData, pRevertedRejitData, cRevertedRejitData))
3372 DumpRejitData(pMethodDescData->MethodDescPtr, &pMethodDescData->rejitDataRequested);
3374 // If we maxed out the reverted versions we dumped, let user know there may be more
3375 if (cRevertedRejitData == kcMaxRevertedRejitData)
3376 ExtOut(" (... possibly more reverted versions ...)\n");
3379 void DumpMDInfoFromMethodDescData(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData, BOOL fStackTraceFormat)
3381 static WCHAR wszNameBuffer[1024]; // should be large enough
3382 BOOL bFailed = FALSE;
3383 if (g_sos->GetMethodDescName(pMethodDescData->MethodDescPtr, 1024, wszNameBuffer, NULL) != S_OK)
3385 wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN"));
3389 if (!fStackTraceFormat)
3391 ExtOut("Method Name: %S\n", wszNameBuffer);
3393 DacpMethodTableData mtdata;
3394 if (SUCCEEDED(mtdata.Request(g_sos, pMethodDescData->MethodTablePtr)))
3396 DMLOut("Class: %s\n", DMLClass(mtdata.Class));
3399 DMLOut("MethodTable: %s\n", DMLMethodTable(pMethodDescData->MethodTablePtr));
3400 ExtOut("mdToken: %p\n", SOS_PTR(pMethodDescData->MDToken));
3401 DMLOut("Module: %s\n", DMLModule(pMethodDescData->ModulePtr));
3402 ExtOut("IsJitted: %s\n", pMethodDescData->bHasNativeCode ? "yes" : "no");
3404 DMLOut("Current CodeAddr: %s\n", DMLIP(pMethodDescData->NativeCodeAddr));
3406 struct DacpTieredVersionData codeAddrs[kcMaxTieredVersions];
3409 ReleaseHolder<ISOSDacInterface5> sos5;
3410 if (SUCCEEDED(g_sos->QueryInterface(__uuidof(ISOSDacInterface5), &sos5)) &&
3411 SUCCEEDED(sos5->GetTieredVersions(pMethodDescData->MethodDescPtr,
3412 (int)pMethodDescData->rejitDataCurrent.rejitID,
3414 kcMaxTieredVersions,
3417 DumpTieredNativeCodeAddressInfo(codeAddrs, cCodeAddrs);
3420 DacpMethodDescTransparencyData transparency;
3421 if (SUCCEEDED(transparency.Request(g_sos, pMethodDescData->MethodDescPtr)))
3423 ExtOut("Transparency: %s\n", GetTransparency(transparency));
3426 DumpAllRejitDataIfNecessary(pMethodDescData, pRevertedRejitData, cRevertedRejitData);
3432 ExtOut("%S", wszNameBuffer);
3436 // Only clutter the display with module/token for cases where we
3437 // can't get the MethodDesc name for some reason.
3438 DMLOut("Unknown MethodDesc (Module %s, mdToken %08x)",
3439 DMLModule(pMethodDescData->ModulePtr),
3440 pMethodDescData->MDToken);
3445 void DumpMDInfo(DWORD_PTR dwMethodDescAddr, CLRDATA_ADDRESS dwRequestedIP /* = 0 */, BOOL fStackTraceFormat /* = FALSE */)
3447 DacpMethodDescData MethodDescData;
3448 DacpReJitData revertedRejitData[kcMaxRevertedRejitData];
3449 ULONG cNeededRevertedRejitData;
3450 if (g_sos->GetMethodDescData(
3451 TO_CDADDR(dwMethodDescAddr),
3454 _countof(revertedRejitData),
3456 &cNeededRevertedRejitData) != S_OK)
3458 ExtOut("%p is not a MethodDesc\n", SOS_PTR(dwMethodDescAddr));
3462 DumpMDInfoFromMethodDescData(&MethodDescData, revertedRejitData, cNeededRevertedRejitData, fStackTraceFormat);
3465 void GetDomainList (DWORD_PTR *&domainList, int &numDomain)
3467 DacpAppDomainStoreData adsData;
3471 if (adsData.Request(g_sos)!=S_OK)
3476 // Do prefast integer checks before the malloc.
3478 LONG DomainAllocCount;
3479 if (!ClrSafeInt<LONG>::addition(adsData.DomainCount, 2, DomainAllocCount) ||
3480 !ClrSafeInt<size_t>::multiply(DomainAllocCount, sizeof(PVOID), AllocSize) ||
3481 (domainList = new DWORD_PTR[DomainAllocCount]) == NULL)
3486 domainList[numDomain++] = (DWORD_PTR) adsData.systemDomain;
3487 domainList[numDomain++] = (DWORD_PTR) adsData.sharedDomain;
3489 CLRDATA_ADDRESS *pArray = new CLRDATA_ADDRESS[adsData.DomainCount];
3495 if (g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL)!=S_OK)
3501 for (int n=0;n<adsData.DomainCount;n++)
3505 domainList[numDomain++] = (DWORD_PTR) pArray[n];
3512 HRESULT GetThreadList(DWORD_PTR **threadList, int *numThread)
3514 _ASSERTE(threadList != NULL);
3515 _ASSERTE(numThread != NULL);
3517 if (threadList == NULL || numThread == NULL)
3524 DacpThreadStoreData ThreadStore;
3525 if ( ThreadStore.Request(g_sos) != S_OK)
3527 ExtOut("Failed to request threads from the thread store.");
3531 *threadList = new DWORD_PTR[ThreadStore.threadCount];
3532 if (*threadList == NULL)
3535 return E_OUTOFMEMORY;
3538 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
3539 while (CurThread != NULL)
3544 DacpThreadData Thread;
3545 if (Thread.Request(g_sos, CurThread) != S_OK)
3547 ExtOut("Failed to request Thread at %p\n", SOS_PTR(CurThread));
3551 (*threadList)[(*numThread)++] = (DWORD_PTR)CurThread;
3552 CurThread = Thread.nextThread;
3558 CLRDATA_ADDRESS GetCurrentManagedThread ()
3560 DacpThreadStoreData ThreadStore;
3561 ThreadStore.Request(g_sos);
3564 g_ExtSystem->GetCurrentThreadSystemId(&Tid);
3566 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
3569 DacpThreadData Thread;
3570 if (Thread.Request(g_sos, CurThread) != S_OK)
3575 if (Thread.osThreadId == Tid)
3580 CurThread = Thread.nextThread;
3586 void ReloadSymbolWithLineInfo()
3589 static BOOL bLoadSymbol = FALSE;
3593 g_ExtSymbols->GetSymbolOptions(&Options);
3594 if (!(Options & SYMOPT_LOAD_LINES))
3596 g_ExtSymbols->AddSymbolOptions(SYMOPT_LOAD_LINES);
3598 if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName(MSCOREE_SHIM_A, 0, NULL, NULL)))
3599 g_ExtSymbols->Reload("/f " MSCOREE_SHIM_A);
3601 EEFLAVOR flavor = GetEEFlavor();
3602 if (flavor == MSCORWKS)
3603 g_ExtSymbols->Reload("/f " MAIN_CLR_DLL_NAME_A);
3606 // reload mscoree.pdb and clrjit.pdb to get line info
3612 // Return 1 if the function is our stub
3613 // Return MethodDesc if the function is managed
3614 // Otherwise return 0
3615 size_t FunctionType (size_t EIP)
3618 ULONG ulLoaded, ulUnloaded, ulIndex;
3620 // Get the number of loaded and unloaded modules
3621 if (FAILED(g_ExtSymbols->GetNumberModules(&ulLoaded, &ulUnloaded)))
3625 if (SUCCEEDED(g_ExtSymbols->GetModuleByOffset(TO_CDADDR(EIP), 0, &ulIndex, &base)) && base != 0)
3627 if (ulIndex < ulLoaded)
3629 IMAGE_DOS_HEADER DosHeader;
3630 if (g_ExtData->ReadVirtual(TO_CDADDR(base), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
3632 IMAGE_NT_HEADERS Header;
3633 if (g_ExtData->ReadVirtual(TO_CDADDR(base + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK)
3635 // If there is no COMHeader, this can not be managed code.
3636 if (Header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].VirtualAddress == 0)
3639 IMAGE_COR20_HEADER ComPlusHeader;
3640 if (g_ExtData->ReadVirtual(TO_CDADDR(base + Header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].VirtualAddress),
3641 &ComPlusHeader, sizeof(ComPlusHeader), NULL) != S_OK)
3644 // If there is no Precompiled image info, it can not be prejit code
3645 if (ComPlusHeader.ManagedNativeHeader.VirtualAddress == 0) {
3651 CLRDATA_ADDRESS dwStartAddr = TO_CDADDR(EIP);
3652 CLRDATA_ADDRESS pMD;
3653 if (g_sos->GetMethodDescPtrFromIP(dwStartAddr, &pMD) != S_OK)
3658 return (size_t) pMD;
3664 // Gets version info for the CLR in the debuggee process.
3666 BOOL GetEEVersion(VS_FIXEDFILEINFO *pFileInfo)
3668 _ASSERTE(g_ExtSymbols2);
3669 _ASSERTE(pFileInfo);
3670 // Grab the version info directly from the module.
3671 return g_ExtSymbols2->GetModuleVersionInformation(DEBUG_ANY_ID,
3672 moduleInfo[GetEEFlavor()].baseAddr,
3673 "\\", pFileInfo, sizeof(VS_FIXEDFILEINFO), NULL) == S_OK;
3676 extern HMODULE g_hInstance;
3677 BOOL GetSOSVersion(VS_FIXEDFILEINFO *pFileInfo)
3679 _ASSERTE(pFileInfo);
3681 WCHAR wszFullPath[MAX_LONGPATH];
3682 DWORD cchFullPath = GetModuleFileNameW(g_hInstance, wszFullPath, _countof(wszFullPath));
3685 DWORD infoSize = GetFileVersionInfoSizeW(wszFullPath, &dwHandle);
3688 ArrayHolder<BYTE> pVersionInfo = new BYTE[infoSize];
3691 if (GetFileVersionInfoW(wszFullPath, NULL, infoSize, pVersionInfo))
3693 VS_FIXEDFILEINFO *pTmpFileInfo = NULL;
3695 if (VerQueryValue(pVersionInfo, "\\", (LPVOID *) &pTmpFileInfo, &uLen))
3697 *pFileInfo = *pTmpFileInfo; // Copy the info
3707 #endif // !FEATURE_PAL
3709 size_t ObjectSize(DWORD_PTR obj,BOOL fIsLargeObject)
3713 return ObjectSize(obj, dwMT, FALSE, fIsLargeObject);
3716 size_t ObjectSize(DWORD_PTR obj, DWORD_PTR mt, BOOL fIsValueClass, BOOL fIsLargeObject)
3718 BOOL bContainsPointers;
3720 if (!GetSizeEfficient(obj, mt, fIsLargeObject, size, bContainsPointers))
3727 // This takes an array of values and sets every non-printable character
3729 void Flatten(__out_ecount(len) char *data, unsigned int len)
3731 for (unsigned int i = 0; i < len; ++i)
3732 if (data[i] < 32 || data[i] > 126)
3737 void CharArrayContent(TADDR pos, ULONG num, bool widechar)
3739 if (!pos || num <= 0)
3744 ArrayHolder<WCHAR> data = new WCHAR[num+1];
3752 if (!SafeReadMemory(pos, data, num<<1, &readLen))
3755 Flatten(data.GetPtr(), readLen >> 1);
3756 ExtOut("%S", data.GetPtr());
3760 ArrayHolder<char> data = new char[num+1];
3768 if (!SafeReadMemory(pos, data, num, &readLen))
3771 _ASSERTE(readLen <= num);
3772 Flatten(data, readLen);
3774 ExtOut("%s", data.GetPtr());
3778 void StringObjectContent(size_t obj, BOOL fLiteral, const int length)
3780 DacpObjectData objData;
3781 if (objData.Request(g_sos, TO_CDADDR(obj))!=S_OK)
3783 ExtOut("<Invalid Object>");
3789 if (MOVE(stInfo,obj) != S_OK)
3791 ExtOut ("Error getting string data\n");
3795 if (objData.Size > 0x200000 ||
3796 stInfo.m_StringLength > 0x200000)
3798 ExtOut ("<String is invalid or too large to print>\n");
3802 ArrayHolder<WCHAR> pwszBuf = new WCHAR[stInfo.m_StringLength+1];
3803 if (pwszBuf == NULL)
3808 DWORD_PTR dwAddr = (DWORD_PTR)pwszBuf.GetPtr();
3809 if (g_sos->GetObjectStringData(TO_CDADDR(obj), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK)
3811 ExtOut("Error getting string data\n");
3817 pwszBuf[stInfo.m_StringLength] = L'\0';
3818 ExtOut ("%S", pwszBuf.GetPtr());
3822 ULONG32 count = stInfo.m_StringLength;
3832 wcsncpy_s(buffer,_countof(buffer),(LPWSTR) dwAddr, toRead);
3833 bytesRead = toRead*sizeof(WCHAR);
3834 DWORD wcharsRead = bytesRead/2;
3835 buffer[wcharsRead] = L'\0';
3838 for (j = 0; j < wcharsRead; j ++)
3840 if (_iswprint (buffer[j])) {
3847 switch (buffer[j]) {
3887 count -= wcharsRead;
3888 dwAddr += bytesRead;
3893 #ifdef _TARGET_WIN64_
3897 __int64 str64hex(const char *ptr)
3900 unsigned char nCount = 0;
3905 // Ignore leading 0x if present
3906 if (*ptr=='0' && toupper(*(ptr+1))=='X') {
3914 if (isdigit(*ptr)) {
3916 } else if (isalpha(*ptr)) {
3917 digit = (((char)toupper(*ptr)) - 'A') + 10;
3926 return _UI64_MAX; // would be an overflow
3939 #endif // _TARGET_WIN64_
3941 BOOL GetValueForCMD (const char *ptr, const char *end, ARGTYPE type, size_t *value)
3943 if (type == COSTRING) {
3944 // Allocate memory for the length of the string. Whitespace terminates
3945 // User must free the string data.
3946 char *pszValue = NULL;
3947 size_t dwSize = (end - ptr);
3948 pszValue= new char[dwSize+1];
3949 if (pszValue == NULL)
3953 strncpy_s(pszValue,dwSize+1,ptr,dwSize); // _TRUNCATE
3954 *value = (size_t) pszValue;
3957 if (type == COHEX) {
3958 #ifdef _TARGET_WIN64_
3959 *value = str64hex(ptr);
3961 *value = strtoul(ptr,&last,16);
3965 #ifdef _TARGET_WIN64_
3966 *value = _atoi64(ptr);
3968 *value = strtoul(ptr,&last,10);
3972 #ifdef _TARGET_WIN64_
3973 last = (char *) ptr;
3974 // Ignore leading 0x if present
3975 if (*last=='0' && toupper(*(last+1))=='X') {
3979 while (isdigit(*last) || (toupper(*last)>='A' && toupper(*last)<='F')) {
3992 void SetValueForCMD (void *vptr, ARGTYPE type, size_t value)
3996 *(BOOL*)vptr = (BOOL) value;
4001 *(SIZE_T*)vptr = value;
4006 BOOL GetCMDOption(const char *string, CMDOption *option, size_t nOption,
4007 CMDValue *arg, size_t maxArg, size_t *nArg)
4010 const char *ptr = string;
4011 BOOL endofOption = FALSE;
4013 for (size_t n = 0; n < nOption; n ++)
4018 option[n].hasSeen = FALSE;
4025 while (ptr[0] != '\0')
4031 if (isspace (ptr[0])) {
4032 while (isspace (ptr[0]))
4045 // Arguments can be quoted with ". We'll remove the quotes and
4046 // allow spaces to exist in the string.
4047 BOOL bQuotedArg = FALSE;
4048 if (ptr[0] == '\'' && ptr[1] != '-')
4056 while (end[0] != '\'' && end[0] != '\0')
4065 // Error, th ere was a start quote but no end quote
4066 ExtOut ("Missing quote in %s\n", ptr);
4070 else // whitespace terminates
4072 while (!isspace(end[0]) && end[0] != '\0')
4082 if (ptr[0] != '-' && ptr[0] != '/') {
4084 if (ptr[0] != '-') {
4087 ExtOut ("Incorrect argument: %s\n", ptr);
4091 if (*nArg >= maxArg) {
4092 ExtOut ("Incorrect argument: %s\n", ptr);
4097 if (!GetValueForCMD (ptr,end,arg[*nArg].type,&value)) {
4099 char oldChar = *end;
4100 *(char *)end = '\0';
4101 value = (size_t)GetExpression (ptr);
4102 *(char *)end = oldChar;
4106 It is silly to do this, what if 0 is a valid expression for
4110 ExtOut ("Invalid argument: %s\n", ptr);
4116 SetValueForCMD (arg[*nArg].vptr, arg[*nArg].type, value);
4120 else if (endofOption) {
4121 ExtOut ("Wrong option: %s\n", ptr);
4127 ExtOut ("Invalid option %s\n", ptr);
4130 strncpy_s (buffer,_countof(buffer), ptr, end-ptr);
4133 for (n = 0; n < nOption; n ++)
4138 if (_stricmp (buffer, option[n].name) == 0) {
4139 if (option[n].hasSeen) {
4140 ExtOut ("Invalid option: option specified multiple times: %s\n", buffer);
4143 option[n].hasSeen = TRUE;
4144 if (option[n].hasValue) {
4147 if (isspace (ptr[0])) {
4148 while (isspace (ptr[0]))
4156 if (ptr[0] == '\0') {
4157 ExtOut ("Missing value for option %s\n", buffer);
4161 while (!isspace(end[0]) && end[0] != '\0')
4170 if (!GetValueForCMD (ptr,end,option[n].type,&value)) {
4172 char oldChar = *end;
4173 *(char *)end = '\0';
4174 value = (size_t)GetExpression (ptr);
4175 *(char *)end = oldChar;
4178 SetValueForCMD (option[n].vptr,option[n].type,value);
4181 SetValueForCMD (option[n].vptr,option[n].type,TRUE);
4187 ExtOut ("Unknown option: %s\n", buffer);
4201 ReadVirtualCache g_special_rvCacheSpace;
4202 ReadVirtualCache *rvCache = &g_special_rvCacheSpace;
4204 void ResetGlobals(void)
4206 // There are some globals used in SOS that exist for efficiency in one command,
4207 // but should be reset because the next execution of an SOS command could be on
4208 // another managed process. Reset them to a default state here, as this command
4209 // is called on every SOS entry point.
4210 g_sos->GetUsefulGlobals(&g_special_usefulGlobals);
4211 g_special_mtCache.Clear();
4212 g_special_rvCacheSpace.Clear();
4213 Output::ResetIndent();
4216 //---------------------------------------------------------------------------------------
4218 // Loads private DAC interface, and points g_clrData to it.
4221 // HRESULT indicating success or failure
4223 HRESULT LoadClrDebugDll(void)
4227 static IXCLRDataProcess* s_clrDataProcess = NULL;
4228 if (s_clrDataProcess == NULL)
4230 int err = PAL_InitializeDLL();
4233 return CORDBG_E_UNSUPPORTED;
4235 char dacModulePath[MAX_LONGPATH];
4236 strcpy_s(dacModulePath, _countof(dacModulePath), g_ExtServices->GetCoreClrDirectory());
4237 strcat_s(dacModulePath, _countof(dacModulePath), MAKEDLLNAME_A("mscordaccore"));
4239 HMODULE hdac = LoadLibraryA(dacModulePath);
4242 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
4244 PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance");
4245 if (pfnCLRDataCreateInstance == NULL)
4248 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
4250 ICLRDataTarget *target = new DataTarget();
4251 hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&s_clrDataProcess);
4254 s_clrDataProcess = NULL;
4258 s_clrDataProcess->GetOtherNotificationFlags(&flags);
4259 flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD | CLRDATA_NOTIFY_ON_EXCEPTION);
4260 s_clrDataProcess->SetOtherNotificationFlags(flags);
4262 g_clrData = s_clrDataProcess;
4263 g_clrData->AddRef();
4266 WDBGEXTS_CLR_DATA_INTERFACE Query;
4268 Query.Iid = &__uuidof(IXCLRDataProcess);
4269 if (!Ioctl(IG_GET_CLR_DATA_INTERFACE, &Query, sizeof(Query)))
4274 g_clrData = (IXCLRDataProcess*)Query.Iface;
4276 hr = g_clrData->QueryInterface(__uuidof(ISOSDacInterface), (void**)&g_sos);
4287 // This structure carries some input/output data to the FindFileInPathCallback below
4288 typedef struct _FindFileCallbackData
4293 } FindFileCallbackData;
4296 // A callback used by SymFindFileInPath - called once for each file that matches
4297 // the initial search criteria and allows the user to do arbitrary processing
4298 // This implementation checks that filesize and timestamp are correct, then
4299 // saves the loaded module handle
4301 // filename - the full path the file which was found
4302 // context - a user specified pointer to arbitrary data, in this case a FindFileCallbackData
4304 // TRUE if the search should continue (the file is no good)
4305 // FALSE if the search should stop (the file is good)
4307 FindFileInPathCallback(
4308 ___in PCWSTR filename,
4313 FindFileCallbackData* pCallbackData;
4314 pCallbackData = (FindFileCallbackData*)context;
4318 pCallbackData->hModule = LoadLibraryExW(
4321 LOAD_WITH_ALTERED_SEARCH_PATH); // Ensure we check the dir in wszFullPath first
4322 if (pCallbackData->hModule == NULL)
4324 hr = HRESULT_FROM_WIN32(GetLastError());
4325 ExtOut("Unable to load '%S'. HRESULT = 0x%x.\n", filename, hr);
4329 // Did we load the right one?
4330 MODULEINFO modInfo = {0};
4331 if (!GetModuleInformation(
4332 GetCurrentProcess(),
4333 pCallbackData->hModule,
4337 ExtOut("Failed to read module information for '%S'. HRESULT = 0x%x.\n", filename, HRESULT_FROM_WIN32(GetLastError()));
4338 FreeLibrary(pCallbackData->hModule);
4342 IMAGE_DOS_HEADER * pDOSHeader = (IMAGE_DOS_HEADER *) modInfo.lpBaseOfDll;
4343 IMAGE_NT_HEADERS * pNTHeaders = (IMAGE_NT_HEADERS *) (((LPBYTE) modInfo.lpBaseOfDll) + pDOSHeader->e_lfanew);
4344 DWORD dwSizeActual = pNTHeaders->OptionalHeader.SizeOfImage;
4345 DWORD dwTimeStampActual = pNTHeaders->FileHeader.TimeDateStamp;
4346 if ((dwSizeActual != pCallbackData->filesize) || (dwTimeStampActual != pCallbackData->timestamp))
4348 ExtOut("Found '%S', but it does not match the CLR being debugged.\n", filename);
4349 ExtOut("Size: Expected '0x%x', Actual '0x%x'\n", pCallbackData->filesize, dwSizeActual);
4350 ExtOut("Time stamp: Expected '0x%x', Actual '0x%x'\n", pCallbackData->timestamp, dwTimeStampActual);
4351 FreeLibrary(pCallbackData->hModule);
4355 ExtOut("Loaded %S\n", filename);
4359 #endif // FEATURE_PAL
4361 //---------------------------------------------------------------------------------------
4362 // Provides a way for the public CLR debugging interface to find the appropriate
4363 // mscordbi.dll, DAC, etc.
4364 class SOSLibraryProvider : public ICLRDebuggingLibraryProvider
4367 SOSLibraryProvider() : m_ref(0)
4371 virtual ~SOSLibraryProvider() {}
4373 virtual HRESULT STDMETHODCALLTYPE QueryInterface(
4377 if (InterfaceId == IID_IUnknown)
4379 *pInterface = static_cast<IUnknown *>(this);
4381 else if (InterfaceId == IID_ICLRDebuggingLibraryProvider)
4383 *pInterface = static_cast<ICLRDebuggingLibraryProvider *>(this);
4388 return E_NOINTERFACE;
4395 virtual ULONG STDMETHODCALLTYPE AddRef()
4397 return InterlockedIncrement(&m_ref);
4400 virtual ULONG STDMETHODCALLTYPE Release()
4402 LONG ref = InterlockedDecrement(&m_ref);
4412 // Called by the shim to locate and load mscordacwks and mscordbi
4414 // pwszFileName - the name of the file to load
4415 // dwTimestamp - the expected timestamp of the file
4416 // dwSizeOfImage - the expected SizeOfImage (a PE header data value)
4417 // phModule - a handle to loaded module
4420 // S_OK if the file was loaded, or any error if not
4421 virtual HRESULT STDMETHODCALLTYPE ProvideLibrary(
4422 const WCHAR * pwszFileName,
4424 DWORD dwSizeOfImage,
4429 FindFileCallbackData callbackData = {0};
4430 callbackData.timestamp = dwTimestamp;
4431 callbackData.filesize = dwSizeOfImage;
4433 if ((phModule == NULL) || (pwszFileName == NULL))
4435 return E_INVALIDARG;
4441 // we ensure that windbg loads DAC first so that we can be sure to use the same one
4442 return E_UNEXPECTED;
4444 if (FAILED(hr = g_sos->GetDacModuleHandle(&dacModule)))
4446 ExtOut("Failed to get the dac module handle. hr=0x%x.\n", hr);
4450 WCHAR dacPath[MAX_LONGPATH];
4451 DWORD len = GetModuleFileNameW(dacModule, dacPath, MAX_LONGPATH);
4452 if(len == 0 || len == MAX_LONGPATH)
4454 ExtOut("GetModuleFileName(dacModuleHandle) failed. Last error = 0x%x\n", GetLastError());
4458 // if we are looking for the DAC, just load the one windbg already found
4459 if(_wcsncmp(pwszFileName, W("mscordac"), _wcslen(W("mscordac")))==0)
4461 FindFileInPathCallback(dacPath, &callbackData);
4462 *phModule = callbackData.hModule;
4467 hr = g_ExtSystem->GetCurrentProcessHandle(&hProcess);
4470 ExtOut("IDebugSystemObjects::GetCurrentProcessHandle HRESULT=0x%x.\n", hr);
4474 ToRelease<IDebugSymbols3> spSym3(NULL);
4475 hr = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
4478 ExtOut("Unable to query IDebugSymbol3 HRESULT=0x%x.\n", hr);
4483 hr = spSym3->GetSymbolPathWide(NULL, 0, &pathSize);
4484 if(FAILED(hr)) //S_FALSE if the path doesn't fit, but if the path was size 0 perhaps we would get S_OK?
4486 ExtOut("Unable to get symbol path length. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", hr);
4490 ArrayHolder<WCHAR> symbolPath = new WCHAR[pathSize+MAX_LONGPATH+1];
4494 hr = spSym3->GetSymbolPathWide(symbolPath, pathSize, NULL);
4497 ExtOut("Unable to get symbol path. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", hr);
4501 WCHAR foundPath[MAX_LONGPATH];
4502 BOOL rc = SymFindFileInPathW((HANDLE)hProcess,
4505 (PVOID)(ULONG_PTR) dwTimestamp,
4510 (PFINDFILEINPATHCALLBACKW) &FindFileInPathCallback,
4511 (PVOID) &callbackData
4515 hr = HRESULT_FROM_WIN32(GetLastError());
4516 ExtOut("SymFindFileInPath failed for %S. HRESULT=0x%x.\nPlease ensure that %S is on your symbol path.", pwszFileName, hr, pwszFileName);
4519 *phModule = callbackData.hModule;
4522 WCHAR modulePath[MAX_LONGPATH];
4523 int length = MultiByteToWideChar(CP_ACP, 0, g_ExtServices->GetCoreClrDirectory(), -1, modulePath, _countof(modulePath));
4526 ExtOut("MultiByteToWideChar(coreclrDirectory) failed. Last error = 0x%x\n", GetLastError());
4529 wcscat_s(modulePath, _countof(modulePath), pwszFileName);
4531 *phModule = LoadLibraryW(modulePath);
4532 if (*phModule == NULL)
4534 HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
4535 ExtOut("Unable to load '%S'. HRESULT = 0x%x.\n", pwszFileName, hr);
4539 #endif // FEATURE_PAL
4546 //---------------------------------------------------------------------------------------
4547 // Data target for the debugged process. Provided to OpenVirtualProcess in order to
4548 // get an ICorDebugProcess back
4550 class SOSDataTarget : public ICorDebugMutableDataTarget
4552 , public ICorDebugDataTarget4
4556 SOSDataTarget() : m_ref(0)
4560 virtual ~SOSDataTarget() {}
4562 virtual HRESULT STDMETHODCALLTYPE QueryInterface(
4566 if (InterfaceId == IID_IUnknown)
4568 *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugDataTarget *>(this));
4570 else if (InterfaceId == IID_ICorDebugDataTarget)
4572 *pInterface = static_cast<ICorDebugDataTarget *>(this);
4574 else if (InterfaceId == IID_ICorDebugMutableDataTarget)
4576 *pInterface = static_cast<ICorDebugMutableDataTarget *>(this);
4579 else if (InterfaceId == IID_ICorDebugDataTarget4)
4581 *pInterface = static_cast<ICorDebugDataTarget4 *>(this);
4587 return E_NOINTERFACE;
4594 virtual ULONG STDMETHODCALLTYPE AddRef()
4596 return InterlockedIncrement(&m_ref);
4599 virtual ULONG STDMETHODCALLTYPE Release()
4601 LONG ref = InterlockedDecrement(&m_ref);
4610 // ICorDebugDataTarget.
4613 virtual HRESULT STDMETHODCALLTYPE GetPlatform(CorDebugPlatform * pPlatform)
4615 ULONG platformKind = g_targetMachine->GetPlatform();
4617 if(platformKind == IMAGE_FILE_MACHINE_I386)
4618 *pPlatform = CORDB_PLATFORM_POSIX_X86;
4619 else if(platformKind == IMAGE_FILE_MACHINE_AMD64)
4620 *pPlatform = CORDB_PLATFORM_POSIX_AMD64;
4621 else if(platformKind == IMAGE_FILE_MACHINE_ARMNT)
4622 *pPlatform = CORDB_PLATFORM_POSIX_ARM;
4626 if(platformKind == IMAGE_FILE_MACHINE_I386)
4627 *pPlatform = CORDB_PLATFORM_WINDOWS_X86;
4628 else if(platformKind == IMAGE_FILE_MACHINE_AMD64)
4629 *pPlatform = CORDB_PLATFORM_WINDOWS_AMD64;
4630 else if(platformKind == IMAGE_FILE_MACHINE_ARMNT)
4631 *pPlatform = CORDB_PLATFORM_WINDOWS_ARM;
4632 else if(platformKind == IMAGE_FILE_MACHINE_ARM64)
4633 *pPlatform = CORDB_PLATFORM_WINDOWS_ARM64;
4641 virtual HRESULT STDMETHODCALLTYPE ReadVirtual(
4642 CORDB_ADDRESS address,
4647 if (g_ExtData == NULL)
4649 return E_UNEXPECTED;
4651 return g_ExtData->ReadVirtual(address, pBuffer, request, (PULONG) pcbRead);
4654 virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
4656 ULONG32 contextFlags,
4657 ULONG32 contextSize,
4661 if (g_ExtSystem == NULL)
4663 return E_UNEXPECTED;
4665 return g_ExtSystem->GetThreadContextById(dwThreadOSID, contextFlags, contextSize, context);
4667 ULONG ulThreadIDOrig;
4668 ULONG ulThreadIDRequested;
4672 hr = g_ExtSystem->GetCurrentThreadId(&ulThreadIDOrig);
4678 hr = g_ExtSystem->GetThreadIdBySystemId(dwThreadOSID, &ulThreadIDRequested);
4684 hr = g_ExtSystem->SetCurrentThreadId(ulThreadIDRequested);
4690 // Prepare context structure
4691 ZeroMemory(context, contextSize);
4692 ((CONTEXT*) context)->ContextFlags = contextFlags;
4695 hrRet = g_ExtAdvanced3->GetThreadContext((LPVOID) context, contextSize);
4697 // This is cleanup; failure here doesn't mean GetThreadContext should fail
4698 // (that's determined by hrRet).
4699 g_ExtSystem->SetCurrentThreadId(ulThreadIDOrig);
4702 #endif // FEATURE_PAL
4706 // ICorDebugMutableDataTarget.
4708 virtual HRESULT STDMETHODCALLTYPE WriteVirtual(CORDB_ADDRESS address,
4709 const BYTE * pBuffer,
4710 ULONG32 bytesRequested)
4712 if (g_ExtData == NULL)
4714 return E_UNEXPECTED;
4716 return g_ExtData->WriteVirtual(address, (PVOID)pBuffer, bytesRequested, NULL);
4719 virtual HRESULT STDMETHODCALLTYPE SetThreadContext(DWORD dwThreadID,
4720 ULONG32 contextSize,
4721 const BYTE * pContext)
4726 virtual HRESULT STDMETHODCALLTYPE ContinueStatusChanged(DWORD dwThreadId,
4727 CORDB_CONTINUE_STATUS continueStatus)
4734 // ICorDebugDataTarget4
4736 virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context)
4738 if (g_ExtServices == NULL)
4740 return E_UNEXPECTED;
4742 return g_ExtServices->VirtualUnwind(threadId, contextSize, context);
4745 #endif // FEATURE_PAL
4751 HRESULT InitCorDebugInterfaceFromModule(ULONG64 ulBase, ICLRDebugging * pClrDebugging)
4755 ToRelease<ICorDebugMutableDataTarget> pSOSDataTarget = new SOSDataTarget;
4756 pSOSDataTarget->AddRef();
4758 ToRelease<ICLRDebuggingLibraryProvider> pSOSLibraryProvider = new SOSLibraryProvider;
4759 pSOSLibraryProvider->AddRef();
4761 CLR_DEBUGGING_VERSION clrDebuggingVersionRequested = {0};
4762 clrDebuggingVersionRequested.wMajor = 4;
4764 CLR_DEBUGGING_VERSION clrDebuggingVersionActual = {0};
4766 CLR_DEBUGGING_PROCESS_FLAGS clrDebuggingFlags = (CLR_DEBUGGING_PROCESS_FLAGS)0;
4768 ToRelease<IUnknown> pUnkProcess;
4770 hr = pClrDebugging->OpenVirtualProcess(
4773 pSOSLibraryProvider,
4774 &clrDebuggingVersionRequested,
4775 IID_ICorDebugProcess,
4777 &clrDebuggingVersionActual,
4778 &clrDebuggingFlags);
4784 ICorDebugProcess * pCorDebugProcess = NULL;
4785 hr = pUnkProcess->QueryInterface(IID_ICorDebugProcess, (PVOID*) &pCorDebugProcess);
4791 // Transfer memory ownership of refcount to global
4792 g_pCorDebugProcess = pCorDebugProcess;
4796 //---------------------------------------------------------------------------------------
4798 // Unloads public ICorDebug interfaces, and clears g_pCorDebugProcess
4799 // This is only needed once after CLR unloads, not after every InitCorDebugInterface call
4801 VOID UninitCorDebugInterface()
4803 if(g_pCorDebugProcess != NULL)
4805 g_pCorDebugProcess->Detach();
4806 g_pCorDebugProcess->Release();
4807 g_pCorDebugProcess = NULL;
4811 //---------------------------------------------------------------------------------------
4813 // Loads public ICorDebug interfaces, and points g_pCorDebugProcess to them
4814 // This should be called at least once per windbg stop state to ensure that
4815 // the interface is available and that it doesn't hold stale data. Calling it
4816 // more than once isn't an error, but does have perf overhead from needlessly
4817 // flushing memory caches.
4820 // HRESULT indicating success or failure
4823 HRESULT InitCorDebugInterface()
4825 HMODULE hModule = NULL;
4827 ToRelease<ICLRDebugging> pClrDebugging;
4829 // we may already have an ICorDebug instance we can use
4830 if(g_pCorDebugProcess != NULL)
4832 // ICorDebugProcess4 is currently considered a private experimental interface on ICorDebug, it might go away so
4833 // we need to be sure to handle its absense gracefully
4834 ToRelease<ICorDebugProcess4> pProcess4 = NULL;
4835 if(SUCCEEDED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess4), (void**)&pProcess4)))
4837 // FLUSH_ALL is more expensive than PROCESS_RUNNING, but this allows us to be safe even if things
4838 // like IDNA are in use where we might be looking at non-sequential snapshots of process state
4839 if(SUCCEEDED(pProcess4->ProcessStateChanged(FLUSH_ALL)))
4841 // we already have an ICorDebug instance loaded and flushed, nothing more to do
4846 // this is a very heavy handed way of reseting
4847 UninitCorDebugInterface();
4850 // SOS now has a statically linked version of the loader code that is normally found in mscoree/mscoreei.dll
4851 // Its not much code and takes a big step towards 0 install dependencies
4852 // Need to pick the appropriate SKU of CLR to detect
4853 #if defined(FEATURE_CORESYSTEM)
4854 GUID skuId = CLR_ID_ONECORE_CLR;
4856 GUID skuId = CLR_ID_CORECLR;
4858 CLRDebuggingImpl* pDebuggingImpl = new CLRDebuggingImpl(skuId);
4859 hr = pDebuggingImpl->QueryInterface(IID_ICLRDebugging, (LPVOID *)&pClrDebugging);
4862 delete pDebuggingImpl;
4867 ULONG cLoadedModules;
4868 ULONG cUnloadedModules;
4869 hr = g_ExtSymbols->GetNumberModules(&cLoadedModules, &cUnloadedModules);
4876 for (ULONG i = 0; i < cLoadedModules; i++)
4878 hr = g_ExtSymbols->GetModuleByIndex(i, &ulBase);
4884 // Dunno if this is a CLR module or not (or even if it's the particular one the
4885 // user cares about during inproc SxS scenarios). For now, just try to use it
4886 // to grab an ICorDebugProcess. If it works, great. Else, continue the loop
4887 // until we find the first one that works.
4888 hr = InitCorDebugInterfaceFromModule(ulBase, pClrDebugging);
4894 // On failure, just iterate to the next module and try again...
4897 // Still here? Didn't find the right module.
4898 // TODO: Anything useful to return or log here?
4902 hr = g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_DLL_NAME_A, 0, NULL, &ulBase);
4905 hr = InitCorDebugInterfaceFromModule(ulBase, pClrDebugging);
4908 #endif // FEATURE_PAL
4914 GC_HEAP_INVALID = 0,
4919 /**********************************************************************\
4920 * Routine Description: *
4922 * This function is called to find out if runtime is server build *
4924 \**********************************************************************/
4926 DacpGcHeapData *g_pHeapData = NULL;
4927 DacpGcHeapData g_HeapData;
4929 BOOL InitializeHeapData()
4931 if (g_pHeapData == NULL)
4933 if (g_HeapData.Request(g_sos) != S_OK)
4937 g_pHeapData = &g_HeapData;
4942 BOOL IsServerBuild()
4944 return InitializeHeapData() ? g_pHeapData->bServerMode : FALSE;
4947 UINT GetMaxGeneration()
4949 return InitializeHeapData() ? g_pHeapData->g_max_generation : 0;
4952 UINT GetGcHeapCount()
4954 return InitializeHeapData() ? g_pHeapData->HeapCount : 0;
4957 BOOL GetGcStructuresValid()
4959 // We don't want to use the cached HeapData, because this can change
4960 // each time the program runs for a while.
4961 DacpGcHeapData heapData;
4962 if (heapData.Request(g_sos) != S_OK)
4967 return heapData.bGcStructuresValid;
4970 void GetAllocContextPtrs(AllocInfo *pallocInfo)
4972 // gets the allocation contexts for all threads. This provides information about how much of
4973 // the current allocation quantum has been allocated and the heap to which the quantum belongs.
4974 // The allocation quantum is a fixed size chunk of zeroed memory from which allocations will come
4975 // until it's filled. Each managed thread has its own allocation context.
4977 pallocInfo->num = 0;
4978 pallocInfo->array = NULL;
4980 // get the thread store (See code:ClrDataAccess::RequestThreadStoreData for details)
4981 DacpThreadStoreData ThreadStore;
4982 if ( ThreadStore.Request(g_sos) != S_OK)
4987 int numThread = ThreadStore.threadCount;
4990 pallocInfo->array = new needed_alloc_context[numThread];
4991 if (pallocInfo->array == NULL)
4997 // get details for each thread in the thread store
4998 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
4999 while (CurThread != NULL)
5004 DacpThreadData Thread;
5005 // Get information about the thread (we're getting the values of several of the
5006 // fields of the Thread instance from the target) See code:ClrDataAccess::RequestThreadData for
5008 if (Thread.Request(g_sos, CurThread) != S_OK)
5013 if (Thread.allocContextPtr != 0)
5015 // get a list of all the allocation contexts
5017 for (j = 0; j < pallocInfo->num; j ++)
5019 if (pallocInfo->array[j].alloc_ptr == (BYTE *) Thread.allocContextPtr)
5022 if (j == pallocInfo->num)
5025 pallocInfo->array[j].alloc_ptr = (BYTE *) Thread.allocContextPtr;
5026 pallocInfo->array[j].alloc_limit = (BYTE *) Thread.allocContextLimit;
5030 CurThread = Thread.nextThread;
5034 HRESULT ReadVirtualCache::Read(TADDR taOffset, PVOID Buffer, ULONG BufferSize, PULONG lpcbBytesRead)
5036 // sign extend the passed in Offset so we can use it in when calling
5037 // IDebugDataSpaces::ReadVirtual()
5039 CLRDATA_ADDRESS Offset = TO_CDADDR(taOffset);
5040 // Offset can be any random ULONG64, as it can come from VerifyObjectMember(), and this
5041 // can pass random pointer values in case of GC heap corruption
5043 ULONG cbBytesRead = 0;
5045 if (BufferSize == 0)
5048 if (BufferSize > CACHE_SIZE)
5050 // Don't even try with the cache
5051 return g_ExtData->ReadVirtual(Offset, Buffer, BufferSize, lpcbBytesRead);
5055 && (taOffset >= m_startCache)
5056 && (taOffset <= m_startCache + m_cacheSize - BufferSize))
5059 // It is within the cache
5060 memcpy(Buffer,(LPVOID) ((ULONG64)m_cache + (taOffset - m_startCache)), BufferSize);
5062 if (lpcbBytesRead != NULL)
5064 *lpcbBytesRead = BufferSize;
5070 m_cacheValid = FALSE;
5071 m_startCache = taOffset;
5073 // avoid an int overflow
5074 if (m_startCache + CACHE_SIZE < m_startCache)
5075 m_startCache = (TADDR)(-CACHE_SIZE);
5077 ret = g_ExtData->ReadVirtual(TO_CDADDR(m_startCache), m_cache, CACHE_SIZE, &cbBytesRead);
5083 m_cacheSize = cbBytesRead;
5084 m_cacheValid = TRUE;
5085 memcpy(Buffer, (LPVOID) ((ULONG64)m_cache + (taOffset - m_startCache)), BufferSize);
5087 if (lpcbBytesRead != NULL)
5089 *lpcbBytesRead = cbBytesRead;
5095 HRESULT GetMTOfObject(TADDR obj, TADDR *mt)
5100 // Read the MethodTable and if we succeed, get rid of the mark bits.
5101 HRESULT hr = rvCache->Read(obj, mt, sizeof(TADDR), NULL);
5110 StressLogMem::~StressLogMem ()
5112 MemRange * range = list;
5116 MemRange * temp = range->next;
5122 bool StressLogMem::Init (ULONG64 stressLogAddr, IDebugDataSpaces* memCallBack)
5124 size_t ThreadStressLogAddr = NULL;
5125 HRESULT hr = memCallBack->ReadVirtual(UL64_TO_CDA(stressLogAddr + offsetof (StressLog, logs)),
5126 &ThreadStressLogAddr, sizeof (ThreadStressLogAddr), 0);
5132 while(ThreadStressLogAddr != NULL)
5134 size_t ChunkListHeadAddr = NULL;
5135 hr = memCallBack->ReadVirtual(TO_CDADDR(ThreadStressLogAddr + ThreadStressLog::OffsetOfListHead ()),
5136 &ChunkListHeadAddr, sizeof (ChunkListHeadAddr), 0);
5137 if (hr != S_OK || ChunkListHeadAddr == NULL)
5142 size_t StressLogChunkAddr = ChunkListHeadAddr;
5146 AddRange (StressLogChunkAddr, sizeof (StressLogChunk));
5147 hr = memCallBack->ReadVirtual(TO_CDADDR(StressLogChunkAddr + offsetof (StressLogChunk, next)),
5148 &StressLogChunkAddr, sizeof (StressLogChunkAddr), 0);
5153 if (StressLogChunkAddr == NULL)
5157 } while (StressLogChunkAddr != ChunkListHeadAddr);
5159 hr = memCallBack->ReadVirtual(TO_CDADDR(ThreadStressLogAddr + ThreadStressLog::OffsetOfNext ()),
5160 &ThreadStressLogAddr, sizeof (ThreadStressLogAddr), 0);
5170 bool StressLogMem::IsInStressLog (ULONG64 addr)
5172 MemRange * range = list;
5175 if (range->InRange (addr))
5177 range = range->next;
5183 #endif // !FEATURE_PAL
5185 unsigned int Output::g_bSuppressOutput = 0;
5186 unsigned int Output::g_Indent = 0;
5187 bool Output::g_bDbgOutput = false;
5188 bool Output::g_bDMLExposed = false;
5189 unsigned int Output::g_DMLEnable = 0;
5191 template <class T, int count, int size> const int StaticData<T, count, size>::Count = count;
5192 template <class T, int count, int size> const int StaticData<T, count, size>::Size = size;
5194 StaticData<char, 4, 1024> CachedString::cache;
5196 CachedString::CachedString()
5197 : mPtr(0), mRefCount(0), mIndex(~0), mSize(cache.Size)
5202 CachedString::CachedString(const CachedString &rhs)
5203 : mPtr(0), mRefCount(0), mIndex(~0), mSize(cache.Size)
5208 CachedString::~CachedString()
5213 const CachedString &CachedString::operator=(const CachedString &rhs)
5220 void CachedString::Copy(const CachedString &rhs)
5229 mIndex = rhs.mIndex;
5234 mRefCount = rhs.mRefCount;
5239 // We only create count the first time we copy it, so
5240 // we initialize it to 2.
5241 mRefCount = rhs.mRefCount = new unsigned int(2);
5248 void CachedString::Clear()
5250 if (!mRefCount || --*mRefCount == 0)
5257 else if (mIndex >= 0 && mIndex < cache.Count)
5259 cache.InUse[mIndex] = false;
5273 void CachedString::Create()
5278 // First try to find a string in the cache to use.
5279 for (int i = 0; i < cache.Count; ++i)
5280 if (!cache.InUse[i])
5282 cache.InUse[i] = true;
5283 mPtr = cache.Data[i];
5288 // We did not find a string to use, so we'll create a new one.
5291 mPtr = new char[cache.Size];
5298 void CachedString::SetOOM()
5304 void CachedString::Allocate(int size)
5307 mPtr = new char[size];
5320 size_t CountHexCharacters(CLRDATA_ADDRESS val)
5333 void WhitespaceOut(int count)
5335 static const int FixedIndentWidth = 0x40;
5336 static const char FixedIndentString[FixedIndentWidth+1] =
5342 int mod = count & 0x3F;
5346 g_ExtControl->Output(DEBUG_OUTPUT_NORMAL, "%.*s", mod, FixedIndentString);
5348 for ( ; count > 0; count -= FixedIndentWidth)
5349 g_ExtControl->Output(DEBUG_OUTPUT_NORMAL, FixedIndentString);
5352 void DMLOut(PCSTR format, ...)
5354 if (Output::IsOutputSuppressed())
5358 va_start(args, format);
5362 if (IsDMLEnabled() && !Output::IsDMLExposed())
5364 g_ExtControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, format, args);
5369 g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, format, args);
5375 void IfDMLOut(PCSTR format, ...)
5378 if (Output::IsOutputSuppressed() || !IsDMLEnabled())
5383 va_start(args, format);
5385 g_ExtControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, format, args);
5390 void ExtOut(PCSTR Format, ...)
5392 if (Output::IsOutputSuppressed())
5397 va_start(Args, Format);
5399 g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
5403 void ExtWarn(PCSTR Format, ...)
5405 if (Output::IsOutputSuppressed())
5410 va_start(Args, Format);
5411 g_ExtControl->OutputVaList(DEBUG_OUTPUT_WARNING, Format, Args);
5415 void ExtErr(PCSTR Format, ...)
5419 va_start(Args, Format);
5420 g_ExtControl->OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args);
5425 void ExtDbgOut(PCSTR Format, ...)
5428 if (Output::g_bDbgOutput)
5432 va_start(Args, Format);
5434 g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
5440 const char * const DMLFormats[] =
5442 NULL, // DML_None (do not use)
5443 "<exec cmd=\"!DumpMT /d %s\">%s</exec>", // DML_MethodTable
5444 "<exec cmd=\"!DumpMD /d %s\">%s</exec>", // DML_MethodDesc
5445 "<exec cmd=\"!DumpClass /d %s\">%s</exec>", // DML_EEClass
5446 "<exec cmd=\"!DumpModule /d %s\">%s</exec>", // DML_Module
5447 "<exec cmd=\"!U /d %s\">%s</exec>", // DML_IP
5448 "<exec cmd=\"!DumpObj /d %s\">%s</exec>", // DML_Object
5449 "<exec cmd=\"!DumpDomain /d %s\">%s</exec>", // DML_Domain
5450 "<exec cmd=\"!DumpAssembly /d %s\">%s</exec>", // DML_Assembly
5451 "<exec cmd=\"~~[%s]s\">%s</exec>", // DML_ThreadID
5452 "<exec cmd=\"!DumpVC /d %s %s\">%s</exec>", // DML_ValueClass
5453 "<exec cmd=\"!DumpHeap /d -mt %s\">%s</exec>", // DML_DumpHeapMT
5454 "<exec cmd=\"!ListNearObj /d %s\">%s</exec>", // DML_ListNearObj
5455 "<exec cmd=\"!ThreadState %s\">%s</exec>", // DML_ThreadState
5456 "<exec cmd=\"!PrintException /d %s\">%s</exec>",// DML_PrintException
5457 "<exec cmd=\"!DumpRCW /d %s\">%s</exec>", // DML_RCWrapper
5458 "<exec cmd=\"!DumpCCW /d %s\">%s</exec>", // DML_CCWrapper
5459 "<exec cmd=\"!ClrStack -i %S %d\">%S</exec>", // DML_ManagedVar
5462 void ConvertToLower(__out_ecount(len) char *buffer, size_t len)
5464 for (size_t i = 0; i < len && buffer[i]; ++i)
5465 buffer[i] = (char)tolower(buffer[i]);
5468 /* Build a hex display of addr.
5470 int GetHex(CLRDATA_ADDRESS addr, __out_ecount(len) char *out, size_t len, bool fill)
5472 int count = sprintf_s(out, len, fill ? "%p" : "%x", (size_t)addr);
5474 ConvertToLower(out, len);
5479 CachedString Output::BuildHexValue(CLRDATA_ADDRESS addr, FormatType type, bool fill)
5490 char hex[POINTERSIZE_BYTES*2 + 1];
5491 GetHex(addr, hex, _countof(hex), fill);
5492 sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], hex, hex);
5496 GetHex(addr, ret, ret.GetStrLen(), fill);
5502 CachedString Output::BuildVCValue(CLRDATA_ADDRESS mt, CLRDATA_ADDRESS addr, FormatType type, bool fill)
5504 _ASSERTE(type == DML_ValueClass);
5514 char hexaddr[POINTERSIZE_BYTES*2 + 1];
5515 char hexmt[POINTERSIZE_BYTES*2 + 1];
5517 GetHex(addr, hexaddr, _countof(hexaddr), fill);
5518 GetHex(mt, hexmt, _countof(hexmt), fill);
5520 sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], hexmt, hexaddr, hexaddr);
5524 GetHex(addr, ret, ret.GetStrLen(), fill);
5530 CachedString Output::BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, __in_z LPCWSTR simpleName, FormatType type)
5532 _ASSERTE(type == DML_ManagedVar);
5540 // calculate the number of digits in frame (this assumes base-10 display of frames)
5541 int numFrameDigits = 0;
5544 ULONG tempFrame = frame;
5545 while (tempFrame > 0)
5556 size_t totalStringLength = strlen(DMLFormats[type]) + _wcslen(expansionName) + numFrameDigits + _wcslen(simpleName) + 1;
5557 if (totalStringLength > ret.GetStrLen())
5559 ret.Allocate(static_cast<int>(totalStringLength));
5569 sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], expansionName, frame, simpleName);
5573 sprintf_s(ret, ret.GetStrLen(), "%S", simpleName);
5579 CachedString Output::BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, int indexInArray, FormatType type)
5581 WCHAR indexString[24];
5582 swprintf_s(indexString, _countof(indexString), W("[%d]"), indexInArray);
5583 return BuildManagedVarValue(expansionName, frame, indexString, type);
5586 EnableDMLHolder::EnableDMLHolder(BOOL enable)
5590 // If the user has not requested that we use DML, it's still possible that
5591 // they have instead specified ".prefer_dml 1". If enable is false,
5592 // we will check here for .prefer_dml. Since this class is only used once
5593 // per command issued to SOS, this should only check the setting once per
5594 // sos command issued.
5595 if (!mEnable && Output::g_DMLEnable <= 0)
5598 HRESULT hr = g_ExtControl->GetEngineOptions(&opts);
5599 mEnable = SUCCEEDED(hr) && (opts & DEBUG_ENGOPT_PREFER_DML) == DEBUG_ENGOPT_PREFER_DML;
5604 Output::g_DMLEnable++;
5606 #endif // FEATURE_PAL
5609 EnableDMLHolder::~EnableDMLHolder()
5613 Output::g_DMLEnable--;
5619 return Output::g_DMLEnable > 0;
5622 NoOutputHolder::NoOutputHolder(BOOL bSuppress)
5623 : mSuppress(bSuppress)
5626 Output::g_bSuppressOutput++;
5629 NoOutputHolder::~NoOutputHolder()
5632 Output::g_bSuppressOutput--;
5636 // Code to support mapping RVAs to managed code line numbers.
5640 // Retrieves the IXCLRDataMethodInstance* instance associated with the
5641 // passed in native offset.
5643 GetClrMethodInstance(
5644 ___in ULONG64 NativeOffset,
5645 ___out IXCLRDataMethodInstance** Method)
5648 CLRDATA_ENUM MethEnum;
5650 Status = g_clrData->StartEnumMethodInstancesByAddress(NativeOffset, NULL, &MethEnum);
5654 Status = g_clrData->EnumMethodInstanceByAddress(&MethEnum, Method);
5655 g_clrData->EndEnumMethodInstancesByAddress(MethEnum);
5658 // Any alternate success is a true failure here.
5659 return (Status == S_OK || FAILED(Status)) ? Status : E_NOINTERFACE;
5663 // Enumerates over the IL address map associated with the passed in
5664 // managed method, and returns the highest non-epilog offset.
5666 GetLastMethodIlOffset(
5667 ___in IXCLRDataMethodInstance* Method,
5668 ___out PULONG32 MethodOffs)
5671 CLRDATA_IL_ADDRESS_MAP MapLocal[16];
5672 CLRDATA_IL_ADDRESS_MAP* Map = MapLocal;
5673 ULONG32 MapCount = _countof(MapLocal);
5675 ULONG32 HighestOffset;
5679 if ((Status = Method->GetILAddressMap(MapCount, &MapNeeded, Map)) != S_OK)
5684 if (MapNeeded <= MapCount)
5689 // Need more map entries.
5690 if (Map != MapLocal)
5692 // Already went around and the answer changed,
5693 // which should not be possible.
5695 return E_UNEXPECTED;
5698 Map = new CLRDATA_IL_ADDRESS_MAP[MapNeeded];
5701 return E_OUTOFMEMORY;
5704 MapCount = MapNeeded;
5708 for (size_t i = 0; i < MapNeeded; i++)
5710 if (Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_NO_MAPPING &&
5711 Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_PROLOG &&
5712 Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_EPILOG &&
5713 Map[i].ilOffset > HighestOffset)
5715 HighestOffset = Map[i].ilOffset;
5719 if (Map != MapLocal)
5724 *MethodOffs = HighestOffset;
5729 // Convert a native offset (possibly already associated with a managed
5730 // method identified by the passed in IXCLRDataMethodInstance) to a
5731 // triplet (ImageInfo, MethodToken, MethodOffset) that can be used to
5732 // represent an "IL offset".
5734 ConvertNativeToIlOffset(
5735 ___in ULONG64 native,
5736 ___out IXCLRDataModule** ppModule,
5737 ___out mdMethodDef* methodToken,
5738 ___out PULONG32 methodOffs)
5740 ToRelease<IXCLRDataMethodInstance> pMethodInst(NULL);
5743 if ((Status = GetClrMethodInstance(native, &pMethodInst)) != S_OK)
5748 if ((Status = pMethodInst->GetILOffsetsByAddress(native, 1, NULL, methodOffs)) != S_OK)
5754 switch((LONG)*methodOffs)
5756 case CLRDATA_IL_OFFSET_NO_MAPPING:
5757 return E_NOINTERFACE;
5759 case CLRDATA_IL_OFFSET_PROLOG:
5760 // Treat all of the prologue as part of
5761 // the first source line.
5765 case CLRDATA_IL_OFFSET_EPILOG:
5766 // Back up until we find the last real
5768 if ((Status = GetLastMethodIlOffset(pMethodInst, methodOffs)) != S_OK)
5776 return pMethodInst->GetTokenAndScope(methodToken, ppModule);
5779 // Based on a native offset, passed in the first argument this function
5780 // identifies the corresponding source file name and line number.
5783 ___in ULONG64 offset,
5784 ___out ULONG *pLinenum,
5785 __out_ecount(cchFileName) WCHAR* pwszFileName,
5786 ___in ULONG cchFileName)
5788 HRESULT Status = S_OK;
5789 ULONG32 methodToken;
5792 // Find the image, method token and IL offset that correspond to "offset"
5793 ToRelease<IXCLRDataModule> pModule(NULL);
5794 IfFailRet(ConvertNativeToIlOffset(offset, &pModule, &methodToken, &methodOffs));
5796 ToRelease<IMetaDataImport> pMDImport(NULL);
5797 IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
5799 SymbolReader symbolReader;
5800 IfFailRet(symbolReader.LoadSymbols(pMDImport, pModule));
5802 return symbolReader.GetLineByILOffset(methodToken, methodOffs, pLinenum, pwszFileName, cchFileName);
5805 void TableOutput::ReInit(int numColumns, int defaultColumnWidth, Alignment alignmentDefault, int indent, int padding)
5809 mColumns = numColumns;
5810 mDefaultWidth = defaultColumnWidth;
5814 mDefaultAlign = alignmentDefault;
5817 void TableOutput::SetWidths(int columns, ...)
5819 SOS_Assert(columns > 0);
5820 SOS_Assert(columns <= mColumns);
5825 va_start(list, columns);
5827 for (int i = 0; i < columns; ++i)
5828 mWidths[i] = va_arg(list, int);
5833 void TableOutput::SetColWidth(int col, int width)
5835 SOS_Assert(col >= 0 && col < mColumns);
5836 SOS_Assert(width >= 0);
5840 mWidths[col] = width;
5843 void TableOutput::SetColAlignment(int col, Alignment align)
5845 SOS_Assert(col >= 0 && col < mColumns);
5849 mAlignments = new Alignment[mColumns];
5850 for (int i = 0; i < mColumns; ++i)
5851 mAlignments[i] = mDefaultAlign;
5854 mAlignments[col] = align;
5859 void TableOutput::Clear()
5863 delete [] mAlignments;
5874 void TableOutput::AllocWidths()
5878 mWidths = new int[mColumns];
5879 for (int i = 0; i < mColumns; ++i)
5880 mWidths[i] = mDefaultWidth;
5884 int TableOutput::GetColumnWidth(int col)
5886 SOS_Assert(col < mColumns);
5889 return mWidths[col];
5891 return mDefaultWidth;
5894 Alignment TableOutput::GetColAlign(int col)
5896 SOS_Assert(col < mColumns);
5898 return mAlignments[col];
5900 return mDefaultAlign;
5903 const char *TableOutput::GetWhitespace(int amount)
5905 static char WhiteSpace[256] = "";
5906 static int count = 0;
5910 count = _countof(WhiteSpace);
5911 for (int i = 0; i < count-1; ++i)
5912 WhiteSpace[i] = ' ';
5913 WhiteSpace[count-1] = 0;
5916 SOS_Assert(amount < count);
5917 return &WhiteSpace[count-amount-1];
5920 void TableOutput::OutputBlankColumns(int col)
5929 for (int i = mCurrCol; i < col; ++i)
5930 whitespace += GetColumnWidth(i) + mPadding;
5932 ExtOut(GetWhitespace(whitespace));
5935 void TableOutput::OutputIndent()
5938 ExtOut(GetWhitespace(mIndent));
5943 PEOffsetMemoryReader::PEOffsetMemoryReader(TADDR moduleBaseAddress) :
5944 m_moduleBaseAddress(moduleBaseAddress),
5948 HRESULT __stdcall PEOffsetMemoryReader::QueryInterface(REFIID riid, VOID** ppInterface)
5950 if(riid == __uuidof(IDiaReadExeAtOffsetCallback))
5952 *ppInterface = static_cast<IDiaReadExeAtOffsetCallback*>(this);
5956 else if(riid == __uuidof(IUnknown))
5958 *ppInterface = static_cast<IUnknown*>(this);
5964 return E_NOINTERFACE;
5968 ULONG __stdcall PEOffsetMemoryReader::AddRef()
5970 return InterlockedIncrement((volatile LONG *) &m_refCount);
5973 ULONG __stdcall PEOffsetMemoryReader::Release()
5975 ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount);
5983 // IDiaReadExeAtOffsetCallback implementation
5984 HRESULT __stdcall PEOffsetMemoryReader::ReadExecutableAt(DWORDLONG fileOffset, DWORD cbData, DWORD* pcbData, BYTE data[])
5986 return SafeReadMemory(m_moduleBaseAddress + fileOffset, data, cbData, pcbData) ? S_OK : E_FAIL;
5989 PERvaMemoryReader::PERvaMemoryReader(TADDR moduleBaseAddress) :
5990 m_moduleBaseAddress(moduleBaseAddress),
5994 HRESULT __stdcall PERvaMemoryReader::QueryInterface(REFIID riid, VOID** ppInterface)
5996 if(riid == __uuidof(IDiaReadExeAtRVACallback))
5998 *ppInterface = static_cast<IDiaReadExeAtRVACallback*>(this);
6002 else if(riid == __uuidof(IUnknown))
6004 *ppInterface = static_cast<IUnknown*>(this);
6010 return E_NOINTERFACE;
6014 ULONG __stdcall PERvaMemoryReader::AddRef()
6016 return InterlockedIncrement((volatile LONG *) &m_refCount);
6019 ULONG __stdcall PERvaMemoryReader::Release()
6021 ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount);
6029 // IDiaReadExeAtOffsetCallback implementation
6030 HRESULT __stdcall PERvaMemoryReader::ReadExecutableAtRVA(DWORD relativeVirtualAddress, DWORD cbData, DWORD* pcbData, BYTE data[])
6032 return SafeReadMemory(m_moduleBaseAddress + relativeVirtualAddress, data, cbData, pcbData) ? S_OK : E_FAIL;
6035 #endif // FEATURE_PAL
6037 HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in ICorDebugModule* pModule)
6039 HRESULT Status = S_OK;
6040 BOOL isDynamic = FALSE;
6041 BOOL isInMemory = FALSE;
6042 IfFailRet(pModule->IsDynamic(&isDynamic));
6043 IfFailRet(pModule->IsInMemory(&isInMemory));
6047 // Dynamic and in memory assemblies are a special case which we will ignore for now
6048 ExtWarn("SOS Warning: Loading symbols for dynamic assemblies is not yet supported\n");
6052 ULONG64 peAddress = 0;
6054 IfFailRet(pModule->GetBaseAddress(&peAddress));
6055 IfFailRet(pModule->GetSize(&peSize));
6058 WCHAR moduleName[MAX_LONGPATH];
6059 IfFailRet(pModule->GetName(_countof(moduleName), &len, moduleName));
6062 if (SUCCEEDED(LoadSymbolsForWindowsPDB(pMD, peAddress, moduleName, isInMemory)))
6066 #endif // FEATURE_PAL
6067 return LoadSymbolsForPortablePDB(moduleName, isInMemory, isInMemory, peAddress, peSize, 0, 0);
6070 HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataModule* pModule)
6072 DacpGetModuleData moduleData;
6073 HRESULT hr = moduleData.Request(pModule);
6076 ExtOut("LoadSymbols moduleData.Request FAILED 0x%08x\n", hr);
6080 if (moduleData.IsDynamic)
6082 ExtWarn("SOS Warning: Loading symbols for dynamic assemblies is not yet supported\n");
6086 ArrayHolder<WCHAR> pModuleName = new WCHAR[MAX_LONGPATH + 1];
6087 ULONG32 nameLen = 0;
6088 hr = pModule->GetFileName(MAX_LONGPATH, &nameLen, pModuleName);
6091 ExtOut("LoadSymbols: IXCLRDataModule->GetFileName FAILED 0x%08x\n", hr);
6096 // TODO: in-memory windows PDB not supported
6097 hr = LoadSymbolsForWindowsPDB(pMD, moduleData.LoadedPEAddress, pModuleName, moduleData.IsFileLayout);
6102 #endif // FEATURE_PAL
6104 return LoadSymbolsForPortablePDB(
6106 moduleData.IsInMemory,
6107 moduleData.IsFileLayout,
6108 moduleData.LoadedPEAddress,
6109 moduleData.LoadedPESize,
6110 moduleData.InMemoryPdbAddress,
6111 moduleData.InMemoryPdbSize);
6116 HRESULT SymbolReader::LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in ULONG64 peAddress, __in_z WCHAR* pModuleName, ___in BOOL isFileLayout)
6118 HRESULT Status = S_OK;
6120 if (m_pSymReader != NULL)
6123 IfFailRet(CoInitialize(NULL));
6125 // We now need a binder object that will take the module and return a
6127 ToRelease<ISymUnmanagedBinder3> pSymBinder;
6128 if (FAILED(Status = CreateInstanceCustom(CLSID_CorSymBinder_SxS,
6129 IID_ISymUnmanagedBinder3,
6130 W("diasymreader.dll"),
6131 cciLatestFx|cciDacColocated|cciDbgPath,
6132 (void**)&pSymBinder)))
6134 ExtOut("SOS Error: Unable to CoCreateInstance class=CLSID_CorSymBinder_SxS, interface=IID_ISymUnmanagedBinder3, hr=0x%x\n", Status);
6135 ExtOut("This usually means the installation of .Net Framework on your machine is missing or needs repair\n");
6139 ToRelease<IDebugSymbols3> spSym3(NULL);
6140 Status = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
6143 ExtOut("SOS Error: Unable to query IDebugSymbols3 HRESULT=0x%x.\n", Status);
6148 Status = spSym3->GetSymbolPathWide(NULL, 0, &pathSize);
6149 if (FAILED(Status)) //S_FALSE if the path doesn't fit, but if the path was size 0 perhaps we would get S_OK?
6151 ExtOut("SOS Error: Unable to get symbol path length. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", Status);
6155 ArrayHolder<WCHAR> symbolPath = new WCHAR[pathSize];
6156 Status = spSym3->GetSymbolPathWide(symbolPath, pathSize, NULL);
6159 ExtOut("SOS Error: Unable to get symbol path. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", Status);
6163 ToRelease<IUnknown> pCallback = NULL;
6166 pCallback = (IUnknown*) new PEOffsetMemoryReader(TO_TADDR(peAddress));
6170 pCallback = (IUnknown*) new PERvaMemoryReader(TO_TADDR(peAddress));
6173 // TODO: this should be better integrated with windbg's symbol lookup
6174 Status = pSymBinder->GetReaderFromCallback(pMD, pModuleName, symbolPath,
6175 AllowRegistryAccess | AllowSymbolServerAccess | AllowOriginalPathAccess | AllowReferencePathAccess, pCallback, &m_pSymReader);
6177 if (FAILED(Status) && m_pSymReader != NULL)
6179 m_pSymReader->Release();
6180 m_pSymReader = NULL;
6185 #endif // FEATURE_PAL
6188 // Pass to managed helper code to read in-memory PEs/PDBs
6189 // Returns the number of bytes read.
6191 int ReadMemoryForSymbols(ULONG64 address, char *buffer, int cb)
6194 if (SafeReadMemory(TO_TADDR(address), (PVOID)buffer, cb, &read))
6201 HRESULT SymbolReader::LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout,
6202 ___in ULONG64 peAddress, ___in ULONG64 peSize, ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize)
6204 HRESULT Status = S_OK;
6206 if (loadSymbolsForModuleDelegate == nullptr)
6208 IfFailRet(PrepareSymbolReader());
6211 // The module name needs to be null for in-memory PE's.
6212 ArrayHolder<char> szModuleName = nullptr;
6213 if (!isInMemory && pModuleName != nullptr)
6215 szModuleName = new char[MAX_LONGPATH];
6216 if (WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szModuleName, MAX_LONGPATH, NULL, NULL) == 0)
6222 m_symbolReaderHandle = loadSymbolsForModuleDelegate(szModuleName, isFileLayout, peAddress,
6223 (int)peSize, inMemoryPdbAddress, (int)inMemoryPdbSize, ReadMemoryForSymbols);
6225 if (m_symbolReaderHandle == 0)
6235 void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
6237 const char * const tpaExtensions[] = {
6238 "*.ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
6244 std::set<std::string> addedAssemblies;
6246 // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
6247 // then files with .dll extension, etc.
6248 for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
6250 const char* ext = tpaExtensions[extIndex];
6251 size_t extLength = strlen(ext);
6253 std::string assemblyPath(directory);
6254 assemblyPath.append(DIRECTORY_SEPARATOR_STR_A);
6255 assemblyPath.append(tpaExtensions[extIndex]);
6257 WIN32_FIND_DATAA data;
6258 HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data);
6260 if (findHandle != INVALID_HANDLE_VALUE)
6264 if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
6267 std::string filename(data.cFileName);
6268 size_t extPos = filename.length() - extLength;
6269 std::string filenameWithoutExt(filename.substr(0, extPos));
6271 // Make sure if we have an assembly with multiple extensions present,
6272 // we insert only one version of it.
6273 if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
6275 addedAssemblies.insert(filenameWithoutExt);
6277 tpaList.append(directory);
6278 tpaList.append(DIRECTORY_SEPARATOR_STR_A);
6279 tpaList.append(filename);
6280 tpaList.append(";");
6284 while (0 != FindNextFileA(findHandle, &data));
6286 FindClose(findHandle);
6291 bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable)
6293 ArrayHolder<char> hostPath = new char[MAX_LONGPATH+1];
6294 if (::GetModuleFileName(NULL, hostPath, MAX_LONGPATH) == 0)
6299 entrypointExecutable.clear();
6300 entrypointExecutable.append(hostPath);
6305 #endif // FEATURE_PAL
6307 HRESULT SymbolReader::PrepareSymbolReader()
6309 static bool attemptedSymbolReaderPreparation = false;
6310 if (attemptedSymbolReaderPreparation)
6312 // If we already tried to set up the symbol reader, we won't try again.
6316 attemptedSymbolReaderPreparation = true;
6318 std::string absolutePath;
6319 std::string coreClrPath;
6323 coreClrPath = g_ExtServices->GetCoreClrDirectory();
6324 if (!GetAbsolutePath(coreClrPath.c_str(), absolutePath))
6326 ExtErr("Error: Failed to get coreclr absolute path\n");
6329 coreClrPath.append(DIRECTORY_SEPARATOR_STR_A);
6330 coreClrPath.append(MAIN_CLR_DLL_NAME_A);
6333 Status = g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A, 0, &index, NULL);
6336 ExtErr("Error: Can't find coreclr module\n");
6339 ArrayHolder<char> szModuleName = new char[MAX_LONGPATH + 1];
6340 Status = g_ExtSymbols->GetModuleNames(index, 0, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL, NULL, 0, NULL);
6343 ExtErr("Error: Failed to get coreclr module name\n");
6346 coreClrPath = szModuleName;
6348 // Parse off the module name to get just the path
6349 size_t pos = coreClrPath.rfind(DIRECTORY_SEPARATOR_CHAR_A);
6350 if (pos == std::string::npos)
6352 ExtErr("Error: Failed to parse coreclr module name\n");
6355 absolutePath.assign(coreClrPath, 0, pos);
6356 #endif // FEATURE_PAL
6358 HMODULE coreclrLib = LoadLibraryA(coreClrPath.c_str());
6359 if (coreclrLib == nullptr)
6361 ExtErr("Error: Failed to load %s\n", coreClrPath.c_str());
6366 unsigned int domainId;
6367 coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)GetProcAddress(coreclrLib, "coreclr_initialize");
6368 if (initializeCoreCLR == nullptr)
6370 ExtErr("Error: coreclr_initialize not found\n");
6374 std::string tpaList;
6375 AddFilesFromDirectoryToTpaList(absolutePath.c_str(), tpaList);
6377 const char *propertyKeys[] = {
6378 "TRUSTED_PLATFORM_ASSEMBLIES", "APP_PATHS", "APP_NI_PATHS",
6379 "NATIVE_DLL_SEARCH_DIRECTORIES", "AppDomainCompatSwitch"};
6381 const char *propertyValues[] = {// TRUSTED_PLATFORM_ASSEMBLIES
6384 absolutePath.c_str(),
6386 absolutePath.c_str(),
6387 // NATIVE_DLL_SEARCH_DIRECTORIES
6388 absolutePath.c_str(),
6389 // AppDomainCompatSwitch
6390 "UseLatestBehaviorWhenTFMNotSpecified"};
6392 std::string entryPointExecutablePath;
6393 if (!GetEntrypointExecutableAbsolutePath(entryPointExecutablePath))
6395 ExtErr("Could not get full path to current executable");
6399 Status = initializeCoreCLR(entryPointExecutablePath.c_str(), "sos",
6400 sizeof(propertyKeys) / sizeof(propertyKeys[0]), propertyKeys, propertyValues, &hostHandle, &domainId);
6404 ExtErr("Error: Fail to initialize CoreCLR %08x\n", Status);
6408 coreclr_create_delegate_ptr createDelegate = (coreclr_create_delegate_ptr)GetProcAddress(coreclrLib, "coreclr_create_delegate");
6409 if (createDelegate == nullptr)
6411 ExtErr("Error: coreclr_create_delegate not found\n");
6415 IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "LoadSymbolsForModule", (void **)&loadSymbolsForModuleDelegate));
6416 IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "Dispose", (void **)&disposeDelegate));
6417 IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "ResolveSequencePoint", (void **)&resolveSequencePointDelegate));
6418 IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "GetLocalVariableName", (void **)&getLocalVariableNameDelegate));
6419 IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "GetLineByILOffset", (void **)&getLineByILOffsetDelegate));
6424 HRESULT SymbolReader::GetLineByILOffset(___in mdMethodDef methodToken, ___in ULONG64 ilOffset,
6425 ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, ___in ULONG cchFileName)
6427 HRESULT Status = S_OK;
6429 if (m_symbolReaderHandle != 0)
6431 _ASSERTE(getLineByILOffsetDelegate != nullptr);
6433 BSTR bstrFileName = SysAllocStringLen(0, MAX_LONGPATH);
6434 if (bstrFileName == nullptr)
6436 return E_OUTOFMEMORY;
6438 // Source lines with 0xFEEFEE markers are filtered out on the managed side.
6439 if ((getLineByILOffsetDelegate(m_symbolReaderHandle, methodToken, ilOffset, pLinenum, &bstrFileName) == FALSE) || (*pLinenum == 0))
6441 SysFreeString(bstrFileName);
6444 wcscpy_s(pwszFileName, cchFileName, bstrFileName);
6445 SysFreeString(bstrFileName);
6450 if (m_pSymReader == NULL)
6453 ToRelease<ISymUnmanagedMethod> pSymMethod(NULL);
6454 IfFailRet(m_pSymReader->GetMethod(methodToken, &pSymMethod));
6456 ULONG32 seqPointCount = 0;
6457 IfFailRet(pSymMethod->GetSequencePointCount(&seqPointCount));
6459 if (seqPointCount == 0)
6462 // allocate memory for the objects to be fetched
6463 ArrayHolder<ULONG32> offsets(new ULONG32[seqPointCount]);
6464 ArrayHolder<ULONG32> lines(new ULONG32[seqPointCount]);
6465 ArrayHolder<ULONG32> columns(new ULONG32[seqPointCount]);
6466 ArrayHolder<ULONG32> endlines(new ULONG32[seqPointCount]);
6467 ArrayHolder<ULONG32> endcolumns(new ULONG32[seqPointCount]);
6468 ArrayHolder<ToRelease<ISymUnmanagedDocument>> documents(new ToRelease<ISymUnmanagedDocument>[seqPointCount]);
6470 ULONG32 realSeqPointCount = 0;
6471 IfFailRet(pSymMethod->GetSequencePoints(seqPointCount, &realSeqPointCount, offsets, &(documents[0]), lines, columns, endlines, endcolumns));
6473 const ULONG32 HiddenLine = 0x00feefee;
6476 for (int i = 0; i < (int)realSeqPointCount; i++)
6478 if (offsets[i] > ilOffset)
6481 if (lines[i] != HiddenLine)
6485 if (bestSoFar != -1)
6487 ULONG32 cchNeeded = 0;
6488 IfFailRet(documents[bestSoFar]->GetURL(cchFileName, &cchNeeded, pwszFileName));
6490 *pLinenum = lines[bestSoFar];
6493 #endif // FEATURE_PAL
6498 HRESULT SymbolReader::GetNamedLocalVariable(___in ISymUnmanagedScope * pScope, ___in ICorDebugILFrame * pILFrame, ___in mdMethodDef methodToken,
6499 ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ICorDebugValue** ppValue)
6501 HRESULT Status = S_OK;
6503 if (m_symbolReaderHandle != 0)
6505 _ASSERTE(getLocalVariableNameDelegate != nullptr);
6507 BSTR wszParamName = SysAllocStringLen(0, mdNameLen);
6508 if (wszParamName == NULL)
6510 return E_OUTOFMEMORY;
6513 if (getLocalVariableNameDelegate(m_symbolReaderHandle, methodToken, localIndex, &wszParamName) == FALSE)
6515 SysFreeString(wszParamName);
6519 wcscpy_s(paramName, paramNameLen, wszParamName);
6520 SysFreeString(wszParamName);
6522 if (FAILED(pILFrame->GetLocalVariable(localIndex, ppValue)) || (*ppValue == NULL))
6531 if (m_pSymReader == NULL)
6536 ToRelease<ISymUnmanagedMethod> pSymMethod;
6537 IfFailRet(m_pSymReader->GetMethod(methodToken, &pSymMethod));
6539 ToRelease<ISymUnmanagedScope> pScope;
6540 IfFailRet(pSymMethod->GetRootScope(&pScope));
6542 return GetNamedLocalVariable(pScope, pILFrame, methodToken, localIndex, paramName, paramNameLen, ppValue);
6546 ULONG32 numVars = 0;
6547 IfFailRet(pScope->GetLocals(0, &numVars, NULL));
6549 ArrayHolder<ISymUnmanagedVariable*> pLocals = new ISymUnmanagedVariable*[numVars];
6550 IfFailRet(pScope->GetLocals(numVars, &numVars, pLocals));
6552 for (ULONG i = 0; i < numVars; i++)
6554 ULONG32 varIndexInMethod = 0;
6555 if (SUCCEEDED(pLocals[i]->GetAddressField1(&varIndexInMethod)))
6557 if (varIndexInMethod != localIndex)
6560 ULONG32 nameLen = 0;
6561 if (FAILED(pLocals[i]->GetName(paramNameLen, &nameLen, paramName)))
6562 swprintf_s(paramName, paramNameLen, W("local_%d\0"), localIndex);
6564 if (SUCCEEDED(pILFrame->GetLocalVariable(varIndexInMethod, ppValue)) && (*ppValue != NULL))
6566 for(ULONG j = 0; j < numVars; j++) pLocals[j]->Release();
6572 for(ULONG j = 0; j < numVars; j++) pLocals[j]->Release();
6578 ULONG32 numChildren = 0;
6579 IfFailRet(pScope->GetChildren(0, &numChildren, NULL));
6581 ArrayHolder<ISymUnmanagedScope*> pChildren = new ISymUnmanagedScope*[numChildren];
6582 IfFailRet(pScope->GetChildren(numChildren, &numChildren, pChildren));
6584 for (ULONG i = 0; i < numChildren; i++)
6586 if (SUCCEEDED(GetNamedLocalVariable(pChildren[i], pILFrame, methodToken, localIndex, paramName, paramNameLen, ppValue)))
6588 for (ULONG j = 0; j < numChildren; j++) pChildren[j]->Release();
6593 for (ULONG j = 0; j < numChildren; j++) pChildren[j]->Release();
6595 #endif // FEATURE_PAL
6600 HRESULT SymbolReader::GetNamedLocalVariable(___in ICorDebugFrame * pFrame, ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName,
6601 ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue)
6603 HRESULT Status = S_OK;
6606 paramName[0] = L'\0';
6608 ToRelease<ICorDebugILFrame> pILFrame;
6609 IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame));
6611 ToRelease<ICorDebugFunction> pFunction;
6612 IfFailRet(pFrame->GetFunction(&pFunction));
6614 mdMethodDef methodDef;
6615 ToRelease<ICorDebugClass> pClass;
6616 ToRelease<ICorDebugModule> pModule;
6617 IfFailRet(pFunction->GetClass(&pClass));
6618 IfFailRet(pFunction->GetModule(&pModule));
6619 IfFailRet(pFunction->GetToken(&methodDef));
6621 return GetNamedLocalVariable(NULL, pILFrame, methodDef, localIndex, paramName, paramNameLen, ppValue);
6624 HRESULT SymbolReader::ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___in TADDR mod, ___out mdMethodDef* pToken, ___out ULONG32* pIlOffset)
6626 HRESULT Status = S_OK;
6628 if (m_symbolReaderHandle != 0)
6630 _ASSERTE(resolveSequencePointDelegate != nullptr);
6632 char szName[mdNameLen];
6633 if (WideCharToMultiByte(CP_ACP, 0, pFilename, (int)(_wcslen(pFilename) + 1), szName, mdNameLen, NULL, NULL) == 0)
6637 if (resolveSequencePointDelegate(m_symbolReaderHandle, szName, lineNumber, pToken, pIlOffset) == FALSE)
6645 if (m_pSymReader == NULL)
6649 ULONG32 cDocsNeeded = 0;
6650 ArrayHolder<ToRelease<ISymUnmanagedDocument>> pDocs = NULL;
6652 IfFailRet(m_pSymReader->GetDocuments(cDocs, &cDocsNeeded, NULL));
6653 pDocs = new ToRelease<ISymUnmanagedDocument>[cDocsNeeded];
6654 cDocs = cDocsNeeded;
6655 IfFailRet(m_pSymReader->GetDocuments(cDocs, &cDocsNeeded, &(pDocs[0])));
6657 ULONG32 filenameLen = (ULONG32) _wcslen(pFilename);
6659 for (ULONG32 i = 0; i < cDocs; i++)
6662 ULONG32 cchUrlNeeded = 0;
6663 ArrayHolder<WCHAR> pUrl = NULL;
6664 IfFailRet(pDocs[i]->GetURL(cchUrl, &cchUrlNeeded, pUrl));
6665 pUrl = new WCHAR[cchUrlNeeded];
6666 cchUrl = cchUrlNeeded;
6667 IfFailRet(pDocs[i]->GetURL(cchUrl, &cchUrlNeeded, pUrl));
6669 // If the URL is exactly as long as the filename then compare the two names directly
6670 if (cchUrl-1 == filenameLen)
6672 if (0!=_wcsicmp(pUrl, pFilename))
6675 // does the URL suffix match [back]slash + filename?
6676 else if (cchUrl-1 > filenameLen)
6678 WCHAR* slashLocation = pUrl + (cchUrl - filenameLen - 2);
6679 if (*slashLocation != L'\\' && *slashLocation != L'/')
6681 if (0 != _wcsicmp(slashLocation+1, pFilename))
6684 // URL is too short to match
6688 ULONG32 closestLine = 0;
6689 if (FAILED(pDocs[i]->FindClosestLine(lineNumber, &closestLine)))
6692 ToRelease<ISymUnmanagedMethod> pSymUnmanagedMethod;
6693 IfFailRet(m_pSymReader->GetMethodFromDocumentPosition(pDocs[i], closestLine, 0, &pSymUnmanagedMethod));
6694 IfFailRet(pSymUnmanagedMethod->GetToken(pToken));
6695 IfFailRet(pSymUnmanagedMethod->GetOffset(pDocs[i], closestLine, 0, pIlOffset));
6698 if (*pIlOffset == -1)
6704 #endif // FEATURE_PAL
6709 static void AddAssemblyName(WString& methodOutput, CLRDATA_ADDRESS mdesc)
6711 DacpMethodDescData mdescData;
6712 if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
6715 if (SUCCEEDED(dmd.Request(g_sos, mdescData.ModulePtr)))
6717 ToRelease<IXCLRDataModule> pModule;
6718 if (SUCCEEDED(g_sos->GetModule(mdescData.ModulePtr, &pModule)))
6720 ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH + 1];
6721 ULONG32 nameLen = 0;
6722 if (SUCCEEDED(pModule->GetFileName(MAX_LONGPATH, &nameLen, wszFileName)))
6724 if (wszFileName[0] != W('\0'))
6726 WCHAR *pJustName = _wcsrchr(wszFileName, DIRECTORY_SEPARATOR_CHAR_W);
6727 if (pJustName == NULL)
6728 pJustName = wszFileName - 1;
6729 methodOutput += (pJustName + 1);
6730 methodOutput += W("!");
6738 WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackWalk, BOOL bAssemblyName)
6741 MOVE(vtAddr, frameAddr);
6743 WString frameOutput;
6744 frameOutput += W("[");
6746 if (SUCCEEDED(g_sos->GetFrameName(TO_CDADDR(vtAddr), mdNameLen, g_mdName, NULL)))
6747 frameOutput += g_mdName;
6749 frameOutput += W("Frame");
6751 frameOutput += WString(W(": ")) + Pointer(frameAddr) + W("] ");
6753 // Print the frame's associated function info, if it has any.
6754 CLRDATA_ADDRESS mdesc = 0;
6755 if (SUCCEEDED(g_sos->GetMethodDescPtrFromFrame(frameAddr, &mdesc)))
6757 if (SUCCEEDED(g_sos->GetMethodDescName(mdesc, mdNameLen, g_mdName, NULL)))
6761 AddAssemblyName(frameOutput, mdesc);
6764 frameOutput += g_mdName;
6768 frameOutput += W("<unknown method>");
6771 else if (pStackWalk)
6773 // The Frame did not have direct function info, so try to get the method instance
6774 // (in this case a MethodDesc), and read the name from it.
6775 ToRelease<IXCLRDataFrame> frame;
6776 if (SUCCEEDED(pStackWalk->GetFrame(&frame)))
6778 ToRelease<IXCLRDataMethodInstance> methodInstance;
6779 if (SUCCEEDED(frame->GetMethodInstance(&methodInstance)))
6781 // GetName can return S_FALSE if mdNameLen is not large enough. However we are already
6782 // passing a pretty big buffer in. If this returns S_FALSE (meaning the buffer is too
6783 // small) then we should not output it anyway.
6784 if (methodInstance->GetName(0, mdNameLen, NULL, g_mdName) == S_OK)
6785 frameOutput += g_mdName;
6793 WString MethodNameFromIP(CLRDATA_ADDRESS ip, BOOL bSuppressLines, BOOL bAssemblyName, BOOL bDisplacement)
6796 WString methodOutput;
6797 CLRDATA_ADDRESS mdesc = 0;
6799 if (FAILED(g_sos->GetMethodDescPtrFromIP(ip, &mdesc)))
6801 methodOutput = W("<unknown>");
6805 DacpMethodDescData mdescData;
6806 if (SUCCEEDED(g_sos->GetMethodDescName(mdesc, mdNameLen, g_mdName, NULL)))
6810 AddAssemblyName(methodOutput, mdesc);
6813 methodOutput += g_mdName;
6817 if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
6819 ULONG64 disp = (ip - mdescData.NativeCodeAddr);
6822 methodOutput += W(" + ");
6823 methodOutput += Decimal(disp);
6828 else if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
6831 BOOL bModuleNameWorked = FALSE;
6832 ULONG64 addrInModule = ip;
6833 if (SUCCEEDED(dmd.Request(g_sos, mdescData.ModulePtr)))
6835 CLRDATA_ADDRESS peFileBase = 0;
6836 if (SUCCEEDED(g_sos->GetPEFileBase(dmd.File, &peFileBase)))
6840 addrInModule = peFileBase;
6846 if (SUCCEEDED(g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index, &moduleBase)))
6848 ArrayHolder<char> szModuleName = new char[MAX_LONGPATH+1];
6850 if (SUCCEEDED(g_ExtSymbols->GetModuleNames(Index, moduleBase, NULL, 0, NULL, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL)))
6852 MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, g_mdName, _countof(g_mdName));
6853 methodOutput += g_mdName;
6854 methodOutput += W("!");
6857 methodOutput += W("<unknown method>");
6861 methodOutput = W("<unknown>");
6864 ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH];
6865 if (!bSuppressLines &&
6866 SUCCEEDED(GetLineByOffset(TO_CDADDR(ip), &linenum, wszFileName, MAX_LONGPATH)))
6868 methodOutput += WString(W(" [")) + wszFileName + W(" @ ") + Decimal(linenum) + W("]");
6872 return methodOutput;
6875 HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount)
6877 if (ppRefs == NULL || pRefCnt == NULL)
6884 unsigned int count = 0;
6885 ToRelease<ISOSStackRefEnum> pEnum;
6886 if (FAILED(g_sos->GetStackReferences(osID, &pEnum)) || FAILED(pEnum->GetCount(&count)))
6888 ExtOut("Failed to enumerate GC references.\n");
6892 *ppRefs = new SOSStackRefData[count];
6893 if (FAILED(pEnum->Next(count, *ppRefs, pRefCnt)))
6895 ExtOut("Failed to enumerate GC references.\n");
6899 SOS_Assert(count == *pRefCnt);
6901 // Enumerate errors found. Any bad HRESULT recieved while enumerating errors is NOT a fatal error.
6902 // Hence we return S_FALSE if we encounter one.
6904 if (ppErrors && pErrCount)
6906 ToRelease<ISOSStackRefErrorEnum> pErrors;
6907 if (FAILED(pEnum->EnumerateErrors(&pErrors)))
6909 ExtOut("Failed to enumerate GC reference errors.\n");
6913 if (FAILED(pErrors->GetCount(&count)))
6915 ExtOut("Failed to enumerate GC reference errors.\n");
6919 *ppErrors = new SOSStackRefError[count];
6920 if (FAILED(pErrors->Next(count, *ppErrors, pErrCount)))
6922 ExtOut("Failed to enumerate GC reference errors.\n");
6927 SOS_Assert(count == *pErrCount);
6933 InternalFrameManager::InternalFrameManager() : m_cInternalFramesActual(0), m_iInternalFrameCur(0) {}
6935 HRESULT InternalFrameManager::Init(ICorDebugThread3 * pThread3)
6937 _ASSERTE(pThread3 != NULL);
6939 return pThread3->GetActiveInternalFrames(
6940 _countof(m_rgpInternalFrame2),
6941 &m_cInternalFramesActual,
6942 &(m_rgpInternalFrame2[0]));
6945 HRESULT InternalFrameManager::PrintPrecedingInternalFrames(ICorDebugFrame * pFrame)
6949 for (; m_iInternalFrameCur < m_cInternalFramesActual; m_iInternalFrameCur++)
6951 BOOL bIsCloser = FALSE;
6952 IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->IsCloserToLeaf(pFrame, &bIsCloser));
6956 // Current internal frame is now past pFrame, so we're done
6960 IfFailRet(PrintCurrentInternalFrame());
6963 // Exhausted list of internal frames. Done!
6967 HRESULT InternalFrameManager::PrintCurrentInternalFrame()
6969 _ASSERTE(m_iInternalFrameCur < m_cInternalFramesActual);
6973 CORDB_ADDRESS address;
6974 IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->GetAddress(&address));
6976 ToRelease<ICorDebugInternalFrame> pInternalFrame;
6977 IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame));
6979 CorDebugInternalFrameType type;
6980 IfFailRet(pInternalFrame->GetFrameType(&type));
6982 LPCSTR szFrameType = NULL;
6986 szFrameType = "Unknown internal frame.";
6990 szFrameType = "Managed to Unmanaged transition";
6994 szFrameType = "Unmanaged to Managed transition";
6997 case STUBFRAME_APPDOMAIN_TRANSITION:
6998 szFrameType = "AppDomain transition";
7001 case STUBFRAME_LIGHTWEIGHT_FUNCTION:
7002 szFrameType = "Lightweight function";
7005 case STUBFRAME_FUNC_EVAL:
7006 szFrameType = "Function evaluation";
7009 case STUBFRAME_INTERNALCALL:
7010 szFrameType = "Internal call";
7013 case STUBFRAME_CLASS_INIT:
7014 szFrameType = "Class initialization";
7017 case STUBFRAME_EXCEPTION:
7018 szFrameType = "Exception";
7021 case STUBFRAME_SECURITY:
7022 szFrameType = "Security";
7025 case STUBFRAME_JIT_COMPILATION:
7026 szFrameType = "JIT Compilation";
7030 DMLOut("%p %s ", SOS_PTR(address), SOS_PTR(0));
7031 ExtOut("[%s: %p]\n", szFrameType, SOS_PTR(address));