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) |
43 | char symBuffer[SYM_BUFFER_SIZE]; |
44 | PIMAGEHLP_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 | |
54 | LoadSymbolsForModuleDelegate SymbolReader::loadSymbolsForModuleDelegate; |
55 | DisposeDelegate SymbolReader::disposeDelegate; |
56 | ResolveSequencePointDelegate SymbolReader::resolveSequencePointDelegate; |
57 | GetLocalVariableName SymbolReader::getLocalVariableNameDelegate; |
58 | GetLineByILOffsetDelegate SymbolReader::getLineByILOffsetDelegate; |
59 | |
60 | const 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 | |
67 | const 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 | |
74 | IXCLRDataProcess *g_clrData = NULL; |
75 | ISOSDacInterface *g_sos = NULL; |
76 | ICorDebugProcess *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 |
91 | const UINT kcMaxRevertedRejitData = 10; |
92 | const UINT kcMaxTieredVersions = 10; |
93 | #ifndef FEATURE_PAL |
94 | |
95 | // ensure we always allocate on the process heap |
96 | void* __cdecl operator new(size_t size) throw() |
97 | { return HeapAlloc(GetProcessHeap(), 0, size); } |
98 | void __cdecl operator delete(void* pObj) throw() |
99 | { HeapFree(GetProcessHeap(), 0, pObj); } |
100 | |
101 | void* __cdecl operator new[](size_t size) throw() |
102 | { return HeapAlloc(GetProcessHeap(), 0, size); } |
103 | void __cdecl operator delete[](void* pObj) throw() |
104 | { HeapFree(GetProcessHeap(), 0, pObj); } |
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 | |
112 | typedef unsigned __int64 QWORD; |
113 | |
114 | namespace 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 | \**********************************************************************/ |
191 | HRESULT 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 | \**********************************************************************/ |
327 | HRESULT 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 | |
392 | Error: |
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 | \**********************************************************************/ |
422 | HRESULT 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 | |
456 | Error: |
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 | \**********************************************************************/ |
472 | void 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 | \**********************************************************************/ |
497 | HRESULT 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 | \**********************************************************************/ |
582 | QWORD 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 | \**********************************************************************/ |
611 | BOOL 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 | \**********************************************************************/ |
650 | HRESULT 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 | \**********************************************************************/ |
671 | DWORD_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 | |
727 | ModuleInfo moduleInfo[MSCOREND] = {{0,FALSE,0},{0,FALSE,0},{0,FALSE,0}}; |
728 | |
729 | void ReportOOM() |
730 | { |
731 | ExtOut("SOS Error: Out of memory\n" ); |
732 | } |
733 | |
734 | HRESULT 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 | |
805 | EEFLAVOR 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 | |
819 | BOOL 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 | |
834 | BOOL g_InMinidumpSafeMode = FALSE; |
835 | |
836 | BOOL 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. |
859 | BOOL 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 | |
877 | ULONG 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 |
890 | BOOL 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 | |
903 | BOOL 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 | \**********************************************************************/ |
921 | BOOL 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 | \**********************************************************************/ |
958 | BOOL 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 | |
975 | ULONG OSPageSize () |
976 | { |
977 | static ULONG pageSize = 0; |
978 | if (pageSize == 0) |
979 | g_ExtControl->GetPageSize(&pageSize); |
980 | |
981 | return pageSize; |
982 | } |
983 | |
984 | size_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 | \**********************************************************************/ |
998 | void IP2MethodDesc (DWORD_PTR IP, DWORD_PTR &methodDesc, JITTypes &jitType, |
999 | DWORD_PTR &gcinfoAddr) |
1000 | { |
1001 | |
1002 | CLRDATA_ADDRESS EIP = TO_CDADDR(IP); |
1003 | DacpCodeHeaderData ; |
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 | |
1018 | BOOL IsValueField (DacpFieldDescData *pFD) |
1019 | { |
1020 | return (pFD->Type == ELEMENT_TYPE_VALUETYPE); |
1021 | } |
1022 | |
1023 | void 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 | |
1157 | void 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 | |
1185 | void 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 | |
1206 | void 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 | |
1243 | void 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 | |
1307 | void 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 | |
1362 | const 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 | |
1402 | const char * ElementTypeNamespace(unsigned type) |
1403 | { |
1404 | if ((type >= _countof(CorElementTypeName)) || (CorElementTypeNamespace[type] == NULL)) |
1405 | { |
1406 | return "" ; |
1407 | } |
1408 | return CorElementTypeNamespace[type]; |
1409 | } |
1410 | |
1411 | void 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 |
1430 | LPWSTR 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 | \**********************************************************************/ |
1452 | void 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 |
1627 | int 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 |
1639 | int 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 |
1710 | int 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 |
1768 | CLRDATA_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 | |
1856 | CLRDATA_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 | |
1887 | CLRDATA_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 | |
1998 | HRESULT 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 |
2019 | HRESULT 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 | |
2054 | void 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 | |
2095 | const 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 | \**********************************************************************/ |
2135 | void 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 | \**********************************************************************/ |
2206 | BOOL 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 | \**********************************************************************/ |
2236 | BOOL 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 | |
2242 | WCHAR *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 | \**********************************************************************/ |
2293 | BOOL 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 | \**********************************************************************/ |
2325 | BOOL 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 | \**********************************************************************/ |
2338 | BOOL 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 | \**********************************************************************/ |
2356 | BOOL 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 | |
2368 | DacpUsefulGlobalsData g_special_usefulGlobals; |
2369 | |
2370 | BOOL IsObjectArray (DacpObjectData *pData) |
2371 | { |
2372 | if (pData->ObjectType == OBJ_ARRAY) |
2373 | return g_special_usefulGlobals.ArrayMethodTable == pData->MethodTable; |
2374 | |
2375 | return FALSE; |
2376 | } |
2377 | |
2378 | BOOL 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 | |
2387 | BOOL 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 | |
2397 | BOOL 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 | |
2420 | BOOL 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 ; |
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 | |
2456 | void 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 | |
2494 | void 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 | |
2502 | void 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 | |
2516 | void DumpRegObjectHelper(const char *regName, BOOL verifyFields) |
2517 | { |
2518 | DWORD_PTR reg; |
2519 | #ifdef FEATURE_PAL |
2520 | if (FAILED(g_ExtRegisters->GetValueByName(regName, ®))) |
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 | |
2541 | void 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(®s, &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 | |
2559 | void 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 | |
2598 | BOOL 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 | |
2631 | BOOL 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 | |
2667 | DWORD_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. |
2830 | Failure: |
2831 | delete [] moduleList; |
2832 | return NULL; |
2833 | } |
2834 | |
2835 | /**********************************************************************\ |
2836 | * Routine Description: * |
2837 | * * |
2838 | * Find the EE data given a name. * |
2839 | * * |
2840 | \**********************************************************************/ |
2841 | void 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 | \**********************************************************************/ |
2953 | DWORD_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 | \**********************************************************************/ |
2982 | HRESULT 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 | \**********************************************************************/ |
3034 | HRESULT 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 | \**********************************************************************/ |
3102 | void 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 | |
3228 | BOOL IsMTForFreeObj(DWORD_PTR pMT) |
3229 | { |
3230 | return (pMT == g_special_usefulGlobals.FreeMethodTable); |
3231 | } |
3232 | |
3233 | const 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 | |
3247 | void 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 | |
3277 | void 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. |
3323 | BOOL 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 | |
3341 | void 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 | |
3371 | void 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 | |
3431 | void 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 | |
3451 | void 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 | |
3502 | HRESULT 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 | |
3548 | CLRDATA_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 | |
3576 | void 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 |
3605 | size_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 ; |
3620 | if (g_ExtData->ReadVirtual(TO_CDADDR(base), &DosHeader, sizeof(DosHeader), NULL) != S_OK) |
3621 | return 0; |
3622 | IMAGE_NT_HEADERS ; |
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 ; |
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 | // |
3656 | BOOL 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 | |
3666 | extern HMODULE g_hInstance; |
3667 | BOOL 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 | |
3699 | size_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 | |
3706 | size_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. |
3719 | void 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 | |
3727 | void 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 | |
3768 | void 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 | |
3931 | BOOL 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 | |
3982 | void 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 | |
3996 | BOOL 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 | |
4191 | ReadVirtualCache g_special_rvCacheSpace; |
4192 | ReadVirtualCache *rvCache = &g_special_rvCacheSpace; |
4193 | |
4194 | void 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 | // |
4213 | HRESULT 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 |
4278 | typedef 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) |
4296 | BOOL |
4297 | FindFileInPathCallback( |
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. |
4354 | class SOSLibraryProvider : public ICLRDebuggingLibraryProvider |
4355 | { |
4356 | public: |
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 | |
4532 | protected: |
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 | // |
4540 | class SOSDataTarget : public ICorDebugMutableDataTarget |
4541 | #ifdef FEATURE_PAL |
4542 | , public ICorDebugDataTarget4 |
4543 | #endif |
4544 | { |
4545 | public: |
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 | |
4737 | protected: |
4738 | LONG m_ref; |
4739 | }; |
4740 | |
4741 | HRESULT 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 | // |
4791 | VOID 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 | |
4813 | HRESULT 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 | |
4902 | typedef 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 | |
4916 | DacpGcHeapData *g_pHeapData = NULL; |
4917 | DacpGcHeapData g_HeapData; |
4918 | |
4919 | BOOL 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 | |
4932 | BOOL IsServerBuild() |
4933 | { |
4934 | return InitializeHeapData() ? g_pHeapData->bServerMode : FALSE; |
4935 | } |
4936 | |
4937 | UINT GetMaxGeneration() |
4938 | { |
4939 | return InitializeHeapData() ? g_pHeapData->g_max_generation : 0; |
4940 | } |
4941 | |
4942 | UINT GetGcHeapCount() |
4943 | { |
4944 | return InitializeHeapData() ? g_pHeapData->HeapCount : 0; |
4945 | } |
4946 | |
4947 | BOOL 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 | |
4960 | void 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 | |
5024 | HRESULT 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 | |
5085 | HRESULT 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 | |
5100 | StressLogMem::~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 | |
5112 | bool 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 | |
5160 | bool 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 | |
5175 | unsigned int Output::g_bSuppressOutput = 0; |
5176 | unsigned int Output::g_Indent = 0; |
5177 | bool Output::g_bDbgOutput = false; |
5178 | bool Output::g_bDMLExposed = false; |
5179 | unsigned int Output::g_DMLEnable = 0; |
5180 | |
5181 | template <class T, int count, int size> const int StaticData<T, count, size>::Count = count; |
5182 | template <class T, int count, int size> const int StaticData<T, count, size>::Size = size; |
5183 | |
5184 | StaticData<char, 4, 1024> CachedString::cache; |
5185 | |
5186 | CachedString::CachedString() |
5187 | : mPtr(0), mRefCount(0), mIndex(~0), mSize(cache.Size) |
5188 | { |
5189 | Create(); |
5190 | } |
5191 | |
5192 | CachedString::CachedString(const CachedString &rhs) |
5193 | : mPtr(0), mRefCount(0), mIndex(~0), mSize(cache.Size) |
5194 | { |
5195 | Copy(rhs); |
5196 | } |
5197 | |
5198 | CachedString::~CachedString() |
5199 | { |
5200 | Clear(); |
5201 | } |
5202 | |
5203 | const CachedString &CachedString::operator=(const CachedString &rhs) |
5204 | { |
5205 | Clear(); |
5206 | Copy(rhs); |
5207 | return *this; |
5208 | } |
5209 | |
5210 | void 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 | |
5238 | void 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 | |
5263 | void 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 | |
5288 | void CachedString::SetOOM() |
5289 | { |
5290 | Clear(); |
5291 | mIndex = -2; |
5292 | } |
5293 | |
5294 | void 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 | |
5310 | size_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 | |
5323 | void 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 | |
5342 | void 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 | |
5365 | void 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 | |
5380 | void 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 | |
5393 | void 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 | |
5405 | void 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 | |
5415 | void 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 | |
5430 | const 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 | |
5453 | void 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 | */ |
5461 | int 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 | |
5470 | CachedString 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 | |
5493 | CachedString 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 | |
5521 | CachedString 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 | |
5570 | CachedString 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 | |
5577 | EnableDMLHolder::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 | |
5600 | EnableDMLHolder::~EnableDMLHolder() |
5601 | { |
5602 | #ifndef FEATURE_PAL |
5603 | if (mEnable) |
5604 | Output::g_DMLEnable--; |
5605 | #endif |
5606 | } |
5607 | |
5608 | bool IsDMLEnabled() |
5609 | { |
5610 | return Output::g_DMLEnable > 0; |
5611 | } |
5612 | |
5613 | NoOutputHolder::NoOutputHolder(BOOL bSuppress) |
5614 | : mSuppress(bSuppress) |
5615 | { |
5616 | if (mSuppress) |
5617 | Output::g_bSuppressOutput++; |
5618 | } |
5619 | |
5620 | NoOutputHolder::~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. |
5633 | HRESULT |
5634 | GetClrMethodInstance( |
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. |
5656 | HRESULT |
5657 | GetLastMethodIlOffset( |
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". |
5724 | HRESULT |
5725 | ConvertNativeToIlOffset( |
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. |
5772 | HRESULT |
5773 | GetLineByOffset( |
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 | |
5796 | void 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 | |
5808 | void 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 | |
5824 | void 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 | |
5834 | void 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 | |
5850 | void 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 | |
5865 | void 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 | |
5875 | int TableOutput::GetColumnWidth(int col) |
5876 | { |
5877 | SOS_Assert(col < mColumns); |
5878 | |
5879 | if (mWidths) |
5880 | return mWidths[col]; |
5881 | |
5882 | return mDefaultWidth; |
5883 | } |
5884 | |
5885 | Alignment TableOutput::GetColAlign(int col) |
5886 | { |
5887 | SOS_Assert(col < mColumns); |
5888 | if (mAlignments) |
5889 | return mAlignments[col]; |
5890 | |
5891 | return mDefaultAlign; |
5892 | } |
5893 | |
5894 | const 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 | |
5911 | void 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 | |
5926 | void TableOutput::OutputIndent() |
5927 | { |
5928 | if (mIndent) |
5929 | ExtOut(GetWhitespace(mIndent)); |
5930 | } |
5931 | |
5932 | #ifndef FEATURE_PAL |
5933 | |
5934 | PEOffsetMemoryReader::PEOffsetMemoryReader(TADDR moduleBaseAddress) : |
5935 | m_moduleBaseAddress(moduleBaseAddress), |
5936 | m_refCount(1) |
5937 | {} |
5938 | |
5939 | HRESULT __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 | |
5959 | ULONG __stdcall PEOffsetMemoryReader::AddRef() |
5960 | { |
5961 | return InterlockedIncrement((volatile LONG *) &m_refCount); |
5962 | } |
5963 | |
5964 | ULONG __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 |
5975 | HRESULT __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 | |
5980 | PERvaMemoryReader::PERvaMemoryReader(TADDR moduleBaseAddress) : |
5981 | m_moduleBaseAddress(moduleBaseAddress), |
5982 | m_refCount(1) |
5983 | {} |
5984 | |
5985 | HRESULT __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 | |
6005 | ULONG __stdcall PERvaMemoryReader::AddRef() |
6006 | { |
6007 | return InterlockedIncrement((volatile LONG *) &m_refCount); |
6008 | } |
6009 | |
6010 | ULONG __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 |
6021 | HRESULT __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 | |
6028 | HRESULT 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 | |
6061 | HRESULT 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 | |
6107 | HRESULT 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 | // |
6182 | int 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 | |
6192 | HRESULT 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 | |
6226 | void 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 | |
6282 | bool 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 | |
6298 | HRESULT 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 | |
6415 | HRESULT 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 | |
6489 | HRESULT 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 | |
6591 | HRESULT 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 | |
6615 | HRESULT 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 | |
6700 | static 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 | |
6729 | WString 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 | |
6784 | WString 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 | |
6866 | HRESULT 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 | |
6924 | InternalFrameManager::InternalFrameManager() : m_cInternalFramesActual(0), m_iInternalFrameCur(0) {} |
6925 | |
6926 | HRESULT 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 | |
6936 | HRESULT 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 | |
6958 | HRESULT 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 | |