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.
4
5// ==++==
6//
7
8//
9// ==--==
10#include "sos.h"
11#include "disasm.h"
12#include <dbghelp.h>
13
14#include "corhdr.h"
15#include "cor.h"
16#include "dacprivate.h"
17#include "sospriv.h"
18#include "corerror.h"
19#include "safemath.h"
20
21#include <psapi.h>
22#include <cordebug.h>
23#include <xcordebug.h>
24#include <metahost.h>
25#include <mscoree.h>
26#include <tchar.h>
27#include "debugshim.h"
28
29#ifdef FEATURE_PAL
30#include "datatarget.h"
31#endif // FEATURE_PAL
32#include "gcinfo.h"
33
34#ifndef STRESS_LOG
35#define STRESS_LOG
36#endif // STRESS_LOG
37#define STRESS_LOG_READONLY
38#include "stresslog.h"
39
40#ifndef FEATURE_PAL
41#define MAX_SYMBOL_LEN 4096
42#define SYM_BUFFER_SIZE (sizeof(IMAGEHLP_SYMBOL) + MAX_SYMBOL_LEN)
43char symBuffer[SYM_BUFFER_SIZE];
44PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) symBuffer;
45#else
46#include <sys/stat.h>
47#include <coreruncommon.h>
48#include <dlfcn.h>
49#endif // !FEATURE_PAL
50
51#include <coreclrhost.h>
52#include <set>
53
54LoadSymbolsForModuleDelegate SymbolReader::loadSymbolsForModuleDelegate;
55DisposeDelegate SymbolReader::disposeDelegate;
56ResolveSequencePointDelegate SymbolReader::resolveSequencePointDelegate;
57GetLocalVariableName SymbolReader::getLocalVariableNameDelegate;
58GetLineByILOffsetDelegate SymbolReader::getLineByILOffsetDelegate;
59
60const char * const CorElementTypeName[ELEMENT_TYPE_MAX]=
61{
62#define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) c,
63#include "cortypeinfo.h"
64#undef TYPEINFO
65};
66
67const char * const CorElementTypeNamespace[ELEMENT_TYPE_MAX]=
68{
69#define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) ns,
70#include "cortypeinfo.h"
71#undef TYPEINFO
72};
73
74IXCLRDataProcess *g_clrData = NULL;
75ISOSDacInterface *g_sos = NULL;
76ICorDebugProcess *g_pCorDebugProcess = NULL;
77
78#ifndef IfFailRet
79#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
80#endif
81
82#ifndef IfFailGoto
83#define IfFailGoto(EXPR, label) do { Status = (EXPR); if(FAILED(Status)) { goto label; } } while (0)
84#endif // IfFailGoto
85
86#ifndef IfFailGo
87#define IfFailGo(EXPR) IfFailGoto(EXPR, Error)
88#endif // IfFailGo
89
90// Max number of reverted rejit versions that !dumpmd and !ip2md will print
91const UINT kcMaxRevertedRejitData = 10;
92const UINT kcMaxTieredVersions = 10;
93#ifndef FEATURE_PAL
94
95// ensure we always allocate on the process heap
96void* __cdecl operator new(size_t size) throw()
97{ return HeapAlloc(GetProcessHeap(), 0, size); }
98void __cdecl operator delete(void* pObj) throw()
99{ HeapFree(GetProcessHeap(), 0, pObj); }
100
101void* __cdecl operator new[](size_t size) throw()
102{ return HeapAlloc(GetProcessHeap(), 0, size); }
103void __cdecl operator delete[](void* pObj) throw()
104{ HeapFree(GetProcessHeap(), 0, pObj); }
105
106/**********************************************************************\
107* Here we define types and functions that support custom COM *
108* activation rules, as defined by the CIOptions enum. *
109* *
110\**********************************************************************/
111
112typedef unsigned __int64 QWORD;
113
114namespace com_activation
115{
116 //
117 // Forward declarations for the implementation methods
118 //
119
120 HRESULT CreateInstanceCustomImpl(
121 REFCLSID clsid,
122 REFIID iid,
123 LPCWSTR dllName,
124 CIOptions cciOptions,
125 void** ppItf);
126 HRESULT ClrCreateInstance(
127 REFCLSID clsid,
128 REFIID iid,
129 LPCWSTR dllName,
130 CIOptions cciOptions,
131 void** ppItf);
132 HRESULT CreateInstanceFromPath(
133 REFCLSID clsid,
134 REFIID iid,
135 LPCWSTR path,
136 void** ppItf);
137 BOOL GetPathFromModule(
138 HMODULE hModule,
139 __in_ecount(cFqPath) LPWSTR fqPath,
140 DWORD cFqPath);
141 HRESULT PickClrRuntimeInfo(
142 ICLRMetaHost *pMetaHost,
143 CIOptions cciOptions,
144 ICLRRuntimeInfo** ppClr);
145 QWORD VerString2Qword(LPCWSTR vStr);
146 void CleanupClsidHmodMap();
147
148 // Helper structures for defining the CLSID -> HMODULE hash table we
149 // use for caching already activated objects
150 class hash_compareGUID
151 {
152 public:
153 static const size_t bucket_size = 4;
154 static const size_t min_buckets = 8;
155 hash_compareGUID()
156 { }
157
158 size_t operator( )(const GUID& _Key) const
159 {
160 DWORD *pdw = (DWORD*)&_Key;
161 return (size_t)(pdw[0] ^ pdw[1] ^ pdw[2] ^ pdw[3]);
162 }
163
164 bool operator( )(const GUID& _Key1, const GUID& _Key2) const
165 { return memcmp(&_Key1, &_Key2, sizeof(GUID)) == -1; }
166 };
167
168 static std::unordered_map<GUID, HMODULE, hash_compareGUID> *g_pClsidHmodMap = NULL;
169
170
171
172/**********************************************************************\
173* Routine Description: *
174* *
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\**********************************************************************/
191HRESULT CreateInstanceCustomImpl(
192 REFCLSID clsid,
193 REFIID iid,
194 LPCWSTR dllName,
195 CIOptions cciOptions,
196 void** ppItf)
197{
198 _ASSERTE(ppItf != NULL);
199
200 if (ppItf == NULL)
201 return E_POINTER;
202
203 WCHAR wszClsid[64] = W("<CLSID>");
204
205 // Step 1: Attempt activation using an installed runtime
206 if ((cciOptions & cciFxMask) != 0)
207 {
208 CIOptions opt = cciOptions & cciFxMask;
209 if (SUCCEEDED(ClrCreateInstance(clsid, iid, dllName, opt, ppItf)))
210 return S_OK;
211
212 ExtDbgOut("Failed to instantiate {%ls} from installed .NET framework locations.\n", wszClsid);
213 }
214
215 if ((cciOptions & cciDbiColocated) != 0)
216 {
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.
219 }
220
221 // Step 2: attempt activation using the folder the DAC was loaded from
222 if ((cciOptions & cciDacColocated) != 0)
223 {
224 _ASSERTE(dllName != NULL);
225 HMODULE hDac = NULL;
226 WCHAR path[MAX_LONGPATH];
227
228 if (SUCCEEDED(g_sos->GetDacModuleHandle(&hDac))
229 && GetPathFromModule(hDac, path, _countof(path)))
230 {
231 // build the fully qualified file name and attempt instantiation
232 if (wcscat_s(path, dllName) == 0
233 && SUCCEEDED(CreateInstanceFromPath(clsid, iid, path, ppItf)))
234 {
235 return S_OK;
236 }
237 }
238
239 ExtDbgOut("Failed to instantiate {%ls} from DAC location.\n", wszClsid);
240 }
241
242 // Step 3: attempt activation using the debugger's .exepath and .sympath
243 if ((cciOptions & cciDbgPath) != 0)
244 {
245 _ASSERTE(dllName != NULL);
246
247 ToRelease<IDebugSymbols3> spSym3(NULL);
248 HRESULT hr = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
249 if (FAILED(hr))
250 {
251 ExtDbgOut("Unable to query IDebugSymbol3 HRESULT=0x%x.\n", hr);
252 goto ErrDbgPath;
253 }
254
255 typedef HRESULT (__stdcall IDebugSymbols3::*GetPathFunc)(LPWSTR , ULONG, ULONG*);
256
257 {
258 // Handle both the image path and the symbol path
259 GetPathFunc rgGetPathFuncs[] =
260 { &IDebugSymbols3::GetImagePathWide, &IDebugSymbols3::GetSymbolPathWide };
261
262 for (int i = 0; i < _countof(rgGetPathFuncs); ++i)
263 {
264 ULONG pathSize = 0;
265
266 // get the path buffer size
267 if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(NULL, 0, &pathSize) != S_OK)
268 {
269 continue;
270 }
271
272 ArrayHolder<WCHAR> imgPath = new WCHAR[pathSize+MAX_LONGPATH+1];
273 if (imgPath == NULL)
274 {
275 continue;
276 }
277
278 // actually get the path
279 if ((spSym3.GetPtr()->*rgGetPathFuncs[i])(imgPath, pathSize, NULL) != S_OK)
280 {
281 continue;
282 }
283
284 LPWSTR ctx;
285 LPCWSTR pathElem = wcstok_s(imgPath, W(";"), &ctx);
286 while (pathElem != NULL)
287 {
288 WCHAR fullName[MAX_LONGPATH];
289 wcscpy_s(fullName, _countof(fullName), pathElem);
290 if (wcscat_s(fullName, W("\\")) == 0 && wcscat_s(fullName, dllName) == 0)
291 {
292 if (SUCCEEDED(CreateInstanceFromPath(clsid, iid, fullName, ppItf)))
293 {
294 return S_OK;
295 }
296 }
297
298 pathElem = wcstok_s(NULL, W(";"), &ctx);
299 }
300 }
301 }
302
303 ErrDbgPath:
304 ExtDbgOut("Failed to instantiate {%ls} from debugger's image path.\n", wszClsid);
305 }
306
307 return REGDB_E_CLASSNOTREG;
308}
309
310
311#ifdef _MSC_VER
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)
315#endif // _MSC_VER
316
317
318/**********************************************************************\
319* Routine Description: *
320* *
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\**********************************************************************/
327HRESULT ClrCreateInstance(
328 REFCLSID clsid,
329 REFIID iid,
330 LPCWSTR dllName,
331 CIOptions cciOptions,
332 void** ppItf)
333{
334 _ASSERTE((cciOptions & ~cciFxMask) == 0 && (cciOptions & cciFxMask) != 0);
335 HRESULT Status = S_OK;
336
337 static CIOptions prevOpt = 0;
338 static HRESULT prevHr = S_OK;
339
340 // if we already tried to use NetFx install and failed don't try it again
341 if (prevOpt == cciOptions && FAILED(prevHr))
342 {
343 return prevHr;
344 }
345
346 prevOpt = cciOptions;
347
348 // first try usig the metahost API:
349 HRESULT (__stdcall *pfnCLRCreateInstance)(REFCLSID clsid, REFIID riid, LPVOID * ppInterface) = NULL;
350 HMODULE hMscoree = NULL;
351
352 // if there's a v4+ shim on the debugger machine
353 if (GetProcAddressT("CLRCreateInstance", W("mscoree.dll"), &pfnCLRCreateInstance, &hMscoree))
354 {
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)
359 {
360 // E_NOTIMPL means we have a v4 aware mscoree but no v4+ framework
361 IfFailGo( CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, ppItf) );
362 }
363 else
364 {
365 IfFailGo( Status );
366
367 // pick a runtime according to cciOptions
368 ToRelease<ICLRRuntimeInfo> spClr;
369 IfFailGo( PickClrRuntimeInfo(spMH, cciOptions, &spClr) );
370
371 // activate the COM object
372 Status = spClr->GetInterface(clsid, iid, ppItf);
373
374 if (FAILED(Status) && dllName)
375 {
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) );
383 }
384 }
385 }
386 else
387 {
388 // otherwise fallback to regular COM activation
389 IfFailGo( CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, ppItf) );
390 }
391
392Error:
393 if (hMscoree != NULL)
394 {
395 FreeLibrary(hMscoree);
396 }
397
398 // remember if we succeeded or failed
399 prevHr = Status;
400
401 return Status;
402}
403
404#ifdef _MSC_VER
405#pragma warning(pop)
406#endif // _MSC_VER
407
408
409/**********************************************************************\
410* Routine Description: *
411* *
412* CreateInstanceFromPath() instantiates a COM object using a passed in *
413* fully-qualified path and a CLSID. *
414* *
415* Note: *
416* *
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\**********************************************************************/
422HRESULT CreateInstanceFromPath(
423 REFCLSID clsid,
424 REFIID iid,
425 LPCWSTR path,
426 void** ppItf)
427{
428 HRESULT Status = S_OK;
429 HRESULT (__stdcall *pfnDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv) = NULL;
430
431 HMODULE hmod = NULL;
432
433 if (g_pClsidHmodMap == NULL)
434 {
435 g_pClsidHmodMap = new std::unordered_map<GUID, HMODULE, hash_compareGUID>();
436 OnUnloadTask::Register(CleanupClsidHmodMap);
437 }
438
439 auto it = g_pClsidHmodMap->find(clsid);
440 if (it != g_pClsidHmodMap->end())
441 hmod = it->second;
442
443 if (!GetProcAddressT("DllGetClassObject", path, &pfnDllGetClassObject, &hmod))
444 return REGDB_E_CLASSNOTREG;
445
446 ToRelease<IClassFactory> pFactory;
447 IfFailGo(pfnDllGetClassObject(clsid, IID_IClassFactory, (void**)&pFactory));
448
449 IfFailGo(pFactory->CreateInstance(NULL, iid, ppItf));
450
451 // only cache the HMODULE if we successfully created the COM object
452 (*g_pClsidHmodMap)[clsid] = hmod;
453
454 return S_OK;
455
456Error:
457 if (hmod != NULL)
458 FreeLibrary(hmod);
459
460 return Status;
461}
462
463
464/**********************************************************************\
465* Routine Description: *
466* *
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\**********************************************************************/
472void CleanupClsidHmodMap()
473{
474 if (g_pClsidHmodMap != NULL)
475 {
476 for (auto it = g_pClsidHmodMap->begin(); it != g_pClsidHmodMap->end(); ++it)
477 {
478 _ASSERTE(it->second != NULL);
479 FreeLibrary(it->second);
480 }
481
482 delete g_pClsidHmodMap;
483 g_pClsidHmodMap = NULL;
484 }
485}
486
487/**********************************************************************\
488* Routine Description: *
489* *
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\**********************************************************************/
497HRESULT PickClrRuntimeInfo(
498 ICLRMetaHost *pMetaHost,
499 CIOptions cciOptions,
500 ICLRRuntimeInfo** ppClr)
501{
502 if (ppClr == NULL)
503 return E_POINTER;
504
505 // only support "Any framework" and "latest framework"
506 if (cciOptions != cciAnyFx && cciOptions != cciLatestFx)
507 return E_INVALIDARG;
508
509 HRESULT Status = S_OK;
510 *ppClr = NULL;
511
512 // get the CLRRuntime enumerator
513 ToRelease<IEnumUnknown> spClrsEnum;
514 IfFailRet(pMetaHost->EnumerateInstalledRuntimes(&spClrsEnum));
515
516 ToRelease<ICLRRuntimeInfo> spChosenClr;
517 QWORD verMax = 0;
518
519 int cntClr = 0;
520 while (1)
521 {
522 // retrieve the next ICLRRuntimeInfo
523 ULONG cnt;
524 ToRelease<IUnknown> spClrUnk;
525 if (spClrsEnum->Next(1, &spClrUnk, &cnt) != S_OK || cnt != 1)
526 break;
527
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))
533 || !bLoadable)
534 {
535 continue;
536 }
537
538 WCHAR vStr[128];
539 DWORD cStr = _countof(vStr);
540 if (FAILED(spClr->GetVersionString(vStr, &cStr)))
541 continue;
542
543 ++cntClr;
544
545 if ((cciOptions & cciAnyFx) != 0)
546 {
547 spChosenClr = spClr.Detach();
548 break;
549 }
550
551 QWORD ver = VerString2Qword(vStr);
552 if ((cciOptions & cciLatestFx) != 0)
553 {
554 if (ver > verMax)
555 {
556 verMax = ver;
557 spChosenClr = spClr.Detach();
558 }
559 }
560 }
561
562 if (cntClr == 0 || spChosenClr == NULL)
563 {
564 *ppClr = NULL;
565 return E_NOINTERFACE;
566 }
567 else
568 {
569 *ppClr = spChosenClr.Detach();
570 return S_OK;
571 }
572}
573
574
575/**********************************************************************\
576* Routine Description: *
577* *
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\**********************************************************************/
582QWORD VerString2Qword(LPCWSTR vStr)
583{
584 _ASSERTE(vStr[0] == L'v' || vStr[0] == L'V');
585 QWORD result = 0;
586
587 DWORD v1, v2, v3;
588 if (swscanf_s(vStr+1, W("%d.%d.%d"), &v1, &v2, &v3) == 3)
589 {
590 result = ((QWORD)v1 << 48) | ((QWORD)v2 << 32) | ((QWORD)v3 << 16);
591 }
592 else if (swscanf_s(vStr+1, W("%d.%d"), &v1, &v2) == 2)
593 {
594 result = ((QWORD)v1 << 48) | ((QWORD)v2 << 32);
595 }
596 else if (swscanf_s(vStr+1, W("%d"), &v1) == 1)
597 {
598 result = ((QWORD)v1 << 48);
599 }
600
601 return result;
602}
603
604
605/**********************************************************************\
606* Routine Description: *
607* *
608* GetPathFromModule() returns the name of the folder containing the *
609* file associated with hModule. *
610 \**********************************************************************/
611BOOL GetPathFromModule(
612 HMODULE hModule,
613 __in_ecount(cFqPath) LPWSTR fqPath,
614 DWORD cFqPath)
615{
616 int len = GetModuleFileNameW(hModule, fqPath, cFqPath);
617 if (len == 0 || len == cFqPath)
618 return FALSE;
619
620 WCHAR *pLastSep = _wcsrchr(fqPath, DIRECTORY_SEPARATOR_CHAR_W);
621 if (pLastSep == NULL || pLastSep+1 >= fqPath+cFqPath)
622 return FALSE;
623
624 *(pLastSep+1) = L'\0';
625
626 return TRUE;
627}
628
629}
630
631/**********************************************************************\
632* Routine Description: *
633* *
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\**********************************************************************/
650HRESULT CreateInstanceCustom(
651 REFCLSID clsid,
652 REFIID iid,
653 LPCWSTR dllName,
654 CIOptions cciOptions,
655 void** ppItf)
656{
657 return com_activation::CreateInstanceCustomImpl(clsid, iid, dllName, cciOptions, ppItf);
658}
659
660
661
662
663/**********************************************************************\
664* Routine Description: *
665* *
666* This function is called to get the memory address given a symbol *
667* name. It handles difference in symbol name between ntsd and *
668* windbg. *
669* *
670\**********************************************************************/
671DWORD_PTR GetValueFromExpression (___in __in_z const char *const instr)
672{
673 ULONG64 dwAddr;
674 const char *str = instr;
675 char name[256];
676
677 dwAddr = 0;
678 HRESULT hr = g_ExtSymbols->GetOffsetByName (str, &dwAddr);
679 if (SUCCEEDED(hr))
680 return (DWORD_PTR)dwAddr;
681 else if (hr == S_FALSE && dwAddr)
682 return (DWORD_PTR)dwAddr;
683
684 strcpy_s (name, _countof(name), str);
685 char *ptr;
686 if ((ptr = strstr (name, "__")) != NULL)
687 {
688 ptr[0] = ':';
689 ptr[1] = ':';
690 ptr += 2;
691 while ((ptr = strstr(ptr, "__")) != NULL)
692 {
693 ptr[0] = ':';
694 ptr[1] = ':';
695 ptr += 2;
696 }
697 dwAddr = 0;
698 hr = g_ExtSymbols->GetOffsetByName (name, &dwAddr);
699 if (SUCCEEDED(hr))
700 return (DWORD_PTR)dwAddr;
701 else if (hr == S_FALSE && dwAddr)
702 return (DWORD_PTR)dwAddr;
703 }
704 else if ((ptr = strstr (name, "::")) != NULL)
705 {
706 ptr[0] = '_';
707 ptr[1] = '_';
708 ptr += 2;
709 while ((ptr = strstr(ptr, "::")) != NULL)
710 {
711 ptr[0] = '_';
712 ptr[1] = '_';
713 ptr += 2;
714 }
715 dwAddr = 0;
716 hr = g_ExtSymbols->GetOffsetByName (name, &dwAddr);
717 if (SUCCEEDED(hr))
718 return (DWORD_PTR)dwAddr;
719 else if (hr == S_FALSE && dwAddr)
720 return (DWORD_PTR)dwAddr;
721 }
722 return 0;
723}
724
725#endif // FEATURE_PAL
726
727ModuleInfo moduleInfo[MSCOREND] = {{0,FALSE,0},{0,FALSE,0},{0,FALSE,0}};
728
729void ReportOOM()
730{
731 ExtOut("SOS Error: Out of memory\n");
732}
733
734HRESULT CheckEEDll()
735{
736#ifndef FEATURE_PAL
737 VS_FIXEDFILEINFO ee = {};
738
739 static VS_FIXEDFILEINFO sos = {};
740 static BOOL sosDataInit = FALSE;
741
742 BOOL result = GetEEVersion(&ee);
743 if (result && !sosDataInit)
744 {
745 result = GetSOSVersion(&sos);
746
747 if (result)
748 sosDataInit = TRUE;
749 }
750
751 // We will ignore errors because it's possible sos is being loaded before CLR.
752 if (result)
753 {
754 if ((ee.dwFileVersionMS != sos.dwFileVersionMS) || (ee.dwFileVersionLS != sos.dwFileVersionLS))
755 {
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));
763
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));
769 }
770 }
771
772 DEBUG_MODULE_PARAMETERS Params;
773
774 // Do we have clr.dll
775 if (moduleInfo[MSCORWKS].baseAddr == 0)
776 {
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)
780 {
781 g_ExtSymbols->GetModuleParameters (1, &moduleInfo[MSCORWKS].baseAddr, 0, &Params);
782 if (Params.SymbolType == SymDeferred)
783 {
784 g_ExtSymbols->Reload("/f " MAIN_CLR_DLL_NAME_A);
785 g_ExtSymbols->GetModuleParameters (1, &moduleInfo[MSCORWKS].baseAddr, 0, &Params);
786 }
787
788 if (Params.SymbolType == SymPdb || Params.SymbolType == SymDia)
789 {
790 moduleInfo[MSCORWKS].hasPdb = TRUE;
791 }
792
793 moduleInfo[MSCORWKS].size = Params.Size;
794 }
795 if (moduleInfo[MSCORWKS].baseAddr != 0 && moduleInfo[MSCORWKS].hasPdb == FALSE)
796 ExtOut("PDB symbol for clr.dll not loaded\n");
797 }
798
799 return (moduleInfo[MSCORWKS].baseAddr != 0) ? S_OK : E_FAIL;
800#else
801 return S_OK;
802#endif // FEATURE_PAL
803}
804
805EEFLAVOR GetEEFlavor ()
806{
807#ifdef FEATURE_PAL
808 return MSCORWKS;
809#else // FEATUER_PAL
810 EEFLAVOR flavor = UNKNOWNEE;
811
812 if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A,0,NULL,NULL))) {
813 flavor = MSCORWKS;
814 }
815 return flavor;
816#endif // FEATURE_PAL else
817}
818
819BOOL IsDumpFile ()
820{
821 static int g_fDumpFile = -1;
822 if (g_fDumpFile == -1) {
823 ULONG Class;
824 ULONG Qualifier;
825 g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
826 if (Qualifier >= DEBUG_DUMP_SMALL)
827 g_fDumpFile = 1;
828 else
829 g_fDumpFile = 0;
830 }
831 return g_fDumpFile != 0;
832}
833
834BOOL g_InMinidumpSafeMode = FALSE;
835
836BOOL IsMiniDumpFileNODAC ()
837{
838#ifndef FEATURE_PAL
839 ULONG Class;
840 ULONG Qualifier;
841 g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
842 if (Qualifier == DEBUG_DUMP_SMALL)
843 {
844 g_ExtControl->GetDumpFormatFlags(&Qualifier);
845 if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0)
846 {
847 return TRUE;
848 }
849 }
850
851#endif // FEATURE_PAL
852 return FALSE;
853}
854
855
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.
859BOOL IsMiniDumpFile ()
860{
861#ifndef FEATURE_PAL
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())
866 {
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;
871 }
872
873#endif // FEATURE_PAL
874 return FALSE;
875}
876
877ULONG DebuggeeType()
878{
879 static ULONG Class = DEBUG_CLASS_UNINITIALIZED;
880 if (Class == DEBUG_CLASS_UNINITIALIZED) {
881 ULONG Qualifier;
882 g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
883 }
884 return Class;
885}
886
887#ifndef FEATURE_PAL
888
889// Check if a file exist
890BOOL FileExist (const char *filename)
891{
892 WIN32_FIND_DATA FindFileData;
893 HANDLE handle = FindFirstFile (filename, &FindFileData);
894 if (handle != INVALID_HANDLE_VALUE) {
895 FindClose (handle);
896 return TRUE;
897 }
898 else
899 return FALSE;
900}
901
902
903BOOL FileExist (const WCHAR *filename)
904{
905 WIN32_FIND_DATAW FindFileData;
906 HANDLE handle = FindFirstFileW (filename, &FindFileData);
907 if (handle != INVALID_HANDLE_VALUE) {
908 FindClose (handle);
909 return TRUE;
910 }
911 else
912 return FALSE;
913}
914
915/**********************************************************************\
916* Routine Description: *
917* *
918* This function is called to find out if a dll is bbt-ized *
919* *
920\**********************************************************************/
921BOOL IsRetailBuild (size_t base)
922{
923 IMAGE_DOS_HEADER DosHeader;
924 if (g_ExtData->ReadVirtual(TO_CDADDR(base), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
925 return FALSE;
926 IMAGE_NT_HEADERS32 Header32;
927 if (g_ExtData->ReadVirtual(TO_CDADDR(base + DosHeader.e_lfanew), &Header32, sizeof(Header32), NULL) != S_OK)
928 return FALSE;
929 // If there is no COMHeader, this can not be managed code.
930 if (Header32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress == 0)
931 return FALSE;
932
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;
936 size_t nbytes = 0;
937 while (nbytes < nSize) {
938 if (g_ExtData->ReadVirtual(TO_CDADDR(debugDirAddr+nbytes), &debugDir, sizeof(debugDir), NULL) != S_OK)
939 return FALSE;
940 if (debugDir.Type == 0xA) {
941 return TRUE;
942 }
943 nbytes += sizeof(debugDir);
944 }
945 return FALSE;
946}
947
948#endif // !FEATURE_PAL
949
950/**********************************************************************\
951* Routine Description: *
952* *
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". *
956* *
957\**********************************************************************/
958BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb,
959 PULONG lpcbBytesRead)
960{
961 BOOL bRet = FALSE;
962
963 bRet = SUCCEEDED(g_ExtData->ReadVirtual(TO_CDADDR(offset), lpBuffer, cb,
964 lpcbBytesRead));
965
966 if (!bRet)
967 {
968 cb = (ULONG)(NextOSPageAddress(offset) - offset);
969 bRet = SUCCEEDED(g_ExtData->ReadVirtual(TO_CDADDR(offset), lpBuffer, cb,
970 lpcbBytesRead));
971 }
972 return bRet;
973}
974
975ULONG OSPageSize ()
976{
977 static ULONG pageSize = 0;
978 if (pageSize == 0)
979 g_ExtControl->GetPageSize(&pageSize);
980
981 return pageSize;
982}
983
984size_t NextOSPageAddress (size_t addr)
985{
986 size_t pageSize = OSPageSize();
987 return (addr+pageSize)&(~(pageSize-1));
988}
989
990
991/**********************************************************************\
992* Routine Description: *
993* *
994* This function is called to get the address of MethodDesc *
995* given an ip address *
996* *
997\**********************************************************************/
998void IP2MethodDesc (DWORD_PTR IP, DWORD_PTR &methodDesc, JITTypes &jitType,
999 DWORD_PTR &gcinfoAddr)
1000{
1001
1002 CLRDATA_ADDRESS EIP = TO_CDADDR(IP);
1003 DacpCodeHeaderData codeHeaderData;
1004
1005 methodDesc = NULL;
1006 gcinfoAddr = NULL;
1007
1008 if (codeHeaderData.Request(g_sos, EIP) != S_OK)
1009 {
1010 return;
1011 }
1012
1013 methodDesc = (DWORD_PTR) codeHeaderData.MethodDescPtr;
1014 jitType = (JITTypes) codeHeaderData.JITType;
1015 gcinfoAddr = (DWORD_PTR) codeHeaderData.GCInfo;
1016}
1017
1018BOOL IsValueField (DacpFieldDescData *pFD)
1019{
1020 return (pFD->Type == ELEMENT_TYPE_VALUETYPE);
1021}
1022
1023void DisplayDataMember (DacpFieldDescData* pFD, DWORD_PTR dwAddr, BOOL fAlign=TRUE)
1024{
1025 if (dwAddr > 0)
1026 {
1027 // we must have called this function for a "real" (non-zero size) data type
1028 PREFIX_ASSUME(gElementTypeInfo[pFD->Type] != 0);
1029
1030 DWORD_PTR dwTmp = dwAddr;
1031 bool bVTStatic = (pFD->bIsStatic && pFD->Type == ELEMENT_TYPE_VALUETYPE);
1032
1033 if (gElementTypeInfo[pFD->Type] != NO_SIZE || bVTStatic)
1034 {
1035 union Value
1036 {
1037 char ch;
1038 short Short;
1039 DWORD_PTR ptr;
1040 int Int;
1041 unsigned int UInt;
1042 __int64 Int64;
1043 unsigned __int64 UInt64;
1044 float Float;
1045 double Double;
1046 } value;
1047
1048 ZeroMemory(&value, sizeof(value));
1049 if (bVTStatic)
1050 {
1051 // static VTypes are boxed
1052 moveBlock (value, dwTmp, gElementTypeInfo[ELEMENT_TYPE_CLASS]);
1053 }
1054 else
1055 {
1056 moveBlock (value, dwTmp, gElementTypeInfo[pFD->Type]);
1057 }
1058
1059 switch (pFD->Type)
1060 {
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)
1065 if (fAlign)
1066 ExtOut("%" POINTERSIZE "hd", (short)value.ch);
1067 else
1068 ExtOut("%d", value.ch);
1069 break;
1070 case ELEMENT_TYPE_I2:
1071 if (fAlign)
1072 ExtOut("%" POINTERSIZE "hd", value.Short);
1073 else
1074 ExtOut("%d", value.Short);
1075 break;
1076 case ELEMENT_TYPE_I4:
1077 if (fAlign)
1078 ExtOut("%" POINTERSIZE "d", value.Int);
1079 else
1080 ExtOut("%d", value.Int);
1081 break;
1082 case ELEMENT_TYPE_I8:
1083 ExtOut("%I64d", value.Int64);
1084 break;
1085 case ELEMENT_TYPE_U1:
1086 case ELEMENT_TYPE_BOOLEAN:
1087 if (fAlign)
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);
1092 else
1093 ExtOut("%u", value.ch);
1094 break;
1095 case ELEMENT_TYPE_U2:
1096 if (fAlign)
1097 ExtOut("%" POINTERSIZE "hu", value.Short);
1098 else
1099 ExtOut("%u", value.Short);
1100 break;
1101 case ELEMENT_TYPE_U4:
1102 if (fAlign)
1103 ExtOut("%" POINTERSIZE "u", value.UInt);
1104 else
1105 ExtOut("%u", value.UInt);
1106 break;
1107 case ELEMENT_TYPE_U8:
1108 ExtOut("%I64u", value.UInt64);
1109 break;
1110 case ELEMENT_TYPE_I:
1111 case ELEMENT_TYPE_U:
1112 if (fAlign)
1113 ExtOut("%" POINTERSIZE "p", SOS_PTR(value.ptr));
1114 else
1115 ExtOut("%p", SOS_PTR(value.ptr));
1116 break;
1117 case ELEMENT_TYPE_R4:
1118 ExtOut("%f", value.Float);
1119 break;
1120 case ELEMENT_TYPE_R8:
1121 ExtOut("%f", value.Double);
1122 break;
1123 case ELEMENT_TYPE_CHAR:
1124 if (fAlign)
1125 ExtOut("%" POINTERSIZE "hx", value.Short);
1126 else
1127 ExtOut("%x", value.Short);
1128 break;
1129 case ELEMENT_TYPE_VALUETYPE:
1130 if (value.ptr)
1131 DMLOut(DMLValueClass(pFD->MTOfType, dwTmp));
1132 else
1133 ExtOut("%p", SOS_PTR(0));
1134 break;
1135 default:
1136 if (value.ptr)
1137 DMLOut(DMLObject(value.ptr));
1138 else
1139 ExtOut("%p", SOS_PTR(0));
1140 break;
1141 }
1142 }
1143 else
1144 {
1145 if (pFD->Type == ELEMENT_TYPE_VALUETYPE)
1146 DMLOut(DMLValueClass(pFD->MTOfType, dwTmp));
1147 else
1148 ExtOut("%p", SOS_PTR(0));
1149 }
1150 }
1151 else
1152 {
1153 ExtOut("%" POINTERSIZE "s", " ");
1154 }
1155}
1156
1157void GetStaticFieldPTR(DWORD_PTR* pOutPtr, DacpDomainLocalModuleData* pDLMD, DacpMethodTableData* pMTD, DacpFieldDescData* pFDD, BYTE* pFlags = 0)
1158{
1159 DWORD_PTR dwTmp;
1160
1161 if (pFDD->Type == ELEMENT_TYPE_VALUETYPE
1162 || pFDD->Type == ELEMENT_TYPE_CLASS)
1163 {
1164 dwTmp = (DWORD_PTR) pDLMD->pGCStaticDataStart + pFDD->dwOffset;
1165 }
1166 else
1167 {
1168 dwTmp = (DWORD_PTR) pDLMD->pNonGCStaticDataStart + pFDD->dwOffset;
1169 }
1170
1171 *pOutPtr = 0;
1172
1173 if (pMTD->bIsDynamic)
1174 {
1175 ExtOut("dynamic statics NYI");
1176 return;
1177 }
1178 else
1179 {
1180 *pOutPtr = dwTmp;
1181 }
1182 return;
1183}
1184
1185void GetDLMFlags(DacpDomainLocalModuleData* pDLMD, DacpMethodTableData* pMTD, BYTE* pFlags)
1186{
1187 if (pMTD->bIsDynamic)
1188 {
1189 ExtOut("dynamic statics NYI");
1190 return;
1191 }
1192 else
1193 {
1194 if (pFlags)
1195 {
1196 BYTE flags;
1197 DWORD_PTR pTargetFlags = (DWORD_PTR) pDLMD->pClassData + RidFromToken(pMTD->cl) - 1;
1198 move_xp (flags, pTargetFlags);
1199
1200 *pFlags = flags;
1201 }
1202 }
1203 return;
1204}
1205
1206void GetThreadStaticFieldPTR(DWORD_PTR* pOutPtr, DacpThreadLocalModuleData* pTLMD, DacpMethodTableData* pMTD, DacpFieldDescData* pFDD, BYTE* pFlags = 0)
1207{
1208 DWORD_PTR dwTmp;
1209
1210 if (pFDD->Type == ELEMENT_TYPE_VALUETYPE
1211 || pFDD->Type == ELEMENT_TYPE_CLASS)
1212 {
1213 dwTmp = (DWORD_PTR) pTLMD->pGCStaticDataStart + pFDD->dwOffset;
1214 }
1215 else
1216 {
1217 dwTmp = (DWORD_PTR) pTLMD->pNonGCStaticDataStart + pFDD->dwOffset;
1218 }
1219
1220 *pOutPtr = 0;
1221
1222 if (pMTD->bIsDynamic)
1223 {
1224 ExtOut("dynamic thread statics NYI");
1225 return;
1226 }
1227 else
1228 {
1229 if (pFlags)
1230 {
1231 BYTE flags;
1232 DWORD_PTR pTargetFlags = (DWORD_PTR) pTLMD->pClassData + RidFromToken(pMTD->cl) - 1;
1233 move_xp (flags, pTargetFlags);
1234
1235 *pFlags = flags;
1236 }
1237
1238 *pOutPtr = dwTmp;
1239 }
1240 return;
1241}
1242
1243void DisplaySharedStatic(ULONG64 dwModuleDomainID, DacpMethodTableData* pMT, DacpFieldDescData *pFD)
1244{
1245 DacpAppDomainStoreData adsData;
1246 if (adsData.Request(g_sos)!=S_OK)
1247 {
1248 ExtOut("Unable to get AppDomain information\n");
1249 }
1250
1251 ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adsData.DomainCount];
1252 if (pArray==NULL)
1253 {
1254 ReportOOM();
1255 return;
1256 }
1257
1258 if (g_sos->GetAppDomainList(adsData.DomainCount,pArray, NULL)!=S_OK)
1259 {
1260 ExtOut("Unable to get array of AppDomains\n");
1261 return;
1262 }
1263
1264#if defined(_TARGET_WIN64_)
1265 ExtOut(" >> Domain:Value ");
1266#else
1267 ExtOut(" >> Domain:Value ");
1268#endif
1269 // Skip the SystemDomain and SharedDomain
1270 for (int i = 0; i < adsData.DomainCount ; i ++)
1271 {
1272 DacpAppDomainData appdomainData;
1273 if (appdomainData.Request(g_sos,pArray[i])!=S_OK)
1274 {
1275 ExtOut("Unable to get AppDomain %lx\n",pArray[i]);
1276 return;
1277 }
1278
1279 DacpDomainLocalModuleData vDomainLocalModule;
1280 if (g_sos->GetDomainLocalModuleDataFromAppDomain(appdomainData.AppDomainPtr, (int)dwModuleDomainID, &vDomainLocalModule) != S_OK)
1281 {
1282 DMLOut(" %s:NotInit ", DMLDomain(pArray[i]));
1283 continue;
1284 }
1285
1286 DWORD_PTR dwTmp;
1287 BYTE Flags = 0;
1288 GetStaticFieldPTR(&dwTmp, &vDomainLocalModule , pMT, pFD, &Flags);
1289
1290 if ((Flags&1) == 0) {
1291 // We have not initialized this yet.
1292 DMLOut(" %s:NotInit ", DMLDomain(pArray[i]));
1293 continue;
1294 }
1295 else if (Flags & 2) {
1296 // We have not initialized this yet.
1297 DMLOut(" %s:FailInit", DMLDomain(pArray[i]));
1298 continue;
1299 }
1300
1301 DMLOut(" %s:", DMLDomain(appdomainData.AppDomainPtr));
1302 DisplayDataMember(pFD, dwTmp, FALSE);
1303 }
1304 ExtOut(" <<\n");
1305}
1306
1307void DisplayThreadStatic(DacpModuleData* pModule, DacpMethodTableData* pMT, DacpFieldDescData *pFD)
1308{
1309 SIZE_T dwModuleIndex = (SIZE_T)pModule->dwModuleIndex;
1310 SIZE_T dwModuleDomainID = (SIZE_T)pModule->dwModuleID;
1311
1312 DacpThreadStoreData ThreadStore;
1313 ThreadStore.Request(g_sos);
1314
1315 ExtOut(" >> Thread:Value");
1316 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
1317 while (CurThread)
1318 {
1319 DacpThreadData vThread;
1320 if (vThread.Request(g_sos, CurThread) != S_OK)
1321 {
1322 ExtOut(" error getting thread %p, aborting this field\n", SOS_PTR(CurThread));
1323 return;
1324 }
1325
1326 if (vThread.osThreadId != 0)
1327 {
1328 CLRDATA_ADDRESS appDomainAddr = vThread.domain;
1329
1330 // Get the TLM
1331 DacpThreadLocalModuleData vThreadLocalModule;
1332 if (g_sos->GetThreadLocalModuleData(CurThread, (int)dwModuleIndex, &vThreadLocalModule) != S_OK)
1333 {
1334 // Not initialized, go to next thread
1335 // and continue looping
1336 CurThread = vThread.nextThread;
1337 continue;
1338 }
1339
1340 DWORD_PTR dwTmp;
1341 BYTE Flags = 0;
1342 GetThreadStaticFieldPTR(&dwTmp, &vThreadLocalModule, pMT, pFD, &Flags);
1343
1344 if ((Flags&4) == 0)
1345 {
1346 // Not allocated, go to next thread
1347 // and continue looping
1348 CurThread = vThread.nextThread;
1349 continue;
1350 }
1351
1352 ExtOut(" %x:", vThread.osThreadId);
1353 DisplayDataMember(pFD, dwTmp, FALSE);
1354 }
1355
1356 // Go to next thread
1357 CurThread = vThread.nextThread;
1358 }
1359 ExtOut(" <<\n");
1360}
1361
1362const char * ElementTypeName(unsigned type)
1363{
1364 switch (type) {
1365 case ELEMENT_TYPE_PTR:
1366 return "PTR";
1367 break;
1368 case ELEMENT_TYPE_BYREF:
1369 return "BYREF";
1370 break;
1371 case ELEMENT_TYPE_VALUETYPE:
1372 return "VALUETYPE";
1373 break;
1374 case ELEMENT_TYPE_CLASS:
1375 return "CLASS";
1376 break;
1377 case ELEMENT_TYPE_VAR:
1378 return "VAR";
1379 break;
1380 case ELEMENT_TYPE_ARRAY:
1381 return "ARRAY";
1382 break;
1383 case ELEMENT_TYPE_FNPTR:
1384 return "FNPTR";
1385 break;
1386 case ELEMENT_TYPE_SZARRAY:
1387 return "SZARRAY";
1388 break;
1389 case ELEMENT_TYPE_MVAR:
1390 return "MVAR";
1391 break;
1392 default:
1393 if ((type >= _countof(CorElementTypeName)) || (CorElementTypeName[type] == NULL))
1394 {
1395 return "";
1396 }
1397 return CorElementTypeName[type];
1398 break;
1399 }
1400} // ElementTypeName
1401
1402const char * ElementTypeNamespace(unsigned type)
1403{
1404 if ((type >= _countof(CorElementTypeName)) || (CorElementTypeNamespace[type] == NULL))
1405 {
1406 return "";
1407 }
1408 return CorElementTypeNamespace[type];
1409}
1410
1411void ComposeName_s(CorElementType Type, __out_ecount(capacity_buffer) LPSTR buffer, size_t capacity_buffer)
1412{
1413 const char *p = ElementTypeNamespace(Type);
1414 if ((p) && (*p != '\0'))
1415 {
1416 strcpy_s(buffer,capacity_buffer,p);
1417 strcat_s(buffer,capacity_buffer,".");
1418 strcat_s(buffer,capacity_buffer,ElementTypeName(Type));
1419 }
1420 else
1421 {
1422 strcpy_s(buffer,capacity_buffer,ElementTypeName(Type));
1423 }
1424}
1425
1426// NOTE: pszName is changed
1427// INPUT MAXCHARS RETURN
1428// HelloThere 5 ...re
1429// HelloThere 8 ...There
1430LPWSTR FormatTypeName (__out_ecount (maxChars) LPWSTR pszName, UINT maxChars)
1431{
1432 UINT iStart = 0;
1433 UINT iLen = (int) _wcslen(pszName);
1434 if (iLen > maxChars)
1435 {
1436 iStart = iLen - maxChars;
1437 UINT numDots = (maxChars < 3) ? maxChars : 3;
1438 for (UINT i=0; i < numDots; i++)
1439 pszName[iStart+i] = '.';
1440 }
1441 return pszName + iStart;
1442}
1443
1444/**********************************************************************\
1445* Routine Description: *
1446* *
1447* This function is called to dump all fields of a managed object. *
1448* dwStartAddr specifies the beginning memory address. *
1449* bFirst is used to avoid printing header every time. *
1450* *
1451\**********************************************************************/
1452void DisplayFields(CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodTableFieldData *pMTFD, DWORD_PTR dwStartAddr, BOOL bFirst, BOOL bValueClass)
1453{
1454 static DWORD numInstanceFields = 0;
1455 if (bFirst)
1456 {
1457 ExtOutIndent();
1458 ExtOut("%" POINTERSIZE "s %8s %8s %20s %2s %8s %" POINTERSIZE "s %s\n",
1459 "MT", "Field", "Offset", "Type", "VT", "Attr", "Value", "Name");
1460 numInstanceFields = 0;
1461 }
1462
1463 if (pMTD->ParentMethodTable)
1464 {
1465 DacpMethodTableData vParentMethTable;
1466 if (vParentMethTable.Request(g_sos,pMTD->ParentMethodTable) != S_OK)
1467 {
1468 ExtOut("Invalid parent MethodTable\n");
1469 return;
1470 }
1471
1472 DacpMethodTableFieldData vParentMethTableFields;
1473 if (vParentMethTableFields.Request(g_sos,pMTD->ParentMethodTable) != S_OK)
1474 {
1475 ExtOut("Invalid parent EEClass\n");
1476 return;
1477 }
1478
1479 DisplayFields(pMTD->ParentMethodTable, &vParentMethTable, &vParentMethTableFields, dwStartAddr, FALSE, bValueClass);
1480 }
1481
1482 DWORD numStaticFields = 0;
1483 CLRDATA_ADDRESS dwAddr = pMTFD->FirstField;
1484 DacpFieldDescData vFieldDesc;
1485
1486 // Get the module name
1487 DacpModuleData module;
1488 if (module.Request(g_sos, pMTD->Module)!=S_OK)
1489 return;
1490
1491 ToRelease<IMetaDataImport> pImport = MDImportForModule(&module);
1492
1493 while (numInstanceFields < pMTFD->wNumInstanceFields
1494 || numStaticFields < pMTFD->wNumStaticFields)
1495 {
1496 if (IsInterrupt())
1497 return;
1498
1499 ExtOutIndent ();
1500
1501 if ((vFieldDesc.Request(g_sos, dwAddr)!=S_OK) ||
1502 (vFieldDesc.Type >= ELEMENT_TYPE_MAX))
1503 {
1504 ExtOut("Unable to display fields\n");
1505 return;
1506 }
1507 dwAddr = vFieldDesc.NextField;
1508
1509 DWORD offset = vFieldDesc.dwOffset;
1510 if(!(vFieldDesc.bIsThreadLocal && vFieldDesc.bIsStatic))
1511 {
1512 if (!bValueClass)
1513 {
1514 offset += sizeof(BaseObject);
1515 }
1516 }
1517
1518 DMLOut("%s %8x %8x ", DMLMethodTable(vFieldDesc.MTOfType),
1519 TokenFromRid(vFieldDesc.mb, mdtFieldDef),
1520 offset);
1521
1522 char ElementName[mdNameLen];
1523 if ((vFieldDesc.Type == ELEMENT_TYPE_VALUETYPE ||
1524 vFieldDesc.Type == ELEMENT_TYPE_CLASS) && vFieldDesc.MTOfType)
1525 {
1526 NameForMT_s((DWORD_PTR)vFieldDesc.MTOfType, g_mdName, mdNameLen);
1527 ExtOut("%20.20S ", FormatTypeName(g_mdName, 20));
1528 }
1529 else
1530 {
1531 if (vFieldDesc.Type == ELEMENT_TYPE_CLASS && vFieldDesc.TokenOfType != mdTypeDefNil)
1532 {
1533 // Get the name from Metadata!!!
1534 NameForToken_s(TokenFromRid(vFieldDesc.TokenOfType, mdtTypeDef), pImport, g_mdName, mdNameLen, false);
1535 ExtOut("%20.20S ", FormatTypeName(g_mdName, 20));
1536 }
1537 else
1538 {
1539 // If ET type from signature is different from fielddesc, then the signature one is more descriptive.
1540 // For example, E_T_STRING in field desc will be E_T_CLASS. In minidump's case, we won't have
1541 // the method table for it.
1542 ComposeName_s(vFieldDesc.Type != vFieldDesc.sigType ? vFieldDesc.sigType : vFieldDesc.Type, ElementName, sizeof(ElementName)/sizeof(ElementName[0]));
1543 ExtOut("%20.20s ", ElementName);
1544 }
1545 }
1546
1547 ExtOut("%2s ", (IsElementValueType(vFieldDesc.Type)) ? "1" : "0");
1548
1549 if (vFieldDesc.bIsStatic && vFieldDesc.bIsThreadLocal)
1550 {
1551 numStaticFields ++;
1552 ExtOut("%8s ", vFieldDesc.bIsThreadLocal ? "TLstatic" : "CLstatic");
1553
1554 NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
1555 ExtOut(" %S\n", g_mdName);
1556
1557 if (IsMiniDumpFile())
1558 {
1559 ExtOut(" <no information>\n");
1560 }
1561 else
1562 {
1563 if (vFieldDesc.bIsThreadLocal)
1564 {
1565 DacpModuleData vModule;
1566 if (vModule.Request(g_sos,pMTD->Module) == S_OK)
1567 {
1568 DisplayThreadStatic(&vModule, pMTD, &vFieldDesc);
1569 }
1570 }
1571 }
1572
1573 }
1574 else if (vFieldDesc.bIsStatic)
1575 {
1576 numStaticFields ++;
1577
1578 ExtOut("%8s ", "static");
1579
1580 DacpDomainLocalModuleData vDomainLocalModule;
1581
1582 // The MethodTable isn't shared, so the module must not be loaded domain neutral. We can
1583 // get the specific DomainLocalModule instance without needing to know the AppDomain in advance.
1584 if (g_sos->GetDomainLocalModuleDataFromModule(pMTD->Module, &vDomainLocalModule) != S_OK)
1585 {
1586 ExtOut(" <no information>\n");
1587 }
1588 else
1589 {
1590 DWORD_PTR dwTmp;
1591 GetStaticFieldPTR(&dwTmp, &vDomainLocalModule, pMTD, &vFieldDesc);
1592 DisplayDataMember(&vFieldDesc, dwTmp);
1593
1594 NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
1595 ExtOut(" %S\n", g_mdName);
1596 }
1597 }
1598 else
1599 {
1600 numInstanceFields ++;
1601
1602 ExtOut("%8s ", "instance");
1603
1604 if (dwStartAddr > 0)
1605 {
1606 DWORD_PTR dwTmp = dwStartAddr + vFieldDesc.dwOffset + (bValueClass ? 0 : sizeof(BaseObject));
1607 DisplayDataMember(&vFieldDesc, dwTmp);
1608 }
1609 else
1610 {
1611 ExtOut(" %8s", " ");
1612 }
1613
1614
1615 NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
1616 ExtOut(" %S\n", g_mdName);
1617 }
1618
1619 }
1620
1621 return;
1622}
1623
1624// Return value: -1 = error,
1625// 0 = field not found,
1626// > 0 = offset to field from objAddr
1627int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, __in_z LPCWSTR wszFieldName, BOOL bFirst)
1628{
1629 TADDR mt = NULL;
1630 if FAILED(GetMTOfObject(TO_TADDR(cdaObj), &mt))
1631 return -1;
1632
1633 return GetObjFieldOffset(cdaObj, TO_CDADDR(mt), wszFieldName, bFirst);
1634}
1635
1636// Return value: -1 = error,
1637// 0 = field not found,
1638// > 0 = offset to field from objAddr
1639int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName,
1640 BOOL bFirst/*=TRUE*/, DacpFieldDescData* pDacpFieldDescData/*=NULL*/)
1641{
1642
1643#define EXITPOINT(EXPR) do { if(!(EXPR)) { return -1; } } while (0)
1644
1645 DacpObjectData objData;
1646 DacpMethodTableData dmtd;
1647 DacpMethodTableFieldData vMethodTableFields;
1648 DacpFieldDescData vFieldDesc;
1649 DacpModuleData module;
1650 static DWORD numInstanceFields = 0; // Static due to recursion visiting parents
1651
1652 if (bFirst)
1653 {
1654 numInstanceFields = 0;
1655 }
1656
1657 EXITPOINT(objData.Request(g_sos, cdaObj) == S_OK);
1658 EXITPOINT(dmtd.Request(g_sos, cdaMT) == S_OK);
1659
1660 if (dmtd.ParentMethodTable)
1661 {
1662 DWORD retVal = GetObjFieldOffset (cdaObj, dmtd.ParentMethodTable,
1663 wszFieldName, FALSE, pDacpFieldDescData);
1664 if (retVal != 0)
1665 {
1666 // return in case of error or success.
1667 // Fall through for field-not-found.
1668 return retVal;
1669 }
1670 }
1671
1672 EXITPOINT (vMethodTableFields.Request(g_sos,cdaMT) == S_OK);
1673 EXITPOINT (module.Request(g_sos,dmtd.Module) == S_OK);
1674
1675 CLRDATA_ADDRESS dwAddr = vMethodTableFields.FirstField;
1676 ToRelease<IMetaDataImport> pImport = MDImportForModule(&module);
1677
1678 while (numInstanceFields < vMethodTableFields.wNumInstanceFields)
1679 {
1680 EXITPOINT (vFieldDesc.Request(g_sos, dwAddr) == S_OK);
1681
1682 if (!vFieldDesc.bIsStatic)
1683 {
1684 DWORD offset = vFieldDesc.dwOffset + sizeof(BaseObject);
1685 NameForToken_s (TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
1686 if (_wcscmp (wszFieldName, g_mdName) == 0)
1687 {
1688 if (pDacpFieldDescData != NULL)
1689 {
1690 *pDacpFieldDescData = vFieldDesc;
1691 }
1692 return offset;
1693 }
1694 numInstanceFields ++;
1695 }
1696
1697 dwAddr = vFieldDesc.NextField;
1698 }
1699
1700 // Field name not found...
1701 return 0;
1702
1703#undef EXITPOINT
1704}
1705
1706
1707// Return value: -1 = error
1708// -2 = not found
1709// >= 0 = offset to field from cdaValue
1710int GetValueFieldOffset(CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, DacpFieldDescData* pDacpFieldDescData)
1711{
1712#define EXITPOINT(EXPR) do { if(!(EXPR)) { return -1; } } while (0)
1713
1714 const int NOT_FOUND = -2;
1715 DacpMethodTableData dmtd;
1716 DacpMethodTableFieldData vMethodTableFields;
1717 DacpFieldDescData vFieldDesc;
1718 DacpModuleData module;
1719 static DWORD numInstanceFields = 0; // Static due to recursion visiting parents
1720 numInstanceFields = 0;
1721
1722 EXITPOINT(vMethodTableFields.Request(g_sos, cdaMT) == S_OK);
1723
1724 EXITPOINT(dmtd.Request(g_sos, cdaMT) == S_OK);
1725 EXITPOINT(module.Request(g_sos, dmtd.Module) == S_OK);
1726 if (dmtd.ParentMethodTable)
1727 {
1728 DWORD retVal = GetValueFieldOffset(dmtd.ParentMethodTable, wszFieldName, pDacpFieldDescData);
1729 if (retVal != NOT_FOUND)
1730 {
1731 // Return in case of error or success. Fall through for field-not-found.
1732 return retVal;
1733 }
1734 }
1735
1736 CLRDATA_ADDRESS dwAddr = vMethodTableFields.FirstField;
1737 ToRelease<IMetaDataImport> pImport = MDImportForModule(&module);
1738
1739 while (numInstanceFields < vMethodTableFields.wNumInstanceFields)
1740 {
1741 EXITPOINT(vFieldDesc.Request(g_sos, dwAddr) == S_OK);
1742
1743 if (!vFieldDesc.bIsStatic)
1744 {
1745 NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
1746 if (_wcscmp(wszFieldName, g_mdName) == 0)
1747 {
1748 if (pDacpFieldDescData != NULL)
1749 {
1750 *pDacpFieldDescData = vFieldDesc;
1751 }
1752 return vFieldDesc.dwOffset;
1753 }
1754 numInstanceFields++;
1755 }
1756
1757 dwAddr = vFieldDesc.NextField;
1758 }
1759
1760 // Field name not found...
1761 return NOT_FOUND;
1762
1763#undef EXITPOINT
1764}
1765
1766// Returns an AppDomain address if AssemblyPtr is loaded into that domain only. Otherwise
1767// returns NULL
1768CLRDATA_ADDRESS IsInOneDomainOnly(CLRDATA_ADDRESS AssemblyPtr)
1769{
1770 CLRDATA_ADDRESS appDomain = NULL;
1771
1772 DacpAppDomainStoreData adstore;
1773 if (adstore.Request(g_sos) != S_OK)
1774 {
1775 ExtOut("Unable to get appdomain store\n");
1776 return NULL;
1777 }
1778
1779 size_t AllocSize;
1780 if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), adstore.DomainCount, AllocSize))
1781 {
1782 ReportOOM();
1783 return NULL;
1784 }
1785
1786 ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adstore.DomainCount];
1787 if (pArray==NULL)
1788 {
1789 ReportOOM();
1790 return NULL;
1791 }
1792
1793 if (g_sos->GetAppDomainList(adstore.DomainCount, pArray, NULL)!=S_OK)
1794 {
1795 ExtOut ("Failed to get appdomain list\n");
1796 return NULL;
1797 }
1798
1799 for (int i = 0; i < adstore.DomainCount; i++)
1800 {
1801 if (IsInterrupt())
1802 return NULL;
1803
1804 DacpAppDomainData dadd;
1805 if (dadd.Request(g_sos, pArray[i]) != S_OK)
1806 {
1807 ExtOut ("Unable to get AppDomain %p\n", SOS_PTR(pArray[i]));
1808 return NULL;
1809 }
1810
1811 if (dadd.AssemblyCount)
1812 {
1813 size_t AssemblyAllocSize;
1814 if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), dadd.AssemblyCount, AssemblyAllocSize))
1815 {
1816 ReportOOM();
1817 return NULL;
1818 }
1819
1820 ArrayHolder<CLRDATA_ADDRESS> pAsmArray = new CLRDATA_ADDRESS[dadd.AssemblyCount];
1821 if (pAsmArray==NULL)
1822 {
1823 ReportOOM();
1824 return NULL;
1825 }
1826
1827 if (g_sos->GetAssemblyList(dadd.AppDomainPtr,dadd.AssemblyCount,pAsmArray, NULL)!=S_OK)
1828 {
1829 ExtOut("Unable to get array of Assemblies\n");
1830 return NULL;
1831 }
1832
1833 for (LONG n = 0; n < dadd.AssemblyCount; n ++)
1834 {
1835 if (IsInterrupt())
1836 return NULL;
1837
1838 if (AssemblyPtr == pAsmArray[n])
1839 {
1840 if (appDomain != NULL)
1841 {
1842 // We have found more than one AppDomain that loaded this
1843 // assembly, we must return NULL.
1844 return NULL;
1845 }
1846 appDomain = dadd.AppDomainPtr;
1847 }
1848 }
1849 }
1850 }
1851
1852
1853 return appDomain;
1854}
1855
1856CLRDATA_ADDRESS GetAppDomainForMT(CLRDATA_ADDRESS mtPtr)
1857{
1858 DacpMethodTableData mt;
1859 if (mt.Request(g_sos, mtPtr) != S_OK)
1860 {
1861 return NULL;
1862 }
1863
1864 DacpModuleData module;
1865 if (module.Request(g_sos, mt.Module) != S_OK)
1866 {
1867 return NULL;
1868 }
1869
1870 DacpAssemblyData assembly;
1871 if (assembly.Request(g_sos, module.Assembly) != S_OK)
1872 {
1873 return NULL;
1874 }
1875
1876 DacpAppDomainStoreData adstore;
1877 if (adstore.Request(g_sos) != S_OK)
1878 {
1879 return NULL;
1880 }
1881
1882 return (assembly.ParentDomain == adstore.sharedDomain) ?
1883 IsInOneDomainOnly(assembly.AssemblyPtr) :
1884 assembly.ParentDomain;
1885}
1886
1887CLRDATA_ADDRESS GetAppDomain(CLRDATA_ADDRESS objPtr)
1888{
1889 CLRDATA_ADDRESS appDomain = NULL;
1890
1891 DacpObjectData objData;
1892 if (objData.Request(g_sos,objPtr) != S_OK)
1893 {
1894 return NULL;
1895 }
1896
1897 // First check eeclass->module->assembly->domain.
1898 // Then check the object flags word
1899 // finally, search threads for a reference to the object, and look at the thread context.
1900
1901 DacpMethodTableData mt;
1902 if (mt.Request(g_sos,objData.MethodTable) != S_OK)
1903 {
1904 return NULL;
1905 }
1906
1907 DacpModuleData module;
1908 if (module.Request(g_sos,mt.Module) != S_OK)
1909 {
1910 return NULL;
1911 }
1912
1913 DacpAssemblyData assembly;
1914 if (assembly.Request(g_sos,module.Assembly) != S_OK)
1915 {
1916 return NULL;
1917 }
1918
1919 DacpAppDomainStoreData adstore;
1920 if (adstore.Request(g_sos) != S_OK)
1921 {
1922 return NULL;
1923 }
1924
1925 if (assembly.ParentDomain == adstore.sharedDomain)
1926 {
1927 sos::Object obj(TO_TADDR(objPtr));
1928 ULONG value = 0;
1929 if (!obj.TryGetHeader(value))
1930 {
1931 return NULL;
1932 }
1933
1934 DWORD adIndex = (value >> SBLK_APPDOMAIN_SHIFT) & SBLK_MASK_APPDOMAININDEX;
1935 if ( ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0) || adIndex==0)
1936 {
1937 // No AppDomainID information. We'll make use of a heuristic.
1938 // If the assembly is in the shared domain, we can report it as
1939 // being in domain X if the only other domain that has the assembly
1940 // loaded is domain X.
1941 appDomain = IsInOneDomainOnly(assembly.AssemblyPtr);
1942 if (appDomain == NULL && ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0))
1943 {
1944 if ((value & BIT_SBLK_IS_HASHCODE) == 0)
1945 {
1946 UINT index = value & MASK_SYNCBLOCKINDEX;
1947 // We have a syncblock, the appdomain ID may be in there.
1948 DacpSyncBlockData syncBlockData;
1949 if (syncBlockData.Request(g_sos,index) == S_OK)
1950 {
1951 appDomain = syncBlockData.appDomainPtr;
1952 }
1953 }
1954 }
1955 }
1956 else if ((value & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) == 0)
1957 {
1958 size_t AllocSize;
1959 if (!ClrSafeInt<size_t>::multiply(sizeof(CLRDATA_ADDRESS), adstore.DomainCount, AllocSize))
1960 {
1961 return NULL;
1962 }
1963 // we know we have a non-zero adIndex. Find the appdomain.
1964 ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[adstore.DomainCount];
1965 if (pArray==NULL)
1966 {
1967 return NULL;
1968 }
1969
1970 if (g_sos->GetAppDomainList(adstore.DomainCount, pArray, NULL)!=S_OK)
1971 {
1972 return NULL;
1973 }
1974
1975 for (int i = 0; i < adstore.DomainCount; i++)
1976 {
1977 DacpAppDomainData dadd;
1978 if (dadd.Request(g_sos, pArray[i]) != S_OK)
1979 {
1980 return NULL;
1981 }
1982 if (dadd.dwId == adIndex)
1983 {
1984 appDomain = pArray[i];
1985 break;
1986 }
1987 }
1988 }
1989 }
1990 else
1991 {
1992 appDomain = assembly.ParentDomain;
1993 }
1994
1995 return appDomain;
1996}
1997
1998HRESULT FileNameForModule (DWORD_PTR pModuleAddr, __out_ecount (MAX_LONGPATH) WCHAR *fileName)
1999{
2000 DacpModuleData ModuleData;
2001 fileName[0] = L'\0';
2002
2003 HRESULT hr = ModuleData.Request(g_sos, TO_CDADDR(pModuleAddr));
2004 if (SUCCEEDED(hr))
2005 {
2006 hr = FileNameForModule(&ModuleData,fileName);
2007 }
2008
2009 return hr;
2010}
2011
2012/**********************************************************************\
2013* Routine Description: *
2014* *
2015* This function is called to find the file name given a Module. *
2016* *
2017\**********************************************************************/
2018// fileName should be at least MAX_LONGPATH
2019HRESULT FileNameForModule (DacpModuleData *pModule, __out_ecount (MAX_LONGPATH) WCHAR *fileName)
2020{
2021 fileName[0] = L'\0';
2022
2023 HRESULT hr = S_OK;
2024 CLRDATA_ADDRESS dwAddr = pModule->File;
2025 if (dwAddr == 0)
2026 {
2027 // TODO: We have dynamic module
2028 return E_NOTIMPL;
2029 }
2030
2031 CLRDATA_ADDRESS base = 0;
2032 hr = g_sos->GetPEFileBase(dwAddr, &base);
2033 if (SUCCEEDED(hr))
2034 {
2035 hr = g_sos->GetPEFileName(dwAddr, MAX_LONGPATH, fileName, NULL);
2036 if (SUCCEEDED(hr))
2037 {
2038 if (fileName[0] != W('\0'))
2039 return hr; // done
2040 }
2041#ifndef FEATURE_PAL
2042 // Try the base *
2043 if (base)
2044 {
2045 hr = DllsName((ULONG_PTR) base, fileName);
2046 }
2047#endif // !FEATURE_PAL
2048 }
2049
2050 // If we got here, either DllsName worked, or we couldn't find a name
2051 return hr;
2052}
2053
2054void AssemblyInfo(DacpAssemblyData *pAssembly)
2055{
2056 ExtOut("ClassLoader: %p\n", SOS_PTR(pAssembly->ClassLoader));
2057 if ((ULONG64)pAssembly->AssemblySecDesc != NULL)
2058 ExtOut("SecurityDescriptor: %p\n", SOS_PTR(pAssembly->AssemblySecDesc));
2059 ExtOut(" Module Name\n");
2060
2061 ArrayHolder<CLRDATA_ADDRESS> Modules = new CLRDATA_ADDRESS[pAssembly->ModuleCount];
2062 if (Modules == NULL
2063 || g_sos->GetAssemblyModuleList(pAssembly->AssemblyPtr, pAssembly->ModuleCount, Modules, NULL) != S_OK)
2064 {
2065 ReportOOM();
2066 return;
2067 }
2068
2069 for (UINT n=0;n<pAssembly->ModuleCount;n++)
2070 {
2071 if (IsInterrupt())
2072 {
2073 return;
2074 }
2075
2076 CLRDATA_ADDRESS ModuleAddr = Modules[n];
2077 DMLOut("%s " WIN86_8SPACES, DMLModule(ModuleAddr));
2078 DacpModuleData moduleData;
2079 if (moduleData.Request(g_sos,ModuleAddr)==S_OK)
2080 {
2081 WCHAR fileName[MAX_LONGPATH];
2082 FileNameForModule (&moduleData, fileName);
2083 if (fileName[0])
2084 {
2085 ExtOut("%S\n", fileName);
2086 }
2087 else
2088 {
2089 ExtOut("%S\n", (moduleData.bIsReflection) ? W("Dynamic Module") : W("Unknown Module"));
2090 }
2091 }
2092 }
2093}
2094
2095const char *GetStageText(DacpAppDomainDataStage stage)
2096{
2097 switch(stage)
2098 {
2099 case STAGE_CREATING:
2100 return "CREATING";
2101 case STAGE_READYFORMANAGEDCODE:
2102 return "READYFORMANAGEDCODE";
2103 case STAGE_ACTIVE:
2104 return "ACTIVE";
2105 case STAGE_OPEN:
2106 return "OPEN";
2107 case STAGE_UNLOAD_REQUESTED:
2108 return "UNLOAD_REQUESTED";
2109 case STAGE_EXITING:
2110 return "EXITING";
2111 case STAGE_EXITED:
2112 return "EXITED";
2113 case STAGE_FINALIZING:
2114 return "FINALIZING";
2115 case STAGE_FINALIZED:
2116 return "FINALIZED";
2117 case STAGE_HANDLETABLE_NOACCESS:
2118 return "HANDLETABLE_NOACCESS";
2119 case STAGE_CLEARED:
2120 return "CLEARED";
2121 case STAGE_COLLECTED:
2122 return "COLLECTED";
2123 case STAGE_CLOSED:
2124 return "CLOSED";
2125 }
2126 return "UNKNOWN";
2127}
2128
2129/**********************************************************************\
2130* Routine Description: *
2131* *
2132* This function is called to dump the contents of a domain. *
2133* *
2134\**********************************************************************/
2135void DomainInfo (DacpAppDomainData *pDomain)
2136{
2137 ExtOut("LowFrequencyHeap: %p\n", SOS_PTR(pDomain->pLowFrequencyHeap));
2138 ExtOut("HighFrequencyHeap: %p\n", SOS_PTR(pDomain->pHighFrequencyHeap));
2139 ExtOut("StubHeap: %p\n", SOS_PTR(pDomain->pStubHeap));
2140 ExtOut("Stage: %s\n", GetStageText(pDomain->appDomainStage));
2141 if ((ULONG64)pDomain->AppSecDesc != NULL)
2142 ExtOut("SecurityDescriptor: %p\n", SOS_PTR(pDomain->AppSecDesc));
2143 ExtOut("Name: ");
2144
2145 if (g_sos->GetAppDomainName(pDomain->AppDomainPtr, mdNameLen, g_mdName, NULL)!=S_OK)
2146 {
2147 ExtOut("Error getting AppDomain friendly name\n");
2148 }
2149 else
2150 {
2151 ExtOut("%S\n", (g_mdName[0] != L'\0') ? g_mdName : W("None"));
2152 }
2153
2154 if (pDomain->AssemblyCount == 0)
2155 return;
2156
2157 ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[pDomain->AssemblyCount];
2158 if (pArray==NULL)
2159 {
2160 ReportOOM();
2161 return;
2162 }
2163
2164 if (g_sos->GetAssemblyList(pDomain->AppDomainPtr,pDomain->AssemblyCount,pArray, NULL)!=S_OK)
2165 {
2166 ExtOut("Unable to get array of Assemblies\n");
2167 return;
2168 }
2169
2170 LONG n;
2171 // Assembly vAssembly;
2172 for (n = 0; n < pDomain->AssemblyCount; n ++)
2173 {
2174 if (IsInterrupt())
2175 return;
2176
2177 if (n != 0)
2178 ExtOut("\n");
2179
2180 DMLOut("Assembly: %s", DMLAssembly(pArray[n]));
2181 DacpAssemblyData assemblyData;
2182 if (assemblyData.Request(g_sos, pArray[n], pDomain->AppDomainPtr) == S_OK)
2183 {
2184 if (assemblyData.isDynamic)
2185 ExtOut(" (Dynamic)");
2186
2187 ExtOut(" [");
2188 if (g_sos->GetAssemblyName(pArray[n], mdNameLen, g_mdName, NULL) == S_OK)
2189 ExtOut("%S", g_mdName);
2190 ExtOut("]\n");
2191
2192 AssemblyInfo(&assemblyData);
2193 }
2194 }
2195
2196 ExtOut("\n");
2197}
2198
2199/**********************************************************************\
2200* Routine Description: *
2201* *
2202* This function is called to find the name of a MethodDesc using *
2203* metadata API. *
2204* *
2205\**********************************************************************/
2206BOOL NameForMD_s (DWORD_PTR pMD, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName)
2207{
2208 mdName[0] = L'\0';
2209 CLRDATA_ADDRESS StartAddr = TO_CDADDR(pMD);
2210 DacpMethodDescData MethodDescData;
2211
2212 // don't need to check for minidump file as all commands are seals
2213 // We also do not have EEJitManager to validate anyway.
2214 //
2215 if (!IsMiniDumpFile() && MethodDescData.Request(g_sos,StartAddr) != S_OK)
2216 {
2217 ExtOut("%p is not a MethodDesc\n", SOS_PTR(StartAddr));
2218 return FALSE;
2219 }
2220
2221 if (g_sos->GetMethodDescName(StartAddr, mdNameLen, mdName, NULL) != S_OK)
2222 {
2223 wcscpy_s(mdName, capacity_mdName, W("UNKNOWN"));
2224 return FALSE;
2225 }
2226 return TRUE;
2227}
2228
2229/**********************************************************************\
2230* Routine Description: *
2231* *
2232* This function is called to find the name of a MethodTable using *
2233* metadata API. *
2234* *
2235\**********************************************************************/
2236BOOL NameForMT_s(DWORD_PTR MTAddr, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName)
2237{
2238 HRESULT hr = g_sos->GetMethodTableName(TO_CDADDR(MTAddr), (ULONG32)capacity_mdName, mdName, NULL);
2239 return SUCCEEDED(hr);
2240}
2241
2242WCHAR *CreateMethodTableName(TADDR mt, TADDR cmt)
2243{
2244 bool array = false;
2245 WCHAR *res = NULL;
2246
2247 if (mt == sos::MethodTable::GetFreeMT())
2248 {
2249 res = new WCHAR[5];
2250 wcscpy_s(res, 5, W("Free"));
2251 return res;
2252 }
2253
2254 if (mt == sos::MethodTable::GetArrayMT() && cmt != NULL)
2255 {
2256 mt = cmt;
2257 array = true;
2258 }
2259
2260 unsigned int needed = 0;
2261 HRESULT hr = g_sos->GetMethodTableName(mt, 0, NULL, &needed);
2262
2263 // If failed, we will return null.
2264 if (SUCCEEDED(hr))
2265 {
2266 // +2 for [], if we need it.
2267 res = new WCHAR[needed+2];
2268 hr = g_sos->GetMethodTableName(mt, needed, res, NULL);
2269
2270 if (FAILED(hr))
2271 {
2272 delete [] res;
2273 res = NULL;
2274 }
2275 else if (array)
2276 {
2277 res[needed-1] = '[';
2278 res[needed] = ']';
2279 res[needed+1] = 0;
2280 }
2281 }
2282
2283 return res;
2284}
2285
2286/**********************************************************************\
2287* Routine Description: *
2288* *
2289* Return TRUE if str2 is a substring of str1 and str1 and str2 *
2290* share the same file path.
2291* *
2292\**********************************************************************/
2293BOOL IsSameModuleName (const char *str1, const char *str2)
2294{
2295 if (strlen (str1) < strlen (str2))
2296 return FALSE;
2297 const char *ptr1 = str1 + strlen(str1)-1;
2298 const char *ptr2 = str2 + strlen(str2)-1;
2299 while (ptr2 >= str2)
2300 {
2301#ifndef FEATURE_PAL
2302 if (tolower(*ptr1) != tolower(*ptr2))
2303#else
2304 if (*ptr1 != *ptr2)
2305#endif
2306 {
2307 return FALSE;
2308 }
2309 ptr2--;
2310 ptr1--;
2311 }
2312 if (ptr1 >= str1 && *ptr1 != DIRECTORY_SEPARATOR_CHAR_A && *ptr1 != ':')
2313 {
2314 return FALSE;
2315 }
2316 return TRUE;
2317}
2318
2319/**********************************************************************\
2320* Routine Description: *
2321* *
2322* Return TRUE if moduleAddr is the address of a module. *
2323* *
2324\**********************************************************************/
2325BOOL IsModule (DWORD_PTR moduleAddr)
2326{
2327 DacpModuleData module;
2328 return (module.Request(g_sos, TO_CDADDR(moduleAddr))==S_OK);
2329}
2330
2331/**********************************************************************\
2332* Routine Description: *
2333* *
2334* Return TRUE if value is the address of a MethodTable. *
2335* We verify that MethodTable and EEClass are right.
2336* *
2337\**********************************************************************/
2338BOOL IsMethodTable (DWORD_PTR value)
2339{
2340 DacpMethodTableData mtabledata;
2341 if (mtabledata.Request(g_sos, TO_CDADDR(value))!=S_OK)
2342 {
2343 return FALSE;
2344 }
2345
2346 return TRUE;
2347}
2348
2349/**********************************************************************\
2350* Routine Description: *
2351* *
2352* Return TRUE if value is the address of a MethodDesc. *
2353* We verify that MethodTable and EEClass are right.
2354* *
2355\**********************************************************************/
2356BOOL IsMethodDesc (DWORD_PTR value)
2357{
2358 // Just by retrieving one successfully from the DAC, we know we have a MethodDesc.
2359 DacpMethodDescData MethodDescData;
2360 if (MethodDescData.Request(g_sos, TO_CDADDR(value)) != S_OK)
2361 {
2362 return FALSE;
2363 }
2364
2365 return TRUE;
2366}
2367
2368DacpUsefulGlobalsData g_special_usefulGlobals;
2369
2370BOOL IsObjectArray (DacpObjectData *pData)
2371{
2372 if (pData->ObjectType == OBJ_ARRAY)
2373 return g_special_usefulGlobals.ArrayMethodTable == pData->MethodTable;
2374
2375 return FALSE;
2376}
2377
2378BOOL IsObjectArray (DWORD_PTR obj)
2379{
2380 DWORD_PTR mtAddr = NULL;
2381 if (SUCCEEDED(GetMTOfObject(obj, &mtAddr)))
2382 return TO_TADDR(g_special_usefulGlobals.ArrayMethodTable) == mtAddr;
2383
2384 return FALSE;
2385}
2386
2387BOOL IsStringObject (size_t obj)
2388{
2389 DWORD_PTR mtAddr = NULL;
2390
2391 if (SUCCEEDED(GetMTOfObject(obj, &mtAddr)))
2392 return TO_TADDR(g_special_usefulGlobals.StringMethodTable) == mtAddr;
2393
2394 return FALSE;
2395}
2396
2397BOOL IsDerivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPCWSTR baseString)
2398{
2399 DacpMethodTableData dmtd;
2400 CLRDATA_ADDRESS walkMT = mtObj;
2401 while (walkMT != NULL)
2402 {
2403 if (dmtd.Request(g_sos, walkMT) != S_OK)
2404 {
2405 break;
2406 }
2407
2408 NameForMT_s(TO_TADDR(walkMT), g_mdName, mdNameLen);
2409 if (_wcscmp(baseString, g_mdName) == 0)
2410 {
2411 return TRUE;
2412 }
2413
2414 walkMT = dmtd.ParentMethodTable;
2415 }
2416
2417 return FALSE;
2418}
2419
2420BOOL TryGetMethodDescriptorForDelegate(CLRDATA_ADDRESS delegateAddr, CLRDATA_ADDRESS* pMD)
2421{
2422 if (!sos::IsObject(delegateAddr, false))
2423 {
2424 return FALSE;
2425 }
2426
2427 sos::Object delegateObj = TO_TADDR(delegateAddr);
2428
2429 for (int i = 0; i < 2; i++)
2430 {
2431 int offset;
2432 if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), i == 0 ? W("_methodPtrAux") : W("_methodPtr"))) != 0)
2433 {
2434 CLRDATA_ADDRESS methodPtr;
2435 MOVE(methodPtr, delegateObj.GetAddress() + offset);
2436 if (methodPtr != NULL)
2437 {
2438 if (g_sos->GetMethodDescPtrFromIP(methodPtr, pMD) == S_OK)
2439 {
2440 return TRUE;
2441 }
2442
2443 DacpCodeHeaderData codeHeaderData;
2444 if (codeHeaderData.Request(g_sos, methodPtr) == S_OK)
2445 {
2446 *pMD = codeHeaderData.MethodDescPtr;
2447 return TRUE;
2448 }
2449 }
2450 }
2451 }
2452
2453 return FALSE;
2454}
2455
2456void DumpStackObjectsOutput(const char *location, DWORD_PTR objAddr, BOOL verifyFields)
2457{
2458 // rule out pointers that are outside of the gc heap.
2459 if (g_snapshot.GetHeap(objAddr) == NULL)
2460 return;
2461
2462 DacpObjectData objectData;
2463 if (objectData.Request(g_sos, TO_CDADDR(objAddr)) != S_OK)
2464 return;
2465
2466 if (sos::IsObject(objAddr, verifyFields != FALSE)
2467 && !sos::MethodTable::IsFreeMT(TO_TADDR(objectData.MethodTable)))
2468 {
2469 DMLOut("%-" POINTERSIZE "s %s ", location, DMLObject(objAddr));
2470 if (g_sos->GetObjectClassName(TO_CDADDR(objAddr), mdNameLen, g_mdName, NULL)==S_OK)
2471 {
2472 ExtOut("%S", g_mdName);
2473
2474 if (IsStringObject(objAddr))
2475 {
2476 ExtOut(" ");
2477 StringObjectContent(objAddr, FALSE, 40);
2478 }
2479 else if (IsObjectArray(objAddr) &&
2480 (g_sos->GetMethodTableName(objectData.ElementTypeHandle, mdNameLen, g_mdName, NULL) == S_OK))
2481 {
2482 ExtOut(" ");
2483 ExtOut("(%S[])", g_mdName);
2484 }
2485 }
2486 else
2487 {
2488 ExtOut("<unknown type>");
2489 }
2490 ExtOut("\n");
2491 }
2492}
2493
2494void DumpStackObjectsOutput(DWORD_PTR ptr, DWORD_PTR objAddr, BOOL verifyFields)
2495{
2496 char location[64];
2497 sprintf_s(location, 64, "%p", (DWORD_PTR *)ptr);
2498
2499 DumpStackObjectsOutput(location, objAddr, verifyFields);
2500}
2501
2502void DumpStackObjectsInternal(size_t StackTop, size_t StackBottom, BOOL verifyFields)
2503{
2504 for (DWORD_PTR ptr = StackTop; ptr <= StackBottom; ptr += sizeof(DWORD_PTR))
2505 {
2506 if (IsInterrupt())
2507 return;
2508
2509 DWORD_PTR objAddr;
2510 move_xp(objAddr, ptr);
2511
2512 DumpStackObjectsOutput(ptr, objAddr, verifyFields);
2513 }
2514}
2515
2516void DumpRegObjectHelper(const char *regName, BOOL verifyFields)
2517{
2518 DWORD_PTR reg;
2519#ifdef FEATURE_PAL
2520 if (FAILED(g_ExtRegisters->GetValueByName(regName, &reg)))
2521 return;
2522#else
2523 DEBUG_VALUE value;
2524 ULONG IREG;
2525 if (FAILED(g_ExtRegisters->GetIndexByName(regName, &IREG)) ||
2526 FAILED(g_ExtRegisters->GetValue(IREG, &value)))
2527 return;
2528
2529#if defined(SOS_TARGET_X86) || defined(SOS_TARGET_ARM)
2530 reg = (DWORD_PTR) value.I32;
2531#elif defined(SOS_TARGET_AMD64) || defined(SOS_TARGET_ARM64)
2532 reg = (DWORD_PTR) value.I64;
2533#else
2534#error Unsupported target
2535#endif
2536#endif // FEATURE_PAL
2537
2538 DumpStackObjectsOutput(regName, reg, verifyFields);
2539}
2540
2541void DumpStackObjectsHelper (
2542 TADDR StackTop,
2543 TADDR StackBottom,
2544 BOOL verifyFields)
2545{
2546 ExtOut(g_targetMachine->GetDumpStackObjectsHeading());
2547
2548 LPCSTR* regs;
2549 unsigned int cnt;
2550 g_targetMachine->GetGCRegisters(&regs, &cnt);
2551
2552 for (size_t i = 0; i < cnt; ++i)
2553 DumpRegObjectHelper(regs[i], verifyFields);
2554
2555 // Make certain StackTop is dword aligned:
2556 DumpStackObjectsInternal(StackTop & ~ALIGNCONST, StackBottom, verifyFields);
2557}
2558
2559void AddToModuleList(DWORD_PTR * &moduleList, int &numModule, int &maxList,
2560 DWORD_PTR dwModuleAddr)
2561{
2562 int i;
2563 for (i = 0; i < numModule; i ++)
2564 {
2565 if (moduleList[i] == dwModuleAddr)
2566 break;
2567 }
2568 if (i == numModule)
2569 {
2570 moduleList[numModule] = dwModuleAddr;
2571 numModule ++;
2572 if (numModule == maxList)
2573 {
2574 int listLength = 0;
2575 if (!ClrSafeInt<int>::multiply(maxList, 2, listLength))
2576 {
2577 ExtOut("<integer overflow>\n");
2578 numModule = 0;
2579 ControlC = 1;
2580 return;
2581 }
2582 DWORD_PTR *list = new DWORD_PTR [listLength];
2583
2584 if (list == NULL)
2585 {
2586 numModule = 0;
2587 ControlC = 1;
2588 return;
2589 }
2590 memcpy (list, moduleList, maxList * sizeof(PVOID));
2591 delete[] moduleList;
2592 moduleList = list;
2593 maxList *= 2;
2594 }
2595 }
2596}
2597
2598BOOL IsFusionLoadedModule (LPCSTR fusionName, LPCSTR mName)
2599{
2600 // The fusion name will be in this format:
2601 // <module name>, Version=<version>, Culture=<culture>, PublicKeyToken=<token>
2602 // If fusionName up to the comma matches mName (case insensitive),
2603 // we consider that a match was found.
2604 LPCSTR commaPos = strchr (fusionName, ',');
2605 if (commaPos)
2606 {
2607 // verify that fusionName and mName match up to a comma.
2608 while (*fusionName != ',')
2609 {
2610 if (*mName == '\0')
2611 {
2612 return FALSE;
2613 }
2614
2615#ifndef FEATURE_PAL
2616 if (tolower(*fusionName) != tolower(*mName))
2617#else
2618 if (*fusionName != *mName)
2619#endif
2620 {
2621 return FALSE;
2622 }
2623 fusionName++;
2624 mName++;
2625 }
2626 return TRUE;
2627 }
2628 return FALSE;
2629}
2630
2631BOOL DebuggerModuleNamesMatch (CLRDATA_ADDRESS PEFileAddr, ___in __in_z LPSTR mName)
2632{
2633 // Another way to see if a module is the same is
2634 // to accept that mName may be the debugger's name for
2635 // a loaded module. We can get the debugger's name for
2636 // the module we are looking at right now, and compare
2637 // it with mName, if they match exactly, we can add
2638 // the module to the list.
2639 if (PEFileAddr)
2640 {
2641 CLRDATA_ADDRESS pebase = 0;
2642 if (g_sos->GetPEFileBase(PEFileAddr, &pebase) == S_OK)
2643 {
2644 if (pebase)
2645 {
2646 ULONG Index;
2647 ULONG64 base;
2648 if (g_ExtSymbols->GetModuleByOffset(pebase, 0, &Index, &base) == S_OK)
2649 {
2650 CHAR ModuleName[MAX_LONGPATH+1];
2651
2652 if (g_ExtSymbols->GetModuleNames(Index, base, NULL, 0, NULL, ModuleName,
2653 MAX_LONGPATH, NULL, NULL, 0, NULL) == S_OK)
2654 {
2655 if (_stricmp (ModuleName, mName) == 0)
2656 {
2657 return TRUE;
2658 }
2659 }
2660 }
2661 }
2662 }
2663 }
2664 return FALSE;
2665}
2666
2667DWORD_PTR *ModuleFromName(__in_opt LPSTR mName, int *numModule)
2668{
2669 if (numModule == NULL)
2670 return NULL;
2671
2672 DWORD_PTR *moduleList = NULL;
2673 *numModule = 0;
2674
2675 DacpAppDomainStoreData adsData;
2676 if (adsData.Request(g_sos)!=S_OK)
2677 return NULL;
2678
2679 ArrayHolder<CLRDATA_ADDRESS> pAssemblyArray = NULL;
2680 ArrayHolder<CLRDATA_ADDRESS> pModules = NULL;
2681 int arrayLength = 0;
2682 int numSpecialDomains = (adsData.sharedDomain != NULL) ? 2 : 1;
2683 if (!ClrSafeInt<int>::addition(adsData.DomainCount, numSpecialDomains, arrayLength))
2684 {
2685 ExtOut("<integer overflow>\n");
2686 return NULL;
2687 }
2688 ArrayHolder<CLRDATA_ADDRESS> pArray = new CLRDATA_ADDRESS[arrayLength];
2689
2690 if (pArray==NULL)
2691 {
2692 ReportOOM();
2693 return NULL;
2694 }
2695
2696 pArray[0] = adsData.systemDomain;
2697 if (adsData.sharedDomain != NULL)
2698 {
2699 pArray[1] = adsData.sharedDomain;
2700 }
2701 if (g_sos->GetAppDomainList(adsData.DomainCount, pArray.GetPtr()+numSpecialDomains, NULL)!=S_OK)
2702 {
2703 ExtOut("Unable to get array of AppDomains\n");
2704 return NULL;
2705 }
2706
2707 // List all domain
2708 size_t AllocSize;
2709 int maxList = arrayLength; // account for system and shared domains
2710 if (maxList <= 0 || !ClrSafeInt<size_t>::multiply(maxList, sizeof(PVOID), AllocSize))
2711 {
2712 ExtOut("Integer overflow error.\n");
2713 return NULL;
2714 }
2715
2716 moduleList = new DWORD_PTR[maxList];
2717 if (moduleList == NULL)
2718 {
2719 ReportOOM();
2720 return NULL;
2721 }
2722
2723 WCHAR StringData[MAX_LONGPATH];
2724 char fileName[sizeof(StringData)/2];
2725
2726 // Search all domains to find a module
2727 for (int n = 0; n < adsData.DomainCount+numSpecialDomains; n++)
2728 {
2729 if (IsInterrupt())
2730 {
2731 ExtOut("<interrupted>\n");
2732 goto Failure;
2733 }
2734
2735 DacpAppDomainData appDomain;
2736 if (FAILED(appDomain.Request(g_sos,pArray[n])))
2737 {
2738 // Don't print a failure message here, there is a very normal case when checking
2739 // for modules after clr is loaded but before any AppDomains or assemblies are created
2740 // for example:
2741 // >sxe ld:clr
2742 // >g
2743 // ...
2744 // ModLoad: clr.dll
2745 // >!bpmd Foo.dll Foo.Bar
2746
2747 // we will correctly give the answer that whatever module you were looking for, it isn't loaded yet
2748 goto Failure;
2749 }
2750
2751 if (appDomain.AssemblyCount)
2752 {
2753 pAssemblyArray = new CLRDATA_ADDRESS[appDomain.AssemblyCount];
2754 if (pAssemblyArray==NULL)
2755 {
2756 ReportOOM();
2757 goto Failure;
2758 }
2759
2760 if (FAILED(g_sos->GetAssemblyList(appDomain.AppDomainPtr, appDomain.AssemblyCount, pAssemblyArray, NULL)))
2761 {
2762 ExtOut("Unable to get array of Assemblies for the given AppDomain..\n");
2763 goto Failure;
2764 }
2765
2766 for (int nAssem = 0; nAssem < appDomain.AssemblyCount; nAssem ++)
2767 {
2768 if (IsInterrupt())
2769 {
2770 ExtOut("<interrupted>\n");
2771 goto Failure;
2772 }
2773
2774 DacpAssemblyData assemblyData;
2775 if (FAILED(assemblyData.Request(g_sos, pAssemblyArray[nAssem])))
2776 {
2777 ExtOut("Failed to request assembly.\n");
2778 goto Failure;
2779 }
2780
2781 pModules = new CLRDATA_ADDRESS[assemblyData.ModuleCount];
2782 if (FAILED(g_sos->GetAssemblyModuleList(assemblyData.AssemblyPtr, assemblyData.ModuleCount, pModules, NULL)))
2783 {
2784 ExtOut("Failed to get the modules for the given assembly.\n");
2785 goto Failure;
2786 }
2787
2788 for (UINT nModule = 0; nModule < assemblyData.ModuleCount; nModule++)
2789 {
2790 if (IsInterrupt())
2791 {
2792 ExtOut("<interrupted>\n");
2793 goto Failure;
2794 }
2795
2796 CLRDATA_ADDRESS ModuleAddr = pModules[nModule];
2797 DacpModuleData ModuleData;
2798 if (FAILED(ModuleData.Request(g_sos,ModuleAddr)))
2799 {
2800 ExtOut("Failed to request Module data from assembly.\n");
2801 goto Failure;
2802 }
2803
2804 FileNameForModule ((DWORD_PTR)ModuleAddr, StringData);
2805 int m;
2806 for (m = 0; StringData[m] != L'\0'; m++)
2807 {
2808 fileName[m] = (char)StringData[m];
2809 }
2810 fileName[m] = '\0';
2811
2812 if ((mName == NULL) ||
2813 IsSameModuleName(fileName, mName) ||
2814 DebuggerModuleNamesMatch(ModuleData.File, mName) ||
2815 IsFusionLoadedModule(fileName, mName))
2816 {
2817 AddToModuleList(moduleList, *numModule, maxList, (DWORD_PTR)ModuleAddr);
2818 }
2819 }
2820
2821 pModules = NULL;
2822 }
2823 pAssemblyArray = NULL;
2824 }
2825 }
2826
2827 return moduleList;
2828
2829 // We do not want to return a half-constructed list. Instead, we return NULL on a failure.
2830Failure:
2831 delete [] moduleList;
2832 return NULL;
2833}
2834
2835/**********************************************************************\
2836* Routine Description: *
2837* *
2838* Find the EE data given a name. *
2839* *
2840\**********************************************************************/
2841void GetInfoFromName(DWORD_PTR ModulePtr, const char* name)
2842{
2843 ToRelease<IMetaDataImport> pImport = MDImportForModule (ModulePtr);
2844 if (pImport == 0)
2845 return;
2846
2847 static WCHAR wszName[MAX_CLASSNAME_LENGTH];
2848 size_t n;
2849 size_t length = strlen (name);
2850 for (n = 0; n <= length; n ++)
2851 wszName[n] = name[n];
2852
2853 // First enumerate methods. We're taking advantage of the DAC's
2854 // CLRDataModule::EnumMethodDefinitionByName which can parse
2855 // method names (whether in nested classes, or explicit interface
2856 // method implementations).
2857 ToRelease<IXCLRDataModule> ModuleDefinition;
2858 if (g_sos->GetModule(ModulePtr, &ModuleDefinition) == S_OK)
2859 {
2860 CLRDATA_ENUM h;
2861 if (ModuleDefinition->StartEnumMethodDefinitionsByName(wszName, 0, &h) == S_OK)
2862 {
2863 IXCLRDataMethodDefinition *pMeth = NULL;
2864 BOOL fStatus = FALSE;
2865 while (ModuleDefinition->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
2866 {
2867 if (fStatus)
2868 ExtOut("-----------------------\n");
2869
2870 mdTypeDef token;
2871 if (pMeth->GetTokenAndScope(&token, NULL) == S_OK)
2872 {
2873 GetInfoFromModule(ModulePtr, token);
2874 fStatus = TRUE;
2875 }
2876 pMeth->Release();
2877 }
2878 ModuleDefinition->EndEnumMethodDefinitionsByName(h);
2879 if (fStatus)
2880 return;
2881 }
2882 }
2883
2884 // Now look for types, type members and fields
2885 mdTypeDef cl;
2886 mdToken tkEnclose = mdTokenNil;
2887 WCHAR *pName;
2888 WCHAR *pHead = wszName;
2889 while ( ((pName = _wcschr (pHead,L'+')) != NULL) ||
2890 ((pName = _wcschr (pHead,L'/')) != NULL)) {
2891 pName[0] = L'\0';
2892 if (FAILED(pImport->FindTypeDefByName(pHead,tkEnclose,&tkEnclose)))
2893 return;
2894 pHead = pName+1;
2895 }
2896
2897 pName = pHead;
2898
2899 // @todo: Handle Nested classes correctly.
2900 if (SUCCEEDED (pImport->FindTypeDefByName (pName, tkEnclose, &cl)))
2901 {
2902 GetInfoFromModule(ModulePtr, cl);
2903 return;
2904 }
2905
2906 // See if it is a method
2907 WCHAR *pwzMethod;
2908 if ((pwzMethod = _wcsrchr(pName, L'.')) == NULL)
2909 return;
2910
2911 if (pwzMethod[-1] == L'.')
2912 pwzMethod --;
2913 pwzMethod[0] = L'\0';
2914 pwzMethod ++;
2915
2916 // @todo: Handle Nested classes correctly.
2917 if (SUCCEEDED(pImport->FindTypeDefByName (pName, tkEnclose, &cl)))
2918 {
2919 mdMethodDef token;
2920 ULONG cTokens;
2921 HCORENUM henum = NULL;
2922
2923 // is Member?
2924 henum = NULL;
2925 if (SUCCEEDED (pImport->EnumMembersWithName (&henum, cl, pwzMethod,
2926 &token, 1, &cTokens))
2927 && cTokens == 1)
2928 {
2929 ExtOut("Member (mdToken token) of\n");
2930 GetInfoFromModule(ModulePtr, cl);
2931 return;
2932 }
2933
2934 // is Field?
2935 henum = NULL;
2936 if (SUCCEEDED (pImport->EnumFieldsWithName (&henum, cl, pwzMethod,
2937 &token, 1, &cTokens))
2938 && cTokens == 1)
2939 {
2940 ExtOut("Field (mdToken token) of\n");
2941 GetInfoFromModule(ModulePtr, cl);
2942 return;
2943 }
2944 }
2945}
2946
2947/**********************************************************************\
2948* Routine Description: *
2949* *
2950* Find the EE data given a token. *
2951* *
2952\**********************************************************************/
2953DWORD_PTR GetMethodDescFromModule(DWORD_PTR ModuleAddr, ULONG token)
2954{
2955 if (TypeFromToken(token) != mdtMethodDef)
2956 return NULL;
2957
2958 CLRDATA_ADDRESS md = 0;
2959 if (FAILED(g_sos->GetMethodDescFromToken(ModuleAddr, token, &md)))
2960 {
2961 return NULL;
2962 }
2963 else if (0 == md)
2964 {
2965 // a NULL ReturnValue means the method desc is not loaded yet
2966 return MD_NOT_YET_LOADED;
2967 }
2968 else if ( !IsMethodDesc((DWORD_PTR)md))
2969 {
2970 return NULL;
2971 }
2972
2973 return (DWORD_PTR)md;
2974}
2975
2976/**********************************************************************\
2977* Routine Description: *
2978* *
2979* Find the MethodDefinitions given a name. *
2980* *
2981\**********************************************************************/
2982HRESULT GetMethodDefinitionsFromName(TADDR ModulePtr, IXCLRDataModule* mod, const char *name, IXCLRDataMethodDefinition **ppOut, int numMethods, int *numMethodsNeeded)
2983{
2984 if (name == NULL)
2985 return E_FAIL;
2986
2987 size_t n;
2988 size_t length = strlen (name);
2989 for (n = 0; n <= length; n ++)
2990 g_mdName[n] = name[n];
2991
2992 CLRDATA_ENUM h;
2993 int methodCount = 0;
2994 if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
2995 {
2996 IXCLRDataMethodDefinition *pMeth = NULL;
2997 while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
2998 {
2999 methodCount++;
3000 pMeth->Release();
3001 }
3002 mod->EndEnumMethodDefinitionsByName(h);
3003 }
3004
3005 if(numMethodsNeeded != NULL)
3006 *numMethodsNeeded = methodCount;
3007 if(ppOut == NULL)
3008 return S_OK;
3009 if(numMethods > methodCount)
3010 numMethods = methodCount;
3011
3012 if (methodCount > 0)
3013 {
3014 if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
3015 {
3016 IXCLRDataMethodDefinition *pMeth = NULL;
3017 for (int i = 0; i < numMethods && mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK; i++)
3018 {
3019 ppOut[i] = pMeth;
3020 }
3021 mod->EndEnumMethodDefinitionsByName(h);
3022 }
3023 }
3024
3025 return S_OK;
3026}
3027
3028/**********************************************************************\
3029* Routine Description: *
3030* *
3031* Find the EE data given a name. *
3032* *
3033\**********************************************************************/
3034HRESULT GetMethodDescsFromName(TADDR ModulePtr, IXCLRDataModule* mod, const char *name, DWORD_PTR **pOut,int *numMethods)
3035{
3036 if (name == NULL || pOut == NULL || numMethods == NULL)
3037 return E_FAIL;
3038
3039 *pOut = NULL;
3040 *numMethods = 0;
3041
3042 size_t n;
3043 size_t length = strlen (name);
3044 for (n = 0; n <= length; n ++)
3045 g_mdName[n] = name[n];
3046
3047 CLRDATA_ENUM h;
3048 int methodCount = 0;
3049 if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
3050 {
3051 IXCLRDataMethodDefinition *pMeth = NULL;
3052 while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
3053 {
3054 methodCount++;
3055 pMeth->Release();
3056 }
3057 mod->EndEnumMethodDefinitionsByName(h);
3058 }
3059
3060 if (methodCount > 0)
3061 {
3062 *pOut = new TADDR[methodCount];
3063 if (*pOut==NULL)
3064 {
3065 ReportOOM();
3066 return E_OUTOFMEMORY;
3067 }
3068
3069 *numMethods = methodCount;
3070
3071 if (mod->StartEnumMethodDefinitionsByName(g_mdName, 0, &h) == S_OK)
3072 {
3073 int i = 0;
3074 IXCLRDataMethodDefinition *pMeth = NULL;
3075 while (mod->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
3076 {
3077 mdTypeDef token;
3078 if (pMeth->GetTokenAndScope(&token, NULL) != S_OK)
3079 (*pOut)[i] = NULL;
3080 (*pOut)[i] = GetMethodDescFromModule(ModulePtr, token);
3081 if ((*pOut)[i] == NULL)
3082 {
3083 *numMethods = 0;
3084 return E_FAIL;
3085 }
3086 i++;
3087 pMeth->Release();
3088 }
3089 mod->EndEnumMethodDefinitionsByName(h);
3090 }
3091 }
3092
3093 return S_OK;
3094}
3095
3096/**********************************************************************\
3097* Routine Description: *
3098* *
3099* Find the EE data given a token. *
3100* *
3101\**********************************************************************/
3102void GetInfoFromModule (DWORD_PTR ModuleAddr, ULONG token, DWORD_PTR *ret)
3103{
3104 switch (TypeFromToken(token))
3105 {
3106 case mdtMethodDef:
3107 break;
3108 case mdtTypeDef:
3109 break;
3110 case mdtTypeRef:
3111 break;
3112 case mdtFieldDef:
3113 break;
3114 default:
3115 ExtOut("This token type is not supported\n");
3116 return;
3117 break;
3118 }
3119
3120 CLRDATA_ADDRESS md = 0;
3121 if (FAILED(g_sos->GetMethodDescFromToken(ModuleAddr, token, &md)) || !IsValidToken (ModuleAddr, token))
3122 {
3123 ExtOut("<invalid module token>\n");
3124 return;
3125 }
3126
3127 if (ret != NULL)
3128 {
3129 *ret = (DWORD_PTR)md;
3130 return;
3131 }
3132
3133 ExtOut("Token: %p\n", SOS_PTR(token));
3134
3135 switch (TypeFromToken(token))
3136 {
3137 case mdtFieldDef:
3138 {
3139 NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
3140 ExtOut("Field name: %S\n", g_mdName);
3141 break;
3142 }
3143 case mdtMethodDef:
3144 {
3145 if (md)
3146 {
3147 DMLOut("MethodDesc: %s\n", DMLMethodDesc(md));
3148
3149 // Easiest to get full parameterized method name from ..::GetMethodName
3150 if (g_sos->GetMethodDescName(md, mdNameLen, g_mdName, NULL) != S_OK)
3151 {
3152 // Fall back to just method name without parameters..
3153 NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
3154 }
3155 }
3156 else
3157 {
3158 ExtOut("MethodDesc: <not loaded yet>\n");
3159 NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
3160 }
3161
3162 ExtOut("Name: %S\n", g_mdName);
3163 // Nice to have a little more data
3164 if (md)
3165 {
3166 DacpMethodDescData MethodDescData;
3167 if (MethodDescData.Request(g_sos, md) == S_OK)
3168 {
3169 if (MethodDescData.bHasNativeCode)
3170 {
3171 DMLOut("JITTED Code Address: %s\n", DMLIP(MethodDescData.NativeCodeAddr));
3172 }
3173 else
3174 {
3175#ifndef FEATURE_PAL
3176 if (IsDMLEnabled())
3177 DMLOut("Not JITTED yet. Use <exec cmd=\"!bpmd -md %p\">!bpmd -md %p</exec> to break on run.\n",
3178 SOS_PTR(md), SOS_PTR(md));
3179 else
3180 ExtOut("Not JITTED yet. Use !bpmd -md %p to break on run.\n", SOS_PTR(md));
3181#else
3182 ExtOut("Not JITTED yet. Use 'bpmd -md %p' to break on run.\n", SOS_PTR(md));
3183#endif
3184 }
3185 }
3186 else
3187 {
3188 ExtOut ("<Error getting MethodDesc information>\n");
3189 }
3190 }
3191 else
3192 {
3193 ExtOut("Not JITTED yet.\n");
3194 }
3195 break;
3196 }
3197 case mdtTypeDef:
3198 case mdtTypeRef:
3199 {
3200 if (md)
3201 {
3202 DMLOut("MethodTable: %s\n", DMLMethodTable(md));
3203 DacpMethodTableData mtabledata;
3204 if (mtabledata.Request(g_sos, md) == S_OK)
3205 {
3206 DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
3207 }
3208 else
3209 {
3210 ExtOut("EEClass: <error getting EEClass>\n");
3211 }
3212 }
3213 else
3214 {
3215 ExtOut("MethodTable: <not loaded yet>\n");
3216 ExtOut("EEClass: <not loaded yet>\n");
3217 }
3218 NameForToken_s(ModuleAddr, token, g_mdName, mdNameLen);
3219 ExtOut("Name: %S\n", g_mdName);
3220 break;
3221 }
3222 default:
3223 break;
3224 }
3225 return;
3226}
3227
3228BOOL IsMTForFreeObj(DWORD_PTR pMT)
3229{
3230 return (pMT == g_special_usefulGlobals.FreeMethodTable);
3231}
3232
3233const char *EHTypeName(EHClauseType et)
3234{
3235 if (et == EHFault)
3236 return "FAULT";
3237 else if (et == EHFinally)
3238 return "FINALLY";
3239 else if (et == EHFilter)
3240 return "FILTER";
3241 else if (et == EHTyped)
3242 return "TYPED";
3243 else
3244 return "UNKNOWN";
3245}
3246
3247void DumpTieredNativeCodeAddressInfo(struct DacpTieredVersionData * pTieredVersionData, const UINT cTieredVersionData)
3248{
3249 ExtOut("Code Version History:\n");
3250
3251 for(int i = cTieredVersionData - 1; i >= 0; --i)
3252 {
3253 const char *descriptor = NULL;
3254 switch(pTieredVersionData[i].TieredInfo)
3255 {
3256 case DacpTieredVersionData::TIERED_UNKNOWN:
3257 default:
3258 _ASSERTE(!"Update SOS to understand the new tier");
3259 descriptor = "Unknown Tier";
3260 break;
3261 case DacpTieredVersionData::NON_TIERED:
3262 descriptor = "Non-Tiered";
3263 break;
3264 case DacpTieredVersionData::TIERED_0:
3265 descriptor = "Tier 0";
3266 break;
3267 case DacpTieredVersionData::TIERED_1:
3268 descriptor = "Tier 1";
3269 break;
3270 }
3271
3272 DMLOut(" CodeAddr: %s (%s)\n", DMLIP(pTieredVersionData[i].NativeCodeAddr), descriptor);
3273 ExtOut(" NativeCodeVersion: %p\n", SOS_PTR(pTieredVersionData[i].NativeCodeVersionNodePtr));
3274 }
3275}
3276
3277void DumpRejitData(CLRDATA_ADDRESS pMethodDesc, DacpReJitData * pReJitData)
3278{
3279 ExtOut(" ReJITID %p: ", SOS_PTR(pReJitData->rejitID));
3280
3281 struct DacpTieredVersionData codeAddrs[kcMaxTieredVersions];
3282 int cCodeAddrs;
3283
3284 ReleaseHolder<ISOSDacInterface5> sos5;
3285 if (SUCCEEDED(g_sos->QueryInterface(__uuidof(ISOSDacInterface5), &sos5)) &&
3286 SUCCEEDED(sos5->GetTieredVersions(pMethodDesc,
3287 (int)pReJitData->rejitID,
3288 codeAddrs,
3289 kcMaxTieredVersions,
3290 &cCodeAddrs)))
3291 {
3292 DumpTieredNativeCodeAddressInfo(codeAddrs, cCodeAddrs);
3293 }
3294
3295 LPCSTR szFlags;
3296 switch (pReJitData->flags)
3297 {
3298 default:
3299 case DacpReJitData::kUnknown:
3300 szFlags = "";
3301 break;
3302
3303 case DacpReJitData::kRequested:
3304 szFlags = " (READY to jit on next call)";
3305 break;
3306
3307 case DacpReJitData::kActive:
3308 szFlags = " (CURRENT)";
3309 break;
3310
3311 case DacpReJitData::kReverted:
3312 szFlags = " (reverted)";
3313 break;
3314 }
3315
3316 ExtOut("%s\n", szFlags);
3317}
3318
3319// For !ip2md requests, this function helps us ensure that rejitted version corresponding
3320// to the specified IP always gets dumped. It may have already been dumped if it was the
3321// current rejit version (which is always dumped) or one of the reverted versions that we
3322// happened to dump before we clipped their number down to kcRejitDataRevertedMax.
3323BOOL ShouldDumpRejitDataRequested(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData)
3324{
3325 if (pMethodDescData->rejitDataRequested.rejitID == 0)
3326 return FALSE;
3327
3328 if (pMethodDescData->rejitDataRequested.rejitID == pMethodDescData->rejitDataCurrent.rejitID)
3329 return FALSE;
3330
3331 for (ULONG i=0; i < cRevertedRejitData; i++)
3332 {
3333 if (pMethodDescData->rejitDataRequested.rejitID == pRevertedRejitData[i].rejitID)
3334 return FALSE;
3335 }
3336
3337 return TRUE;
3338}
3339
3340
3341void DumpAllRejitDataIfNecessary(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData)
3342{
3343 // If there's no rejit info to output, then skip
3344 if ((pMethodDescData->rejitDataCurrent.rejitID == 0) &&
3345 (pMethodDescData->rejitDataRequested.rejitID == 0) &&
3346 (cRevertedRejitData == 0))
3347 {
3348 return;
3349 }
3350 ExtOut("ReJITed versions:\n");
3351
3352 // Dump CURRENT rejit info
3353 DumpRejitData(pMethodDescData->MethodDescPtr, &pMethodDescData->rejitDataCurrent);
3354
3355 // Dump reverted rejit infos
3356 for (ULONG i=0; i < cRevertedRejitData; i++)
3357 {
3358 DumpRejitData(pMethodDescData->MethodDescPtr, &pRevertedRejitData[i]);
3359 }
3360
3361 // For !ip2md, ensure we dump the rejit version corresponding to the specified IP
3362 // (if not already dumped)
3363 if (ShouldDumpRejitDataRequested(pMethodDescData, pRevertedRejitData, cRevertedRejitData))
3364 DumpRejitData(pMethodDescData->MethodDescPtr, &pMethodDescData->rejitDataRequested);
3365
3366 // If we maxed out the reverted versions we dumped, let user know there may be more
3367 if (cRevertedRejitData == kcMaxRevertedRejitData)
3368 ExtOut(" (... possibly more reverted versions ...)\n");
3369}
3370
3371void DumpMDInfoFromMethodDescData(DacpMethodDescData * pMethodDescData, DacpReJitData * pRevertedRejitData, UINT cRevertedRejitData, BOOL fStackTraceFormat)
3372{
3373 static WCHAR wszNameBuffer[1024]; // should be large enough
3374 BOOL bFailed = FALSE;
3375 if (g_sos->GetMethodDescName(pMethodDescData->MethodDescPtr, 1024, wszNameBuffer, NULL) != S_OK)
3376 {
3377 wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN"));
3378 bFailed = TRUE;
3379 }
3380
3381 if (!fStackTraceFormat)
3382 {
3383 ExtOut("Method Name: %S\n", wszNameBuffer);
3384
3385 DacpMethodTableData mtdata;
3386 if (SUCCEEDED(mtdata.Request(g_sos, pMethodDescData->MethodTablePtr)))
3387 {
3388 DMLOut("Class: %s\n", DMLClass(mtdata.Class));
3389 }
3390
3391 DMLOut("MethodTable: %s\n", DMLMethodTable(pMethodDescData->MethodTablePtr));
3392 ExtOut("mdToken: %p\n", SOS_PTR(pMethodDescData->MDToken));
3393 DMLOut("Module: %s\n", DMLModule(pMethodDescData->ModulePtr));
3394 ExtOut("IsJitted: %s\n", pMethodDescData->bHasNativeCode ? "yes" : "no");
3395
3396 DMLOut("Current CodeAddr: %s\n", DMLIP(pMethodDescData->NativeCodeAddr));
3397
3398 struct DacpTieredVersionData codeAddrs[kcMaxTieredVersions];
3399 int cCodeAddrs;
3400
3401 ReleaseHolder<ISOSDacInterface5> sos5;
3402 if (SUCCEEDED(g_sos->QueryInterface(__uuidof(ISOSDacInterface5), &sos5)) &&
3403 SUCCEEDED(sos5->GetTieredVersions(pMethodDescData->MethodDescPtr,
3404 (int)pMethodDescData->rejitDataCurrent.rejitID,
3405 codeAddrs,
3406 kcMaxTieredVersions,
3407 &cCodeAddrs)))
3408 {
3409 DumpTieredNativeCodeAddressInfo(codeAddrs, cCodeAddrs);
3410 }
3411
3412 DumpAllRejitDataIfNecessary(pMethodDescData, pRevertedRejitData, cRevertedRejitData);
3413 }
3414 else
3415 {
3416 if (!bFailed)
3417 {
3418 ExtOut("%S", wszNameBuffer);
3419 }
3420 else
3421 {
3422 // Only clutter the display with module/token for cases where we
3423 // can't get the MethodDesc name for some reason.
3424 DMLOut("Unknown MethodDesc (Module %s, mdToken %08x)",
3425 DMLModule(pMethodDescData->ModulePtr),
3426 pMethodDescData->MDToken);
3427 }
3428 }
3429}
3430
3431void DumpMDInfo(DWORD_PTR dwMethodDescAddr, CLRDATA_ADDRESS dwRequestedIP /* = 0 */, BOOL fStackTraceFormat /* = FALSE */)
3432{
3433 DacpMethodDescData MethodDescData;
3434 DacpReJitData revertedRejitData[kcMaxRevertedRejitData];
3435 ULONG cNeededRevertedRejitData;
3436 if (g_sos->GetMethodDescData(
3437 TO_CDADDR(dwMethodDescAddr),
3438 dwRequestedIP,
3439 &MethodDescData,
3440 _countof(revertedRejitData),
3441 revertedRejitData,
3442 &cNeededRevertedRejitData) != S_OK)
3443 {
3444 ExtOut("%p is not a MethodDesc\n", SOS_PTR(dwMethodDescAddr));
3445 return;
3446 }
3447
3448 DumpMDInfoFromMethodDescData(&MethodDescData, revertedRejitData, cNeededRevertedRejitData, fStackTraceFormat);
3449}
3450
3451void GetDomainList (DWORD_PTR *&domainList, int &numDomain)
3452{
3453 DacpAppDomainStoreData adsData;
3454
3455 numDomain = 0;
3456
3457 if (adsData.Request(g_sos)!=S_OK)
3458 {
3459 return;
3460 }
3461
3462 // Do prefast integer checks before the malloc.
3463 size_t AllocSize;
3464 LONG DomainAllocCount;
3465 LONG NumExtraDomains = (adsData.sharedDomain != NULL) ? 2 : 1;
3466 if (!ClrSafeInt<LONG>::addition(adsData.DomainCount, NumExtraDomains, DomainAllocCount) ||
3467 !ClrSafeInt<size_t>::multiply(DomainAllocCount, sizeof(PVOID), AllocSize) ||
3468 (domainList = new DWORD_PTR[DomainAllocCount]) == NULL)
3469 {
3470 return;
3471 }
3472
3473 domainList[numDomain++] = (DWORD_PTR) adsData.systemDomain;
3474 if (adsData.sharedDomain != NULL)
3475 {
3476 domainList[numDomain++] = (DWORD_PTR) adsData.sharedDomain;
3477 }
3478
3479 CLRDATA_ADDRESS *pArray = new CLRDATA_ADDRESS[adsData.DomainCount];
3480 if (pArray==NULL)
3481 {
3482 return;
3483 }
3484
3485 if (g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL)!=S_OK)
3486 {
3487 delete [] pArray;
3488 return;
3489 }
3490
3491 for (int n=0;n<adsData.DomainCount;n++)
3492 {
3493 if (IsInterrupt())
3494 break;
3495 domainList[numDomain++] = (DWORD_PTR) pArray[n];
3496 }
3497
3498 delete [] pArray;
3499}
3500
3501
3502HRESULT GetThreadList(DWORD_PTR **threadList, int *numThread)
3503{
3504 _ASSERTE(threadList != NULL);
3505 _ASSERTE(numThread != NULL);
3506
3507 if (threadList == NULL || numThread == NULL)
3508 {
3509 return E_FAIL;
3510 }
3511
3512 *numThread = 0;
3513
3514 DacpThreadStoreData ThreadStore;
3515 if ( ThreadStore.Request(g_sos) != S_OK)
3516 {
3517 ExtOut("Failed to request threads from the thread store.");
3518 return E_FAIL;
3519 }
3520
3521 *threadList = new DWORD_PTR[ThreadStore.threadCount];
3522 if (*threadList == NULL)
3523 {
3524 ReportOOM();
3525 return E_OUTOFMEMORY;
3526 }
3527
3528 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
3529 while (CurThread != NULL)
3530 {
3531 if (IsInterrupt())
3532 return S_FALSE;
3533
3534 DacpThreadData Thread;
3535 if (Thread.Request(g_sos, CurThread) != S_OK)
3536 {
3537 ExtOut("Failed to request Thread at %p\n", SOS_PTR(CurThread));
3538 return E_FAIL;
3539 }
3540
3541 (*threadList)[(*numThread)++] = (DWORD_PTR)CurThread;
3542 CurThread = Thread.nextThread;
3543 }
3544
3545 return S_OK;
3546}
3547
3548CLRDATA_ADDRESS GetCurrentManagedThread ()
3549{
3550 DacpThreadStoreData ThreadStore;
3551 ThreadStore.Request(g_sos);
3552
3553 ULONG Tid;
3554 g_ExtSystem->GetCurrentThreadSystemId(&Tid);
3555
3556 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
3557 while (CurThread)
3558 {
3559 DacpThreadData Thread;
3560 if (Thread.Request(g_sos, CurThread) != S_OK)
3561 {
3562 return NULL;
3563 }
3564
3565 if (Thread.osThreadId == Tid)
3566 {
3567 return CurThread;
3568 }
3569
3570 CurThread = Thread.nextThread;
3571 }
3572 return NULL;
3573}
3574
3575
3576void ReloadSymbolWithLineInfo()
3577{
3578#ifndef FEATURE_PAL
3579 static BOOL bLoadSymbol = FALSE;
3580 if (!bLoadSymbol)
3581 {
3582 ULONG Options;
3583 g_ExtSymbols->GetSymbolOptions(&Options);
3584 if (!(Options & SYMOPT_LOAD_LINES))
3585 {
3586 g_ExtSymbols->AddSymbolOptions(SYMOPT_LOAD_LINES);
3587
3588 if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName(MSCOREE_SHIM_A, 0, NULL, NULL)))
3589 g_ExtSymbols->Reload("/f " MSCOREE_SHIM_A);
3590
3591 EEFLAVOR flavor = GetEEFlavor();
3592 if (flavor == MSCORWKS)
3593 g_ExtSymbols->Reload("/f " MAIN_CLR_DLL_NAME_A);
3594 }
3595
3596 // reload mscoree.pdb and clrjit.pdb to get line info
3597 bLoadSymbol = TRUE;
3598 }
3599#endif
3600}
3601
3602// Return 1 if the function is our stub
3603// Return MethodDesc if the function is managed
3604// Otherwise return 0
3605size_t FunctionType (size_t EIP)
3606{
3607 ULONG64 base = 0;
3608 ULONG ulLoaded, ulUnloaded, ulIndex;
3609
3610 // Get the number of loaded and unloaded modules
3611 if (FAILED(g_ExtSymbols->GetNumberModules(&ulLoaded, &ulUnloaded)))
3612 return 0;
3613
3614
3615 if (SUCCEEDED(g_ExtSymbols->GetModuleByOffset(TO_CDADDR(EIP), 0, &ulIndex, &base)) && base != 0)
3616 {
3617 if (ulIndex < ulLoaded)
3618 {
3619 IMAGE_DOS_HEADER DosHeader;
3620 if (g_ExtData->ReadVirtual(TO_CDADDR(base), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
3621 return 0;
3622 IMAGE_NT_HEADERS Header;
3623 if (g_ExtData->ReadVirtual(TO_CDADDR(base + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK)
3624 return 0;
3625 // If there is no COMHeader, this can not be managed code.
3626 if (Header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].VirtualAddress == 0)
3627 return 0;
3628
3629 IMAGE_COR20_HEADER ComPlusHeader;
3630 if (g_ExtData->ReadVirtual(TO_CDADDR(base + Header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].VirtualAddress),
3631 &ComPlusHeader, sizeof(ComPlusHeader), NULL) != S_OK)
3632 return 0;
3633
3634 // If there is no Precompiled image info, it can not be prejit code
3635 if (ComPlusHeader.ManagedNativeHeader.VirtualAddress == 0) {
3636 return 0;
3637 }
3638 }
3639 }
3640
3641 CLRDATA_ADDRESS dwStartAddr = TO_CDADDR(EIP);
3642 CLRDATA_ADDRESS pMD;
3643 if (g_sos->GetMethodDescPtrFromIP(dwStartAddr, &pMD) != S_OK)
3644 {
3645 return 1;
3646 }
3647
3648 return (size_t) pMD;
3649}
3650
3651#ifndef FEATURE_PAL
3652
3653//
3654// Gets version info for the CLR in the debuggee process.
3655//
3656BOOL GetEEVersion(VS_FIXEDFILEINFO *pFileInfo)
3657{
3658 _ASSERTE(g_ExtSymbols2);
3659 _ASSERTE(pFileInfo);
3660 // Grab the version info directly from the module.
3661 return g_ExtSymbols2->GetModuleVersionInformation(DEBUG_ANY_ID,
3662 moduleInfo[GetEEFlavor()].baseAddr,
3663 "\\", pFileInfo, sizeof(VS_FIXEDFILEINFO), NULL) == S_OK;
3664}
3665
3666extern HMODULE g_hInstance;
3667BOOL GetSOSVersion(VS_FIXEDFILEINFO *pFileInfo)
3668{
3669 _ASSERTE(pFileInfo);
3670
3671 WCHAR wszFullPath[MAX_LONGPATH];
3672 DWORD cchFullPath = GetModuleFileNameW(g_hInstance, wszFullPath, _countof(wszFullPath));
3673
3674 DWORD dwHandle = 0;
3675 DWORD infoSize = GetFileVersionInfoSizeW(wszFullPath, &dwHandle);
3676 if (infoSize)
3677 {
3678 ArrayHolder<BYTE> pVersionInfo = new BYTE[infoSize];
3679 if (pVersionInfo)
3680 {
3681 if (GetFileVersionInfoW(wszFullPath, NULL, infoSize, pVersionInfo))
3682 {
3683 VS_FIXEDFILEINFO *pTmpFileInfo = NULL;
3684 UINT uLen = 0;
3685 if (VerQueryValue(pVersionInfo, "\\", (LPVOID *) &pTmpFileInfo, &uLen))
3686 {
3687 *pFileInfo = *pTmpFileInfo; // Copy the info
3688 return TRUE;
3689 }
3690 }
3691 }
3692 }
3693
3694 return FALSE;
3695}
3696
3697#endif // !FEATURE_PAL
3698
3699size_t ObjectSize(DWORD_PTR obj,BOOL fIsLargeObject)
3700{
3701 DWORD_PTR dwMT;
3702 MOVE(dwMT, obj);
3703 return ObjectSize(obj, dwMT, FALSE, fIsLargeObject);
3704}
3705
3706size_t ObjectSize(DWORD_PTR obj, DWORD_PTR mt, BOOL fIsValueClass, BOOL fIsLargeObject)
3707{
3708 BOOL bContainsPointers;
3709 size_t size = 0;
3710 if (!GetSizeEfficient(obj, mt, fIsLargeObject, size, bContainsPointers))
3711 {
3712 return 0;
3713 }
3714 return size;
3715}
3716
3717// This takes an array of values and sets every non-printable character
3718// to be a period.
3719void Flatten(__out_ecount(len) char *data, unsigned int len)
3720{
3721 for (unsigned int i = 0; i < len; ++i)
3722 if (data[i] < 32 || data[i] > 126)
3723 data[i] = '.';
3724 data[len] = 0;
3725}
3726
3727void CharArrayContent(TADDR pos, ULONG num, bool widechar)
3728{
3729 if (!pos || num <= 0)
3730 return;
3731
3732 if (widechar)
3733 {
3734 ArrayHolder<WCHAR> data = new WCHAR[num+1];
3735 if (!data)
3736 {
3737 ReportOOM();
3738 return;
3739 }
3740
3741 ULONG readLen = 0;
3742 if (!SafeReadMemory(pos, data, num<<1, &readLen))
3743 return;
3744
3745 Flatten(data.GetPtr(), readLen >> 1);
3746 ExtOut("%S", data.GetPtr());
3747 }
3748 else
3749 {
3750 ArrayHolder<char> data = new char[num+1];
3751 if (!data)
3752 {
3753 ReportOOM();
3754 return;
3755 }
3756
3757 ULONG readLen = 0;
3758 if (!SafeReadMemory(pos, data, num, &readLen))
3759 return;
3760
3761 _ASSERTE(readLen <= num);
3762 Flatten(data, readLen);
3763
3764 ExtOut("%s", data.GetPtr());
3765 }
3766}
3767
3768void StringObjectContent(size_t obj, BOOL fLiteral, const int length)
3769{
3770 DacpObjectData objData;
3771 if (objData.Request(g_sos, TO_CDADDR(obj))!=S_OK)
3772 {
3773 ExtOut("<Invalid Object>");
3774 return;
3775 }
3776
3777 strobjInfo stInfo;
3778
3779 if (MOVE(stInfo,obj) != S_OK)
3780 {
3781 ExtOut ("Error getting string data\n");
3782 return;
3783 }
3784
3785 if (objData.Size > 0x200000 ||
3786 stInfo.m_StringLength > 0x200000)
3787 {
3788 ExtOut ("<String is invalid or too large to print>\n");
3789 return;
3790 }
3791
3792 ArrayHolder<WCHAR> pwszBuf = new WCHAR[stInfo.m_StringLength+1];
3793 if (pwszBuf == NULL)
3794 {
3795 return;
3796 }
3797
3798 DWORD_PTR dwAddr = (DWORD_PTR)pwszBuf.GetPtr();
3799 if (g_sos->GetObjectStringData(TO_CDADDR(obj), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK)
3800 {
3801 ExtOut("Error getting string data\n");
3802 return;
3803 }
3804
3805 if (!fLiteral)
3806 {
3807 pwszBuf[stInfo.m_StringLength] = L'\0';
3808 ExtOut ("%S", pwszBuf.GetPtr());
3809 }
3810 else
3811 {
3812 ULONG32 count = stInfo.m_StringLength;
3813 WCHAR buffer[256];
3814 WCHAR out[512];
3815 while (count)
3816 {
3817 DWORD toRead = 255;
3818 if (count < toRead)
3819 toRead = count;
3820
3821 ULONG bytesRead;
3822 wcsncpy_s(buffer,_countof(buffer),(LPWSTR) dwAddr, toRead);
3823 bytesRead = toRead*sizeof(WCHAR);
3824 DWORD wcharsRead = bytesRead/2;
3825 buffer[wcharsRead] = L'\0';
3826
3827 ULONG j,k=0;
3828 for (j = 0; j < wcharsRead; j ++)
3829 {
3830 if (_iswprint (buffer[j])) {
3831 out[k] = buffer[j];
3832 k ++;
3833 }
3834 else
3835 {
3836 out[k++] = L'\\';
3837 switch (buffer[j]) {
3838 case L'\n':
3839 out[k++] = L'n';
3840 break;
3841 case L'\0':
3842 out[k++] = L'0';
3843 break;
3844 case L'\t':
3845 out[k++] = L't';
3846 break;
3847 case L'\v':
3848 out[k++] = L'v';
3849 break;
3850 case L'\b':
3851 out[k++] = L'b';
3852 break;
3853 case L'\r':
3854 out[k++] = L'r';
3855 break;
3856 case L'\f':
3857 out[k++] = L'f';
3858 break;
3859 case L'\a':
3860 out[k++] = L'a';
3861 break;
3862 case L'\\':
3863 break;
3864 case L'\?':
3865 out[k++] = L'?';
3866 break;
3867 default:
3868 out[k++] = L'?';
3869 break;
3870 }
3871 }
3872 }
3873
3874 out[k] = L'\0';
3875 ExtOut ("%S", out);
3876
3877 count -= wcharsRead;
3878 dwAddr += bytesRead;
3879 }
3880 }
3881}
3882
3883#ifdef _TARGET_WIN64_
3884
3885#include <limits.h>
3886
3887__int64 str64hex(const char *ptr)
3888{
3889 __int64 value = 0;
3890 unsigned char nCount = 0;
3891
3892 if(ptr==NULL)
3893 return 0;
3894
3895 // Ignore leading 0x if present
3896 if (*ptr=='0' && toupper(*(ptr+1))=='X') {
3897 ptr = ptr + 2;
3898 }
3899
3900 while (1) {
3901
3902 char digit;
3903
3904 if (isdigit(*ptr)) {
3905 digit = *ptr - '0';
3906 } else if (isalpha(*ptr)) {
3907 digit = (((char)toupper(*ptr)) - 'A') + 10;
3908 if (digit >= 16) {
3909 break; // terminate
3910 }
3911 } else {
3912 break;
3913 }
3914
3915 if (nCount>15) {
3916 return _UI64_MAX; // would be an overflow
3917 }
3918
3919 value = value << 4;
3920 value |= digit;
3921
3922 ptr++;
3923 nCount++;
3924 }
3925
3926 return value;
3927}
3928
3929#endif // _TARGET_WIN64_
3930
3931BOOL GetValueForCMD (const char *ptr, const char *end, ARGTYPE type, size_t *value)
3932{
3933 if (type == COSTRING) {
3934 // Allocate memory for the length of the string. Whitespace terminates
3935 // User must free the string data.
3936 char *pszValue = NULL;
3937 size_t dwSize = (end - ptr);
3938 pszValue= new char[dwSize+1];
3939 if (pszValue == NULL)
3940 {
3941 return FALSE;
3942 }
3943 strncpy_s(pszValue,dwSize+1,ptr,dwSize); // _TRUNCATE
3944 *value = (size_t) pszValue;
3945 } else {
3946 char *last;
3947 if (type == COHEX) {
3948#ifdef _TARGET_WIN64_
3949 *value = str64hex(ptr);
3950#else
3951 *value = strtoul(ptr,&last,16);
3952#endif
3953 }
3954 else {
3955#ifdef _TARGET_WIN64_
3956 *value = _atoi64(ptr);
3957#else
3958 *value = strtoul(ptr,&last,10);
3959#endif
3960 }
3961
3962#ifdef _TARGET_WIN64_
3963 last = (char *) ptr;
3964 // Ignore leading 0x if present
3965 if (*last=='0' && toupper(*(last+1))=='X') {
3966 last = last + 2;
3967 }
3968
3969 while (isdigit(*last) || (toupper(*last)>='A' && toupper(*last)<='F')) {
3970 last++;
3971 }
3972#endif
3973
3974 if (last != end) {
3975 return FALSE;
3976 }
3977 }
3978
3979 return TRUE;
3980}
3981
3982void SetValueForCMD (void *vptr, ARGTYPE type, size_t value)
3983{
3984 switch (type) {
3985 case COBOOL:
3986 *(BOOL*)vptr = (BOOL) value;
3987 break;
3988 case COSIZE_T:
3989 case COSTRING:
3990 case COHEX:
3991 *(SIZE_T*)vptr = value;
3992 break;
3993 }
3994}
3995
3996BOOL GetCMDOption(const char *string, CMDOption *option, size_t nOption,
3997 CMDValue *arg, size_t maxArg, size_t *nArg)
3998{
3999 const char *end;
4000 const char *ptr = string;
4001 BOOL endofOption = FALSE;
4002
4003 for (size_t n = 0; n < nOption; n ++)
4004 {
4005 if (IsInterrupt())
4006 return FALSE;
4007
4008 option[n].hasSeen = FALSE;
4009 }
4010
4011 if (nArg) {
4012 *nArg = 0;
4013 }
4014
4015 while (ptr[0] != '\0')
4016 {
4017 if (IsInterrupt())
4018 return FALSE;
4019
4020 // skip any space
4021 if (isspace (ptr[0])) {
4022 while (isspace (ptr[0]))
4023 {
4024 if (IsInterrupt())
4025 return FALSE;
4026
4027 ptr ++;
4028 }
4029
4030 continue;
4031 }
4032
4033 end = ptr;
4034
4035 // Arguments can be quoted with ". We'll remove the quotes and
4036 // allow spaces to exist in the string.
4037 BOOL bQuotedArg = FALSE;
4038 if (ptr[0] == '\'' && ptr[1] != '-')
4039 {
4040 bQuotedArg = TRUE;
4041
4042 // skip quote
4043 ptr++;
4044 end++;
4045
4046 while (end[0] != '\'' && end[0] != '\0')
4047 {
4048 if (IsInterrupt())
4049 return FALSE;
4050
4051 end ++;
4052 }
4053 if (end[0] != '\'')
4054 {
4055 // Error, th ere was a start quote but no end quote
4056 ExtOut ("Missing quote in %s\n", ptr);
4057 return FALSE;
4058 }
4059 }
4060 else // whitespace terminates
4061 {
4062 while (!isspace(end[0]) && end[0] != '\0')
4063 {
4064 if (IsInterrupt())
4065 return FALSE;
4066
4067 end ++;
4068 }
4069 }
4070
4071#ifndef FEATURE_PAL
4072 if (ptr[0] != '-' && ptr[0] != '/') {
4073#else
4074 if (ptr[0] != '-') {
4075#endif
4076 if (maxArg == 0) {
4077 ExtOut ("Incorrect argument: %s\n", ptr);
4078 return FALSE;
4079 }
4080 endofOption = TRUE;
4081 if (*nArg >= maxArg) {
4082 ExtOut ("Incorrect argument: %s\n", ptr);
4083 return FALSE;
4084 }
4085
4086 size_t value;
4087 if (!GetValueForCMD (ptr,end,arg[*nArg].type,&value)) {
4088
4089 char oldChar = *end;
4090 *(char *)end = '\0';
4091 value = (size_t)GetExpression (ptr);
4092 *(char *)end = oldChar;
4093
4094 /*
4095
4096 It is silly to do this, what if 0 is a valid expression for
4097 the command?
4098
4099 if (value == 0) {
4100 ExtOut ("Invalid argument: %s\n", ptr);
4101 return FALSE;
4102 }
4103 */
4104 }
4105
4106 SetValueForCMD (arg[*nArg].vptr, arg[*nArg].type, value);
4107
4108 (*nArg) ++;
4109 }
4110 else if (endofOption) {
4111 ExtOut ("Wrong option: %s\n", ptr);
4112 return FALSE;
4113 }
4114 else {
4115 char buffer[80];
4116 if (end-ptr > 79) {
4117 ExtOut ("Invalid option %s\n", ptr);
4118 return FALSE;
4119 }
4120 strncpy_s (buffer,_countof(buffer), ptr, end-ptr);
4121
4122 size_t n;
4123 for (n = 0; n < nOption; n ++)
4124 {
4125 if (IsInterrupt())
4126 return FALSE;
4127
4128 if (_stricmp (buffer, option[n].name) == 0) {
4129 if (option[n].hasSeen) {
4130 ExtOut ("Invalid option: option specified multiple times: %s\n", buffer);
4131 return FALSE;
4132 }
4133 option[n].hasSeen = TRUE;
4134 if (option[n].hasValue) {
4135 // skip any space
4136 ptr = end;
4137 if (isspace (ptr[0])) {
4138 while (isspace (ptr[0]))
4139 {
4140 if (IsInterrupt())
4141 return FALSE;
4142
4143 ptr ++;
4144 }
4145 }
4146 if (ptr[0] == '\0') {
4147 ExtOut ("Missing value for option %s\n", buffer);
4148 return FALSE;
4149 }
4150 end = ptr;
4151 while (!isspace(end[0]) && end[0] != '\0')
4152 {
4153 if (IsInterrupt())
4154 return FALSE;
4155
4156 end ++;
4157 }
4158
4159 size_t value;
4160 if (!GetValueForCMD (ptr,end,option[n].type,&value)) {
4161
4162 char oldChar = *end;
4163 *(char *)end = '\0';
4164 value = (size_t)GetExpression (ptr);
4165 *(char *)end = oldChar;
4166 }
4167
4168 SetValueForCMD (option[n].vptr,option[n].type,value);
4169 }
4170 else {
4171 SetValueForCMD (option[n].vptr,option[n].type,TRUE);
4172 }
4173 break;
4174 }
4175 }
4176 if (n == nOption) {
4177 ExtOut ("Unknown option: %s\n", buffer);
4178 return FALSE;
4179 }
4180 }
4181
4182 ptr = end;
4183 if (bQuotedArg)
4184 {
4185 ptr++;
4186 }
4187 }
4188 return TRUE;
4189}
4190
4191ReadVirtualCache g_special_rvCacheSpace;
4192ReadVirtualCache *rvCache = &g_special_rvCacheSpace;
4193
4194void ResetGlobals(void)
4195{
4196 // There are some globals used in SOS that exist for efficiency in one command,
4197 // but should be reset because the next execution of an SOS command could be on
4198 // another managed process. Reset them to a default state here, as this command
4199 // is called on every SOS entry point.
4200 g_sos->GetUsefulGlobals(&g_special_usefulGlobals);
4201 g_special_mtCache.Clear();
4202 g_special_rvCacheSpace.Clear();
4203 Output::ResetIndent();
4204}
4205
4206//---------------------------------------------------------------------------------------
4207//
4208// Loads private DAC interface, and points g_clrData to it.
4209//
4210// Return Value:
4211// HRESULT indicating success or failure
4212//
4213HRESULT LoadClrDebugDll(void)
4214{
4215 HRESULT hr = S_OK;
4216#ifdef FEATURE_PAL
4217 static IXCLRDataProcess* s_clrDataProcess = NULL;
4218 if (s_clrDataProcess == NULL)
4219 {
4220 int err = PAL_InitializeDLL();
4221 if(err != 0)
4222 {
4223 return CORDBG_E_UNSUPPORTED;
4224 }
4225 char dacModulePath[MAX_LONGPATH];
4226 strcpy_s(dacModulePath, _countof(dacModulePath), g_ExtServices->GetCoreClrDirectory());
4227 strcat_s(dacModulePath, _countof(dacModulePath), MAKEDLLNAME_A("mscordaccore"));
4228
4229 HMODULE hdac = LoadLibraryA(dacModulePath);
4230 if (hdac == NULL)
4231 {
4232 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
4233 }
4234 PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance");
4235 if (pfnCLRDataCreateInstance == NULL)
4236 {
4237 FreeLibrary(hdac);
4238 return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
4239 }
4240 ICLRDataTarget *target = new DataTarget();
4241 hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&s_clrDataProcess);
4242 if (FAILED(hr))
4243 {
4244 s_clrDataProcess = NULL;
4245 return hr;
4246 }
4247 ULONG32 flags = 0;
4248 s_clrDataProcess->GetOtherNotificationFlags(&flags);
4249 flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD | CLRDATA_NOTIFY_ON_EXCEPTION);
4250 s_clrDataProcess->SetOtherNotificationFlags(flags);
4251 }
4252 g_clrData = s_clrDataProcess;
4253 g_clrData->AddRef();
4254 g_clrData->Flush();
4255#else
4256 WDBGEXTS_CLR_DATA_INTERFACE Query;
4257
4258 Query.Iid = &__uuidof(IXCLRDataProcess);
4259 if (!Ioctl(IG_GET_CLR_DATA_INTERFACE, &Query, sizeof(Query)))
4260 {
4261 return E_FAIL;
4262 }
4263
4264 g_clrData = (IXCLRDataProcess*)Query.Iface;
4265#endif
4266 hr = g_clrData->QueryInterface(__uuidof(ISOSDacInterface), (void**)&g_sos);
4267 if (FAILED(hr))
4268 {
4269 g_sos = NULL;
4270 return hr;
4271 }
4272 return S_OK;
4273}
4274
4275#ifndef FEATURE_PAL
4276
4277// This structure carries some input/output data to the FindFileInPathCallback below
4278typedef struct _FindFileCallbackData
4279{
4280 DWORD timestamp;
4281 DWORD filesize;
4282 HMODULE hModule;
4283} FindFileCallbackData;
4284
4285
4286// A callback used by SymFindFileInPath - called once for each file that matches
4287// the initial search criteria and allows the user to do arbitrary processing
4288// This implementation checks that filesize and timestamp are correct, then
4289// saves the loaded module handle
4290// Parameters
4291// filename - the full path the file which was found
4292// context - a user specified pointer to arbitrary data, in this case a FindFileCallbackData
4293// Return Value
4294// TRUE if the search should continue (the file is no good)
4295// FALSE if the search should stop (the file is good)
4296BOOL
4297FindFileInPathCallback(
4298 ___in PCWSTR filename,
4299 ___in PVOID context
4300 )
4301{
4302 HRESULT hr;
4303 FindFileCallbackData* pCallbackData;
4304 pCallbackData = (FindFileCallbackData*)context;
4305 if (!pCallbackData)
4306 return TRUE;
4307
4308 pCallbackData->hModule = LoadLibraryExW(
4309 filename,
4310 NULL, // __reserved
4311 LOAD_WITH_ALTERED_SEARCH_PATH); // Ensure we check the dir in wszFullPath first
4312 if (pCallbackData->hModule == NULL)
4313 {
4314 hr = HRESULT_FROM_WIN32(GetLastError());
4315 ExtOut("Unable to load '%S'. HRESULT = 0x%x.\n", filename, hr);
4316 return TRUE;
4317 }
4318
4319 // Did we load the right one?
4320 MODULEINFO modInfo = {0};
4321 if (!GetModuleInformation(
4322 GetCurrentProcess(),
4323 pCallbackData->hModule,
4324 &modInfo,
4325 sizeof(modInfo)))
4326 {
4327 ExtOut("Failed to read module information for '%S'. HRESULT = 0x%x.\n", filename, HRESULT_FROM_WIN32(GetLastError()));
4328 FreeLibrary(pCallbackData->hModule);
4329 return TRUE;
4330 }
4331
4332 IMAGE_DOS_HEADER * pDOSHeader = (IMAGE_DOS_HEADER *) modInfo.lpBaseOfDll;
4333 IMAGE_NT_HEADERS * pNTHeaders = (IMAGE_NT_HEADERS *) (((LPBYTE) modInfo.lpBaseOfDll) + pDOSHeader->e_lfanew);
4334 DWORD dwSizeActual = pNTHeaders->OptionalHeader.SizeOfImage;
4335 DWORD dwTimeStampActual = pNTHeaders->FileHeader.TimeDateStamp;
4336 if ((dwSizeActual != pCallbackData->filesize) || (dwTimeStampActual != pCallbackData->timestamp))
4337 {
4338 ExtOut("Found '%S', but it does not match the CLR being debugged.\n", filename);
4339 ExtOut("Size: Expected '0x%x', Actual '0x%x'\n", pCallbackData->filesize, dwSizeActual);
4340 ExtOut("Time stamp: Expected '0x%x', Actual '0x%x'\n", pCallbackData->timestamp, dwTimeStampActual);
4341 FreeLibrary(pCallbackData->hModule);
4342 return TRUE;
4343 }
4344
4345 ExtOut("Loaded %S\n", filename);
4346 return FALSE;
4347}
4348
4349#endif // FEATURE_PAL
4350
4351//---------------------------------------------------------------------------------------
4352// Provides a way for the public CLR debugging interface to find the appropriate
4353// mscordbi.dll, DAC, etc.
4354class SOSLibraryProvider : public ICLRDebuggingLibraryProvider
4355{
4356public:
4357 SOSLibraryProvider() : m_ref(0)
4358 {
4359 }
4360
4361 virtual ~SOSLibraryProvider() {}
4362
4363 virtual HRESULT STDMETHODCALLTYPE QueryInterface(
4364 REFIID InterfaceId,
4365 PVOID* pInterface)
4366 {
4367 if (InterfaceId == IID_IUnknown)
4368 {
4369 *pInterface = static_cast<IUnknown *>(this);
4370 }
4371 else if (InterfaceId == IID_ICLRDebuggingLibraryProvider)
4372 {
4373 *pInterface = static_cast<ICLRDebuggingLibraryProvider *>(this);
4374 }
4375 else
4376 {
4377 *pInterface = NULL;
4378 return E_NOINTERFACE;
4379 }
4380
4381 AddRef();
4382 return S_OK;
4383 }
4384
4385 virtual ULONG STDMETHODCALLTYPE AddRef()
4386 {
4387 return InterlockedIncrement(&m_ref);
4388 }
4389
4390 virtual ULONG STDMETHODCALLTYPE Release()
4391 {
4392 LONG ref = InterlockedDecrement(&m_ref);
4393 if (ref == 0)
4394 {
4395 delete this;
4396 }
4397 return ref;
4398 }
4399
4400
4401
4402 // Called by the shim to locate and load mscordacwks and mscordbi
4403 // Parameters:
4404 // pwszFileName - the name of the file to load
4405 // dwTimestamp - the expected timestamp of the file
4406 // dwSizeOfImage - the expected SizeOfImage (a PE header data value)
4407 // phModule - a handle to loaded module
4408 //
4409 // Return Value
4410 // S_OK if the file was loaded, or any error if not
4411 virtual HRESULT STDMETHODCALLTYPE ProvideLibrary(
4412 const WCHAR * pwszFileName,
4413 DWORD dwTimestamp,
4414 DWORD dwSizeOfImage,
4415 HMODULE * phModule)
4416 {
4417#ifndef FEATURE_PAL
4418 HRESULT hr;
4419 FindFileCallbackData callbackData = {0};
4420 callbackData.timestamp = dwTimestamp;
4421 callbackData.filesize = dwSizeOfImage;
4422
4423 if ((phModule == NULL) || (pwszFileName == NULL))
4424 {
4425 return E_INVALIDARG;
4426 }
4427
4428 HMODULE dacModule;
4429 if(g_sos == NULL)
4430 {
4431 // we ensure that windbg loads DAC first so that we can be sure to use the same one
4432 return E_UNEXPECTED;
4433 }
4434 if (FAILED(hr = g_sos->GetDacModuleHandle(&dacModule)))
4435 {
4436 ExtOut("Failed to get the dac module handle. hr=0x%x.\n", hr);
4437 return hr;
4438 }
4439
4440 WCHAR dacPath[MAX_LONGPATH];
4441 DWORD len = GetModuleFileNameW(dacModule, dacPath, MAX_LONGPATH);
4442 if(len == 0 || len == MAX_LONGPATH)
4443 {
4444 ExtOut("GetModuleFileName(dacModuleHandle) failed. Last error = 0x%x\n", GetLastError());
4445 return E_FAIL;
4446 }
4447
4448 // if we are looking for the DAC, just load the one windbg already found
4449 if(_wcsncmp(pwszFileName, W("mscordac"), _wcslen(W("mscordac")))==0)
4450 {
4451 FindFileInPathCallback(dacPath, &callbackData);
4452 *phModule = callbackData.hModule;
4453 return hr;
4454 }
4455
4456 ULONG64 hProcess;
4457 hr = g_ExtSystem->GetCurrentProcessHandle(&hProcess);
4458 if (FAILED(hr))
4459 {
4460 ExtOut("IDebugSystemObjects::GetCurrentProcessHandle HRESULT=0x%x.\n", hr);
4461 return hr;
4462 }
4463
4464 ToRelease<IDebugSymbols3> spSym3(NULL);
4465 hr = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
4466 if (FAILED(hr))
4467 {
4468 ExtOut("Unable to query IDebugSymbol3 HRESULT=0x%x.\n", hr);
4469 return hr;
4470 }
4471
4472 ULONG pathSize = 0;
4473 hr = spSym3->GetSymbolPathWide(NULL, 0, &pathSize);
4474 if(FAILED(hr)) //S_FALSE if the path doesn't fit, but if the path was size 0 perhaps we would get S_OK?
4475 {
4476 ExtOut("Unable to get symbol path length. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", hr);
4477 return hr;
4478 }
4479
4480 ArrayHolder<WCHAR> symbolPath = new WCHAR[pathSize+MAX_LONGPATH+1];
4481
4482
4483
4484 hr = spSym3->GetSymbolPathWide(symbolPath, pathSize, NULL);
4485 if(S_OK != hr)
4486 {
4487 ExtOut("Unable to get symbol path. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", hr);
4488 return hr;
4489 }
4490
4491 WCHAR foundPath[MAX_LONGPATH];
4492 BOOL rc = SymFindFileInPathW((HANDLE)hProcess,
4493 symbolPath,
4494 pwszFileName,
4495 (PVOID)(ULONG_PTR) dwTimestamp,
4496 dwSizeOfImage,
4497 0,
4498 SSRVOPT_DWORD,
4499 foundPath,
4500 (PFINDFILEINPATHCALLBACKW) &FindFileInPathCallback,
4501 (PVOID) &callbackData
4502 );
4503 if(!rc)
4504 {
4505 hr = HRESULT_FROM_WIN32(GetLastError());
4506 ExtOut("SymFindFileInPath failed for %S. HRESULT=0x%x.\nPlease ensure that %S is on your symbol path.", pwszFileName, hr, pwszFileName);
4507 }
4508
4509 *phModule = callbackData.hModule;
4510 return hr;
4511#else
4512 WCHAR modulePath[MAX_LONGPATH];
4513 int length = MultiByteToWideChar(CP_ACP, 0, g_ExtServices->GetCoreClrDirectory(), -1, modulePath, _countof(modulePath));
4514 if (0 >= length)
4515 {
4516 ExtOut("MultiByteToWideChar(coreclrDirectory) failed. Last error = 0x%x\n", GetLastError());
4517 return E_FAIL;
4518 }
4519 wcscat_s(modulePath, _countof(modulePath), pwszFileName);
4520
4521 *phModule = LoadLibraryW(modulePath);
4522 if (*phModule == NULL)
4523 {
4524 HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
4525 ExtOut("Unable to load '%S'. HRESULT = 0x%x.\n", pwszFileName, hr);
4526 return hr;
4527 }
4528 return S_OK;
4529#endif // FEATURE_PAL
4530 }
4531
4532protected:
4533 LONG m_ref;
4534};
4535
4536//---------------------------------------------------------------------------------------
4537// Data target for the debugged process. Provided to OpenVirtualProcess in order to
4538// get an ICorDebugProcess back
4539//
4540class SOSDataTarget : public ICorDebugMutableDataTarget
4541#ifdef FEATURE_PAL
4542, public ICorDebugDataTarget4
4543#endif
4544{
4545public:
4546 SOSDataTarget() : m_ref(0)
4547 {
4548 }
4549
4550 virtual ~SOSDataTarget() {}
4551
4552 virtual HRESULT STDMETHODCALLTYPE QueryInterface(
4553 REFIID InterfaceId,
4554 PVOID* pInterface)
4555 {
4556 if (InterfaceId == IID_IUnknown)
4557 {
4558 *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugDataTarget *>(this));
4559 }
4560 else if (InterfaceId == IID_ICorDebugDataTarget)
4561 {
4562 *pInterface = static_cast<ICorDebugDataTarget *>(this);
4563 }
4564 else if (InterfaceId == IID_ICorDebugMutableDataTarget)
4565 {
4566 *pInterface = static_cast<ICorDebugMutableDataTarget *>(this);
4567 }
4568#ifdef FEATURE_PAL
4569 else if (InterfaceId == IID_ICorDebugDataTarget4)
4570 {
4571 *pInterface = static_cast<ICorDebugDataTarget4 *>(this);
4572 }
4573#endif
4574 else
4575 {
4576 *pInterface = NULL;
4577 return E_NOINTERFACE;
4578 }
4579
4580 AddRef();
4581 return S_OK;
4582 }
4583
4584 virtual ULONG STDMETHODCALLTYPE AddRef()
4585 {
4586 return InterlockedIncrement(&m_ref);
4587 }
4588
4589 virtual ULONG STDMETHODCALLTYPE Release()
4590 {
4591 LONG ref = InterlockedDecrement(&m_ref);
4592 if (ref == 0)
4593 {
4594 delete this;
4595 }
4596 return ref;
4597 }
4598
4599 //
4600 // ICorDebugDataTarget.
4601 //
4602
4603 virtual HRESULT STDMETHODCALLTYPE GetPlatform(CorDebugPlatform * pPlatform)
4604 {
4605 ULONG platformKind = g_targetMachine->GetPlatform();
4606#ifdef FEATURE_PAL
4607 if(platformKind == IMAGE_FILE_MACHINE_I386)
4608 *pPlatform = CORDB_PLATFORM_POSIX_X86;
4609 else if(platformKind == IMAGE_FILE_MACHINE_AMD64)
4610 *pPlatform = CORDB_PLATFORM_POSIX_AMD64;
4611 else if(platformKind == IMAGE_FILE_MACHINE_ARMNT)
4612 *pPlatform = CORDB_PLATFORM_POSIX_ARM;
4613 else
4614 return E_FAIL;
4615#else
4616 if(platformKind == IMAGE_FILE_MACHINE_I386)
4617 *pPlatform = CORDB_PLATFORM_WINDOWS_X86;
4618 else if(platformKind == IMAGE_FILE_MACHINE_AMD64)
4619 *pPlatform = CORDB_PLATFORM_WINDOWS_AMD64;
4620 else if(platformKind == IMAGE_FILE_MACHINE_ARMNT)
4621 *pPlatform = CORDB_PLATFORM_WINDOWS_ARM;
4622 else if(platformKind == IMAGE_FILE_MACHINE_ARM64)
4623 *pPlatform = CORDB_PLATFORM_WINDOWS_ARM64;
4624 else
4625 return E_FAIL;
4626#endif
4627
4628 return S_OK;
4629 }
4630
4631 virtual HRESULT STDMETHODCALLTYPE ReadVirtual(
4632 CORDB_ADDRESS address,
4633 BYTE * pBuffer,
4634 ULONG32 request,
4635 ULONG32 * pcbRead)
4636 {
4637 if (g_ExtData == NULL)
4638 {
4639 return E_UNEXPECTED;
4640 }
4641 return g_ExtData->ReadVirtual(address, pBuffer, request, (PULONG) pcbRead);
4642 }
4643
4644 virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
4645 DWORD dwThreadOSID,
4646 ULONG32 contextFlags,
4647 ULONG32 contextSize,
4648 BYTE * context)
4649 {
4650#ifdef FEATURE_PAL
4651 if (g_ExtSystem == NULL)
4652 {
4653 return E_UNEXPECTED;
4654 }
4655 return g_ExtSystem->GetThreadContextById(dwThreadOSID, contextFlags, contextSize, context);
4656#else
4657 ULONG ulThreadIDOrig;
4658 ULONG ulThreadIDRequested;
4659 HRESULT hr;
4660 HRESULT hrRet;
4661
4662 hr = g_ExtSystem->GetCurrentThreadId(&ulThreadIDOrig);
4663 if (FAILED(hr))
4664 {
4665 return hr;
4666 }
4667
4668 hr = g_ExtSystem->GetThreadIdBySystemId(dwThreadOSID, &ulThreadIDRequested);
4669 if (FAILED(hr))
4670 {
4671 return hr;
4672 }
4673
4674 hr = g_ExtSystem->SetCurrentThreadId(ulThreadIDRequested);
4675 if (FAILED(hr))
4676 {
4677 return hr;
4678 }
4679
4680 // Prepare context structure
4681 ZeroMemory(context, contextSize);
4682 ((CONTEXT*) context)->ContextFlags = contextFlags;
4683
4684 // Ok, do it!
4685 hrRet = g_ExtAdvanced3->GetThreadContext((LPVOID) context, contextSize);
4686
4687 // This is cleanup; failure here doesn't mean GetThreadContext should fail
4688 // (that's determined by hrRet).
4689 g_ExtSystem->SetCurrentThreadId(ulThreadIDOrig);
4690
4691 return hrRet;
4692#endif // FEATURE_PAL
4693 }
4694
4695 //
4696 // ICorDebugMutableDataTarget.
4697 //
4698 virtual HRESULT STDMETHODCALLTYPE WriteVirtual(CORDB_ADDRESS address,
4699 const BYTE * pBuffer,
4700 ULONG32 bytesRequested)
4701 {
4702 if (g_ExtData == NULL)
4703 {
4704 return E_UNEXPECTED;
4705 }
4706 return g_ExtData->WriteVirtual(address, (PVOID)pBuffer, bytesRequested, NULL);
4707 }
4708
4709 virtual HRESULT STDMETHODCALLTYPE SetThreadContext(DWORD dwThreadID,
4710 ULONG32 contextSize,
4711 const BYTE * pContext)
4712 {
4713 return E_NOTIMPL;
4714 }
4715
4716 virtual HRESULT STDMETHODCALLTYPE ContinueStatusChanged(DWORD dwThreadId,
4717 CORDB_CONTINUE_STATUS continueStatus)
4718 {
4719 return E_NOTIMPL;
4720 }
4721
4722#ifdef FEATURE_PAL
4723 //
4724 // ICorDebugDataTarget4
4725 //
4726 virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context)
4727 {
4728 if (g_ExtServices == NULL)
4729 {
4730 return E_UNEXPECTED;
4731 }
4732 return g_ExtServices->VirtualUnwind(threadId, contextSize, context);
4733
4734 }
4735#endif // FEATURE_PAL
4736
4737protected:
4738 LONG m_ref;
4739};
4740
4741HRESULT InitCorDebugInterfaceFromModule(ULONG64 ulBase, ICLRDebugging * pClrDebugging)
4742{
4743 HRESULT hr;
4744
4745 ToRelease<ICorDebugMutableDataTarget> pSOSDataTarget = new SOSDataTarget;
4746 pSOSDataTarget->AddRef();
4747
4748 ToRelease<ICLRDebuggingLibraryProvider> pSOSLibraryProvider = new SOSLibraryProvider;
4749 pSOSLibraryProvider->AddRef();
4750
4751 CLR_DEBUGGING_VERSION clrDebuggingVersionRequested = {0};
4752 clrDebuggingVersionRequested.wMajor = 4;
4753
4754 CLR_DEBUGGING_VERSION clrDebuggingVersionActual = {0};
4755
4756 CLR_DEBUGGING_PROCESS_FLAGS clrDebuggingFlags = (CLR_DEBUGGING_PROCESS_FLAGS)0;
4757
4758 ToRelease<IUnknown> pUnkProcess;
4759
4760 hr = pClrDebugging->OpenVirtualProcess(
4761 ulBase,
4762 pSOSDataTarget,
4763 pSOSLibraryProvider,
4764 &clrDebuggingVersionRequested,
4765 IID_ICorDebugProcess,
4766 &pUnkProcess,
4767 &clrDebuggingVersionActual,
4768 &clrDebuggingFlags);
4769 if (FAILED(hr))
4770 {
4771 return hr;
4772 }
4773
4774 ICorDebugProcess * pCorDebugProcess = NULL;
4775 hr = pUnkProcess->QueryInterface(IID_ICorDebugProcess, (PVOID*) &pCorDebugProcess);
4776 if (FAILED(hr))
4777 {
4778 return hr;
4779 }
4780
4781 // Transfer memory ownership of refcount to global
4782 g_pCorDebugProcess = pCorDebugProcess;
4783 return S_OK;
4784}
4785
4786//---------------------------------------------------------------------------------------
4787//
4788// Unloads public ICorDebug interfaces, and clears g_pCorDebugProcess
4789// This is only needed once after CLR unloads, not after every InitCorDebugInterface call
4790//
4791VOID UninitCorDebugInterface()
4792{
4793 if(g_pCorDebugProcess != NULL)
4794 {
4795 g_pCorDebugProcess->Detach();
4796 g_pCorDebugProcess->Release();
4797 g_pCorDebugProcess = NULL;
4798 }
4799}
4800
4801//---------------------------------------------------------------------------------------
4802//
4803// Loads public ICorDebug interfaces, and points g_pCorDebugProcess to them
4804// This should be called at least once per windbg stop state to ensure that
4805// the interface is available and that it doesn't hold stale data. Calling it
4806// more than once isn't an error, but does have perf overhead from needlessly
4807// flushing memory caches.
4808//
4809// Return Value:
4810// HRESULT indicating success or failure
4811//
4812
4813HRESULT InitCorDebugInterface()
4814{
4815 HMODULE hModule = NULL;
4816 HRESULT hr;
4817 ToRelease<ICLRDebugging> pClrDebugging;
4818
4819 // we may already have an ICorDebug instance we can use
4820 if(g_pCorDebugProcess != NULL)
4821 {
4822 // ICorDebugProcess4 is currently considered a private experimental interface on ICorDebug, it might go away so
4823 // we need to be sure to handle its absence gracefully
4824 ToRelease<ICorDebugProcess4> pProcess4 = NULL;
4825 if(SUCCEEDED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess4), (void**)&pProcess4)))
4826 {
4827 // FLUSH_ALL is more expensive than PROCESS_RUNNING, but this allows us to be safe even if things
4828 // like IDNA are in use where we might be looking at non-sequential snapshots of process state
4829 if(SUCCEEDED(pProcess4->ProcessStateChanged(FLUSH_ALL)))
4830 {
4831 // we already have an ICorDebug instance loaded and flushed, nothing more to do
4832 return S_OK;
4833 }
4834 }
4835
4836 // this is a very heavy handed way of reseting
4837 UninitCorDebugInterface();
4838 }
4839
4840 // SOS now has a statically linked version of the loader code that is normally found in mscoree/mscoreei.dll
4841 // Its not much code and takes a big step towards 0 install dependencies
4842 // Need to pick the appropriate SKU of CLR to detect
4843#if defined(FEATURE_CORESYSTEM)
4844 GUID skuId = CLR_ID_ONECORE_CLR;
4845#else
4846 GUID skuId = CLR_ID_CORECLR;
4847#endif
4848 CLRDebuggingImpl* pDebuggingImpl = new CLRDebuggingImpl(skuId);
4849 hr = pDebuggingImpl->QueryInterface(IID_ICLRDebugging, (LPVOID *)&pClrDebugging);
4850 if (FAILED(hr))
4851 {
4852 delete pDebuggingImpl;
4853 return hr;
4854 }
4855
4856#ifndef FEATURE_PAL
4857 ULONG cLoadedModules;
4858 ULONG cUnloadedModules;
4859 hr = g_ExtSymbols->GetNumberModules(&cLoadedModules, &cUnloadedModules);
4860 if (FAILED(hr))
4861 {
4862 return hr;
4863 }
4864
4865 ULONG64 ulBase;
4866 for (ULONG i = 0; i < cLoadedModules; i++)
4867 {
4868 hr = g_ExtSymbols->GetModuleByIndex(i, &ulBase);
4869 if (FAILED(hr))
4870 {
4871 return hr;
4872 }
4873
4874 // Dunno if this is a CLR module or not (or even if it's the particular one the
4875 // user cares about during inproc SxS scenarios). For now, just try to use it
4876 // to grab an ICorDebugProcess. If it works, great. Else, continue the loop
4877 // until we find the first one that works.
4878 hr = InitCorDebugInterfaceFromModule(ulBase, pClrDebugging);
4879 if (SUCCEEDED(hr))
4880 {
4881 return hr;
4882 }
4883
4884 // On failure, just iterate to the next module and try again...
4885 }
4886
4887 // Still here? Didn't find the right module.
4888 // TODO: Anything useful to return or log here?
4889 return E_FAIL;
4890#else
4891 ULONG64 ulBase;
4892 hr = g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_DLL_NAME_A, 0, NULL, &ulBase);
4893 if (SUCCEEDED(hr))
4894 {
4895 hr = InitCorDebugInterfaceFromModule(ulBase, pClrDebugging);
4896 }
4897 return hr;
4898#endif // FEATURE_PAL
4899}
4900
4901
4902typedef enum
4903{
4904 GC_HEAP_INVALID = 0,
4905 GC_HEAP_WKS = 1,
4906 GC_HEAP_SVR = 2
4907} GC_HEAP_TYPE;
4908
4909/**********************************************************************\
4910* Routine Description: *
4911* *
4912* This function is called to find out if runtime is server build *
4913* *
4914\**********************************************************************/
4915
4916DacpGcHeapData *g_pHeapData = NULL;
4917DacpGcHeapData g_HeapData;
4918
4919BOOL InitializeHeapData()
4920{
4921 if (g_pHeapData == NULL)
4922 {
4923 if (g_HeapData.Request(g_sos) != S_OK)
4924 {
4925 return FALSE;
4926 }
4927 g_pHeapData = &g_HeapData;
4928 }
4929 return TRUE;
4930}
4931
4932BOOL IsServerBuild()
4933{
4934 return InitializeHeapData() ? g_pHeapData->bServerMode : FALSE;
4935}
4936
4937UINT GetMaxGeneration()
4938{
4939 return InitializeHeapData() ? g_pHeapData->g_max_generation : 0;
4940}
4941
4942UINT GetGcHeapCount()
4943{
4944 return InitializeHeapData() ? g_pHeapData->HeapCount : 0;
4945}
4946
4947BOOL GetGcStructuresValid()
4948{
4949 // We don't want to use the cached HeapData, because this can change
4950 // each time the program runs for a while.
4951 DacpGcHeapData heapData;
4952 if (heapData.Request(g_sos) != S_OK)
4953 {
4954 return FALSE;
4955 }
4956
4957 return heapData.bGcStructuresValid;
4958}
4959
4960void GetAllocContextPtrs(AllocInfo *pallocInfo)
4961{
4962 // gets the allocation contexts for all threads. This provides information about how much of
4963 // the current allocation quantum has been allocated and the heap to which the quantum belongs.
4964 // The allocation quantum is a fixed size chunk of zeroed memory from which allocations will come
4965 // until it's filled. Each managed thread has its own allocation context.
4966
4967 pallocInfo->num = 0;
4968 pallocInfo->array = NULL;
4969
4970 // get the thread store (See code:ClrDataAccess::RequestThreadStoreData for details)
4971 DacpThreadStoreData ThreadStore;
4972 if ( ThreadStore.Request(g_sos) != S_OK)
4973 {
4974 return;
4975 }
4976
4977 int numThread = ThreadStore.threadCount;
4978 if (numThread)
4979 {
4980 pallocInfo->array = new needed_alloc_context[numThread];
4981 if (pallocInfo->array == NULL)
4982 {
4983 return;
4984 }
4985 }
4986
4987 // get details for each thread in the thread store
4988 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
4989 while (CurThread != NULL)
4990 {
4991 if (IsInterrupt())
4992 return;
4993
4994 DacpThreadData Thread;
4995 // Get information about the thread (we're getting the values of several of the
4996 // fields of the Thread instance from the target) See code:ClrDataAccess::RequestThreadData for
4997 // details
4998 if (Thread.Request(g_sos, CurThread) != S_OK)
4999 {
5000 return;
5001 }
5002
5003 if (Thread.allocContextPtr != 0)
5004 {
5005 // get a list of all the allocation contexts
5006 int j;
5007 for (j = 0; j < pallocInfo->num; j ++)
5008 {
5009 if (pallocInfo->array[j].alloc_ptr == (BYTE *) Thread.allocContextPtr)
5010 break;
5011 }
5012 if (j == pallocInfo->num)
5013 {
5014 pallocInfo->num ++;
5015 pallocInfo->array[j].alloc_ptr = (BYTE *) Thread.allocContextPtr;
5016 pallocInfo->array[j].alloc_limit = (BYTE *) Thread.allocContextLimit;
5017 }
5018 }
5019
5020 CurThread = Thread.nextThread;
5021 }
5022}
5023
5024HRESULT ReadVirtualCache::Read(TADDR taOffset, PVOID Buffer, ULONG BufferSize, PULONG lpcbBytesRead)
5025{
5026 // sign extend the passed in Offset so we can use it in when calling
5027 // IDebugDataSpaces::ReadVirtual()
5028
5029 CLRDATA_ADDRESS Offset = TO_CDADDR(taOffset);
5030 // Offset can be any random ULONG64, as it can come from VerifyObjectMember(), and this
5031 // can pass random pointer values in case of GC heap corruption
5032 HRESULT ret;
5033 ULONG cbBytesRead = 0;
5034
5035 if (BufferSize == 0)
5036 return S_OK;
5037
5038 if (BufferSize > CACHE_SIZE)
5039 {
5040 // Don't even try with the cache
5041 return g_ExtData->ReadVirtual(Offset, Buffer, BufferSize, lpcbBytesRead);
5042 }
5043
5044 if ((m_cacheValid)
5045 && (taOffset >= m_startCache)
5046 && (taOffset <= m_startCache + m_cacheSize - BufferSize))
5047
5048 {
5049 // It is within the cache
5050 memcpy(Buffer,(LPVOID) ((ULONG64)m_cache + (taOffset - m_startCache)), BufferSize);
5051
5052 if (lpcbBytesRead != NULL)
5053 {
5054 *lpcbBytesRead = BufferSize;
5055 }
5056
5057 return S_OK;
5058 }
5059
5060 m_cacheValid = FALSE;
5061 m_startCache = taOffset;
5062
5063 // avoid an int overflow
5064 if (m_startCache + CACHE_SIZE < m_startCache)
5065 m_startCache = (TADDR)(-CACHE_SIZE);
5066
5067 ret = g_ExtData->ReadVirtual(TO_CDADDR(m_startCache), m_cache, CACHE_SIZE, &cbBytesRead);
5068 if (ret != S_OK)
5069 {
5070 return ret;
5071 }
5072
5073 m_cacheSize = cbBytesRead;
5074 m_cacheValid = TRUE;
5075 memcpy(Buffer, (LPVOID) ((ULONG64)m_cache + (taOffset - m_startCache)), BufferSize);
5076
5077 if (lpcbBytesRead != NULL)
5078 {
5079 *lpcbBytesRead = cbBytesRead;
5080 }
5081
5082 return S_OK;
5083}
5084
5085HRESULT GetMTOfObject(TADDR obj, TADDR *mt)
5086{
5087 if (!mt)
5088 return E_POINTER;
5089
5090 // Read the MethodTable and if we succeed, get rid of the mark bits.
5091 HRESULT hr = rvCache->Read(obj, mt, sizeof(TADDR), NULL);
5092 if (SUCCEEDED(hr))
5093 *mt &= ~3;
5094
5095 return hr;
5096}
5097
5098#ifndef FEATURE_PAL
5099
5100StressLogMem::~StressLogMem ()
5101{
5102 MemRange * range = list;
5103
5104 while (range)
5105 {
5106 MemRange * temp = range->next;
5107 delete range;
5108 range = temp;
5109 }
5110}
5111
5112bool StressLogMem::Init (ULONG64 stressLogAddr, IDebugDataSpaces* memCallBack)
5113{
5114 size_t ThreadStressLogAddr = NULL;
5115 HRESULT hr = memCallBack->ReadVirtual(UL64_TO_CDA(stressLogAddr + offsetof (StressLog, logs)),
5116 &ThreadStressLogAddr, sizeof (ThreadStressLogAddr), 0);
5117 if (hr != S_OK)
5118 {
5119 return false;
5120 }
5121
5122 while(ThreadStressLogAddr != NULL)
5123 {
5124 size_t ChunkListHeadAddr = NULL;
5125 hr = memCallBack->ReadVirtual(TO_CDADDR(ThreadStressLogAddr + ThreadStressLog::OffsetOfListHead ()),
5126 &ChunkListHeadAddr, sizeof (ChunkListHeadAddr), 0);
5127 if (hr != S_OK || ChunkListHeadAddr == NULL)
5128 {
5129 return false;
5130 }
5131
5132 size_t StressLogChunkAddr = ChunkListHeadAddr;
5133
5134 do
5135 {
5136 AddRange (StressLogChunkAddr, sizeof (StressLogChunk));
5137 hr = memCallBack->ReadVirtual(TO_CDADDR(StressLogChunkAddr + offsetof (StressLogChunk, next)),
5138 &StressLogChunkAddr, sizeof (StressLogChunkAddr), 0);
5139 if (hr != S_OK)
5140 {
5141 return false;
5142 }
5143 if (StressLogChunkAddr == NULL)
5144 {
5145 return true;
5146 }
5147 } while (StressLogChunkAddr != ChunkListHeadAddr);
5148
5149 hr = memCallBack->ReadVirtual(TO_CDADDR(ThreadStressLogAddr + ThreadStressLog::OffsetOfNext ()),
5150 &ThreadStressLogAddr, sizeof (ThreadStressLogAddr), 0);
5151 if (hr != S_OK)
5152 {
5153 return false;
5154 }
5155 }
5156
5157 return true;
5158}
5159
5160bool StressLogMem::IsInStressLog (ULONG64 addr)
5161{
5162 MemRange * range = list;
5163 while (range)
5164 {
5165 if (range->InRange (addr))
5166 return true;
5167 range = range->next;
5168 }
5169
5170 return false;
5171}
5172
5173#endif // !FEATURE_PAL
5174
5175unsigned int Output::g_bSuppressOutput = 0;
5176unsigned int Output::g_Indent = 0;
5177bool Output::g_bDbgOutput = false;
5178bool Output::g_bDMLExposed = false;
5179unsigned int Output::g_DMLEnable = 0;
5180
5181template <class T, int count, int size> const int StaticData<T, count, size>::Count = count;
5182template <class T, int count, int size> const int StaticData<T, count, size>::Size = size;
5183
5184StaticData<char, 4, 1024> CachedString::cache;
5185
5186CachedString::CachedString()
5187: mPtr(0), mRefCount(0), mIndex(~0), mSize(cache.Size)
5188{
5189 Create();
5190}
5191
5192CachedString::CachedString(const CachedString &rhs)
5193: mPtr(0), mRefCount(0), mIndex(~0), mSize(cache.Size)
5194{
5195 Copy(rhs);
5196}
5197
5198CachedString::~CachedString()
5199{
5200 Clear();
5201}
5202
5203const CachedString &CachedString::operator=(const CachedString &rhs)
5204{
5205 Clear();
5206 Copy(rhs);
5207 return *this;
5208}
5209
5210void CachedString::Copy(const CachedString &rhs)
5211{
5212 if (rhs.IsOOM())
5213 {
5214 SetOOM();
5215 }
5216 else
5217 {
5218 mPtr = rhs.mPtr;
5219 mIndex = rhs.mIndex;
5220 mSize = rhs.mSize;
5221
5222 if (rhs.mRefCount)
5223 {
5224 mRefCount = rhs.mRefCount;
5225 (*mRefCount)++;
5226 }
5227 else
5228 {
5229 // We only create count the first time we copy it, so
5230 // we initialize it to 2.
5231 mRefCount = rhs.mRefCount = new unsigned int(2);
5232 if (!mRefCount)
5233 SetOOM();
5234 }
5235 }
5236}
5237
5238void CachedString::Clear()
5239{
5240 if (!mRefCount || --*mRefCount == 0)
5241 {
5242 if (mIndex == -1)
5243 {
5244 if (mPtr)
5245 delete [] mPtr;
5246 }
5247 else if (mIndex >= 0 && mIndex < cache.Count)
5248 {
5249 cache.InUse[mIndex] = false;
5250 }
5251
5252 if (mRefCount)
5253 delete mRefCount;
5254 }
5255
5256 mPtr = 0;
5257 mIndex = ~0;
5258 mRefCount = 0;
5259 mSize = cache.Size;
5260}
5261
5262
5263void CachedString::Create()
5264{
5265 mIndex = -1;
5266 mRefCount = 0;
5267
5268 // First try to find a string in the cache to use.
5269 for (int i = 0; i < cache.Count; ++i)
5270 if (!cache.InUse[i])
5271 {
5272 cache.InUse[i] = true;
5273 mPtr = cache.Data[i];
5274 mIndex = i;
5275 break;
5276 }
5277
5278 // We did not find a string to use, so we'll create a new one.
5279 if (mIndex == -1)
5280 {
5281 mPtr = new char[cache.Size];
5282 if (!mPtr)
5283 SetOOM();
5284 }
5285}
5286
5287
5288void CachedString::SetOOM()
5289{
5290 Clear();
5291 mIndex = -2;
5292}
5293
5294void CachedString::Allocate(int size)
5295{
5296 Clear();
5297 mPtr = new char[size];
5298
5299 if (mPtr)
5300 {
5301 mSize = size;
5302 mIndex = -1;
5303 }
5304 else
5305 {
5306 SetOOM();
5307 }
5308}
5309
5310size_t CountHexCharacters(CLRDATA_ADDRESS val)
5311{
5312 size_t ret = 0;
5313
5314 while (val)
5315 {
5316 val >>= 4;
5317 ret++;
5318 }
5319
5320 return ret;
5321}
5322
5323void WhitespaceOut(int count)
5324{
5325 static const int FixedIndentWidth = 0x40;
5326 static const char FixedIndentString[FixedIndentWidth+1] =
5327 " ";
5328
5329 if (count <= 0)
5330 return;
5331
5332 int mod = count & 0x3F;
5333 count &= ~0x3F;
5334
5335 if (mod > 0)
5336 g_ExtControl->Output(DEBUG_OUTPUT_NORMAL, "%.*s", mod, FixedIndentString);
5337
5338 for ( ; count > 0; count -= FixedIndentWidth)
5339 g_ExtControl->Output(DEBUG_OUTPUT_NORMAL, FixedIndentString);
5340}
5341
5342void DMLOut(PCSTR format, ...)
5343{
5344 if (Output::IsOutputSuppressed())
5345 return;
5346
5347 va_list args;
5348 va_start(args, format);
5349 ExtOutIndent();
5350
5351#ifndef FEATURE_PAL
5352 if (IsDMLEnabled() && !Output::IsDMLExposed())
5353 {
5354 g_ExtControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, format, args);
5355 }
5356 else
5357#endif
5358 {
5359 g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, format, args);
5360 }
5361
5362 va_end(args);
5363}
5364
5365void IfDMLOut(PCSTR format, ...)
5366{
5367#ifndef FEATURE_PAL
5368 if (Output::IsOutputSuppressed() || !IsDMLEnabled())
5369 return;
5370
5371 va_list args;
5372
5373 va_start(args, format);
5374 ExtOutIndent();
5375 g_ExtControl->ControlledOutputVaList(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, format, args);
5376 va_end(args);
5377#endif
5378}
5379
5380void ExtOut(PCSTR Format, ...)
5381{
5382 if (Output::IsOutputSuppressed())
5383 return;
5384
5385 va_list Args;
5386
5387 va_start(Args, Format);
5388 ExtOutIndent();
5389 g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
5390 va_end(Args);
5391}
5392
5393void ExtWarn(PCSTR Format, ...)
5394{
5395 if (Output::IsOutputSuppressed())
5396 return;
5397
5398 va_list Args;
5399
5400 va_start(Args, Format);
5401 g_ExtControl->OutputVaList(DEBUG_OUTPUT_WARNING, Format, Args);
5402 va_end(Args);
5403}
5404
5405void ExtErr(PCSTR Format, ...)
5406{
5407 va_list Args;
5408
5409 va_start(Args, Format);
5410 g_ExtControl->OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args);
5411 va_end(Args);
5412}
5413
5414
5415void ExtDbgOut(PCSTR Format, ...)
5416{
5417#ifdef _DEBUG
5418 if (Output::g_bDbgOutput)
5419 {
5420 va_list Args;
5421
5422 va_start(Args, Format);
5423 ExtOutIndent();
5424 g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
5425 va_end(Args);
5426 }
5427#endif
5428}
5429
5430const char * const DMLFormats[] =
5431{
5432 NULL, // DML_None (do not use)
5433 "<exec cmd=\"!DumpMT /d %s\">%s</exec>", // DML_MethodTable
5434 "<exec cmd=\"!DumpMD /d %s\">%s</exec>", // DML_MethodDesc
5435 "<exec cmd=\"!DumpClass /d %s\">%s</exec>", // DML_EEClass
5436 "<exec cmd=\"!DumpModule /d %s\">%s</exec>", // DML_Module
5437 "<exec cmd=\"!U /d %s\">%s</exec>", // DML_IP
5438 "<exec cmd=\"!DumpObj /d %s\">%s</exec>", // DML_Object
5439 "<exec cmd=\"!DumpDomain /d %s\">%s</exec>", // DML_Domain
5440 "<exec cmd=\"!DumpAssembly /d %s\">%s</exec>", // DML_Assembly
5441 "<exec cmd=\"~~[%s]s\">%s</exec>", // DML_ThreadID
5442 "<exec cmd=\"!DumpVC /d %s %s\">%s</exec>", // DML_ValueClass
5443 "<exec cmd=\"!DumpHeap /d -mt %s\">%s</exec>", // DML_DumpHeapMT
5444 "<exec cmd=\"!ListNearObj /d %s\">%s</exec>", // DML_ListNearObj
5445 "<exec cmd=\"!ThreadState %s\">%s</exec>", // DML_ThreadState
5446 "<exec cmd=\"!PrintException /d %s\">%s</exec>",// DML_PrintException
5447 "<exec cmd=\"!DumpRCW /d %s\">%s</exec>", // DML_RCWrapper
5448 "<exec cmd=\"!DumpCCW /d %s\">%s</exec>", // DML_CCWrapper
5449 "<exec cmd=\"!ClrStack -i %S %d\">%S</exec>", // DML_ManagedVar
5450 "<exec cmd=\"!DumpAsync -addr %s -tasks -completed -fields -stacks -roots\">%s</exec>", // DML_Async
5451};
5452
5453void ConvertToLower(__out_ecount(len) char *buffer, size_t len)
5454{
5455 for (size_t i = 0; i < len && buffer[i]; ++i)
5456 buffer[i] = (char)tolower(buffer[i]);
5457}
5458
5459/* Build a hex display of addr.
5460 */
5461int GetHex(CLRDATA_ADDRESS addr, __out_ecount(len) char *out, size_t len, bool fill)
5462{
5463 int count = sprintf_s(out, len, fill ? "%p" : "%x", (size_t)addr);
5464
5465 ConvertToLower(out, len);
5466
5467 return count;
5468}
5469
5470CachedString Output::BuildHexValue(CLRDATA_ADDRESS addr, FormatType type, bool fill)
5471{
5472 CachedString ret;
5473 if (ret.IsOOM())
5474 {
5475 ReportOOM();
5476 return ret;
5477 }
5478
5479 if (IsDMLEnabled())
5480 {
5481 char hex[POINTERSIZE_BYTES*2 + 1];
5482 GetHex(addr, hex, _countof(hex), fill);
5483 sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], hex, hex);
5484 }
5485 else
5486 {
5487 GetHex(addr, ret, ret.GetStrLen(), fill);
5488 }
5489
5490 return ret;
5491}
5492
5493CachedString Output::BuildVCValue(CLRDATA_ADDRESS mt, CLRDATA_ADDRESS addr, FormatType type, bool fill)
5494{
5495 _ASSERTE(type == DML_ValueClass);
5496 CachedString ret;
5497 if (ret.IsOOM())
5498 {
5499 ReportOOM();
5500 return ret;
5501 }
5502
5503 if (IsDMLEnabled())
5504 {
5505 char hexaddr[POINTERSIZE_BYTES*2 + 1];
5506 char hexmt[POINTERSIZE_BYTES*2 + 1];
5507
5508 GetHex(addr, hexaddr, _countof(hexaddr), fill);
5509 GetHex(mt, hexmt, _countof(hexmt), fill);
5510
5511 sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], hexmt, hexaddr, hexaddr);
5512 }
5513 else
5514 {
5515 GetHex(addr, ret, ret.GetStrLen(), fill);
5516 }
5517
5518 return ret;
5519}
5520
5521CachedString Output::BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, __in_z LPCWSTR simpleName, FormatType type)
5522{
5523 _ASSERTE(type == DML_ManagedVar);
5524 CachedString ret;
5525 if (ret.IsOOM())
5526 {
5527 ReportOOM();
5528 return ret;
5529 }
5530
5531 // calculate the number of digits in frame (this assumes base-10 display of frames)
5532 int numFrameDigits = 0;
5533 if (frame > 0)
5534 {
5535 ULONG tempFrame = frame;
5536 while (tempFrame > 0)
5537 {
5538 ++numFrameDigits;
5539 tempFrame /= 10;
5540 }
5541 }
5542 else
5543 {
5544 numFrameDigits = 1;
5545 }
5546
5547 size_t totalStringLength = strlen(DMLFormats[type]) + _wcslen(expansionName) + numFrameDigits + _wcslen(simpleName) + 1;
5548 if (totalStringLength > ret.GetStrLen())
5549 {
5550 ret.Allocate(static_cast<int>(totalStringLength));
5551 if (ret.IsOOM())
5552 {
5553 ReportOOM();
5554 return ret;
5555 }
5556 }
5557
5558 if (IsDMLEnabled())
5559 {
5560 sprintf_s(ret, ret.GetStrLen(), DMLFormats[type], expansionName, frame, simpleName);
5561 }
5562 else
5563 {
5564 sprintf_s(ret, ret.GetStrLen(), "%S", simpleName);
5565 }
5566
5567 return ret;
5568}
5569
5570CachedString Output::BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, int indexInArray, FormatType type)
5571{
5572 WCHAR indexString[24];
5573 swprintf_s(indexString, _countof(indexString), W("[%d]"), indexInArray);
5574 return BuildManagedVarValue(expansionName, frame, indexString, type);
5575}
5576
5577EnableDMLHolder::EnableDMLHolder(BOOL enable)
5578 : mEnable(enable)
5579{
5580#ifndef FEATURE_PAL
5581 // If the user has not requested that we use DML, it's still possible that
5582 // they have instead specified ".prefer_dml 1". If enable is false,
5583 // we will check here for .prefer_dml. Since this class is only used once
5584 // per command issued to SOS, this should only check the setting once per
5585 // sos command issued.
5586 if (!mEnable && Output::g_DMLEnable <= 0)
5587 {
5588 ULONG opts;
5589 HRESULT hr = g_ExtControl->GetEngineOptions(&opts);
5590 mEnable = SUCCEEDED(hr) && (opts & DEBUG_ENGOPT_PREFER_DML) == DEBUG_ENGOPT_PREFER_DML;
5591 }
5592
5593 if (mEnable)
5594 {
5595 Output::g_DMLEnable++;
5596 }
5597#endif // FEATURE_PAL
5598}
5599
5600EnableDMLHolder::~EnableDMLHolder()
5601{
5602#ifndef FEATURE_PAL
5603 if (mEnable)
5604 Output::g_DMLEnable--;
5605#endif
5606}
5607
5608bool IsDMLEnabled()
5609{
5610 return Output::g_DMLEnable > 0;
5611}
5612
5613NoOutputHolder::NoOutputHolder(BOOL bSuppress)
5614 : mSuppress(bSuppress)
5615{
5616 if (mSuppress)
5617 Output::g_bSuppressOutput++;
5618}
5619
5620NoOutputHolder::~NoOutputHolder()
5621{
5622 if (mSuppress)
5623 Output::g_bSuppressOutput--;
5624}
5625
5626//
5627// Code to support mapping RVAs to managed code line numbers.
5628//
5629
5630//
5631// Retrieves the IXCLRDataMethodInstance* instance associated with the
5632// passed in native offset.
5633HRESULT
5634GetClrMethodInstance(
5635 ___in ULONG64 NativeOffset,
5636 ___out IXCLRDataMethodInstance** Method)
5637{
5638 HRESULT Status;
5639 CLRDATA_ENUM MethEnum;
5640
5641 Status = g_clrData->StartEnumMethodInstancesByAddress(NativeOffset, NULL, &MethEnum);
5642
5643 if (Status == S_OK)
5644 {
5645 Status = g_clrData->EnumMethodInstanceByAddress(&MethEnum, Method);
5646 g_clrData->EndEnumMethodInstancesByAddress(MethEnum);
5647 }
5648
5649 // Any alternate success is a true failure here.
5650 return (Status == S_OK || FAILED(Status)) ? Status : E_NOINTERFACE;
5651}
5652
5653//
5654// Enumerates over the IL address map associated with the passed in
5655// managed method, and returns the highest non-epilog offset.
5656HRESULT
5657GetLastMethodIlOffset(
5658 ___in IXCLRDataMethodInstance* Method,
5659 ___out PULONG32 MethodOffs)
5660{
5661 HRESULT Status;
5662 CLRDATA_IL_ADDRESS_MAP MapLocal[16];
5663 CLRDATA_IL_ADDRESS_MAP* Map = MapLocal;
5664 ULONG32 MapCount = _countof(MapLocal);
5665 ULONG32 MapNeeded;
5666 ULONG32 HighestOffset;
5667
5668 for (;;)
5669 {
5670 if ((Status = Method->GetILAddressMap(MapCount, &MapNeeded, Map)) != S_OK)
5671 {
5672 return Status;
5673 }
5674
5675 if (MapNeeded <= MapCount)
5676 {
5677 break;
5678 }
5679
5680 // Need more map entries.
5681 if (Map != MapLocal)
5682 {
5683 // Already went around and the answer changed,
5684 // which should not be possible.
5685 delete[] Map;
5686 return E_UNEXPECTED;
5687 }
5688
5689 Map = new CLRDATA_IL_ADDRESS_MAP[MapNeeded];
5690 if (!Map)
5691 {
5692 return E_OUTOFMEMORY;
5693 }
5694
5695 MapCount = MapNeeded;
5696 }
5697
5698 HighestOffset = 0;
5699 for (size_t i = 0; i < MapNeeded; i++)
5700 {
5701 if (Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_NO_MAPPING &&
5702 Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_PROLOG &&
5703 Map[i].ilOffset != (ULONG32)CLRDATA_IL_OFFSET_EPILOG &&
5704 Map[i].ilOffset > HighestOffset)
5705 {
5706 HighestOffset = Map[i].ilOffset;
5707 }
5708 }
5709
5710 if (Map != MapLocal)
5711 {
5712 delete[] Map;
5713 }
5714
5715 *MethodOffs = HighestOffset;
5716 return S_OK;
5717}
5718
5719//
5720// Convert a native offset (possibly already associated with a managed
5721// method identified by the passed in IXCLRDataMethodInstance) to a
5722// triplet (ImageInfo, MethodToken, MethodOffset) that can be used to
5723// represent an "IL offset".
5724HRESULT
5725ConvertNativeToIlOffset(
5726 ___in ULONG64 native,
5727 ___out IXCLRDataModule** ppModule,
5728 ___out mdMethodDef* methodToken,
5729 ___out PULONG32 methodOffs)
5730{
5731 ToRelease<IXCLRDataMethodInstance> pMethodInst(NULL);
5732 HRESULT Status;
5733
5734 if ((Status = GetClrMethodInstance(native, &pMethodInst)) != S_OK)
5735 {
5736 return Status;
5737 }
5738
5739 if ((Status = pMethodInst->GetILOffsetsByAddress(native, 1, NULL, methodOffs)) != S_OK)
5740 {
5741 *methodOffs = 0;
5742 }
5743 else
5744 {
5745 switch((LONG)*methodOffs)
5746 {
5747 case CLRDATA_IL_OFFSET_NO_MAPPING:
5748 return E_NOINTERFACE;
5749
5750 case CLRDATA_IL_OFFSET_PROLOG:
5751 // Treat all of the prologue as part of
5752 // the first source line.
5753 *methodOffs = 0;
5754 break;
5755
5756 case CLRDATA_IL_OFFSET_EPILOG:
5757 // Back up until we find the last real
5758 // IL offset.
5759 if ((Status = GetLastMethodIlOffset(pMethodInst, methodOffs)) != S_OK)
5760 {
5761 return Status;
5762 }
5763 break;
5764 }
5765 }
5766
5767 return pMethodInst->GetTokenAndScope(methodToken, ppModule);
5768}
5769
5770// Based on a native offset, passed in the first argument this function
5771// identifies the corresponding source file name and line number.
5772HRESULT
5773GetLineByOffset(
5774 ___in ULONG64 offset,
5775 ___out ULONG *pLinenum,
5776 __out_ecount(cchFileName) WCHAR* pwszFileName,
5777 ___in ULONG cchFileName)
5778{
5779 HRESULT Status = S_OK;
5780 ULONG32 methodToken;
5781 ULONG32 methodOffs;
5782
5783 // Find the image, method token and IL offset that correspond to "offset"
5784 ToRelease<IXCLRDataModule> pModule(NULL);
5785 IfFailRet(ConvertNativeToIlOffset(offset, &pModule, &methodToken, &methodOffs));
5786
5787 ToRelease<IMetaDataImport> pMDImport(NULL);
5788 IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
5789
5790 SymbolReader symbolReader;
5791 IfFailRet(symbolReader.LoadSymbols(pMDImport, pModule));
5792
5793 return symbolReader.GetLineByILOffset(methodToken, methodOffs, pLinenum, pwszFileName, cchFileName);
5794}
5795
5796void TableOutput::ReInit(int numColumns, int defaultColumnWidth, Alignment alignmentDefault, int indent, int padding)
5797{
5798 Clear();
5799
5800 mColumns = numColumns;
5801 mDefaultWidth = defaultColumnWidth;
5802 mIndent = indent;
5803 mPadding = padding;
5804 mCurrCol = 0;
5805 mDefaultAlign = alignmentDefault;
5806}
5807
5808void TableOutput::SetWidths(int columns, ...)
5809{
5810 SOS_Assert(columns > 0);
5811 SOS_Assert(columns <= mColumns);
5812
5813 AllocWidths();
5814
5815 va_list list;
5816 va_start(list, columns);
5817
5818 for (int i = 0; i < columns; ++i)
5819 mWidths[i] = va_arg(list, int);
5820
5821 va_end(list);
5822}
5823
5824void TableOutput::SetColWidth(int col, int width)
5825{
5826 SOS_Assert(col >= 0 && col < mColumns);
5827 SOS_Assert(width >= 0);
5828
5829 AllocWidths();
5830
5831 mWidths[col] = width;
5832}
5833
5834void TableOutput::SetColAlignment(int col, Alignment align)
5835{
5836 SOS_Assert(col >= 0 && col < mColumns);
5837
5838 if (!mAlignments)
5839 {
5840 mAlignments = new Alignment[mColumns];
5841 for (int i = 0; i < mColumns; ++i)
5842 mAlignments[i] = mDefaultAlign;
5843 }
5844
5845 mAlignments[col] = align;
5846}
5847
5848
5849
5850void TableOutput::Clear()
5851{
5852 if (mAlignments)
5853 {
5854 delete [] mAlignments;
5855 mAlignments = 0;
5856 }
5857
5858 if (mWidths)
5859 {
5860 delete [] mWidths;
5861 mWidths = 0;
5862 }
5863}
5864
5865void TableOutput::AllocWidths()
5866{
5867 if (!mWidths)
5868 {
5869 mWidths = new int[mColumns];
5870 for (int i = 0; i < mColumns; ++i)
5871 mWidths[i] = mDefaultWidth;
5872 }
5873}
5874
5875int TableOutput::GetColumnWidth(int col)
5876{
5877 SOS_Assert(col < mColumns);
5878
5879 if (mWidths)
5880 return mWidths[col];
5881
5882 return mDefaultWidth;
5883}
5884
5885Alignment TableOutput::GetColAlign(int col)
5886{
5887 SOS_Assert(col < mColumns);
5888 if (mAlignments)
5889 return mAlignments[col];
5890
5891 return mDefaultAlign;
5892}
5893
5894const char *TableOutput::GetWhitespace(int amount)
5895{
5896 static char WhiteSpace[256] = "";
5897 static int count = 0;
5898
5899 if (count == 0)
5900 {
5901 count = _countof(WhiteSpace);
5902 for (int i = 0; i < count-1; ++i)
5903 WhiteSpace[i] = ' ';
5904 WhiteSpace[count-1] = 0;
5905 }
5906
5907 SOS_Assert(amount < count);
5908 return &WhiteSpace[count-amount-1];
5909}
5910
5911void TableOutput::OutputBlankColumns(int col)
5912{
5913 if (col < mCurrCol)
5914 {
5915 ExtOut("\n");
5916 mCurrCol = 0;
5917 }
5918
5919 int whitespace = 0;
5920 for (int i = mCurrCol; i < col; ++i)
5921 whitespace += GetColumnWidth(i) + mPadding;
5922
5923 ExtOut(GetWhitespace(whitespace));
5924}
5925
5926void TableOutput::OutputIndent()
5927{
5928 if (mIndent)
5929 ExtOut(GetWhitespace(mIndent));
5930}
5931
5932#ifndef FEATURE_PAL
5933
5934PEOffsetMemoryReader::PEOffsetMemoryReader(TADDR moduleBaseAddress) :
5935 m_moduleBaseAddress(moduleBaseAddress),
5936 m_refCount(1)
5937 {}
5938
5939HRESULT __stdcall PEOffsetMemoryReader::QueryInterface(REFIID riid, VOID** ppInterface)
5940{
5941 if(riid == __uuidof(IDiaReadExeAtOffsetCallback))
5942 {
5943 *ppInterface = static_cast<IDiaReadExeAtOffsetCallback*>(this);
5944 AddRef();
5945 return S_OK;
5946 }
5947 else if(riid == __uuidof(IUnknown))
5948 {
5949 *ppInterface = static_cast<IUnknown*>(this);
5950 AddRef();
5951 return S_OK;
5952 }
5953 else
5954 {
5955 return E_NOINTERFACE;
5956 }
5957}
5958
5959ULONG __stdcall PEOffsetMemoryReader::AddRef()
5960{
5961 return InterlockedIncrement((volatile LONG *) &m_refCount);
5962}
5963
5964ULONG __stdcall PEOffsetMemoryReader::Release()
5965{
5966 ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount);
5967 if(count == 0)
5968 {
5969 delete this;
5970 }
5971 return count;
5972}
5973
5974// IDiaReadExeAtOffsetCallback implementation
5975HRESULT __stdcall PEOffsetMemoryReader::ReadExecutableAt(DWORDLONG fileOffset, DWORD cbData, DWORD* pcbData, BYTE data[])
5976{
5977 return SafeReadMemory(m_moduleBaseAddress + fileOffset, data, cbData, pcbData) ? S_OK : E_FAIL;
5978}
5979
5980PERvaMemoryReader::PERvaMemoryReader(TADDR moduleBaseAddress) :
5981 m_moduleBaseAddress(moduleBaseAddress),
5982 m_refCount(1)
5983 {}
5984
5985HRESULT __stdcall PERvaMemoryReader::QueryInterface(REFIID riid, VOID** ppInterface)
5986{
5987 if(riid == __uuidof(IDiaReadExeAtRVACallback))
5988 {
5989 *ppInterface = static_cast<IDiaReadExeAtRVACallback*>(this);
5990 AddRef();
5991 return S_OK;
5992 }
5993 else if(riid == __uuidof(IUnknown))
5994 {
5995 *ppInterface = static_cast<IUnknown*>(this);
5996 AddRef();
5997 return S_OK;
5998 }
5999 else
6000 {
6001 return E_NOINTERFACE;
6002 }
6003}
6004
6005ULONG __stdcall PERvaMemoryReader::AddRef()
6006{
6007 return InterlockedIncrement((volatile LONG *) &m_refCount);
6008}
6009
6010ULONG __stdcall PERvaMemoryReader::Release()
6011{
6012 ULONG count = InterlockedDecrement((volatile LONG *) &m_refCount);
6013 if(count == 0)
6014 {
6015 delete this;
6016 }
6017 return count;
6018}
6019
6020// IDiaReadExeAtOffsetCallback implementation
6021HRESULT __stdcall PERvaMemoryReader::ReadExecutableAtRVA(DWORD relativeVirtualAddress, DWORD cbData, DWORD* pcbData, BYTE data[])
6022{
6023 return SafeReadMemory(m_moduleBaseAddress + relativeVirtualAddress, data, cbData, pcbData) ? S_OK : E_FAIL;
6024}
6025
6026#endif // FEATURE_PAL
6027
6028HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in ICorDebugModule* pModule)
6029{
6030 HRESULT Status = S_OK;
6031 BOOL isDynamic = FALSE;
6032 BOOL isInMemory = FALSE;
6033 IfFailRet(pModule->IsDynamic(&isDynamic));
6034 IfFailRet(pModule->IsInMemory(&isInMemory));
6035
6036 if (isDynamic)
6037 {
6038 // Dynamic and in memory assemblies are a special case which we will ignore for now
6039 ExtWarn("SOS Warning: Loading symbols for dynamic assemblies is not yet supported\n");
6040 return E_FAIL;
6041 }
6042
6043 ULONG64 peAddress = 0;
6044 ULONG32 peSize = 0;
6045 IfFailRet(pModule->GetBaseAddress(&peAddress));
6046 IfFailRet(pModule->GetSize(&peSize));
6047
6048 ULONG32 len = 0;
6049 WCHAR moduleName[MAX_LONGPATH];
6050 IfFailRet(pModule->GetName(_countof(moduleName), &len, moduleName));
6051
6052#ifndef FEATURE_PAL
6053 if (SUCCEEDED(LoadSymbolsForWindowsPDB(pMD, peAddress, moduleName, isInMemory)))
6054 {
6055 return S_OK;
6056 }
6057#endif // FEATURE_PAL
6058 return LoadSymbolsForPortablePDB(moduleName, isInMemory, isInMemory, peAddress, peSize, 0, 0);
6059}
6060
6061HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataModule* pModule)
6062{
6063 DacpGetModuleData moduleData;
6064 HRESULT hr = moduleData.Request(pModule);
6065 if (FAILED(hr))
6066 {
6067 ExtOut("LoadSymbols moduleData.Request FAILED 0x%08x\n", hr);
6068 return hr;
6069 }
6070
6071 if (moduleData.IsDynamic)
6072 {
6073 ExtWarn("SOS Warning: Loading symbols for dynamic assemblies is not yet supported\n");
6074 return E_FAIL;
6075 }
6076
6077 ArrayHolder<WCHAR> pModuleName = new WCHAR[MAX_LONGPATH + 1];
6078 ULONG32 nameLen = 0;
6079 hr = pModule->GetFileName(MAX_LONGPATH, &nameLen, pModuleName);
6080 if (FAILED(hr))
6081 {
6082 ExtOut("LoadSymbols: IXCLRDataModule->GetFileName FAILED 0x%08x\n", hr);
6083 return hr;
6084 }
6085
6086#ifndef FEATURE_PAL
6087 // TODO: in-memory windows PDB not supported
6088 hr = LoadSymbolsForWindowsPDB(pMD, moduleData.LoadedPEAddress, pModuleName, moduleData.IsFileLayout);
6089 if (SUCCEEDED(hr))
6090 {
6091 return hr;
6092 }
6093#endif // FEATURE_PAL
6094
6095 return LoadSymbolsForPortablePDB(
6096 pModuleName,
6097 moduleData.IsInMemory,
6098 moduleData.IsFileLayout,
6099 moduleData.LoadedPEAddress,
6100 moduleData.LoadedPESize,
6101 moduleData.InMemoryPdbAddress,
6102 moduleData.InMemoryPdbSize);
6103}
6104
6105#ifndef FEATURE_PAL
6106
6107HRESULT SymbolReader::LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in ULONG64 peAddress, __in_z WCHAR* pModuleName, ___in BOOL isFileLayout)
6108{
6109 HRESULT Status = S_OK;
6110
6111 if (m_pSymReader != NULL)
6112 return S_OK;
6113
6114 IfFailRet(CoInitialize(NULL));
6115
6116 // We now need a binder object that will take the module and return a
6117 // reader object
6118 ToRelease<ISymUnmanagedBinder3> pSymBinder;
6119 if (FAILED(Status = CreateInstanceCustom(CLSID_CorSymBinder_SxS,
6120 IID_ISymUnmanagedBinder3,
6121 NATIVE_SYMBOL_READER_DLL,
6122 cciLatestFx|cciDacColocated|cciDbgPath,
6123 (void**)&pSymBinder)))
6124 {
6125 ExtOut("SOS Error: Unable to CoCreateInstance class=CLSID_CorSymBinder_SxS, interface=IID_ISymUnmanagedBinder3, hr=0x%x\n", Status);
6126 ExtOut("This usually means SOS was unable to locate a suitable version of DiaSymReader. The dll searched for was '%S'\n", NATIVE_SYMBOL_READER_DLL);
6127 return Status;
6128 }
6129
6130 ToRelease<IDebugSymbols3> spSym3(NULL);
6131 Status = g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3);
6132 if (FAILED(Status))
6133 {
6134 ExtOut("SOS Error: Unable to query IDebugSymbols3 HRESULT=0x%x.\n", Status);
6135 return Status;
6136 }
6137
6138 ULONG pathSize = 0;
6139 Status = spSym3->GetSymbolPathWide(NULL, 0, &pathSize);
6140 if (FAILED(Status)) //S_FALSE if the path doesn't fit, but if the path was size 0 perhaps we would get S_OK?
6141 {
6142 ExtOut("SOS Error: Unable to get symbol path length. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", Status);
6143 return Status;
6144 }
6145
6146 ArrayHolder<WCHAR> symbolPath = new WCHAR[pathSize];
6147 Status = spSym3->GetSymbolPathWide(symbolPath, pathSize, NULL);
6148 if (S_OK != Status)
6149 {
6150 ExtOut("SOS Error: Unable to get symbol path. IDebugSymbols3::GetSymbolPathWide HRESULT=0x%x.\n", Status);
6151 return Status;
6152 }
6153
6154 ToRelease<IUnknown> pCallback = NULL;
6155 if (isFileLayout)
6156 {
6157 pCallback = (IUnknown*) new PEOffsetMemoryReader(TO_TADDR(peAddress));
6158 }
6159 else
6160 {
6161 pCallback = (IUnknown*) new PERvaMemoryReader(TO_TADDR(peAddress));
6162 }
6163
6164 // TODO: this should be better integrated with windbg's symbol lookup
6165 Status = pSymBinder->GetReaderFromCallback(pMD, pModuleName, symbolPath,
6166 AllowRegistryAccess | AllowSymbolServerAccess | AllowOriginalPathAccess | AllowReferencePathAccess, pCallback, &m_pSymReader);
6167
6168 if (FAILED(Status) && m_pSymReader != NULL)
6169 {
6170 m_pSymReader->Release();
6171 m_pSymReader = NULL;
6172 }
6173 return Status;
6174}
6175
6176#endif // FEATURE_PAL
6177
6178//
6179// Pass to managed helper code to read in-memory PEs/PDBs
6180// Returns the number of bytes read.
6181//
6182int ReadMemoryForSymbols(ULONG64 address, char *buffer, int cb)
6183{
6184 ULONG read;
6185 if (SafeReadMemory(TO_TADDR(address), (PVOID)buffer, cb, &read))
6186 {
6187 return read;
6188 }
6189 return 0;
6190}
6191
6192HRESULT SymbolReader::LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout,
6193 ___in ULONG64 peAddress, ___in ULONG64 peSize, ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize)
6194{
6195 HRESULT Status = S_OK;
6196
6197 if (loadSymbolsForModuleDelegate == nullptr)
6198 {
6199 IfFailRet(PrepareSymbolReader());
6200 }
6201
6202 // The module name needs to be null for in-memory PE's.
6203 ArrayHolder<char> szModuleName = nullptr;
6204 if (!isInMemory && pModuleName != nullptr)
6205 {
6206 szModuleName = new char[MAX_LONGPATH];
6207 if (WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szModuleName, MAX_LONGPATH, NULL, NULL) == 0)
6208 {
6209 return E_FAIL;
6210 }
6211 }
6212
6213 m_symbolReaderHandle = loadSymbolsForModuleDelegate(szModuleName, isFileLayout, peAddress,
6214 (int)peSize, inMemoryPdbAddress, (int)inMemoryPdbSize, ReadMemoryForSymbols);
6215
6216 if (m_symbolReaderHandle == 0)
6217 {
6218 return E_FAIL;
6219 }
6220
6221 return Status;
6222}
6223
6224#ifndef FEATURE_PAL
6225
6226void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
6227{
6228 const char * const tpaExtensions[] = {
6229 "*.ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
6230 "*.dll",
6231 "*.ni.exe",
6232 "*.exe",
6233 };
6234
6235 std::set<std::string> addedAssemblies;
6236
6237 // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
6238 // then files with .dll extension, etc.
6239 for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
6240 {
6241 const char* ext = tpaExtensions[extIndex];
6242 size_t extLength = strlen(ext);
6243
6244 std::string assemblyPath(directory);
6245 assemblyPath.append(DIRECTORY_SEPARATOR_STR_A);
6246 assemblyPath.append(tpaExtensions[extIndex]);
6247
6248 WIN32_FIND_DATAA data;
6249 HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data);
6250
6251 if (findHandle != INVALID_HANDLE_VALUE)
6252 {
6253 do
6254 {
6255 if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
6256 {
6257
6258 std::string filename(data.cFileName);
6259 size_t extPos = filename.length() - extLength;
6260 std::string filenameWithoutExt(filename.substr(0, extPos));
6261
6262 // Make sure if we have an assembly with multiple extensions present,
6263 // we insert only one version of it.
6264 if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
6265 {
6266 addedAssemblies.insert(filenameWithoutExt);
6267
6268 tpaList.append(directory);
6269 tpaList.append(DIRECTORY_SEPARATOR_STR_A);
6270 tpaList.append(filename);
6271 tpaList.append(";");
6272 }
6273 }
6274 }
6275 while (0 != FindNextFileA(findHandle, &data));
6276
6277 FindClose(findHandle);
6278 }
6279 }
6280}
6281
6282bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable)
6283{
6284 ArrayHolder<char> hostPath = new char[MAX_LONGPATH+1];
6285 if (::GetModuleFileName(NULL, hostPath, MAX_LONGPATH) == 0)
6286 {
6287 return false;
6288 }
6289
6290 entrypointExecutable.clear();
6291 entrypointExecutable.append(hostPath);
6292
6293 return true;
6294}
6295
6296#endif // FEATURE_PAL
6297
6298HRESULT SymbolReader::PrepareSymbolReader()
6299{
6300 static bool attemptedSymbolReaderPreparation = false;
6301 if (attemptedSymbolReaderPreparation)
6302 {
6303 // If we already tried to set up the symbol reader, we won't try again.
6304 return E_FAIL;
6305 }
6306
6307 attemptedSymbolReaderPreparation = true;
6308
6309 std::string absolutePath;
6310 std::string coreClrPath;
6311 HRESULT Status;
6312
6313#ifdef FEATURE_PAL
6314 coreClrPath = g_ExtServices->GetCoreClrDirectory();
6315 if (!GetAbsolutePath(coreClrPath.c_str(), absolutePath))
6316 {
6317 ExtErr("Error: Failed to get coreclr absolute path\n");
6318 return E_FAIL;
6319 }
6320 coreClrPath.append(DIRECTORY_SEPARATOR_STR_A);
6321 coreClrPath.append(MAIN_CLR_DLL_NAME_A);
6322#else
6323 ULONG index;
6324 Status = g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A, 0, &index, NULL);
6325 if (FAILED(Status))
6326 {
6327 ExtErr("Error: Can't find coreclr module\n");
6328 return Status;
6329 }
6330 ArrayHolder<char> szModuleName = new char[MAX_LONGPATH + 1];
6331 Status = g_ExtSymbols->GetModuleNames(index, 0, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL, NULL, 0, NULL);
6332 if (FAILED(Status))
6333 {
6334 ExtErr("Error: Failed to get coreclr module name\n");
6335 return Status;
6336 }
6337 coreClrPath = szModuleName;
6338
6339 // Parse off the module name to get just the path
6340 size_t pos = coreClrPath.rfind(DIRECTORY_SEPARATOR_CHAR_A);
6341 if (pos == std::string::npos)
6342 {
6343 ExtErr("Error: Failed to parse coreclr module name\n");
6344 return E_FAIL;
6345 }
6346 absolutePath.assign(coreClrPath, 0, pos);
6347#endif // FEATURE_PAL
6348
6349 HMODULE coreclrLib = LoadLibraryA(coreClrPath.c_str());
6350 if (coreclrLib == nullptr)
6351 {
6352 ExtErr("Error: Failed to load %s\n", coreClrPath.c_str());
6353 return E_FAIL;
6354 }
6355
6356 void *hostHandle;
6357 unsigned int domainId;
6358 coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)GetProcAddress(coreclrLib, "coreclr_initialize");
6359 if (initializeCoreCLR == nullptr)
6360 {
6361 ExtErr("Error: coreclr_initialize not found\n");
6362 return E_FAIL;
6363 }
6364
6365 std::string tpaList;
6366 AddFilesFromDirectoryToTpaList(absolutePath.c_str(), tpaList);
6367
6368 const char *propertyKeys[] = {
6369 "TRUSTED_PLATFORM_ASSEMBLIES", "APP_PATHS", "APP_NI_PATHS",
6370 "NATIVE_DLL_SEARCH_DIRECTORIES", "AppDomainCompatSwitch"};
6371
6372 const char *propertyValues[] = {// TRUSTED_PLATFORM_ASSEMBLIES
6373 tpaList.c_str(),
6374 // APP_PATHS
6375 absolutePath.c_str(),
6376 // APP_NI_PATHS
6377 absolutePath.c_str(),
6378 // NATIVE_DLL_SEARCH_DIRECTORIES
6379 absolutePath.c_str(),
6380 // AppDomainCompatSwitch
6381 "UseLatestBehaviorWhenTFMNotSpecified"};
6382
6383 std::string entryPointExecutablePath;
6384 if (!GetEntrypointExecutableAbsolutePath(entryPointExecutablePath))
6385 {
6386 ExtErr("Could not get full path to current executable");
6387 return E_FAIL;
6388 }
6389
6390 Status = initializeCoreCLR(entryPointExecutablePath.c_str(), "sos",
6391 sizeof(propertyKeys) / sizeof(propertyKeys[0]), propertyKeys, propertyValues, &hostHandle, &domainId);
6392
6393 if (FAILED(Status))
6394 {
6395 ExtErr("Error: Fail to initialize CoreCLR %08x\n", Status);
6396 return Status;
6397 }
6398
6399 coreclr_create_delegate_ptr createDelegate = (coreclr_create_delegate_ptr)GetProcAddress(coreclrLib, "coreclr_create_delegate");
6400 if (createDelegate == nullptr)
6401 {
6402 ExtErr("Error: coreclr_create_delegate not found\n");
6403 return E_FAIL;
6404 }
6405
6406 IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "LoadSymbolsForModule", (void **)&loadSymbolsForModuleDelegate));
6407 IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "Dispose", (void **)&disposeDelegate));
6408 IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "ResolveSequencePoint", (void **)&resolveSequencePointDelegate));
6409 IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "GetLocalVariableName", (void **)&getLocalVariableNameDelegate));
6410 IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "GetLineByILOffset", (void **)&getLineByILOffsetDelegate));
6411
6412 return Status;
6413}
6414
6415HRESULT SymbolReader::GetLineByILOffset(___in mdMethodDef methodToken, ___in ULONG64 ilOffset,
6416 ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, ___in ULONG cchFileName)
6417{
6418 HRESULT Status = S_OK;
6419
6420 if (m_symbolReaderHandle != 0)
6421 {
6422 _ASSERTE(getLineByILOffsetDelegate != nullptr);
6423
6424 BSTR bstrFileName = SysAllocStringLen(0, MAX_LONGPATH);
6425 if (bstrFileName == nullptr)
6426 {
6427 return E_OUTOFMEMORY;
6428 }
6429 // Source lines with 0xFEEFEE markers are filtered out on the managed side.
6430 if ((getLineByILOffsetDelegate(m_symbolReaderHandle, methodToken, ilOffset, pLinenum, &bstrFileName) == FALSE) || (*pLinenum == 0))
6431 {
6432 SysFreeString(bstrFileName);
6433 return E_FAIL;
6434 }
6435 wcscpy_s(pwszFileName, cchFileName, bstrFileName);
6436 SysFreeString(bstrFileName);
6437 return S_OK;
6438 }
6439
6440#ifndef FEATURE_PAL
6441 if (m_pSymReader == NULL)
6442 return E_FAIL;
6443
6444 ToRelease<ISymUnmanagedMethod> pSymMethod(NULL);
6445 IfFailRet(m_pSymReader->GetMethod(methodToken, &pSymMethod));
6446
6447 ULONG32 seqPointCount = 0;
6448 IfFailRet(pSymMethod->GetSequencePointCount(&seqPointCount));
6449
6450 if (seqPointCount == 0)
6451 return E_FAIL;
6452
6453 // allocate memory for the objects to be fetched
6454 ArrayHolder<ULONG32> offsets(new ULONG32[seqPointCount]);
6455 ArrayHolder<ULONG32> lines(new ULONG32[seqPointCount]);
6456 ArrayHolder<ULONG32> columns(new ULONG32[seqPointCount]);
6457 ArrayHolder<ULONG32> endlines(new ULONG32[seqPointCount]);
6458 ArrayHolder<ULONG32> endcolumns(new ULONG32[seqPointCount]);
6459 ArrayHolder<ToRelease<ISymUnmanagedDocument>> documents(new ToRelease<ISymUnmanagedDocument>[seqPointCount]);
6460
6461 ULONG32 realSeqPointCount = 0;
6462 IfFailRet(pSymMethod->GetSequencePoints(seqPointCount, &realSeqPointCount, offsets, &(documents[0]), lines, columns, endlines, endcolumns));
6463
6464 const ULONG32 HiddenLine = 0x00feefee;
6465 int bestSoFar = -1;
6466
6467 for (int i = 0; i < (int)realSeqPointCount; i++)
6468 {
6469 if (offsets[i] > ilOffset)
6470 break;
6471
6472 if (lines[i] != HiddenLine)
6473 bestSoFar = i;
6474 }
6475
6476 if (bestSoFar != -1)
6477 {
6478 ULONG32 cchNeeded = 0;
6479 IfFailRet(documents[bestSoFar]->GetURL(cchFileName, &cchNeeded, pwszFileName));
6480
6481 *pLinenum = lines[bestSoFar];
6482 return S_OK;
6483 }
6484#endif // FEATURE_PAL
6485
6486 return E_FAIL;
6487}
6488
6489HRESULT SymbolReader::GetNamedLocalVariable(___in ISymUnmanagedScope * pScope, ___in ICorDebugILFrame * pILFrame, ___in mdMethodDef methodToken,
6490 ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ICorDebugValue** ppValue)
6491{
6492 HRESULT Status = S_OK;
6493
6494 if (m_symbolReaderHandle != 0)
6495 {
6496 _ASSERTE(getLocalVariableNameDelegate != nullptr);
6497
6498 BSTR wszParamName = SysAllocStringLen(0, mdNameLen);
6499 if (wszParamName == NULL)
6500 {
6501 return E_OUTOFMEMORY;
6502 }
6503
6504 if (getLocalVariableNameDelegate(m_symbolReaderHandle, methodToken, localIndex, &wszParamName) == FALSE)
6505 {
6506 SysFreeString(wszParamName);
6507 return E_FAIL;
6508 }
6509
6510 wcscpy_s(paramName, paramNameLen, wszParamName);
6511 SysFreeString(wszParamName);
6512
6513 if (FAILED(pILFrame->GetLocalVariable(localIndex, ppValue)) || (*ppValue == NULL))
6514 {
6515 *ppValue = NULL;
6516 return E_FAIL;
6517 }
6518 return S_OK;
6519 }
6520
6521#ifndef FEATURE_PAL
6522 if (m_pSymReader == NULL)
6523 return E_FAIL;
6524
6525 if (pScope == NULL)
6526 {
6527 ToRelease<ISymUnmanagedMethod> pSymMethod;
6528 IfFailRet(m_pSymReader->GetMethod(methodToken, &pSymMethod));
6529
6530 ToRelease<ISymUnmanagedScope> pScope;
6531 IfFailRet(pSymMethod->GetRootScope(&pScope));
6532
6533 return GetNamedLocalVariable(pScope, pILFrame, methodToken, localIndex, paramName, paramNameLen, ppValue);
6534 }
6535 else
6536 {
6537 ULONG32 numVars = 0;
6538 IfFailRet(pScope->GetLocals(0, &numVars, NULL));
6539
6540 ArrayHolder<ISymUnmanagedVariable*> pLocals = new ISymUnmanagedVariable*[numVars];
6541 IfFailRet(pScope->GetLocals(numVars, &numVars, pLocals));
6542
6543 for (ULONG i = 0; i < numVars; i++)
6544 {
6545 ULONG32 varIndexInMethod = 0;
6546 if (SUCCEEDED(pLocals[i]->GetAddressField1(&varIndexInMethod)))
6547 {
6548 if (varIndexInMethod != localIndex)
6549 continue;
6550
6551 ULONG32 nameLen = 0;
6552 if (FAILED(pLocals[i]->GetName(paramNameLen, &nameLen, paramName)))
6553 swprintf_s(paramName, paramNameLen, W("local_%d\0"), localIndex);
6554
6555 if (SUCCEEDED(pILFrame->GetLocalVariable(varIndexInMethod, ppValue)) && (*ppValue != NULL))
6556 {
6557 for(ULONG j = 0; j < numVars; j++) pLocals[j]->Release();
6558 return S_OK;
6559 }
6560 else
6561 {
6562 *ppValue = NULL;
6563 for(ULONG j = 0; j < numVars; j++) pLocals[j]->Release();
6564 return E_FAIL;
6565 }
6566 }
6567 }
6568
6569 ULONG32 numChildren = 0;
6570 IfFailRet(pScope->GetChildren(0, &numChildren, NULL));
6571
6572 ArrayHolder<ISymUnmanagedScope*> pChildren = new ISymUnmanagedScope*[numChildren];
6573 IfFailRet(pScope->GetChildren(numChildren, &numChildren, pChildren));
6574
6575 for (ULONG i = 0; i < numChildren; i++)
6576 {
6577 if (SUCCEEDED(GetNamedLocalVariable(pChildren[i], pILFrame, methodToken, localIndex, paramName, paramNameLen, ppValue)))
6578 {
6579 for (ULONG j = 0; j < numChildren; j++) pChildren[j]->Release();
6580 return S_OK;
6581 }
6582 }
6583
6584 for (ULONG j = 0; j < numChildren; j++) pChildren[j]->Release();
6585 }
6586#endif // FEATURE_PAL
6587
6588 return E_FAIL;
6589}
6590
6591HRESULT SymbolReader::GetNamedLocalVariable(___in ICorDebugFrame * pFrame, ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName,
6592 ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue)
6593{
6594 HRESULT Status = S_OK;
6595
6596 *ppValue = NULL;
6597 paramName[0] = L'\0';
6598
6599 ToRelease<ICorDebugILFrame> pILFrame;
6600 IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame));
6601
6602 ToRelease<ICorDebugFunction> pFunction;
6603 IfFailRet(pFrame->GetFunction(&pFunction));
6604
6605 mdMethodDef methodDef;
6606 ToRelease<ICorDebugClass> pClass;
6607 ToRelease<ICorDebugModule> pModule;
6608 IfFailRet(pFunction->GetClass(&pClass));
6609 IfFailRet(pFunction->GetModule(&pModule));
6610 IfFailRet(pFunction->GetToken(&methodDef));
6611
6612 return GetNamedLocalVariable(NULL, pILFrame, methodDef, localIndex, paramName, paramNameLen, ppValue);
6613}
6614
6615HRESULT SymbolReader::ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___in TADDR mod, ___out mdMethodDef* pToken, ___out ULONG32* pIlOffset)
6616{
6617 HRESULT Status = S_OK;
6618
6619 if (m_symbolReaderHandle != 0)
6620 {
6621 _ASSERTE(resolveSequencePointDelegate != nullptr);
6622
6623 char szName[mdNameLen];
6624 if (WideCharToMultiByte(CP_ACP, 0, pFilename, (int)(_wcslen(pFilename) + 1), szName, mdNameLen, NULL, NULL) == 0)
6625 {
6626 return E_FAIL;
6627 }
6628 if (resolveSequencePointDelegate(m_symbolReaderHandle, szName, lineNumber, pToken, pIlOffset) == FALSE)
6629 {
6630 return E_FAIL;
6631 }
6632 return S_OK;
6633 }
6634
6635#ifndef FEATURE_PAL
6636 if (m_pSymReader == NULL)
6637 return E_FAIL;
6638
6639 ULONG32 cDocs = 0;
6640 ULONG32 cDocsNeeded = 0;
6641 ArrayHolder<ToRelease<ISymUnmanagedDocument>> pDocs = NULL;
6642
6643 IfFailRet(m_pSymReader->GetDocuments(cDocs, &cDocsNeeded, NULL));
6644 pDocs = new ToRelease<ISymUnmanagedDocument>[cDocsNeeded];
6645 cDocs = cDocsNeeded;
6646 IfFailRet(m_pSymReader->GetDocuments(cDocs, &cDocsNeeded, &(pDocs[0])));
6647
6648 ULONG32 filenameLen = (ULONG32) _wcslen(pFilename);
6649
6650 for (ULONG32 i = 0; i < cDocs; i++)
6651 {
6652 ULONG32 cchUrl = 0;
6653 ULONG32 cchUrlNeeded = 0;
6654 ArrayHolder<WCHAR> pUrl = NULL;
6655 IfFailRet(pDocs[i]->GetURL(cchUrl, &cchUrlNeeded, pUrl));
6656 pUrl = new WCHAR[cchUrlNeeded];
6657 cchUrl = cchUrlNeeded;
6658 IfFailRet(pDocs[i]->GetURL(cchUrl, &cchUrlNeeded, pUrl));
6659
6660 // If the URL is exactly as long as the filename then compare the two names directly
6661 if (cchUrl-1 == filenameLen)
6662 {
6663 if (0!=_wcsicmp(pUrl, pFilename))
6664 continue;
6665 }
6666 // does the URL suffix match [back]slash + filename?
6667 else if (cchUrl-1 > filenameLen)
6668 {
6669 WCHAR* slashLocation = pUrl + (cchUrl - filenameLen - 2);
6670 if (*slashLocation != L'\\' && *slashLocation != L'/')
6671 continue;
6672 if (0 != _wcsicmp(slashLocation+1, pFilename))
6673 continue;
6674 }
6675 // URL is too short to match
6676 else
6677 continue;
6678
6679 ULONG32 closestLine = 0;
6680 if (FAILED(pDocs[i]->FindClosestLine(lineNumber, &closestLine)))
6681 continue;
6682
6683 ToRelease<ISymUnmanagedMethod> pSymUnmanagedMethod;
6684 IfFailRet(m_pSymReader->GetMethodFromDocumentPosition(pDocs[i], closestLine, 0, &pSymUnmanagedMethod));
6685 IfFailRet(pSymUnmanagedMethod->GetToken(pToken));
6686 IfFailRet(pSymUnmanagedMethod->GetOffset(pDocs[i], closestLine, 0, pIlOffset));
6687
6688 // If this IL
6689 if (*pIlOffset == -1)
6690 {
6691 return E_FAIL;
6692 }
6693 return S_OK;
6694 }
6695#endif // FEATURE_PAL
6696
6697 return E_FAIL;
6698}
6699
6700static void AddAssemblyName(WString& methodOutput, CLRDATA_ADDRESS mdesc)
6701{
6702 DacpMethodDescData mdescData;
6703 if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
6704 {
6705 DacpModuleData dmd;
6706 if (SUCCEEDED(dmd.Request(g_sos, mdescData.ModulePtr)))
6707 {
6708 ToRelease<IXCLRDataModule> pModule;
6709 if (SUCCEEDED(g_sos->GetModule(mdescData.ModulePtr, &pModule)))
6710 {
6711 ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH + 1];
6712 ULONG32 nameLen = 0;
6713 if (SUCCEEDED(pModule->GetFileName(MAX_LONGPATH, &nameLen, wszFileName)))
6714 {
6715 if (wszFileName[0] != W('\0'))
6716 {
6717 WCHAR *pJustName = _wcsrchr(wszFileName, DIRECTORY_SEPARATOR_CHAR_W);
6718 if (pJustName == NULL)
6719 pJustName = wszFileName - 1;
6720 methodOutput += (pJustName + 1);
6721 methodOutput += W("!");
6722 }
6723 }
6724 }
6725 }
6726 }
6727}
6728
6729WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackWalk, BOOL bAssemblyName)
6730{
6731 TADDR vtAddr;
6732 MOVE(vtAddr, frameAddr);
6733
6734 WString frameOutput;
6735 frameOutput += W("[");
6736
6737 if (SUCCEEDED(g_sos->GetFrameName(TO_CDADDR(vtAddr), mdNameLen, g_mdName, NULL)))
6738 frameOutput += g_mdName;
6739 else
6740 frameOutput += W("Frame");
6741
6742 frameOutput += WString(W(": ")) + Pointer(frameAddr) + W("] ");
6743
6744 // Print the frame's associated function info, if it has any.
6745 CLRDATA_ADDRESS mdesc = 0;
6746 if (SUCCEEDED(g_sos->GetMethodDescPtrFromFrame(frameAddr, &mdesc)))
6747 {
6748 if (SUCCEEDED(g_sos->GetMethodDescName(mdesc, mdNameLen, g_mdName, NULL)))
6749 {
6750 if (bAssemblyName)
6751 {
6752 AddAssemblyName(frameOutput, mdesc);
6753 }
6754
6755 frameOutput += g_mdName;
6756 }
6757 else
6758 {
6759 frameOutput += W("<unknown method>");
6760 }
6761 }
6762 else if (pStackWalk)
6763 {
6764 // The Frame did not have direct function info, so try to get the method instance
6765 // (in this case a MethodDesc), and read the name from it.
6766 ToRelease<IXCLRDataFrame> frame;
6767 if (SUCCEEDED(pStackWalk->GetFrame(&frame)))
6768 {
6769 ToRelease<IXCLRDataMethodInstance> methodInstance;
6770 if (SUCCEEDED(frame->GetMethodInstance(&methodInstance)))
6771 {
6772 // GetName can return S_FALSE if mdNameLen is not large enough. However we are already
6773 // passing a pretty big buffer in. If this returns S_FALSE (meaning the buffer is too
6774 // small) then we should not output it anyway.
6775 if (methodInstance->GetName(0, mdNameLen, NULL, g_mdName) == S_OK)
6776 frameOutput += g_mdName;
6777 }
6778 }
6779 }
6780
6781 return frameOutput;
6782}
6783
6784WString MethodNameFromIP(CLRDATA_ADDRESS ip, BOOL bSuppressLines, BOOL bAssemblyName, BOOL bDisplacement)
6785{
6786 ULONG linenum;
6787 WString methodOutput;
6788 CLRDATA_ADDRESS mdesc = 0;
6789
6790 if (FAILED(g_sos->GetMethodDescPtrFromIP(ip, &mdesc)))
6791 {
6792 methodOutput = W("<unknown>");
6793 }
6794 else
6795 {
6796 DacpMethodDescData mdescData;
6797 if (SUCCEEDED(g_sos->GetMethodDescName(mdesc, mdNameLen, g_mdName, NULL)))
6798 {
6799 if (bAssemblyName)
6800 {
6801 AddAssemblyName(methodOutput, mdesc);
6802 }
6803
6804 methodOutput += g_mdName;
6805
6806 if (bDisplacement)
6807 {
6808 if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
6809 {
6810 ULONG64 disp = (ip - mdescData.NativeCodeAddr);
6811 if (disp)
6812 {
6813 methodOutput += W(" + ");
6814 methodOutput += Decimal(disp);
6815 }
6816 }
6817 }
6818 }
6819 else if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
6820 {
6821 DacpModuleData dmd;
6822 BOOL bModuleNameWorked = FALSE;
6823 ULONG64 addrInModule = ip;
6824 if (SUCCEEDED(dmd.Request(g_sos, mdescData.ModulePtr)))
6825 {
6826 CLRDATA_ADDRESS peFileBase = 0;
6827 if (SUCCEEDED(g_sos->GetPEFileBase(dmd.File, &peFileBase)))
6828 {
6829 if (peFileBase)
6830 {
6831 addrInModule = peFileBase;
6832 }
6833 }
6834 }
6835 ULONG Index;
6836 ULONG64 moduleBase;
6837 if (SUCCEEDED(g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index, &moduleBase)))
6838 {
6839 ArrayHolder<char> szModuleName = new char[MAX_LONGPATH+1];
6840
6841 if (SUCCEEDED(g_ExtSymbols->GetModuleNames(Index, moduleBase, NULL, 0, NULL, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL)))
6842 {
6843 MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, g_mdName, _countof(g_mdName));
6844 methodOutput += g_mdName;
6845 methodOutput += W("!");
6846 }
6847 }
6848 methodOutput += W("<unknown method>");
6849 }
6850 else
6851 {
6852 methodOutput = W("<unknown>");
6853 }
6854
6855 ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH];
6856 if (!bSuppressLines &&
6857 SUCCEEDED(GetLineByOffset(TO_CDADDR(ip), &linenum, wszFileName, MAX_LONGPATH)))
6858 {
6859 methodOutput += WString(W(" [")) + wszFileName + W(" @ ") + Decimal(linenum) + W("]");
6860 }
6861 }
6862
6863 return methodOutput;
6864}
6865
6866HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount)
6867{
6868 if (ppRefs == NULL || pRefCnt == NULL)
6869 return E_POINTER;
6870
6871 if (pErrCount)
6872 *pErrCount = 0;
6873
6874 *pRefCnt = 0;
6875 unsigned int count = 0;
6876 ToRelease<ISOSStackRefEnum> pEnum;
6877 if (FAILED(g_sos->GetStackReferences(osID, &pEnum)) || FAILED(pEnum->GetCount(&count)))
6878 {
6879 ExtOut("Failed to enumerate GC references.\n");
6880 return E_FAIL;
6881 }
6882
6883 *ppRefs = new SOSStackRefData[count];
6884 if (FAILED(pEnum->Next(count, *ppRefs, pRefCnt)))
6885 {
6886 ExtOut("Failed to enumerate GC references.\n");
6887 return E_FAIL;
6888 }
6889
6890 SOS_Assert(count == *pRefCnt);
6891
6892 // Enumerate errors found. Any bad HRESULT recieved while enumerating errors is NOT a fatal error.
6893 // Hence we return S_FALSE if we encounter one.
6894
6895 if (ppErrors && pErrCount)
6896 {
6897 ToRelease<ISOSStackRefErrorEnum> pErrors;
6898 if (FAILED(pEnum->EnumerateErrors(&pErrors)))
6899 {
6900 ExtOut("Failed to enumerate GC reference errors.\n");
6901 return S_FALSE;
6902 }
6903
6904 if (FAILED(pErrors->GetCount(&count)))
6905 {
6906 ExtOut("Failed to enumerate GC reference errors.\n");
6907 return S_FALSE;
6908 }
6909
6910 *ppErrors = new SOSStackRefError[count];
6911 if (FAILED(pErrors->Next(count, *ppErrors, pErrCount)))
6912 {
6913 ExtOut("Failed to enumerate GC reference errors.\n");
6914 *pErrCount = 0;
6915 return S_FALSE;
6916 }
6917
6918 SOS_Assert(count == *pErrCount);
6919 }
6920 return S_OK;
6921}
6922
6923
6924InternalFrameManager::InternalFrameManager() : m_cInternalFramesActual(0), m_iInternalFrameCur(0) {}
6925
6926HRESULT InternalFrameManager::Init(ICorDebugThread3 * pThread3)
6927{
6928 _ASSERTE(pThread3 != NULL);
6929
6930 return pThread3->GetActiveInternalFrames(
6931 _countof(m_rgpInternalFrame2),
6932 &m_cInternalFramesActual,
6933 &(m_rgpInternalFrame2[0]));
6934}
6935
6936HRESULT InternalFrameManager::PrintPrecedingInternalFrames(ICorDebugFrame * pFrame)
6937{
6938 HRESULT Status;
6939
6940 for (; m_iInternalFrameCur < m_cInternalFramesActual; m_iInternalFrameCur++)
6941 {
6942 BOOL bIsCloser = FALSE;
6943 IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->IsCloserToLeaf(pFrame, &bIsCloser));
6944
6945 if (!bIsCloser)
6946 {
6947 // Current internal frame is now past pFrame, so we're done
6948 return S_OK;
6949 }
6950
6951 IfFailRet(PrintCurrentInternalFrame());
6952 }
6953
6954 // Exhausted list of internal frames. Done!
6955 return S_OK;
6956}
6957
6958HRESULT InternalFrameManager::PrintCurrentInternalFrame()
6959{
6960 _ASSERTE(m_iInternalFrameCur < m_cInternalFramesActual);
6961
6962 HRESULT Status;
6963
6964 CORDB_ADDRESS address;
6965 IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->GetAddress(&address));
6966
6967 ToRelease<ICorDebugInternalFrame> pInternalFrame;
6968 IfFailRet(m_rgpInternalFrame2[m_iInternalFrameCur]->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame));
6969
6970 CorDebugInternalFrameType type;
6971 IfFailRet(pInternalFrame->GetFrameType(&type));
6972
6973 LPCSTR szFrameType = NULL;
6974 switch(type)
6975 {
6976 default:
6977 szFrameType = "Unknown internal frame.";
6978 break;
6979
6980 case STUBFRAME_M2U:
6981 szFrameType = "Managed to Unmanaged transition";
6982 break;
6983
6984 case STUBFRAME_U2M:
6985 szFrameType = "Unmanaged to Managed transition";
6986 break;
6987
6988 case STUBFRAME_APPDOMAIN_TRANSITION:
6989 szFrameType = "AppDomain transition";
6990 break;
6991
6992 case STUBFRAME_LIGHTWEIGHT_FUNCTION:
6993 szFrameType = "Lightweight function";
6994 break;
6995
6996 case STUBFRAME_FUNC_EVAL:
6997 szFrameType = "Function evaluation";
6998 break;
6999
7000 case STUBFRAME_INTERNALCALL:
7001 szFrameType = "Internal call";
7002 break;
7003
7004 case STUBFRAME_CLASS_INIT:
7005 szFrameType = "Class initialization";
7006 break;
7007
7008 case STUBFRAME_EXCEPTION:
7009 szFrameType = "Exception";
7010 break;
7011
7012 case STUBFRAME_SECURITY:
7013 szFrameType = "Security";
7014 break;
7015
7016 case STUBFRAME_JIT_COMPILATION:
7017 szFrameType = "JIT Compilation";
7018 break;
7019 }
7020
7021 DMLOut("%p %s ", SOS_PTR(address), SOS_PTR(0));
7022 ExtOut("[%s: %p]\n", szFrameType, SOS_PTR(address));
7023
7024 return S_OK;
7025}
7026