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
11// ===========================================================================
12// STRIKE.CPP
13// ===========================================================================
14//
15// History:
16// 09/07/99 Microsoft Created
17//
18//************************************************************************************************
19// SOS is the native debugging extension designed to support investigations into CLR (mis-)
20// behavior by both users of the runtime as well as the code owners. It allows inspection of
21// internal structures, of user visible entities, as well as execution control.
22//
23// This is the main SOS file hosting the implementation of all the exposed commands. A good
24// starting point for understanding the semantics of these commands is the sosdocs.txt file.
25//
26// #CrossPlatformSOS
27// SOS currently supports cross platform debugging from x86 to ARM. It takes a different approach
28// from the DAC: whereas for the DAC we produce one binary for each supported host-target
29// architecture pair, for SOS we produce only one binary for each host architecture; this one
30// binary contains code for all supported target architectures. In doing this SOS depends on two
31// assumptions:
32// . that the debugger will load the appropriate DAC, and
33// . that the host and target word size is identical.
34// The second assumption is identical to the DAC assumption, and there will be considerable effort
35// required (in the EE, the DAC, and SOS) if we ever need to remove it.
36//
37// In an ideal world SOS would be able to retrieve all platform specific information it needs
38// either from the debugger or from DAC. However, SOS has taken some subtle and not so subtle
39// dependencies on the CLR and the target platform.
40// To resolve this problem, SOS now abstracts the target behind the IMachine interface, and uses
41// calls on IMachine to take target-specific actions. It implements X86Machine, ARMMachine, and
42// AMD64Machine. An instance of these exists in each appropriate host (e.g. the X86 version of SOS
43// contains instances of X86Machine and ARMMachine, the ARM version contains an instance of
44// ARMMachine, and the AMD64 version contains an instance of AMD64Machine). The code included in
45// each version if determined by the SosTarget*** MSBuild symbols, and SOS_TARGET_*** conditional
46// compilation symbols (as specified in sos.targets).
47//
48// Most of the target specific code is hosted in disasm.h/.cpp, and disasmX86.cpp, disasmARM.cpp.
49// Some code currently under _TARGET_*** ifdefs may need to be reviewed/revisited.
50//
51// Issues:
52// The one-binary-per-host decision does have some drawbacks:
53// . Currently including system headers or even CLR headers will only account for the host
54// target, IOW, when building the X86 version of SOS, CONTEXT will refer to the X86 CONTEXT
55// structure, so we need to be careful when debugging ARM targets. The CONTEXT issue is
56// partially resolved by CROSS_PLATFORM_CONTEXT (there is still a need to be very careful
57// when handling arrays of CONTEXTs - see _EFN_StackTrace for details on this).
58// . For larger includes (e.g. GC info), we will need to include files in specific namespaces,
59// with specific _TARGET_*** macros defined in order to avoid name clashes and ensure correct
60// system types are used.
61// -----------------------------------------------------------------------------------------------
62
63#define DO_NOT_DISABLE_RAND //this is a standalone tool, and can use rand()
64
65#include <windows.h>
66#include <winver.h>
67#include <winternl.h>
68#include <psapi.h>
69#ifndef FEATURE_PAL
70#include <list>
71#endif // !FEATURE_PAL
72#include <wchar.h>
73
74#include "platformspecific.h"
75
76#define NOEXTAPI
77#define KDEXT_64BIT
78#include <wdbgexts.h>
79#undef DECLARE_API
80#undef StackTrace
81
82#include <dbghelp.h>
83
84#include <stdio.h>
85#include <stdlib.h>
86#include <string.h>
87#include <stddef.h>
88
89#include "strike.h"
90#include "sos.h"
91
92#ifndef STRESS_LOG
93#define STRESS_LOG
94#endif // STRESS_LOG
95#define STRESS_LOG_READONLY
96#include "stresslog.h"
97
98#include "util.h"
99
100#include "corhdr.h"
101#include "cor.h"
102#include "cordebug.h"
103#include "dacprivate.h"
104#include "corexcep.h"
105
106#define CORHANDLE_MASK 0x1
107#define SWITCHED_OUT_FIBER_OSID 0xbaadf00d;
108
109#define DEFINE_EXT_GLOBALS
110
111#include "data.h"
112#include "disasm.h"
113
114#include "predeftlsslot.h"
115
116#include "hillclimbing.h"
117
118#include "sos_md.h"
119
120#ifndef FEATURE_PAL
121
122#include "ExpressionNode.h"
123#include "WatchCmd.h"
124
125#include <algorithm>
126
127#include "tls.h"
128
129typedef struct _VM_COUNTERS {
130 SIZE_T PeakVirtualSize;
131 SIZE_T VirtualSize;
132 ULONG PageFaultCount;
133 SIZE_T PeakWorkingSetSize;
134 SIZE_T WorkingSetSize;
135 SIZE_T QuotaPeakPagedPoolUsage;
136 SIZE_T QuotaPagedPoolUsage;
137 SIZE_T QuotaPeakNonPagedPoolUsage;
138 SIZE_T QuotaNonPagedPoolUsage;
139 SIZE_T PagefileUsage;
140 SIZE_T PeakPagefileUsage;
141} VM_COUNTERS;
142typedef VM_COUNTERS *PVM_COUNTERS;
143
144const PROCESSINFOCLASS ProcessVmCounters = static_cast<PROCESSINFOCLASS>(3);
145
146#endif // !FEATURE_PAL
147
148#include <set>
149#include <vector>
150#include <map>
151
152BOOL CallStatus;
153BOOL ControlC = FALSE;
154
155IMetaDataDispenserEx *pDisp = NULL;
156WCHAR g_mdName[mdNameLen];
157
158#ifndef FEATURE_PAL
159HMODULE g_hInstance = NULL;
160#include <algorithm>
161#endif // !FEATURE_PAL
162
163#ifdef _MSC_VER
164#pragma warning(disable:4244) // conversion from 'unsigned int' to 'unsigned short', possible loss of data
165#pragma warning(disable:4189) // local variable is initialized but not referenced
166#endif
167
168#ifdef FEATURE_PAL
169#define SOSPrefix ""
170#define SOSThreads "clrthreads"
171#else
172#define SOSPrefix "!"
173#define SOSThreads "!threads"
174#endif
175
176#if defined _X86_ && !defined FEATURE_PAL
177// disable FPO for X86 builds
178#pragma optimize("y", off)
179#endif
180
181#undef assert
182
183#ifdef _MSC_VER
184#pragma warning(default:4244)
185#pragma warning(default:4189)
186#endif
187
188#ifndef FEATURE_PAL
189#include "ntinfo.h"
190#endif // FEATURE_PAL
191
192#ifndef IfFailRet
193#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
194#endif
195
196#ifdef FEATURE_PAL
197
198#define NOTHROW
199#define MINIDUMP_NOT_SUPPORTED()
200
201#else // !FEATURE_PAL
202
203#define MINIDUMP_NOT_SUPPORTED() \
204 if (IsMiniDumpFile()) \
205 { \
206 ExtOut("This command is not supported in a minidump without full memory\n"); \
207 ExtOut("To try the command anyway, run !MinidumpMode 0\n"); \
208 return Status; \
209 }
210
211#define NOTHROW (std::nothrow)
212
213#include "safemath.h"
214
215DECLARE_API (MinidumpMode)
216{
217 INIT_API ();
218 DWORD_PTR Value=0;
219
220 CMDValue arg[] =
221 { // vptr, type
222 {&Value, COHEX}
223 };
224
225 size_t nArg;
226 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
227 {
228 return Status;
229 }
230 if (nArg == 0)
231 {
232 // Print status of current mode
233 ExtOut("Current mode: %s - unsafe minidump commands are %s.\n",
234 g_InMinidumpSafeMode ? "1" : "0",
235 g_InMinidumpSafeMode ? "disabled" : "enabled");
236 }
237 else
238 {
239 if (Value != 0 && Value != 1)
240 {
241 ExtOut("Mode must be 0 or 1\n");
242 return Status;
243 }
244
245 g_InMinidumpSafeMode = (BOOL) Value;
246 ExtOut("Unsafe minidump commands are %s.\n",
247 g_InMinidumpSafeMode ? "disabled" : "enabled");
248 }
249
250 return Status;
251}
252
253#endif // FEATURE_PAL
254
255/**********************************************************************\
256* Routine Description: *
257* *
258* This function is called to get the MethodDesc for a given eip *
259* *
260\**********************************************************************/
261DECLARE_API(IP2MD)
262{
263 INIT_API();
264 MINIDUMP_NOT_SUPPORTED();
265
266 BOOL dml = FALSE;
267 TADDR IP = 0;
268 CMDOption option[] =
269 { // name, vptr, type, hasValue
270#ifndef FEATURE_PAL
271 {"/d", &dml, COBOOL, FALSE},
272#endif
273 };
274 CMDValue arg[] =
275 { // vptr, type
276 {&IP, COHEX},
277 };
278 size_t nArg;
279
280 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
281 {
282 return Status;
283 }
284 EnableDMLHolder dmlHolder(dml);
285
286 if (IP == 0)
287 {
288 ExtOut("%s is not IP\n", args);
289 return Status;
290 }
291
292 CLRDATA_ADDRESS cdaStart = TO_CDADDR(IP);
293 CLRDATA_ADDRESS pMD;
294
295
296 if ((Status = g_sos->GetMethodDescPtrFromIP(cdaStart, &pMD)) != S_OK)
297 {
298 ExtOut("Failed to request MethodData, not in JIT code range\n");
299 return Status;
300 }
301
302 DMLOut("MethodDesc: %s\n", DMLMethodDesc(pMD));
303 DumpMDInfo(TO_TADDR(pMD), cdaStart, FALSE /* fStackTraceFormat */);
304
305 WCHAR filename[MAX_LONGPATH];
306 ULONG linenum;
307 // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
308 ULONG symlines = 0;
309 if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
310 {
311 symlines &= SYMOPT_LOAD_LINES;
312 }
313
314 if (symlines != 0 &&
315 SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, _countof(filename))))
316 {
317 ExtOut("Source file: %S @ %d\n", filename, linenum);
318 }
319
320 return Status;
321}
322
323// (MAX_STACK_FRAMES is also used by x86 to prevent infinite loops in _EFN_StackTrace)
324#define MAX_STACK_FRAMES 1000
325
326#if defined(_TARGET_WIN64_)
327#define DEBUG_STACK_CONTEXT AMD64_CONTEXT
328#elif defined(_TARGET_ARM_) // _TARGET_WIN64_
329#define DEBUG_STACK_CONTEXT ARM_CONTEXT
330#elif defined(_TARGET_X86_) // _TARGET_ARM_
331#define DEBUG_STACK_CONTEXT X86_CONTEXT
332#endif // _TARGET_X86_
333
334#ifdef DEBUG_STACK_CONTEXT
335// I use a global set of frames for stack walking on win64 because the debugger's
336// GetStackTrace function doesn't provide a way to find out the total size of a stackwalk,
337// and I'd like to have a reasonably big maximum without overflowing the stack by declaring
338// the buffer locally and I also want to get a managed trace in a low memory environment
339// (so no dynamic allocation if possible).
340DEBUG_STACK_FRAME g_Frames[MAX_STACK_FRAMES];
341DEBUG_STACK_CONTEXT g_FrameContexts[MAX_STACK_FRAMES];
342
343static HRESULT
344GetContextStackTrace(ULONG osThreadId, PULONG pnumFrames)
345{
346 PDEBUG_CONTROL4 debugControl4;
347 HRESULT hr;
348
349 // Do we have advanced capability?
350 if ((hr = g_ExtControl->QueryInterface(__uuidof(IDebugControl4), (void **)&debugControl4)) == S_OK)
351 {
352 ULONG oldId, id;
353 g_ExtSystem->GetCurrentThreadId(&oldId);
354
355 if ((hr = g_ExtSystem->GetThreadIdBySystemId(osThreadId, &id)) != S_OK) {
356 return hr;
357 }
358 g_ExtSystem->SetCurrentThreadId(id);
359
360 // GetContextStackTrace fills g_FrameContexts as an array of
361 // contexts packed as target architecture contexts. We cannot
362 // safely cast this as an array of CROSS_PLATFORM_CONTEXT, since
363 // sizeof(CROSS_PLATFORM_CONTEXT) != sizeof(TGT_CONTEXT)
364 hr = debugControl4->GetContextStackTrace(
365 NULL,
366 0,
367 g_Frames,
368 MAX_STACK_FRAMES,
369 g_FrameContexts,
370 MAX_STACK_FRAMES*g_targetMachine->GetContextSize(),
371 g_targetMachine->GetContextSize(),
372 pnumFrames);
373
374 g_ExtSystem->SetCurrentThreadId(oldId);
375 debugControl4->Release();
376 }
377 return hr;
378}
379
380#endif // DEBUG_STACK_CONTEXT
381
382/**********************************************************************\
383* Routine Description: *
384* *
385* This function displays the stack trace. It looks at each DWORD *
386* on stack. If the DWORD is a return address, the symbol name or
387* managed function name is displayed. *
388* *
389\**********************************************************************/
390void DumpStackInternal(DumpStackFlag *pDSFlag)
391{
392 ReloadSymbolWithLineInfo();
393
394 ULONG64 StackOffset;
395 g_ExtRegisters->GetStackOffset (&StackOffset);
396 if (pDSFlag->top == 0) {
397 pDSFlag->top = TO_TADDR(StackOffset);
398 }
399 size_t value;
400 while (g_ExtData->ReadVirtual(TO_CDADDR(pDSFlag->top), &value, sizeof(size_t), NULL) != S_OK) {
401 if (IsInterrupt())
402 return;
403 pDSFlag->top = NextOSPageAddress(pDSFlag->top);
404 }
405
406#ifndef FEATURE_PAL
407 if (pDSFlag->end == 0) {
408 // Find the current stack range
409 NT_TIB teb;
410 ULONG64 dwTebAddr=0;
411
412 g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
413 if (SafeReadMemory(TO_TADDR(dwTebAddr), &teb, sizeof(NT_TIB), NULL))
414 {
415 if (pDSFlag->top > TO_TADDR(teb.StackLimit)
416 && pDSFlag->top <= TO_TADDR(teb.StackBase))
417 {
418 if (pDSFlag->end == 0 || pDSFlag->end > TO_TADDR(teb.StackBase))
419 pDSFlag->end = TO_TADDR(teb.StackBase);
420 }
421 }
422 }
423#endif // FEATURE_PAL
424
425 if (pDSFlag->end == 0)
426 {
427 ExtOut("TEB information is not available so a stack size of 0xFFFF is assumed\n");
428 pDSFlag->end = pDSFlag->top + 0xFFFF;
429 }
430
431 if (pDSFlag->end < pDSFlag->top)
432 {
433 ExtOut("Wrong option: stack selection wrong\n");
434 return;
435 }
436
437 DumpStackWorker(*pDSFlag);
438}
439
440#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_)
441static BOOL UnwindStackFrames(ULONG32 osThreadId);
442#endif
443
444DECLARE_API(DumpStack)
445{
446 INIT_API_NO_RET_ON_FAILURE();
447
448 MINIDUMP_NOT_SUPPORTED();
449
450 DumpStackFlag DSFlag;
451 DSFlag.fEEonly = FALSE;
452 DSFlag.fSuppressSrcInfo = FALSE;
453 DSFlag.top = 0;
454 DSFlag.end = 0;
455
456 BOOL unwind = FALSE;
457 BOOL dml = FALSE;
458 CMDOption option[] = {
459 // name, vptr, type, hasValue
460 {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
461 {"-n", &DSFlag.fSuppressSrcInfo, COBOOL, FALSE},
462 {"-unwind", &unwind, COBOOL, FALSE},
463#ifndef FEATURE_PAL
464 {"/d", &dml, COBOOL, FALSE}
465#endif
466 };
467 CMDValue arg[] = {
468 // vptr, type
469 {&DSFlag.top, COHEX},
470 {&DSFlag.end, COHEX}
471 };
472 size_t nArg;
473 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
474 return Status;
475
476 // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
477 ULONG symlines = 0;
478 if (!DSFlag.fSuppressSrcInfo && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
479 {
480 symlines &= SYMOPT_LOAD_LINES;
481 }
482 DSFlag.fSuppressSrcInfo = DSFlag.fSuppressSrcInfo || (symlines == 0);
483
484 EnableDMLHolder enabledml(dml);
485
486 ULONG sysId = 0, id = 0;
487 g_ExtSystem->GetCurrentThreadSystemId(&sysId);
488 ExtOut("OS Thread Id: 0x%x ", sysId);
489 g_ExtSystem->GetCurrentThreadId(&id);
490 ExtOut("(%d)\n", id);
491
492#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_)
493 if (unwind)
494 {
495 UnwindStackFrames(sysId);
496 }
497 else
498#endif
499 {
500 DumpStackInternal(&DSFlag);
501 }
502 return Status;
503}
504
505
506/**********************************************************************\
507* Routine Description: *
508* *
509* This function displays the stack trace for threads that EE knows *
510* from ThreadStore. *
511* *
512\**********************************************************************/
513DECLARE_API (EEStack)
514{
515 INIT_API();
516
517 MINIDUMP_NOT_SUPPORTED();
518
519 DumpStackFlag DSFlag;
520 DSFlag.fEEonly = FALSE;
521 DSFlag.fSuppressSrcInfo = FALSE;
522 DSFlag.top = 0;
523 DSFlag.end = 0;
524
525 BOOL bShortList = FALSE;
526 BOOL dml = FALSE;
527 CMDOption option[] =
528 { // name, vptr, type, hasValue
529 {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
530 {"-short", &bShortList, COBOOL, FALSE},
531#ifndef FEATURE_PAL
532 {"/d", &dml, COBOOL, FALSE}
533#endif
534 };
535
536 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
537 {
538 return Status;
539 }
540
541 EnableDMLHolder enableDML(dml);
542
543 ULONG Tid;
544 g_ExtSystem->GetCurrentThreadId(&Tid);
545
546 DacpThreadStoreData ThreadStore;
547 if ((Status = ThreadStore.Request(g_sos)) != S_OK)
548 {
549 ExtOut("Failed to request ThreadStore\n");
550 return Status;
551 }
552
553 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
554 while (CurThread)
555 {
556 if (IsInterrupt())
557 break;
558
559 DacpThreadData Thread;
560 if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
561 {
562 ExtOut("Failed to request Thread at %p\n", CurThread);
563 return Status;
564 }
565
566 ULONG id=0;
567 if (g_ExtSystem->GetThreadIdBySystemId (Thread.osThreadId, &id) != S_OK)
568 {
569 CurThread = Thread.nextThread;
570 continue;
571 }
572
573 ExtOut("---------------------------------------------\n");
574 ExtOut("Thread %3d\n", id);
575 BOOL doIt = FALSE;
576
577
578#define TS_Hijacked 0x00000080
579
580 if (!bShortList)
581 {
582 doIt = TRUE;
583 }
584 else if ((Thread.lockCount > 0) || (Thread.state & TS_Hijacked))
585 {
586 // TODO: bring back || (int)vThread.m_pFrame != -1 {
587 doIt = TRUE;
588 }
589 else
590 {
591 ULONG64 IP;
592 g_ExtRegisters->GetInstructionOffset (&IP);
593 JITTypes jitType;
594 TADDR methodDesc;
595 TADDR gcinfoAddr;
596 IP2MethodDesc (TO_TADDR(IP), methodDesc, jitType, gcinfoAddr);
597 if (methodDesc)
598 {
599 doIt = TRUE;
600 }
601 }
602
603 if (doIt)
604 {
605 g_ExtSystem->SetCurrentThreadId(id);
606 DSFlag.top = 0;
607 DSFlag.end = 0;
608 DumpStackInternal(&DSFlag);
609 }
610
611 CurThread = Thread.nextThread;
612 }
613
614 g_ExtSystem->SetCurrentThreadId(Tid);
615 return Status;
616}
617
618HRESULT DumpStackObjectsRaw(size_t nArg, __in_z LPSTR exprBottom, __in_z LPSTR exprTop, BOOL bVerify)
619{
620 size_t StackTop = 0;
621 size_t StackBottom = 0;
622 if (nArg==0)
623 {
624 ULONG64 StackOffset;
625 g_ExtRegisters->GetStackOffset(&StackOffset);
626
627 StackTop = TO_TADDR(StackOffset);
628 }
629 else
630 {
631 StackTop = GetExpression(exprTop);
632 if (StackTop == 0)
633 {
634 ExtOut("wrong option: %s\n", exprTop);
635 return E_FAIL;
636 }
637
638 if (nArg==2)
639 {
640 StackBottom = GetExpression(exprBottom);
641 if (StackBottom == 0)
642 {
643 ExtOut("wrong option: %s\n", exprBottom);
644 return E_FAIL;
645 }
646 }
647 }
648
649#ifndef FEATURE_PAL
650 NT_TIB teb;
651 ULONG64 dwTebAddr=0;
652 HRESULT hr = g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
653 if (SUCCEEDED(hr) && SafeReadMemory (TO_TADDR(dwTebAddr), &teb, sizeof (NT_TIB), NULL))
654 {
655 if (StackTop > TO_TADDR(teb.StackLimit) && StackTop <= TO_TADDR(teb.StackBase))
656 {
657 if (StackBottom == 0 || StackBottom > TO_TADDR(teb.StackBase))
658 StackBottom = TO_TADDR(teb.StackBase);
659 }
660 }
661#endif
662
663 if (StackBottom == 0)
664 StackBottom = StackTop + 0xFFFF;
665
666 if (StackBottom < StackTop)
667 {
668 ExtOut("Wrong option: stack selection wrong\n");
669 return E_FAIL;
670 }
671
672 // We can use the gc snapshot to eliminate object addresses that are
673 // not on the gc heap.
674 if (!g_snapshot.Build())
675 {
676 ExtOut("Unable to determine bounds of gc heap\n");
677 return E_FAIL;
678 }
679
680 // Print thread ID.
681 ULONG id = 0;
682 g_ExtSystem->GetCurrentThreadSystemId (&id);
683 ExtOut("OS Thread Id: 0x%x ", id);
684 g_ExtSystem->GetCurrentThreadId (&id);
685 ExtOut("(%d)\n", id);
686
687 DumpStackObjectsHelper(StackTop, StackBottom, bVerify);
688 return S_OK;
689}
690
691/**********************************************************************\
692* Routine Description: *
693* *
694* This function is called to dump the address and name of all *
695* Managed Objects on the stack. *
696* *
697\**********************************************************************/
698DECLARE_API(DumpStackObjects)
699{
700 INIT_API();
701 MINIDUMP_NOT_SUPPORTED();
702 StringHolder exprTop, exprBottom;
703
704 BOOL bVerify = FALSE;
705 BOOL dml = FALSE;
706 CMDOption option[] =
707 { // name, vptr, type, hasValue
708 {"-verify", &bVerify, COBOOL, FALSE},
709#ifndef FEATURE_PAL
710 {"/d", &dml, COBOOL, FALSE}
711#endif
712 };
713 CMDValue arg[] =
714 { // vptr, type
715 {&exprTop.data, COSTRING},
716 {&exprBottom.data, COSTRING}
717 };
718 size_t nArg;
719
720 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
721 {
722 return Status;
723 }
724
725 EnableDMLHolder enableDML(dml);
726
727 return DumpStackObjectsRaw(nArg, exprBottom.data, exprTop.data, bVerify);
728}
729
730/**********************************************************************\
731* Routine Description: *
732* *
733* This function is called to dump the contents of a MethodDesc *
734* for a given address *
735* *
736\**********************************************************************/
737DECLARE_API(DumpMD)
738{
739 INIT_API();
740 MINIDUMP_NOT_SUPPORTED();
741
742 DWORD_PTR dwStartAddr = NULL;
743 BOOL dml = FALSE;
744
745 CMDOption option[] =
746 { // name, vptr, type, hasValue
747#ifndef FEATURE_PAL
748 {"/d", &dml, COBOOL, FALSE},
749#endif
750 };
751 CMDValue arg[] =
752 { // vptr, type
753 {&dwStartAddr, COHEX},
754 };
755 size_t nArg;
756
757 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
758 {
759 return Status;
760 }
761
762 EnableDMLHolder dmlHolder(dml);
763
764 DumpMDInfo(dwStartAddr);
765
766 return Status;
767}
768
769BOOL GatherDynamicInfo(TADDR DynamicMethodObj, DacpObjectData *codeArray,
770 DacpObjectData *tokenArray, TADDR *ptokenArrayAddr)
771{
772 BOOL bRet = FALSE;
773 int iOffset;
774 DacpObjectData objData; // temp object
775
776 if (codeArray == NULL || tokenArray == NULL)
777 return bRet;
778
779 if (objData.Request(g_sos, TO_CDADDR(DynamicMethodObj)) != S_OK)
780 return bRet;
781
782 iOffset = GetObjFieldOffset(TO_CDADDR(DynamicMethodObj), objData.MethodTable, W("m_resolver"));
783 if (iOffset <= 0)
784 return bRet;
785
786 TADDR resolverPtr;
787 if (FAILED(MOVE(resolverPtr, DynamicMethodObj + iOffset)))
788 return bRet;
789
790 if (objData.Request(g_sos, TO_CDADDR(resolverPtr)) != S_OK)
791 return bRet;
792
793 iOffset = GetObjFieldOffset(TO_CDADDR(resolverPtr), objData.MethodTable, W("m_code"));
794 if (iOffset <= 0)
795 return bRet;
796
797 TADDR codePtr;
798 if (FAILED(MOVE(codePtr, resolverPtr + iOffset)))
799 return bRet;
800
801 if (codeArray->Request(g_sos, TO_CDADDR(codePtr)) != S_OK)
802 return bRet;
803
804 if (codeArray->dwComponentSize != 1)
805 return bRet;
806
807 // We also need the resolution table
808 iOffset = GetObjFieldOffset (TO_CDADDR(resolverPtr), objData.MethodTable, W("m_scope"));
809 if (iOffset <= 0)
810 return bRet;
811
812 TADDR scopePtr;
813 if (FAILED(MOVE(scopePtr, resolverPtr + iOffset)))
814 return bRet;
815
816 if (objData.Request(g_sos, TO_CDADDR(scopePtr)) != S_OK)
817 return bRet;
818
819 iOffset = GetObjFieldOffset (TO_CDADDR(scopePtr), objData.MethodTable, W("m_tokens"));
820 if (iOffset <= 0)
821 return bRet;
822
823 TADDR tokensPtr;
824 if (FAILED(MOVE(tokensPtr, scopePtr + iOffset)))
825 return bRet;
826
827 if (objData.Request(g_sos, TO_CDADDR(tokensPtr)) != S_OK)
828 return bRet;
829
830 iOffset = GetObjFieldOffset(TO_CDADDR(tokensPtr), objData.MethodTable, W("_items"));
831 if (iOffset <= 0)
832 return bRet;
833
834 TADDR itemsPtr;
835 MOVE (itemsPtr, tokensPtr + iOffset);
836
837 *ptokenArrayAddr = itemsPtr;
838
839 if (tokenArray->Request(g_sos, TO_CDADDR(itemsPtr)) != S_OK)
840 return bRet;
841
842 bRet = TRUE; // whew.
843 return bRet;
844}
845
846DECLARE_API(DumpIL)
847{
848 INIT_API();
849 MINIDUMP_NOT_SUPPORTED();
850 DWORD_PTR dwStartAddr = NULL;
851 DWORD_PTR dwDynamicMethodObj = NULL;
852 BOOL dml = FALSE;
853 BOOL fILPointerDirectlySpecified = FALSE;
854
855 CMDOption option[] =
856 { // name, vptr, type, hasValue
857 {"/d", &dml, COBOOL, FALSE},
858 {"/i", &fILPointerDirectlySpecified, COBOOL, FALSE},
859 };
860 CMDValue arg[] =
861 { // vptr, type
862 {&dwStartAddr, COHEX},
863 };
864 size_t nArg;
865
866 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
867 {
868 return Status;
869 }
870
871 EnableDMLHolder dmlHolder(dml);
872 if (dwStartAddr == NULL)
873 {
874 ExtOut("Must pass a valid expression\n");
875 return Status;
876 }
877
878 if (fILPointerDirectlySpecified)
879 {
880 return DecodeILFromAddress(NULL, dwStartAddr);
881 }
882
883 if (!g_snapshot.Build())
884 {
885 ExtOut("Unable to build snapshot of the garbage collector state\n");
886 return Status;
887 }
888
889 if (g_snapshot.GetHeap(dwStartAddr) != NULL)
890 {
891 dwDynamicMethodObj = dwStartAddr;
892 }
893
894 if (dwDynamicMethodObj == NULL)
895 {
896 // We have been given a MethodDesc
897 DacpMethodDescData MethodDescData;
898 if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
899 {
900 ExtOut("%p is not a MethodDesc\n", SOS_PTR(dwStartAddr));
901 return Status;
902 }
903
904 if (MethodDescData.bIsDynamic && MethodDescData.managedDynamicMethodObject)
905 {
906 dwDynamicMethodObj = TO_TADDR(MethodDescData.managedDynamicMethodObject);
907 if (dwDynamicMethodObj == NULL)
908 {
909 ExtOut("Unable to print IL for DynamicMethodDesc %p\n", SOS_PTR(dwDynamicMethodObj));
910 return Status;
911 }
912 }
913 else
914 {
915 // This is not a dynamic method, print the IL for it.
916 // Get the module
917 DacpModuleData dmd;
918 if (dmd.Request(g_sos, MethodDescData.ModulePtr) != S_OK)
919 {
920 ExtOut("Unable to get module\n");
921 return Status;
922 }
923
924 ToRelease<IMetaDataImport> pImport = MDImportForModule(&dmd);
925 if (pImport == NULL)
926 {
927 ExtOut("bad import\n");
928 return Status;
929 }
930
931 ULONG pRva;
932 DWORD dwFlags;
933 if (pImport->GetRVA(MethodDescData.MDToken, &pRva, &dwFlags) != S_OK)
934 {
935 ExtOut("error in import\n");
936 return Status;
937 }
938
939 CLRDATA_ADDRESS ilAddrClr;
940 if (g_sos->GetILForModule(MethodDescData.ModulePtr, pRva, &ilAddrClr) != S_OK)
941 {
942 ExtOut("FindIL failed\n");
943 return Status;
944 }
945
946 TADDR ilAddr = TO_TADDR(ilAddrClr);
947 IfFailRet(DecodeILFromAddress(pImport, ilAddr));
948 }
949 }
950
951 if (dwDynamicMethodObj != NULL)
952 {
953 // We have a DynamicMethod managed object, let us visit the town and paint.
954 DacpObjectData codeArray;
955 DacpObjectData tokenArray;
956 DWORD_PTR tokenArrayAddr;
957 if (!GatherDynamicInfo (dwDynamicMethodObj, &codeArray, &tokenArray, &tokenArrayAddr))
958 {
959 DMLOut("Error gathering dynamic info from object at %s.\n", DMLObject(dwDynamicMethodObj));
960 return Status;
961 }
962
963 // Read the memory into a local buffer
964 BYTE *pArray = new NOTHROW BYTE[(SIZE_T)codeArray.dwNumComponents];
965 if (pArray == NULL)
966 {
967 ExtOut("Not enough memory to read IL\n");
968 return Status;
969 }
970
971 Status = g_ExtData->ReadVirtual(UL64_TO_CDA(codeArray.ArrayDataPtr), pArray, (ULONG)codeArray.dwNumComponents, NULL);
972 if (Status != S_OK)
973 {
974 ExtOut("Failed to read memory\n");
975 delete [] pArray;
976 return Status;
977 }
978
979 // Now we have a local copy of the IL, and a managed array for token resolution.
980 // Visit our IL parser with this info.
981 ExtOut("This is dynamic IL. Exception info is not reported at this time.\n");
982 ExtOut("If a token is unresolved, run \"!do <addr>\" on the addr given\n");
983 ExtOut("in parenthesis. You can also look at the token table yourself, by\n");
984 ExtOut("running \"!DumpArray %p\".\n\n", SOS_PTR(tokenArrayAddr));
985 DecodeDynamicIL(pArray, (ULONG)codeArray.dwNumComponents, tokenArray);
986
987 delete [] pArray;
988 }
989 return Status;
990}
991
992void DumpSigWorker (
993 DWORD_PTR dwSigAddr,
994 DWORD_PTR dwModuleAddr,
995 BOOL fMethod)
996{
997 //
998 // Find the length of the signature and copy it into the debugger process.
999 //
1000
1001 ULONG cbSig = 0;
1002 const ULONG cbSigInc = 256;
1003 ArrayHolder<COR_SIGNATURE> pSig = new NOTHROW COR_SIGNATURE[cbSigInc];
1004 if (pSig == NULL)
1005 {
1006 ReportOOM();
1007 return;
1008 }
1009
1010 CQuickBytes sigString;
1011 for (;;)
1012 {
1013 if (IsInterrupt())
1014 return;
1015
1016 ULONG cbCopied;
1017 if (!SafeReadMemory(TO_TADDR(dwSigAddr + cbSig), pSig + cbSig, cbSigInc, &cbCopied))
1018 return;
1019 cbSig += cbCopied;
1020
1021 sigString.ReSize(0);
1022 GetSignatureStringResults result;
1023 if (fMethod)
1024 result = GetMethodSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
1025 else
1026 result = GetSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
1027
1028 if (GSS_ERROR == result)
1029 return;
1030
1031 if (GSS_SUCCESS == result)
1032 break;
1033
1034 // If we didn't get the full amount back, and we failed to parse the
1035 // signature, it's not valid because of insufficient data
1036 if (cbCopied < 256)
1037 {
1038 ExtOut("Invalid signature\n");
1039 return;
1040 }
1041
1042#ifdef _PREFAST_
1043#pragma warning(push)
1044#pragma warning(disable:6280) // "Suppress PREFast warning about mismatch alloc/free"
1045#endif
1046
1047 PCOR_SIGNATURE pSigNew = (PCOR_SIGNATURE)realloc(pSig, cbSig+cbSigInc);
1048
1049#ifdef _PREFAST_
1050#pragma warning(pop)
1051#endif
1052
1053 if (pSigNew == NULL)
1054 {
1055 ExtOut("Out of memory\n");
1056 return;
1057 }
1058
1059 pSig = pSigNew;
1060 }
1061
1062 ExtOut("%S\n", (PCWSTR)sigString.Ptr());
1063}
1064
1065/**********************************************************************\
1066* Routine Description: *
1067* *
1068* This function is called to dump a signature object. *
1069* *
1070\**********************************************************************/
1071DECLARE_API(DumpSig)
1072{
1073 INIT_API();
1074
1075 MINIDUMP_NOT_SUPPORTED();
1076
1077 //
1078 // Fetch arguments
1079 //
1080
1081 StringHolder sigExpr;
1082 StringHolder moduleExpr;
1083 CMDValue arg[] =
1084 {
1085 {&sigExpr.data, COSTRING},
1086 {&moduleExpr.data, COSTRING}
1087 };
1088 size_t nArg;
1089 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
1090 {
1091 return Status;
1092 }
1093 if (nArg != 2)
1094 {
1095 ExtOut("!DumpSig <sigaddr> <moduleaddr>\n");
1096 return Status;
1097 }
1098
1099 DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);
1100 DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
1101
1102 if (dwSigAddr == 0 || dwModuleAddr == 0)
1103 {
1104 ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
1105 return Status;
1106 }
1107
1108 DumpSigWorker(dwSigAddr, dwModuleAddr, TRUE);
1109 return Status;
1110}
1111
1112/**********************************************************************\
1113* Routine Description: *
1114* *
1115* This function is called to dump a portion of a signature object. *
1116* *
1117\**********************************************************************/
1118DECLARE_API(DumpSigElem)
1119{
1120 INIT_API();
1121
1122 MINIDUMP_NOT_SUPPORTED();
1123
1124
1125 //
1126 // Fetch arguments
1127 //
1128
1129 StringHolder sigExpr;
1130 StringHolder moduleExpr;
1131 CMDValue arg[] =
1132 {
1133 {&sigExpr.data, COSTRING},
1134 {&moduleExpr.data, COSTRING}
1135 };
1136 size_t nArg;
1137 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
1138 {
1139 return Status;
1140 }
1141
1142 if (nArg != 2)
1143 {
1144 ExtOut("!DumpSigElem <sigaddr> <moduleaddr>\n");
1145 return Status;
1146 }
1147
1148 DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);
1149 DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
1150
1151 if (dwSigAddr == 0 || dwModuleAddr == 0)
1152 {
1153 ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
1154 return Status;
1155 }
1156
1157 DumpSigWorker(dwSigAddr, dwModuleAddr, FALSE);
1158 return Status;
1159}
1160
1161/**********************************************************************\
1162* Routine Description: *
1163* *
1164* This function is called to dump the contents of an EEClass from *
1165* a given address
1166* *
1167\**********************************************************************/
1168DECLARE_API(DumpClass)
1169{
1170 INIT_API();
1171 MINIDUMP_NOT_SUPPORTED();
1172
1173 DWORD_PTR dwStartAddr = 0;
1174 BOOL dml = FALSE;
1175
1176 CMDOption option[] =
1177 { // name, vptr, type, hasValue
1178#ifndef FEATURE_PAL
1179 {"/d", &dml, COBOOL, FALSE},
1180#endif
1181 };
1182 CMDValue arg[] =
1183 { // vptr, type
1184 {&dwStartAddr, COHEX}
1185 };
1186
1187 size_t nArg;
1188 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
1189 {
1190 return Status;
1191 }
1192
1193 if (nArg == 0)
1194 {
1195 ExtOut("Missing EEClass address\n");
1196 return Status;
1197 }
1198
1199 EnableDMLHolder dmlHolder(dml);
1200
1201 CLRDATA_ADDRESS methodTable;
1202 if ((Status=g_sos->GetMethodTableForEEClass(TO_CDADDR(dwStartAddr), &methodTable)) != S_OK)
1203 {
1204 ExtOut("Invalid EEClass address\n");
1205 return Status;
1206 }
1207
1208 DacpMethodTableData mtdata;
1209 if ((Status=mtdata.Request(g_sos, TO_CDADDR(methodTable)))!=S_OK)
1210 {
1211 ExtOut("EEClass has an invalid MethodTable address\n");
1212 return Status;
1213 }
1214
1215 sos::MethodTable mt = TO_TADDR(methodTable);
1216 ExtOut("Class Name: %S\n", mt.GetName());
1217
1218 WCHAR fileName[MAX_LONGPATH];
1219 FileNameForModule(TO_TADDR(mtdata.Module), fileName);
1220 ExtOut("mdToken: %p\n", mtdata.cl);
1221 ExtOut("File: %S\n", fileName);
1222
1223 CLRDATA_ADDRESS ParentEEClass = NULL;
1224 if (mtdata.ParentMethodTable)
1225 {
1226 DacpMethodTableData mtdataparent;
1227 if ((Status=mtdataparent.Request(g_sos, TO_CDADDR(mtdata.ParentMethodTable)))!=S_OK)
1228 {
1229 ExtOut("EEClass has an invalid MethodTable address\n");
1230 return Status;
1231 }
1232 ParentEEClass = mtdataparent.Class;
1233 }
1234
1235 DMLOut("Parent Class: %s\n", DMLClass(ParentEEClass));
1236 DMLOut("Module: %s\n", DMLModule(mtdata.Module));
1237 DMLOut("Method Table: %s\n", DMLMethodTable(methodTable));
1238 ExtOut("Vtable Slots: %x\n", mtdata.wNumVirtuals);
1239 ExtOut("Total Method Slots: %x\n", mtdata.wNumVtableSlots);
1240 ExtOut("Class Attributes: %x ", mtdata.dwAttrClass);
1241
1242 if (IsTdInterface(mtdata.dwAttrClass))
1243 ExtOut("Interface, ");
1244 if (IsTdAbstract(mtdata.dwAttrClass))
1245 ExtOut("Abstract, ");
1246 if (IsTdImport(mtdata.dwAttrClass))
1247 ExtOut("ComImport, ");
1248
1249 ExtOut("\n");
1250
1251 DacpMethodTableFieldData vMethodTableFields;
1252 if (SUCCEEDED(vMethodTableFields.Request(g_sos, methodTable)))
1253 {
1254 ExtOut("NumInstanceFields: %x\n", vMethodTableFields.wNumInstanceFields);
1255 ExtOut("NumStaticFields: %x\n", vMethodTableFields.wNumStaticFields);
1256
1257 if (vMethodTableFields.wNumThreadStaticFields != 0)
1258 {
1259 ExtOut("NumThreadStaticFields: %x\n", vMethodTableFields.wNumThreadStaticFields);
1260 }
1261
1262 if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1263 {
1264 DisplayFields(methodTable, &mtdata, &vMethodTableFields, NULL, TRUE, FALSE);
1265 }
1266 }
1267
1268 return Status;
1269}
1270
1271/**********************************************************************\
1272* Routine Description: *
1273* *
1274* This function is called to dump the contents of a MethodTable *
1275* from a given address *
1276* *
1277\**********************************************************************/
1278DECLARE_API(DumpMT)
1279{
1280 DWORD_PTR dwStartAddr=0;
1281 DWORD_PTR dwOriginalAddr;
1282
1283 INIT_API();
1284
1285 MINIDUMP_NOT_SUPPORTED();
1286
1287 BOOL bDumpMDTable = FALSE;
1288 BOOL dml = FALSE;
1289
1290 CMDOption option[] =
1291 { // name, vptr, type, hasValue
1292 {"-MD", &bDumpMDTable, COBOOL, FALSE},
1293#ifndef FEATURE_PAL
1294 {"/d", &dml, COBOOL, FALSE}
1295#endif
1296 };
1297 CMDValue arg[] =
1298 { // vptr, type
1299 {&dwStartAddr, COHEX}
1300 };
1301 size_t nArg;
1302 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
1303 {
1304 return Status;
1305 }
1306
1307 EnableDMLHolder dmlHolder(dml);
1308 TableOutput table(2, 16, AlignLeft, false);
1309
1310 if (nArg == 0)
1311 {
1312 Print("Missing MethodTable address\n");
1313 return Status;
1314 }
1315
1316 dwOriginalAddr = dwStartAddr;
1317 dwStartAddr = dwStartAddr&~3;
1318
1319 if (!IsMethodTable(dwStartAddr))
1320 {
1321 Print(dwOriginalAddr, " is not a MethodTable\n");
1322 return Status;
1323 }
1324
1325 DacpMethodTableData vMethTable;
1326 vMethTable.Request(g_sos, TO_CDADDR(dwStartAddr));
1327
1328 if (vMethTable.bIsFree)
1329 {
1330 Print("Free MethodTable\n");
1331 return Status;
1332 }
1333
1334 DacpMethodTableCollectibleData vMethTableCollectible;
1335 vMethTableCollectible.Request(g_sos, TO_CDADDR(dwStartAddr));
1336
1337 table.WriteRow("EEClass:", EEClassPtr(vMethTable.Class));
1338
1339 table.WriteRow("Module:", ModulePtr(vMethTable.Module));
1340
1341 sos::MethodTable mt = (TADDR)dwStartAddr;
1342 table.WriteRow("Name:", mt.GetName());
1343
1344 WCHAR fileName[MAX_LONGPATH];
1345 FileNameForModule(TO_TADDR(vMethTable.Module), fileName);
1346 table.WriteRow("mdToken:", Pointer(vMethTable.cl));
1347 table.WriteRow("File:", fileName[0] ? fileName : W("Unknown Module"));
1348
1349 if (vMethTableCollectible.LoaderAllocatorObjectHandle != NULL)
1350 {
1351 TADDR loaderAllocator;
1352 if (SUCCEEDED(MOVE(loaderAllocator, vMethTableCollectible.LoaderAllocatorObjectHandle)))
1353 {
1354 table.WriteRow("LoaderAllocator:", ObjectPtr(loaderAllocator));
1355 }
1356 }
1357
1358 table.WriteRow("BaseSize:", PrefixHex(vMethTable.BaseSize));
1359 table.WriteRow("ComponentSize:", PrefixHex(vMethTable.ComponentSize));
1360 table.WriteRow("Slots in VTable:", Decimal(vMethTable.wNumMethods));
1361
1362 table.SetColWidth(0, 29);
1363 table.WriteRow("Number of IFaces in IFaceMap:", Decimal(vMethTable.wNumInterfaces));
1364
1365 if (bDumpMDTable)
1366 {
1367 table.ReInit(4, POINTERSIZE_HEX, AlignRight);
1368 table.SetColAlignment(3, AlignLeft);
1369 table.SetColWidth(2, 6);
1370
1371 Print("--------------------------------------\n");
1372 Print("MethodDesc Table\n");
1373
1374 table.WriteRow("Entry", "MethodDesc", "JIT", "Name");
1375
1376 for (DWORD n = 0; n < vMethTable.wNumMethods; n++)
1377 {
1378 JITTypes jitType;
1379 DWORD_PTR methodDesc=0;
1380 DWORD_PTR gcinfoAddr;
1381
1382 CLRDATA_ADDRESS entry;
1383 if (g_sos->GetMethodTableSlot(dwStartAddr, n, &entry) != S_OK)
1384 {
1385 PrintLn("<error getting slot ", Decimal(n), ">");
1386 continue;
1387 }
1388
1389 IP2MethodDesc((DWORD_PTR)entry, methodDesc, jitType, gcinfoAddr);
1390 table.WriteColumn(0, entry);
1391 table.WriteColumn(1, MethodDescPtr(methodDesc));
1392
1393 if (jitType == TYPE_UNKNOWN && methodDesc != NULL)
1394 {
1395 // We can get a more accurate jitType from NativeCodeAddr of the methoddesc,
1396 // because the methodtable entry hasn't always been patched.
1397 DacpMethodDescData tmpMethodDescData;
1398 if (tmpMethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
1399 {
1400 DacpCodeHeaderData codeHeaderData;
1401 if (codeHeaderData.Request(g_sos,tmpMethodDescData.NativeCodeAddr) == S_OK)
1402 {
1403 jitType = (JITTypes) codeHeaderData.JITType;
1404 }
1405 }
1406 }
1407
1408 const char *pszJitType = "NONE";
1409 if (jitType == TYPE_JIT)
1410 pszJitType = "JIT";
1411 else if (jitType == TYPE_PJIT)
1412 pszJitType = "PreJIT";
1413 else
1414 {
1415 DacpMethodDescData MethodDescData;
1416 if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
1417 {
1418 // Is it an fcall?
1419 if ((TO_TADDR(MethodDescData.NativeCodeAddr) >= TO_TADDR(moduleInfo[MSCORWKS].baseAddr)) &&
1420 ((TO_TADDR(MethodDescData.NativeCodeAddr) < TO_TADDR(moduleInfo[MSCORWKS].baseAddr + moduleInfo[MSCORWKS].size))))
1421 {
1422 pszJitType = "FCALL";
1423 }
1424 }
1425 }
1426
1427 table.WriteColumn(2, pszJitType);
1428
1429 NameForMD_s(methodDesc,g_mdName,mdNameLen);
1430 table.WriteColumn(3, g_mdName);
1431 }
1432 }
1433 return Status;
1434}
1435
1436extern size_t Align (size_t nbytes);
1437
1438HRESULT PrintVC(TADDR taMT, TADDR taObject, BOOL bPrintFields = TRUE)
1439{
1440 HRESULT Status;
1441 DacpMethodTableData mtabledata;
1442 if ((Status = mtabledata.Request(g_sos, TO_CDADDR(taMT)))!=S_OK)
1443 return Status;
1444
1445 size_t size = mtabledata.BaseSize;
1446 if ((Status=g_sos->GetMethodTableName(TO_CDADDR(taMT), mdNameLen, g_mdName, NULL))!=S_OK)
1447 return Status;
1448
1449 ExtOut("Name: %S\n", g_mdName);
1450 DMLOut("MethodTable: %s\n", DMLMethodTable(taMT));
1451 DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
1452 ExtOut("Size: %d(0x%x) bytes\n", size, size);
1453
1454 FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
1455 ExtOut("File: %S\n", g_mdName[0] ? g_mdName : W("Unknown Module"));
1456
1457 if (bPrintFields)
1458 {
1459 DacpMethodTableFieldData vMethodTableFields;
1460 if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(taMT)))!=S_OK)
1461 return Status;
1462
1463 ExtOut("Fields:\n");
1464
1465 if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1466 DisplayFields(TO_CDADDR(taMT), &mtabledata, &vMethodTableFields, taObject, TRUE, TRUE);
1467 }
1468
1469 return S_OK;
1470}
1471
1472void PrintRuntimeTypeInfo(TADDR p_rtObject, const DacpObjectData & rtObjectData)
1473{
1474 // Get the method table
1475 int iOffset = GetObjFieldOffset(TO_CDADDR(p_rtObject), rtObjectData.MethodTable, W("m_handle"));
1476 if (iOffset > 0)
1477 {
1478 TADDR mtPtr;
1479 if (SUCCEEDED(GetMTOfObject(p_rtObject + iOffset, &mtPtr)))
1480 {
1481 sos::MethodTable mt = mtPtr;
1482 ExtOut("Type Name: %S\n", mt.GetName());
1483 DMLOut("Type MT: %s\n", DMLMethodTable(mtPtr));
1484 }
1485 }
1486}
1487
1488HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE)
1489{
1490 if (!sos::IsObject(taObj, true))
1491 {
1492 ExtOut("<Note: this object has an invalid CLASS field>\n");
1493 }
1494
1495 DacpObjectData objData;
1496 HRESULT Status;
1497 if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
1498 {
1499 ExtOut("Invalid object\n");
1500 return Status;
1501 }
1502
1503 if (objData.ObjectType==OBJ_FREE)
1504 {
1505 ExtOut("Free Object\n");
1506 DWORD_PTR size = (DWORD_PTR)objData.Size;
1507 ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1508 return S_OK;
1509 }
1510
1511 sos::Object obj = taObj;
1512 ExtOut("Name: %S\n", obj.GetTypeName());
1513 DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
1514
1515
1516 DacpMethodTableData mtabledata;
1517 if ((Status=mtabledata.Request(g_sos,objData.MethodTable)) == S_OK)
1518 {
1519 DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
1520 }
1521 else
1522 {
1523 ExtOut("Invalid EEClass address\n");
1524 return Status;
1525 }
1526
1527 if (objData.RCW != NULL)
1528 {
1529 DMLOut("RCW: %s\n", DMLRCWrapper(objData.RCW));
1530 }
1531 if (objData.CCW != NULL)
1532 {
1533 DMLOut("CCW: %s\n", DMLCCWrapper(objData.CCW));
1534 }
1535
1536 DWORD_PTR size = (DWORD_PTR)objData.Size;
1537 ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1538
1539 if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType")) == 0)
1540 {
1541 PrintRuntimeTypeInfo(taObj, objData);
1542 }
1543
1544 if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType+RuntimeTypeCache")) == 0)
1545 {
1546 // Get the method table
1547 int iOffset = GetObjFieldOffset (TO_CDADDR(taObj), objData.MethodTable, W("m_runtimeType"));
1548 if (iOffset > 0)
1549 {
1550 TADDR rtPtr;
1551 if (MOVE(rtPtr, taObj + iOffset) == S_OK)
1552 {
1553 DacpObjectData rtObjectData;
1554 if ((Status=rtObjectData.Request(g_sos, TO_CDADDR(rtPtr))) != S_OK)
1555 {
1556 ExtOut("Error when reading RuntimeType field\n");
1557 return Status;
1558 }
1559
1560 PrintRuntimeTypeInfo(rtPtr, rtObjectData);
1561 }
1562 }
1563 }
1564
1565 if (objData.ObjectType==OBJ_ARRAY)
1566 {
1567 ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s",
1568 objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
1569
1570 IfDMLOut(" (<exec cmd=\"!DumpArray /d %p\">Print Array</exec>)", SOS_PTR(taObj));
1571 ExtOut("\n");
1572
1573 if (objData.ElementType == ELEMENT_TYPE_I1 ||
1574 objData.ElementType == ELEMENT_TYPE_U1 ||
1575 objData.ElementType == ELEMENT_TYPE_CHAR)
1576 {
1577 bool wide = objData.ElementType == ELEMENT_TYPE_CHAR;
1578
1579 // Get the size of the character array, but clamp it to a reasonable length.
1580 TADDR pos = taObj + (2 * sizeof(DWORD_PTR));
1581 DWORD_PTR num;
1582 moveN(num, taObj + sizeof(DWORD_PTR));
1583
1584 if (IsDMLEnabled())
1585 DMLOut("<exec cmd=\"%s %x L%x\">Content</exec>: ", (wide) ? "dw" : "db", pos, num);
1586 else
1587 ExtOut("Content: ");
1588 CharArrayContent(pos, (ULONG)(num <= 128 ? num : 128), wide);
1589 ExtOut("\n");
1590 }
1591 }
1592 else
1593 {
1594 FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
1595 ExtOut("File: %S\n", g_mdName[0] ? g_mdName : W("Unknown Module"));
1596 }
1597
1598 if (objData.ObjectType == OBJ_STRING)
1599 {
1600 ExtOut("String: ");
1601 StringObjectContent(taObj);
1602 ExtOut("\n");
1603 }
1604 else if (objData.ObjectType == OBJ_OBJECT)
1605 {
1606 ExtOut("Object\n");
1607 }
1608
1609 if (bPrintFields)
1610 {
1611 DacpMethodTableFieldData vMethodTableFields;
1612 if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(objData.MethodTable)))!=S_OK)
1613 return Status;
1614
1615 ExtOut("Fields:\n");
1616 if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1617 {
1618 DisplayFields(objData.MethodTable, &mtabledata, &vMethodTableFields, taObj, TRUE, FALSE);
1619 }
1620 else
1621 {
1622 ExtOut("None\n");
1623 }
1624 }
1625
1626 sos::ThinLockInfo lockInfo;
1627 if (obj.GetThinLock(lockInfo))
1628 {
1629 ExtOut("ThinLock owner %x (%p), Recursive %x\n", lockInfo.ThreadId,
1630 SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
1631 }
1632
1633 return S_OK;
1634}
1635
1636BOOL IndicesInRange (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
1637{
1638 int i = 0;
1639 if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
1640 {
1641 ExtOut("<integer underflow>\n");
1642 return FALSE;
1643 }
1644
1645 for (; i >= 0; i--)
1646 {
1647 if (indices[i] >= bounds[i] + lowerBounds[i])
1648 {
1649 if (i == 0)
1650 {
1651 return FALSE;
1652 }
1653
1654 indices[i] = lowerBounds[i];
1655 indices[i - 1]++;
1656 }
1657 }
1658
1659 return TRUE;
1660}
1661
1662void ExtOutIndices (DWORD * indices, DWORD rank)
1663{
1664 for (DWORD i = 0; i < rank; i++)
1665 {
1666 ExtOut("[%d]", indices[i]);
1667 }
1668}
1669
1670size_t OffsetFromIndices (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
1671{
1672 _ASSERTE(rank >= 0);
1673 size_t multiplier = 1;
1674 size_t offset = 0;
1675 int i = 0;
1676 if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
1677 {
1678 ExtOut("<integer underflow>\n");
1679 return 0;
1680 }
1681
1682 for (; i >= 0; i--)
1683 {
1684 DWORD curIndex = indices[i] - lowerBounds[i];
1685 offset += curIndex * multiplier;
1686 multiplier *= bounds[i];
1687 }
1688
1689 return offset;
1690}
1691HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint);
1692#ifdef _DEBUG
1693HRESULT PrintPermissionSet (TADDR p_PermSet)
1694{
1695 HRESULT Status = S_OK;
1696
1697 DacpObjectData PermSetData;
1698 if ((Status=PermSetData.Request(g_sos, TO_CDADDR(p_PermSet))) != S_OK)
1699 {
1700 ExtOut("Invalid object\n");
1701 return Status;
1702 }
1703
1704
1705 sos::MethodTable mt = TO_TADDR(PermSetData.MethodTable);
1706 if (_wcscmp (W("System.Security.PermissionSet"), mt.GetName()) != 0 && _wcscmp(W("System.Security.NamedPermissionSet"), mt.GetName()) != 0)
1707 {
1708 ExtOut("Invalid PermissionSet object\n");
1709 return S_FALSE;
1710 }
1711
1712 ExtOut("PermissionSet object: %p\n", SOS_PTR(p_PermSet));
1713
1714 // Print basic info
1715
1716 // Walk the fields, printing some fields in a special way.
1717
1718 int iOffset = GetObjFieldOffset (TO_CDADDR(p_PermSet), PermSetData.MethodTable, W("m_Unrestricted"));
1719
1720 if (iOffset > 0)
1721 {
1722 BYTE unrestricted;
1723 MOVE(unrestricted, p_PermSet + iOffset);
1724 if (unrestricted)
1725 ExtOut("Unrestricted: TRUE\n");
1726 else
1727 ExtOut("Unrestricted: FALSE\n");
1728 }
1729
1730 iOffset = GetObjFieldOffset (TO_CDADDR(p_PermSet), PermSetData.MethodTable, W("m_permSet"));
1731 if (iOffset > 0)
1732 {
1733 TADDR tbSetPtr;
1734 MOVE(tbSetPtr, p_PermSet + iOffset);
1735 if (tbSetPtr != NULL)
1736 {
1737 DacpObjectData tbSetData;
1738 if ((Status=tbSetData.Request(g_sos, TO_CDADDR(tbSetPtr))) != S_OK)
1739 {
1740 ExtOut("Invalid object\n");
1741 return Status;
1742 }
1743
1744 iOffset = GetObjFieldOffset (TO_CDADDR(tbSetPtr), tbSetData.MethodTable, W("m_Set"));
1745 if (iOffset > 0)
1746 {
1747 DWORD_PTR PermsArrayPtr;
1748 MOVE(PermsArrayPtr, tbSetPtr + iOffset);
1749 if (PermsArrayPtr != NULL)
1750 {
1751 // Print all the permissions in the array
1752 DacpObjectData objData;
1753 if ((Status=objData.Request(g_sos, TO_CDADDR(PermsArrayPtr))) != S_OK)
1754 {
1755 ExtOut("Invalid object\n");
1756 return Status;
1757 }
1758 DumpArrayFlags flags;
1759 flags.bDetail = TRUE;
1760 return PrintArray(objData, flags, TRUE);
1761 }
1762 }
1763
1764 iOffset = GetObjFieldOffset (TO_CDADDR(tbSetPtr), tbSetData.MethodTable, W("m_Obj"));
1765 if (iOffset > 0)
1766 {
1767 DWORD_PTR PermObjPtr;
1768 MOVE(PermObjPtr, tbSetPtr + iOffset);
1769 if (PermObjPtr != NULL)
1770 {
1771 // Print the permission object
1772 return PrintObj(PermObjPtr);
1773 }
1774 }
1775
1776
1777 }
1778 }
1779 return Status;
1780}
1781
1782#endif // _DEBUG
1783
1784/**********************************************************************\
1785* Routine Description: *
1786* *
1787* This function is called to dump the contents of an object from a *
1788* given address
1789* *
1790\**********************************************************************/
1791DECLARE_API(DumpArray)
1792{
1793 INIT_API();
1794
1795 DumpArrayFlags flags;
1796
1797 MINIDUMP_NOT_SUPPORTED();
1798
1799 BOOL dml = FALSE;
1800
1801 CMDOption option[] =
1802 { // name, vptr, type, hasValue
1803 {"-start", &flags.startIndex, COSIZE_T, TRUE},
1804 {"-length", &flags.Length, COSIZE_T, TRUE},
1805 {"-details", &flags.bDetail, COBOOL, FALSE},
1806 {"-nofields", &flags.bNoFieldsForElement, COBOOL, FALSE},
1807#ifndef FEATURE_PAL
1808 {"/d", &dml, COBOOL, FALSE},
1809#endif
1810 };
1811 CMDValue arg[] =
1812 { // vptr, type
1813 {&flags.strObject, COSTRING}
1814 };
1815 size_t nArg;
1816 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
1817 {
1818 return Status;
1819 }
1820
1821 EnableDMLHolder dmlHolder(dml);
1822 DWORD_PTR p_Object = GetExpression (flags.strObject);
1823 if (p_Object == 0)
1824 {
1825 ExtOut("Invalid parameter %s\n", flags.strObject);
1826 return Status;
1827 }
1828
1829 if (!sos::IsObject(p_Object, true))
1830 {
1831 ExtOut("<Note: this object has an invalid CLASS field>\n");
1832 }
1833
1834 DacpObjectData objData;
1835 if ((Status=objData.Request(g_sos, TO_CDADDR(p_Object))) != S_OK)
1836 {
1837 ExtOut("Invalid object\n");
1838 return Status;
1839 }
1840
1841 if (objData.ObjectType != OBJ_ARRAY)
1842 {
1843 ExtOut("Not an array, please use !DumpObj instead\n");
1844 return S_OK;
1845 }
1846 return PrintArray(objData, flags, FALSE);
1847}
1848
1849
1850HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint)
1851{
1852 HRESULT Status = S_OK;
1853
1854 if (objData.dwRank != 1 && (flags.Length != (DWORD_PTR)-1 ||flags.startIndex != 0))
1855 {
1856 ExtOut("For multi-dimension array, length and start index are supported\n");
1857 return S_OK;
1858 }
1859
1860 if (flags.startIndex > objData.dwNumComponents)
1861 {
1862 ExtOut("Start index out of range\n");
1863 return S_OK;
1864 }
1865
1866 if (!flags.bDetail && flags.bNoFieldsForElement)
1867 {
1868 ExtOut("-nofields has no effect unless -details is specified\n");
1869 }
1870
1871 DWORD i;
1872 if (!isPermSetPrint)
1873 {
1874 // TODO: don't depend on this being a MethodTable
1875 NameForMT_s(TO_TADDR(objData.ElementTypeHandle), g_mdName, mdNameLen);
1876
1877 ExtOut("Name: %S[", g_mdName);
1878 for (i = 1; i < objData.dwRank; i++)
1879 ExtOut(",");
1880 ExtOut("]\n");
1881
1882 DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
1883
1884 {
1885 DacpMethodTableData mtdata;
1886 if (SUCCEEDED(mtdata.Request(g_sos, objData.MethodTable)))
1887 {
1888 DMLOut("EEClass: %s\n", DMLClass(mtdata.Class));
1889 }
1890 }
1891
1892 DWORD_PTR size = (DWORD_PTR)objData.Size;
1893 ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1894
1895 ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s\n",
1896 objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
1897 DMLOut("Element Methodtable: %s\n", DMLMethodTable(objData.ElementTypeHandle));
1898 }
1899
1900 BOOL isElementValueType = IsElementValueType(objData.ElementType);
1901
1902 DWORD dwRankAllocSize;
1903 if (!ClrSafeInt<DWORD>::multiply(sizeof(DWORD), objData.dwRank, dwRankAllocSize))
1904 {
1905 ExtOut("Integer overflow on array rank\n");
1906 return Status;
1907 }
1908
1909 DWORD *lowerBounds = (DWORD *)alloca(dwRankAllocSize);
1910 if (!SafeReadMemory(objData.ArrayLowerBoundsPtr, lowerBounds, dwRankAllocSize, NULL))
1911 {
1912 ExtOut("Failed to read lower bounds info from the array\n");
1913 return S_OK;
1914 }
1915
1916 DWORD *bounds = (DWORD *)alloca(dwRankAllocSize);
1917 if (!SafeReadMemory (objData.ArrayBoundsPtr, bounds, dwRankAllocSize, NULL))
1918 {
1919 ExtOut("Failed to read bounds info from the array\n");
1920 return S_OK;
1921 }
1922
1923 //length is only supported for single-dimension array
1924 if (objData.dwRank == 1 && flags.Length != (DWORD_PTR)-1)
1925 {
1926 bounds[0] = _min(bounds[0], (DWORD)(flags.Length + flags.startIndex) - lowerBounds[0]);
1927 }
1928
1929 DWORD *indices = (DWORD *)alloca(dwRankAllocSize);
1930 for (i = 0; i < objData.dwRank; i++)
1931 {
1932 indices[i] = lowerBounds[i];
1933 }
1934
1935 //start index is only supported for single-dimension array
1936 if (objData.dwRank == 1)
1937 {
1938 indices[0] = (DWORD)flags.startIndex;
1939 }
1940
1941 //Offset should be calculated by OffsetFromIndices. However because of the way
1942 //how we grow indices, incrementing offset by one happens to match indices in every iteration
1943 for (size_t offset = OffsetFromIndices (indices, lowerBounds, bounds, objData.dwRank);
1944 IndicesInRange (indices, lowerBounds, bounds, objData.dwRank);
1945 indices[objData.dwRank - 1]++, offset++)
1946 {
1947 if (IsInterrupt())
1948 {
1949 ExtOut("interrupted by user\n");
1950 break;
1951 }
1952
1953 TADDR elementAddress = TO_TADDR(objData.ArrayDataPtr + offset * objData.dwComponentSize);
1954 TADDR p_Element = NULL;
1955 if (isElementValueType)
1956 {
1957 p_Element = elementAddress;
1958 }
1959 else if (!SafeReadMemory (elementAddress, &p_Element, sizeof (p_Element), NULL))
1960 {
1961 ExtOut("Failed to read element at ");
1962 ExtOutIndices(indices, objData.dwRank);
1963 ExtOut("\n");
1964 continue;
1965 }
1966
1967 if (p_Element)
1968 {
1969 ExtOutIndices(indices, objData.dwRank);
1970
1971 if (isElementValueType)
1972 {
1973 DMLOut( " %s\n", DMLValueClass(objData.ElementTypeHandle, p_Element));
1974 }
1975 else
1976 {
1977 DMLOut(" %s\n", DMLObject(p_Element));
1978 }
1979 }
1980 else if (!isPermSetPrint)
1981 {
1982 ExtOutIndices(indices, objData.dwRank);
1983 ExtOut(" null\n");
1984 }
1985
1986 if (flags.bDetail)
1987 {
1988 IncrementIndent();
1989 if (isElementValueType)
1990 {
1991 PrintVC(TO_TADDR(objData.ElementTypeHandle), elementAddress, !flags.bNoFieldsForElement);
1992 }
1993 else if (p_Element != NULL)
1994 {
1995 PrintObj(p_Element, !flags.bNoFieldsForElement);
1996 }
1997 DecrementIndent();
1998 }
1999 }
2000
2001 return S_OK;
2002}
2003
2004/**********************************************************************\
2005* Routine Description: *
2006* *
2007* This function is called to dump the contents of an object from a *
2008* given address
2009* *
2010\**********************************************************************/
2011DECLARE_API(DumpObj)
2012{
2013 INIT_API();
2014
2015 MINIDUMP_NOT_SUPPORTED();
2016
2017 BOOL dml = FALSE;
2018 BOOL bNoFields = FALSE;
2019 BOOL bRefs = FALSE;
2020 StringHolder str_Object;
2021 CMDOption option[] =
2022 { // name, vptr, type, hasValue
2023 {"-nofields", &bNoFields, COBOOL, FALSE},
2024 {"-refs", &bRefs, COBOOL, FALSE},
2025#ifndef FEATURE_PAL
2026 {"/d", &dml, COBOOL, FALSE},
2027#endif
2028 };
2029 CMDValue arg[] =
2030 { // vptr, type
2031 {&str_Object.data, COSTRING}
2032 };
2033 size_t nArg;
2034 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2035 {
2036 return Status;
2037 }
2038
2039 DWORD_PTR p_Object = GetExpression(str_Object.data);
2040 EnableDMLHolder dmlHolder(dml);
2041 if (p_Object == 0)
2042 {
2043 ExtOut("Invalid parameter %s\n", args);
2044 return Status;
2045 }
2046
2047 Status = PrintObj(p_Object, !bNoFields);
2048
2049 if (SUCCEEDED(Status) && bRefs)
2050 {
2051 ExtOut("GC Refs:\n");
2052 TableOutput out(2, POINTERSIZE_HEX, AlignRight, 4);
2053 out.WriteRow("offset", "object");
2054 for (sos::RefIterator itr(TO_TADDR(p_Object)); itr; ++itr)
2055 out.WriteRow(Hex(itr.GetOffset()), ObjectPtr(*itr));
2056 }
2057
2058 return Status;
2059}
2060
2061/**********************************************************************\
2062* Routine Description: *
2063* *
2064* This function is called to dump the contents of a delegate from a *
2065* given address. *
2066* *
2067\**********************************************************************/
2068
2069DECLARE_API(DumpDelegate)
2070{
2071 INIT_API();
2072 MINIDUMP_NOT_SUPPORTED();
2073
2074 try
2075 {
2076 BOOL dml = FALSE;
2077 DWORD_PTR dwAddr = 0;
2078
2079 CMDOption option[] =
2080 { // name, vptr, type, hasValue
2081 {"/d", &dml, COBOOL, FALSE}
2082 };
2083 CMDValue arg[] =
2084 { // vptr, type
2085 {&dwAddr, COHEX}
2086 };
2087 size_t nArg;
2088 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2089 {
2090 return Status;
2091 }
2092 if (nArg != 1)
2093 {
2094 ExtOut("Usage: !DumpDelegate <delegate object address>\n");
2095 return Status;
2096 }
2097
2098 EnableDMLHolder dmlHolder(dml);
2099 CLRDATA_ADDRESS delegateAddr = TO_CDADDR(dwAddr);
2100
2101 if (!sos::IsObject(delegateAddr))
2102 {
2103 ExtOut("Invalid object.\n");
2104 }
2105 else
2106 {
2107 sos::Object delegateObj = TO_TADDR(delegateAddr);
2108 if (!IsDerivedFrom(TO_CDADDR(delegateObj.GetMT()), W("System.Delegate")))
2109 {
2110 ExtOut("Object of type '%S' is not a delegate.", delegateObj.GetTypeName());
2111 }
2112 else
2113 {
2114 ExtOut("Target Method Name\n");
2115
2116 std::vector<CLRDATA_ADDRESS> delegatesRemaining;
2117 delegatesRemaining.push_back(delegateAddr);
2118 while (delegatesRemaining.size() > 0)
2119 {
2120 delegateAddr = delegatesRemaining.back();
2121 delegatesRemaining.pop_back();
2122 delegateObj = TO_TADDR(delegateAddr);
2123
2124 int offset;
2125 if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_target"))) != 0)
2126 {
2127 CLRDATA_ADDRESS target;
2128 MOVE(target, delegateObj.GetAddress() + offset);
2129
2130 if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_invocationList"))) != 0)
2131 {
2132 CLRDATA_ADDRESS invocationList;
2133 MOVE(invocationList, delegateObj.GetAddress() + offset);
2134
2135 if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_invocationCount"))) != 0)
2136 {
2137 int invocationCount;
2138 MOVE(invocationCount, delegateObj.GetAddress() + offset);
2139
2140 if (invocationList == NULL)
2141 {
2142 CLRDATA_ADDRESS md;
2143 DMLOut("%s ", DMLObject(target));
2144 if (TryGetMethodDescriptorForDelegate(delegateAddr, &md))
2145 {
2146 DMLOut("%s ", DMLMethodDesc(md));
2147 NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
2148 ExtOut("%S\n", g_mdName);
2149 }
2150 else
2151 {
2152 ExtOut("(unknown)\n");
2153 }
2154 }
2155 else if (sos::IsObject(invocationList, false))
2156 {
2157 DacpObjectData objData;
2158 if (objData.Request(g_sos, invocationList) == S_OK &&
2159 objData.ObjectType == OBJ_ARRAY &&
2160 invocationCount <= objData.dwNumComponents)
2161 {
2162 for (int i = 0; i < invocationCount; i++)
2163 {
2164 CLRDATA_ADDRESS elementPtr;
2165 MOVE(elementPtr, TO_CDADDR(objData.ArrayDataPtr + (i * objData.dwComponentSize)));
2166 if (elementPtr != NULL && sos::IsObject(elementPtr, false))
2167 {
2168 delegatesRemaining.push_back(elementPtr);
2169 }
2170 }
2171 }
2172 }
2173 }
2174 }
2175 }
2176 }
2177 }
2178 }
2179
2180 return S_OK;
2181 }
2182 catch (const sos::Exception &e)
2183 {
2184 ExtOut("%s\n", e.what());
2185 return E_FAIL;
2186 }
2187}
2188
2189CLRDATA_ADDRESS isExceptionObj(CLRDATA_ADDRESS mtObj)
2190{
2191 // We want to follow back until we get the mt for System.Exception
2192 DacpMethodTableData dmtd;
2193 CLRDATA_ADDRESS walkMT = mtObj;
2194 while(walkMT != NULL)
2195 {
2196 if (dmtd.Request(g_sos, walkMT) != S_OK)
2197 {
2198 break;
2199 }
2200 if (walkMT == g_special_usefulGlobals.ExceptionMethodTable)
2201 {
2202 return walkMT;
2203 }
2204 walkMT = dmtd.ParentMethodTable;
2205 }
2206 return NULL;
2207}
2208
2209CLRDATA_ADDRESS isSecurityExceptionObj(CLRDATA_ADDRESS mtObj)
2210{
2211 // We want to follow back until we get the mt for System.Exception
2212 DacpMethodTableData dmtd;
2213 CLRDATA_ADDRESS walkMT = mtObj;
2214 while(walkMT != NULL)
2215 {
2216 if (dmtd.Request(g_sos, walkMT) != S_OK)
2217 {
2218 break;
2219 }
2220 NameForMT_s(TO_TADDR(walkMT), g_mdName, mdNameLen);
2221 if (_wcscmp(W("System.Security.SecurityException"), g_mdName) == 0)
2222 {
2223 return walkMT;
2224 }
2225 walkMT = dmtd.ParentMethodTable;
2226 }
2227 return NULL;
2228}
2229
2230// Fill the passed in buffer with a text header for generated exception information.
2231// Returns the number of characters in the wszBuffer array on exit.
2232// If NULL is passed for wszBuffer, just returns the number of characters needed.
2233size_t AddExceptionHeader (__out_ecount_opt(bufferLength) WCHAR *wszBuffer, size_t bufferLength)
2234{
2235#ifdef _TARGET_WIN64_
2236 const WCHAR *wszHeader = W(" SP IP Function\n");
2237#else
2238 const WCHAR *wszHeader = W(" SP IP Function\n");
2239#endif // _TARGET_WIN64_
2240 if (wszBuffer)
2241 {
2242 swprintf_s(wszBuffer, bufferLength, wszHeader);
2243 }
2244 return _wcslen(wszHeader);
2245}
2246
2247struct StackTraceElement
2248{
2249 UINT_PTR ip;
2250 UINT_PTR sp;
2251 DWORD_PTR pFunc; // MethodDesc
2252 // TRUE if this element represents the last frame of the foreign
2253 // exception stack trace.
2254 BOOL fIsLastFrameFromForeignStackTrace;
2255
2256};
2257
2258#include "sos_stacktrace.h"
2259
2260class StringOutput
2261{
2262public:
2263 CQuickString cs;
2264 StringOutput()
2265 {
2266 cs.Alloc(1024);
2267 cs.String()[0] = L'\0';
2268 }
2269
2270 BOOL Append(__in_z LPCWSTR pszStr)
2271 {
2272 size_t iInputLen = _wcslen (pszStr);
2273 size_t iCurLen = _wcslen (cs.String());
2274 if ((iCurLen + iInputLen + 1) > cs.Size())
2275 {
2276 if (cs.ReSize(iCurLen + iInputLen + 1) != S_OK)
2277 {
2278 return FALSE;
2279 }
2280 }
2281
2282 wcsncat_s (cs.String(), cs.Size(), pszStr, _TRUNCATE);
2283 return TRUE;
2284 }
2285
2286 size_t Length()
2287 {
2288 return _wcslen(cs.String());
2289 }
2290
2291 WCHAR *String()
2292 {
2293 return cs.String();
2294 }
2295};
2296
2297static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp, ULONG64 IPAddr, StringOutput& so);
2298
2299// Using heuristics to determine if an exception object represented an async (hardware) or a
2300// managed exception
2301// We need to use these heuristics when the System.Exception object is not the active exception
2302// on some thread, but it's something found somewhere on the managed heap.
2303
2304// uses the MapWin32FaultToCOMPlusException to figure out how we map async exceptions
2305// to managed exceptions and their HRESULTs
2306static const HRESULT AsyncHResultValues[] =
2307{
2308 COR_E_ARITHMETIC, // kArithmeticException
2309 COR_E_OVERFLOW, // kOverflowException
2310 COR_E_DIVIDEBYZERO, // kDivideByZeroException
2311 COR_E_FORMAT, // kFormatException
2312 COR_E_NULLREFERENCE, // kNullReferenceException
2313 E_POINTER, // kAccessViolationException
2314 // the EE is raising the next exceptions more often than the OS will raise an async
2315 // exception for these conditions, so in general treat these as Synchronous
2316 // COR_E_INDEXOUTOFRANGE, // kIndexOutOfRangeException
2317 // COR_E_OUTOFMEMORY, // kOutOfMemoryException
2318 // COR_E_STACKOVERFLOW, // kStackOverflowException
2319 COR_E_DATAMISALIGNED, // kDataMisalignedException
2320
2321};
2322BOOL IsAsyncException(CLRDATA_ADDRESS taObj, CLRDATA_ADDRESS mtObj)
2323{
2324 // by default we'll treat exceptions as synchronous
2325 UINT32 xcode = EXCEPTION_COMPLUS;
2326 int iOffset = GetObjFieldOffset (taObj, mtObj, W("_xcode"));
2327 if (iOffset > 0)
2328 {
2329 HRESULT hr = MOVE(xcode, taObj + iOffset);
2330 if (hr != S_OK)
2331 {
2332 xcode = EXCEPTION_COMPLUS;
2333 goto Done;
2334 }
2335 }
2336
2337 if (xcode == EXCEPTION_COMPLUS)
2338 {
2339 HRESULT ehr = 0;
2340 iOffset = GetObjFieldOffset (taObj, mtObj, W("_HResult"));
2341 if (iOffset > 0)
2342 {
2343 HRESULT hr = MOVE(ehr, taObj + iOffset);
2344 if (hr != S_OK)
2345 {
2346 xcode = EXCEPTION_COMPLUS;
2347 goto Done;
2348 }
2349 for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
2350 {
2351 if (ehr == AsyncHResultValues[idx])
2352 {
2353 xcode = ehr;
2354 break;
2355 }
2356 }
2357 }
2358 }
2359Done:
2360 return xcode != EXCEPTION_COMPLUS;
2361}
2362
2363// Overload that mirrors the code above when the ExceptionObjectData was already retrieved from LS
2364BOOL IsAsyncException(const DacpExceptionObjectData & excData)
2365{
2366 if (excData.XCode != EXCEPTION_COMPLUS)
2367 return TRUE;
2368
2369 HRESULT ehr = excData.HResult;
2370 for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
2371 {
2372 if (ehr == AsyncHResultValues[idx])
2373 {
2374 return TRUE;
2375 }
2376 }
2377
2378 return FALSE;
2379}
2380
2381
2382#define SOS_STACKTRACE_SHOWEXPLICITFRAMES 0x00000002
2383size_t FormatGeneratedException (DWORD_PTR dataPtr,
2384 UINT bytes,
2385 __out_ecount_opt(bufferLength) WCHAR *wszBuffer,
2386 size_t bufferLength,
2387 BOOL bAsync,
2388 BOOL bNestedCase = FALSE,
2389 BOOL bLineNumbers = FALSE)
2390{
2391 UINT count = bytes / sizeof(StackTraceElement);
2392 size_t Length = 0;
2393
2394 if (wszBuffer && bufferLength > 0)
2395 {
2396 wszBuffer[0] = L'\0';
2397 }
2398
2399 // Buffer is calculated for sprintf below (" %p %p %S\n");
2400 WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2 + MAX_LONGPATH + 8];
2401
2402 if (count == 0)
2403 {
2404 return 0;
2405 }
2406
2407 if (bNestedCase)
2408 {
2409 // If we are computing the call stack for a nested exception, we
2410 // don't want to print the last frame, because the outer exception
2411 // will have that frame.
2412 count--;
2413 }
2414
2415 for (UINT i = 0; i < count; i++)
2416 {
2417 StackTraceElement ste;
2418 MOVE (ste, dataPtr + i*sizeof(StackTraceElement));
2419
2420 // ste.ip must be adjusted because of an ancient workaround in the exception
2421 // infrastructure. The workaround is that the exception needs to have
2422 // an ip address that will map to the line number where the exception was thrown.
2423 // (It doesn't matter that it's not a valid instruction). (see /vm/excep.cpp)
2424 //
2425 // This "counterhack" is not 100% accurate
2426 // The biggest issue is that !PrintException must work with exception objects
2427 // that may not be currently active; as a consequence we cannot rely on the
2428 // state of some "current thread" to infer whether the IP values stored in
2429 // the exception object have been adjusted or not. If we could, we may examine
2430 // the topmost "Frame" and make the decision based on whether it's a
2431 // FaultingExceptionFrame or not.
2432 // 1. On IA64 the IP values are never adjusted by the EE so there's nothing
2433 // to adjust back.
2434 // 2. On AMD64:
2435 // (a) if the exception was an async (hardware) exception add 1 to all
2436 // IP values in the exception object
2437 // (b) if the exception was a managed exception (either raised by the
2438 // EE or thrown by managed code) do not adjust any IP values
2439 // 3. On X86:
2440 // (a) if the exception was an async (hardware) exception add 1 to
2441 // all but the topmost IP value in the exception object
2442 // (b) if the exception was a managed exception (either raised by
2443 // the EE or thrown by managed code) add 1 to all IP values in
2444 // the exception object
2445#if defined(_TARGET_AMD64_)
2446 if (bAsync)
2447 {
2448 ste.ip += 1;
2449 }
2450#elif defined(_TARGET_X86_)
2451 if (IsDbgTargetX86() && (!bAsync || i != 0))
2452 {
2453 ste.ip += 1;
2454 }
2455#endif // defined(_TARGET_AMD64_) || defined(_TARGET__X86_)
2456
2457 StringOutput so;
2458 HRESULT Status = DumpMDInfoBuffer(ste.pFunc, SOS_STACKTRACE_SHOWADDRESSES|SOS_STACKTRACE_SHOWEXPLICITFRAMES, ste.sp, ste.ip, so);
2459
2460 // If DumpMDInfoBuffer failed (due to out of memory or missing metadata),
2461 // or did not update so (when ste is an explicit frames), do not update wszBuffer
2462 if (Status == S_OK)
2463 {
2464 WCHAR filename[MAX_LONGPATH] = W("");
2465 ULONG linenum = 0;
2466 if (bLineNumbers &&
2467 SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, _countof(filename))))
2468 {
2469 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s [%s @ %d]\n"), so.String(), filename, linenum);
2470 }
2471 else
2472 {
2473 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s\n"), so.String());
2474 }
2475
2476 Length += _wcslen(wszLineBuffer);
2477
2478 if (wszBuffer)
2479 {
2480 wcsncat_s(wszBuffer, bufferLength, wszLineBuffer, _TRUNCATE);
2481 }
2482 }
2483 }
2484
2485 return Length;
2486}
2487
2488// ExtOut has an internal limit for the string size
2489void SosExtOutLargeString(__inout_z __inout_ecount_opt(len) WCHAR * pwszLargeString, size_t len)
2490{
2491 const size_t chunkLen = 2048;
2492
2493 WCHAR *pwsz = pwszLargeString; // beginning of a chunk
2494 size_t count = len/chunkLen;
2495 // write full chunks
2496 for (size_t idx = 0; idx < count; ++idx)
2497 {
2498 WCHAR *pch = pwsz + chunkLen; // after the chunk
2499 // zero terminate the chunk
2500 WCHAR ch = *pch;
2501 *pch = L'\0';
2502
2503 ExtOut("%S", pwsz);
2504
2505 // restore whacked char
2506 *pch = ch;
2507
2508 // advance to next chunk
2509 pwsz += chunkLen;
2510 }
2511
2512 // last chunk
2513 ExtOut("%S", pwsz);
2514}
2515
2516HRESULT FormatException(CLRDATA_ADDRESS taObj, BOOL bLineNumbers = FALSE)
2517{
2518 HRESULT Status = S_OK;
2519
2520 DacpObjectData objData;
2521 if ((Status=objData.Request(g_sos, taObj)) != S_OK)
2522 {
2523 ExtOut("Invalid object\n");
2524 return Status;
2525 }
2526
2527 // Make sure it is an exception object, and get the MT of Exception
2528 CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
2529 if (exceptionMT == NULL)
2530 {
2531 ExtOut("Not a valid exception object\n");
2532 return Status;
2533 }
2534
2535 DMLOut("Exception object: %s\n", DMLObject(taObj));
2536
2537 if (NameForMT_s(TO_TADDR(objData.MethodTable), g_mdName, mdNameLen))
2538 {
2539 ExtOut("Exception type: %S\n", g_mdName);
2540 }
2541 else
2542 {
2543 ExtOut("Exception type: <Unknown>\n");
2544 }
2545
2546 // Print basic info
2547
2548 // First try to get exception object data using ISOSDacInterface2
2549 DacpExceptionObjectData excData;
2550 BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, taObj));
2551
2552 // Walk the fields, printing some fields in a special way.
2553 // HR, InnerException, Message, StackTrace, StackTraceString
2554
2555 {
2556 TADDR taMsg = 0;
2557 if (bGotExcData)
2558 {
2559 taMsg = TO_TADDR(excData.Message);
2560 }
2561 else
2562 {
2563 int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_message"));
2564 if (iOffset > 0)
2565 {
2566 MOVE (taMsg, taObj + iOffset);
2567 }
2568 }
2569
2570 ExtOut("Message: ");
2571
2572 if (taMsg)
2573 StringObjectContent(taMsg);
2574 else
2575 ExtOut("<none>");
2576
2577 ExtOut("\n");
2578 }
2579
2580 {
2581 TADDR taInnerExc = 0;
2582 if (bGotExcData)
2583 {
2584 taInnerExc = TO_TADDR(excData.InnerException);
2585 }
2586 else
2587 {
2588 int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_innerException"));
2589 if (iOffset > 0)
2590 {
2591 MOVE (taInnerExc, taObj + iOffset);
2592 }
2593 }
2594
2595 ExtOut("InnerException: ");
2596 if (taInnerExc)
2597 {
2598 TADDR taMT;
2599 if (SUCCEEDED(GetMTOfObject(taInnerExc, &taMT)))
2600 {
2601 NameForMT_s(taMT, g_mdName, mdNameLen);
2602 ExtOut("%S, ", g_mdName);
2603 if (IsDMLEnabled())
2604 DMLOut("Use <exec cmd=\"!PrintException /d %p\">!PrintException %p</exec> to see more.\n", taInnerExc, taInnerExc);
2605 else
2606 ExtOut("Use !PrintException %p to see more.\n", SOS_PTR(taInnerExc));
2607 }
2608 else
2609 {
2610 ExtOut("<invalid MethodTable of inner exception>");
2611 }
2612 }
2613 else
2614 {
2615 ExtOut("<none>\n");
2616 }
2617 }
2618
2619 BOOL bAsync = bGotExcData ? IsAsyncException(excData)
2620 : IsAsyncException(taObj, objData.MethodTable);
2621
2622 {
2623 TADDR taStackTrace = 0;
2624 if (bGotExcData)
2625 {
2626 taStackTrace = TO_TADDR(excData.StackTrace);
2627 }
2628 else
2629 {
2630 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTrace"));
2631 if (iOffset > 0)
2632 {
2633 MOVE(taStackTrace, taObj + iOffset);
2634 }
2635 }
2636
2637 ExtOut("StackTrace (generated):\n");
2638 if (taStackTrace)
2639 {
2640 DWORD arrayLen;
2641 HRESULT hr = MOVE(arrayLen, taStackTrace + sizeof(DWORD_PTR));
2642
2643 if (arrayLen != 0 && hr == S_OK)
2644 {
2645#ifdef _TARGET_WIN64_
2646 DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
2647#else
2648 DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD);
2649#endif // _TARGET_WIN64_
2650 size_t stackTraceSize = 0;
2651 MOVE (stackTraceSize, dataPtr);
2652
2653 DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
2654 dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
2655
2656 if (stackTraceSize == 0)
2657 {
2658 ExtOut("Unable to decipher generated stack trace\n");
2659 }
2660 else
2661 {
2662 size_t iHeaderLength = AddExceptionHeader (NULL, 0);
2663 size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, FALSE, bLineNumbers);
2664 WCHAR *pwszBuffer = new NOTHROW WCHAR[iHeaderLength + iLength + 1];
2665 if (pwszBuffer)
2666 {
2667 AddExceptionHeader(pwszBuffer, iHeaderLength + 1);
2668 FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer + iHeaderLength, iLength + 1, bAsync, FALSE, bLineNumbers);
2669 SosExtOutLargeString(pwszBuffer, iHeaderLength + iLength + 1);
2670 delete[] pwszBuffer;
2671 }
2672 ExtOut("\n");
2673 }
2674 }
2675 else
2676 {
2677 ExtOut("<Not Available>\n");
2678 }
2679 }
2680 else
2681 {
2682 ExtOut("<none>\n");
2683 }
2684 }
2685
2686 {
2687 TADDR taStackString;
2688 if (bGotExcData)
2689 {
2690 taStackString = TO_TADDR(excData.StackTraceString);
2691 }
2692 else
2693 {
2694 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTraceString"));
2695 MOVE (taStackString, taObj + iOffset);
2696 }
2697
2698 ExtOut("StackTraceString: ");
2699 if (taStackString)
2700 {
2701 StringObjectContent(taStackString);
2702 ExtOut("\n\n"); // extra newline looks better
2703 }
2704 else
2705 {
2706 ExtOut("<none>\n");
2707 }
2708 }
2709
2710 {
2711 DWORD hResult;
2712 if (bGotExcData)
2713 {
2714 hResult = excData.HResult;
2715 }
2716 else
2717 {
2718 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_HResult"));
2719 MOVE (hResult, taObj + iOffset);
2720 }
2721
2722 ExtOut("HResult: %lx\n", hResult);
2723 }
2724
2725 if (isSecurityExceptionObj(objData.MethodTable) != NULL)
2726 {
2727 // We have a SecurityException Object: print out the debugString if present
2728 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("m_debugString"));
2729 if (iOffset > 0)
2730 {
2731 TADDR taDebugString;
2732 MOVE (taDebugString, taObj + iOffset);
2733
2734 if (taDebugString)
2735 {
2736 ExtOut("SecurityException Message: ");
2737 StringObjectContent(taDebugString);
2738 ExtOut("\n\n"); // extra newline looks better
2739 }
2740 }
2741 }
2742
2743 return Status;
2744}
2745
2746DECLARE_API(PrintException)
2747{
2748 INIT_API();
2749
2750 BOOL dml = FALSE;
2751 BOOL bShowNested = FALSE;
2752 BOOL bLineNumbers = FALSE;
2753 BOOL bCCW = FALSE;
2754 StringHolder strObject;
2755 CMDOption option[] =
2756 { // name, vptr, type, hasValue
2757 {"-nested", &bShowNested, COBOOL, FALSE},
2758 {"-lines", &bLineNumbers, COBOOL, FALSE},
2759 {"-l", &bLineNumbers, COBOOL, FALSE},
2760 {"-ccw", &bCCW, COBOOL, FALSE},
2761#ifndef FEATURE_PAL
2762 {"/d", &dml, COBOOL, FALSE}
2763#endif
2764 };
2765 CMDValue arg[] =
2766 { // vptr, type
2767 {&strObject, COSTRING}
2768 };
2769 size_t nArg;
2770 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2771 {
2772 return Status;
2773 }
2774 if (bLineNumbers)
2775 {
2776 ULONG symlines = 0;
2777 if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
2778 {
2779 symlines &= SYMOPT_LOAD_LINES;
2780 }
2781 if (symlines == 0)
2782 {
2783 ExtOut("In order for the option -lines to enable display of source information\n"
2784 "the debugger must be configured to load the line number information from\n"
2785 "the symbol files. Use the \".lines; .reload\" command to achieve this.\n");
2786 // don't even try
2787 bLineNumbers = FALSE;
2788 }
2789 }
2790
2791 EnableDMLHolder dmlHolder(dml);
2792 DWORD_PTR p_Object = NULL;
2793 if (nArg == 0)
2794 {
2795 if (bCCW)
2796 {
2797 ExtOut("No CCW pointer specified\n");
2798 return Status;
2799 }
2800
2801 // Look at the last exception object on this thread
2802
2803 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
2804 DacpThreadData Thread;
2805
2806 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
2807 {
2808 ExtOut("The current thread is unmanaged\n");
2809 return Status;
2810 }
2811
2812 DWORD_PTR dwAddr = NULL;
2813 if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
2814 &dwAddr,
2815 sizeof(dwAddr), NULL)) || (dwAddr==NULL))
2816 {
2817 ExtOut("There is no current managed exception on this thread\n");
2818 }
2819 else
2820 {
2821 p_Object = dwAddr;
2822 }
2823 }
2824 else
2825 {
2826 p_Object = GetExpression(strObject.data);
2827 if (p_Object == 0)
2828 {
2829 if (bCCW)
2830 {
2831 ExtOut("Invalid CCW pointer %s\n", args);
2832 }
2833 else
2834 {
2835 ExtOut("Invalid exception object %s\n", args);
2836 }
2837 return Status;
2838 }
2839
2840 if (bCCW)
2841 {
2842 // check if the address is a CCW pointer and then
2843 // get the exception object from it
2844 DacpCCWData ccwData;
2845 if (ccwData.Request(g_sos, p_Object) == S_OK)
2846 {
2847 p_Object = TO_TADDR(ccwData.managedObject);
2848 }
2849 }
2850 }
2851
2852 if (p_Object)
2853 {
2854 FormatException(TO_CDADDR(p_Object), bLineNumbers);
2855 }
2856
2857 // Are there nested exceptions?
2858 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
2859 DacpThreadData Thread;
2860
2861 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
2862 {
2863 ExtOut("The current thread is unmanaged\n");
2864 return Status;
2865 }
2866
2867 if (Thread.firstNestedException)
2868 {
2869 if (!bShowNested)
2870 {
2871 ExtOut("There are nested exceptions on this thread. Run with -nested for details\n");
2872 return Status;
2873 }
2874
2875 CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
2876 do
2877 {
2878 CLRDATA_ADDRESS obj = 0, next = 0;
2879 Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
2880
2881 if (Status != S_OK)
2882 {
2883 ExtOut("Error retrieving nested exception info %p\n", SOS_PTR(currentNested));
2884 return Status;
2885 }
2886
2887 if (IsInterrupt())
2888 {
2889 ExtOut("<aborted>\n");
2890 return Status;
2891 }
2892
2893 ExtOut("\nNested exception -------------------------------------------------------------\n");
2894 Status = FormatException(obj, bLineNumbers);
2895 if (Status != S_OK)
2896 {
2897 return Status;
2898 }
2899
2900 currentNested = next;
2901 }
2902 while(currentNested != NULL);
2903 }
2904 return Status;
2905}
2906
2907/**********************************************************************\
2908* Routine Description: *
2909* *
2910* This function is called to dump the contents of an object from a *
2911* given address
2912* *
2913\**********************************************************************/
2914DECLARE_API(DumpVC)
2915{
2916 INIT_API();
2917 MINIDUMP_NOT_SUPPORTED();
2918
2919 DWORD_PTR p_MT = NULL;
2920 DWORD_PTR p_Object = NULL;
2921 BOOL dml = FALSE;
2922
2923 CMDOption option[] =
2924 { // name, vptr, type, hasValue
2925#ifndef FEATURE_PAL
2926 {"/d", &dml, COBOOL, FALSE}
2927#endif
2928 };
2929 CMDValue arg[] =
2930 { // vptr, type
2931 {&p_MT, COHEX},
2932 {&p_Object, COHEX},
2933 };
2934 size_t nArg;
2935 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2936 {
2937 return Status;
2938 }
2939
2940 EnableDMLHolder dmlHolder(dml);
2941 if (nArg!=2)
2942 {
2943 ExtOut("Usage: !DumpVC <Method Table> <Value object start addr>\n");
2944 return Status;
2945 }
2946
2947 if (!IsMethodTable(p_MT))
2948 {
2949 ExtOut("Not a managed object\n");
2950 return S_OK;
2951 }
2952
2953 return PrintVC(p_MT, p_Object);
2954}
2955
2956#ifndef FEATURE_PAL
2957
2958#ifdef FEATURE_COMINTEROP
2959
2960DECLARE_API(DumpRCW)
2961{
2962 INIT_API();
2963
2964 BOOL dml = FALSE;
2965 StringHolder strObject;
2966
2967 CMDOption option[] =
2968 { // name, vptr, type, hasValue
2969 {"/d", &dml, COBOOL, FALSE}
2970 };
2971 CMDValue arg[] =
2972 { // vptr, type
2973 {&strObject, COSTRING}
2974 };
2975 size_t nArg;
2976 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2977 {
2978 return Status;
2979 }
2980
2981 EnableDMLHolder dmlHolder(dml);
2982 if (nArg == 0)
2983 {
2984 ExtOut("Missing RCW address\n");
2985 return Status;
2986 }
2987 else
2988 {
2989 DWORD_PTR p_RCW = GetExpression(strObject.data);
2990 if (p_RCW == 0)
2991 {
2992 ExtOut("Invalid RCW %s\n", args);
2993 }
2994 else
2995 {
2996 DacpRCWData rcwData;
2997 if ((Status = rcwData.Request(g_sos, p_RCW)) != S_OK)
2998 {
2999 ExtOut("Error requesting RCW data\n");
3000 return Status;
3001 }
3002 BOOL isDCOMProxy;
3003 if (FAILED(rcwData.IsDCOMProxy(g_sos, p_RCW, &isDCOMProxy)))
3004 {
3005 isDCOMProxy = FALSE;
3006 }
3007
3008 DMLOut("Managed object: %s\n", DMLObject(rcwData.managedObject));
3009 DMLOut("Creating thread: %p\n", SOS_PTR(rcwData.creatorThread));
3010 ExtOut("IUnknown pointer: %p\n", SOS_PTR(rcwData.unknownPointer));
3011 ExtOut("COM Context: %p\n", SOS_PTR(rcwData.ctxCookie));
3012 ExtOut("Managed ref count: %d\n", rcwData.refCount);
3013 ExtOut("IUnknown V-table pointer : %p (captured at RCW creation time)\n", SOS_PTR(rcwData.vtablePtr));
3014
3015 ExtOut("Flags: %s%s%s%s%s%s%s%s\n",
3016 (rcwData.isDisconnected? "IsDisconnected " : ""),
3017 (rcwData.supportsIInspectable? "SupportsIInspectable " : ""),
3018 (rcwData.isAggregated? "IsAggregated " : ""),
3019 (rcwData.isContained? "IsContained " : ""),
3020 (rcwData.isJupiterObject? "IsJupiterObject " : ""),
3021 (rcwData.isFreeThreaded? "IsFreeThreaded " : ""),
3022 (rcwData.identityPointer == TO_CDADDR(p_RCW)? "IsUnique " : ""),
3023 (isDCOMProxy ? "IsDCOMProxy " : "")
3024 );
3025
3026 // Jupiter data hidden by default
3027 if (rcwData.isJupiterObject)
3028 {
3029 ExtOut("IJupiterObject: %p\n", SOS_PTR(rcwData.jupiterObject));
3030 }
3031
3032 ExtOut("COM interface pointers:\n");
3033
3034 ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[rcwData.interfaceCount];
3035 if (pArray == NULL)
3036 {
3037 ReportOOM();
3038 return Status;
3039 }
3040
3041 if ((Status = g_sos->GetRCWInterfaces(p_RCW, rcwData.interfaceCount, pArray, NULL)) != S_OK)
3042 {
3043 ExtOut("Error requesting COM interface pointers\n");
3044 return Status;
3045 }
3046
3047 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "Context", "MT");
3048 for (int i = 0; i < rcwData.interfaceCount; i++)
3049 {
3050 // Ignore any NULL MethodTable interface cache. At this point only IJupiterObject
3051 // is saved as NULL MethodTable at first slot, and we've already printed outs its
3052 // value earlier.
3053 if (pArray[i].methodTable == NULL)
3054 continue;
3055
3056 NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
3057
3058 DMLOut("%p %p %s %S\n", SOS_PTR(pArray[i].interfacePtr), SOS_PTR(pArray[i].comContext), DMLMethodTable(pArray[i].methodTable), g_mdName);
3059 }
3060 }
3061 }
3062
3063 return Status;
3064}
3065
3066DECLARE_API(DumpCCW)
3067{
3068 INIT_API();
3069
3070 BOOL dml = FALSE;
3071 StringHolder strObject;
3072
3073 CMDOption option[] =
3074 { // name, vptr, type, hasValue
3075 {"/d", &dml, COBOOL, FALSE}
3076 };
3077 CMDValue arg[] =
3078 { // vptr, type
3079 {&strObject, COSTRING}
3080 };
3081 size_t nArg;
3082 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
3083 {
3084 return Status;
3085 }
3086
3087 EnableDMLHolder dmlHolder(dml);
3088 if (nArg == 0)
3089 {
3090 ExtOut("Missing CCW address\n");
3091 return Status;
3092 }
3093 else
3094 {
3095 DWORD_PTR p_CCW = GetExpression(strObject.data);
3096 if (p_CCW == 0)
3097 {
3098 ExtOut("Invalid CCW %s\n", args);
3099 }
3100 else
3101 {
3102 DacpCCWData ccwData;
3103 if ((Status = ccwData.Request(g_sos, p_CCW)) != S_OK)
3104 {
3105 ExtOut("Error requesting CCW data\n");
3106 return Status;
3107 }
3108
3109 if (ccwData.ccwAddress != p_CCW)
3110 ExtOut("CCW: %p\n", SOS_PTR(ccwData.ccwAddress));
3111
3112 DMLOut("Managed object: %s\n", DMLObject(ccwData.managedObject));
3113 ExtOut("Outer IUnknown: %p\n", SOS_PTR(ccwData.outerIUnknown));
3114 ExtOut("Ref count: %d%s\n", ccwData.refCount, ccwData.isNeutered ? " (NEUTERED)" : "");
3115 ExtOut("Flags: %s%s\n",
3116 (ccwData.isExtendsCOMObject? "IsExtendsCOMObject " : ""),
3117 (ccwData.isAggregated? "IsAggregated " : "")
3118 );
3119
3120 // Jupiter information hidden by default
3121 if (ccwData.jupiterRefCount > 0)
3122 {
3123 ExtOut("Jupiter ref count: %d%s%s%s%s\n",
3124 ccwData.jupiterRefCount,
3125 (ccwData.isPegged || ccwData.isGlobalPegged) ? ", Pegged by" : "",
3126 ccwData.isPegged ? " Jupiter " : "",
3127 (ccwData.isPegged && ccwData.isGlobalPegged) ? "&" : "",
3128 ccwData.isGlobalPegged ? " CLR " : ""
3129 );
3130 }
3131
3132 ExtOut("RefCounted Handle: %p%s\n",
3133 SOS_PTR(ccwData.handle),
3134 (ccwData.hasStrongRef ? " (STRONG)" : " (WEAK)"));
3135
3136 ExtOut("COM interface pointers:\n");
3137
3138 ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[ccwData.interfaceCount];
3139 if (pArray == NULL)
3140 {
3141 ReportOOM();
3142 return Status;
3143 }
3144
3145 if ((Status = g_sos->GetCCWInterfaces(p_CCW, ccwData.interfaceCount, pArray, NULL)) != S_OK)
3146 {
3147 ExtOut("Error requesting COM interface pointers\n");
3148 return Status;
3149 }
3150
3151 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "MT", "Type");
3152 for (int i = 0; i < ccwData.interfaceCount; i++)
3153 {
3154 if (pArray[i].methodTable == NULL)
3155 {
3156 wcscpy_s(g_mdName, mdNameLen, W("IDispatch/IUnknown"));
3157 }
3158 else
3159 {
3160 NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
3161 }
3162
3163 DMLOut("%p %s %S\n", pArray[i].interfacePtr, DMLMethodTable(pArray[i].methodTable), g_mdName);
3164 }
3165 }
3166 }
3167
3168 return Status;
3169}
3170
3171#endif // FEATURE_COMINTEROP
3172
3173#ifdef _DEBUG
3174/**********************************************************************\
3175* Routine Description: *
3176* *
3177* This function is called to dump the contents of a PermissionSet *
3178* from a given address. *
3179* *
3180\**********************************************************************/
3181/*
3182 COMMAND: dumppermissionset.
3183 !DumpPermissionSet <PermissionSet object address>
3184
3185 This command allows you to examine a PermissionSet object. Note that you can
3186 also use DumpObj such an object in greater detail. DumpPermissionSet attempts
3187 to extract all the relevant information from a PermissionSet that you might be
3188 interested in when performing Code Access Security (CAS) related debugging.
3189
3190 Here is a simple PermissionSet object:
3191
3192 0:000> !DumpPermissionSet 014615f4
3193 PermissionSet object: 014615f4
3194 Unrestricted: TRUE
3195
3196 Note that this is an unrestricted PermissionSet object that does not contain
3197 any individual permissions.
3198
3199 Here is another example of a PermissionSet object, one that is not unrestricted
3200 and contains a single permission:
3201
3202 0:003> !DumpPermissionSet 01469fa8
3203 PermissionSet object: 01469fa8
3204 Unrestricted: FALSE
3205 Name: System.Security.Permissions.ReflectionPermission
3206 MethodTable: 5b731308
3207 EEClass: 5b7e0d78
3208 Size: 12(0xc) bytes
3209 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.
3210 0.0__b77a5c561934e089\mscorlib.dll)
3211
3212 Fields:
3213 MT Field Offset Type VT Attr Value Name
3214 5b73125c 4001d66 4 System.Int32 0 instance 2 m_flags
3215
3216 Here is another example of an unrestricted PermissionSet, one that contains
3217 several permissions. The numbers in parentheses before each Permission object
3218 represents the index of that Permission in the PermissionSet.
3219
3220 0:003> !DumpPermissionSet 01467bd8
3221 PermissionSet object: 01467bd8
3222 Unrestricted: FALSE
3223 [1] 01467e90
3224 Name: System.Security.Permissions.FileDialogPermission
3225 MethodTable: 5b73023c
3226 EEClass: 5b7dfb18
3227 Size: 12(0xc) bytes
3228 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
3229 Fields:
3230 MT Field Offset Type VT Attr Value Name
3231 5b730190 4001cc2 4 System.Int32 0 instance 1 access
3232 [4] 014682a8
3233 Name: System.Security.Permissions.ReflectionPermission
3234 MethodTable: 5b731308
3235 EEClass: 5b7e0d78
3236 Size: 12(0xc) bytes
3237 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
3238 Fields:
3239 MT Field Offset Type VT Attr Value Name
3240 5b73125c 4001d66 4 System.Int32 0 instance 0 m_flags
3241 [17] 0146c060
3242 Name: System.Diagnostics.EventLogPermission
3243 MethodTable: 569841c4
3244 EEClass: 56a03e5c
3245 Size: 28(0x1c) bytes
3246 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3247 Fields:
3248 MT Field Offset Type VT Attr Value Name
3249 5b6d65d4 4003078 4 System.Object[] 0 instance 0146c190 tagNames
3250 5b6c9ed8 4003079 8 System.Type 0 instance 0146c17c permissionAccessType
3251 5b6cd928 400307a 10 System.Boolean 0 instance 0 isUnrestricted
3252 5b6c45f8 400307b c ...ections.Hashtable 0 instance 0146c1a4 rootTable
3253 5b6c090c 4003077 bfc System.String 0 static 00000000 computerName
3254 56984434 40030e7 14 ...onEntryCollection 0 instance 00000000 innerCollection
3255 [18] 0146ceb4
3256 Name: System.Net.WebPermission
3257 MethodTable: 5696dfc4
3258 EEClass: 569e256c
3259 Size: 20(0x14) bytes
3260 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3261 Fields:
3262 MT Field Offset Type VT Attr Value Name
3263 5b6cd928 400238e c System.Boolean 0 instance 0 m_Unrestricted
3264 5b6cd928 400238f d System.Boolean 0 instance 0 m_UnrestrictedConnect
3265 5b6cd928 4002390 e System.Boolean 0 instance 0 m_UnrestrictedAccept
3266 5b6c639c 4002391 4 ...ections.ArrayList 0 instance 0146cf3c m_connectList
3267 5b6c639c 4002392 8 ...ections.ArrayList 0 instance 0146cf54 m_acceptList
3268 569476f8 4002393 8a4 ...Expressions.Regex 0 static 00000000 s_MatchAllRegex
3269 [19] 0146a5fc
3270 Name: System.Net.DnsPermission
3271 MethodTable: 56966408
3272 EEClass: 569d3c08
3273 Size: 12(0xc) bytes
3274 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3275 Fields:
3276 MT Field Offset Type VT Attr Value Name
3277 5b6cd928 4001d2c 4 System.Boolean 0 instance 1 m_noRestriction
3278 [20] 0146d8ec
3279 Name: System.Web.AspNetHostingPermission
3280 MethodTable: 569831bc
3281 EEClass: 56a02ccc
3282 Size: 12(0xc) bytes
3283 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3284 Fields:
3285 MT Field Offset Type VT Attr Value Name
3286 56983090 4003074 4 System.Int32 0 instance 600 _level
3287 [21] 0146e394
3288 Name: System.Net.NetworkInformation.NetworkInformationPermission
3289 MethodTable: 5697ac70
3290 EEClass: 569f7104
3291 Size: 16(0x10) bytes
3292 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3293 Fields:
3294 MT Field Offset Type VT Attr Value Name
3295 5697ab38 4002c34 4 System.Int32 0 instance 0 access
3296 5b6cd928 4002c35 8 System.Boolean 0 instance 0 unrestricted
3297
3298
3299 The abbreviation !dps can be used for brevity.
3300
3301 \\
3302*/
3303DECLARE_API(DumpPermissionSet)
3304{
3305 INIT_API();
3306 MINIDUMP_NOT_SUPPORTED();
3307
3308 DWORD_PTR p_Object = NULL;
3309
3310 CMDValue arg[] =
3311 {
3312 {&p_Object, COHEX}
3313 };
3314 size_t nArg;
3315 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
3316 {
3317 return Status;
3318 }
3319 if (nArg!=1)
3320 {
3321 ExtOut("Usage: !DumpPermissionSet <PermissionSet object addr>\n");
3322 return Status;
3323 }
3324
3325
3326 return PrintPermissionSet(p_Object);
3327}
3328
3329#endif // _DEBUG
3330
3331void GCPrintGenerationInfo(DacpGcHeapDetails &heap);
3332void GCPrintSegmentInfo(DacpGcHeapDetails &heap, DWORD_PTR &total_size);
3333
3334#endif // FEATURE_PAL
3335
3336void DisplayInvalidStructuresMessage()
3337{
3338 ExtOut("The garbage collector data structures are not in a valid state for traversal.\n");
3339 ExtOut("It is either in the \"plan phase,\" where objects are being moved around, or\n");
3340 ExtOut("we are at the initialization or shutdown of the gc heap. Commands related to \n");
3341 ExtOut("displaying, finding or traversing objects as well as gc heap segments may not \n");
3342 ExtOut("work properly. !dumpheap and !verifyheap may incorrectly complain of heap \n");
3343 ExtOut("consistency errors.\n");
3344}
3345
3346/**********************************************************************\
3347* Routine Description: *
3348* *
3349* This function dumps GC heap size. *
3350* *
3351\**********************************************************************/
3352DECLARE_API(EEHeap)
3353{
3354 INIT_API();
3355 MINIDUMP_NOT_SUPPORTED();
3356
3357 BOOL dml = FALSE;
3358 BOOL showgc = FALSE;
3359 BOOL showloader = FALSE;
3360
3361 CMDOption option[] =
3362 { // name, vptr, type, hasValue
3363 {"-gc", &showgc, COBOOL, FALSE},
3364 {"-loader", &showloader, COBOOL, FALSE},
3365 {"/d", &dml, COBOOL, FALSE},
3366 };
3367
3368 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
3369 {
3370 return Status;
3371 }
3372
3373 EnableDMLHolder dmlHolder(dml);
3374 if (showloader || !showgc)
3375 {
3376 // Loader heap.
3377 DWORD_PTR allHeapSize = 0;
3378 DWORD_PTR wasted = 0;
3379 DacpAppDomainStoreData adsData;
3380 if ((Status=adsData.Request(g_sos))!=S_OK)
3381 {
3382 ExtOut("Unable to get AppDomain information\n");
3383 return Status;
3384 }
3385
3386 // The first one is the system domain.
3387 ExtOut("Loader Heap:\n");
3388 IfFailRet(PrintDomainHeapInfo("System Domain", adsData.systemDomain, &allHeapSize, &wasted));
3389 if (adsData.sharedDomain != NULL)
3390 {
3391 IfFailRet(PrintDomainHeapInfo("Shared Domain", adsData.sharedDomain, &allHeapSize, &wasted));
3392 }
3393
3394 ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
3395
3396 if (pArray==NULL)
3397 {
3398 ReportOOM();
3399 return Status;
3400 }
3401
3402 if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
3403 {
3404 ExtOut("Unable to get the array of all AppDomains.\n");
3405 return Status;
3406 }
3407
3408 for (int n=0;n<adsData.DomainCount;n++)
3409 {
3410 if (IsInterrupt())
3411 break;
3412
3413 char domain[16];
3414 sprintf_s(domain, _countof(domain), "Domain %d", n+1);
3415
3416 IfFailRet(PrintDomainHeapInfo(domain, pArray[n], &allHeapSize, &wasted));
3417
3418 }
3419
3420 // Jit code heap
3421 ExtOut("--------------------------------------\n");
3422 ExtOut("Jit code heap:\n");
3423
3424 if (IsMiniDumpFile())
3425 {
3426 ExtOut("<no information>\n");
3427 }
3428 else
3429 {
3430 allHeapSize += JitHeapInfo();
3431 }
3432
3433
3434 // Module Data
3435 {
3436 int numModule;
3437 ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(NULL, &numModule);
3438 if (moduleList == NULL)
3439 {
3440 ExtOut("Failed to request module list.\n");
3441 }
3442 else
3443 {
3444 // Module Thunk Heaps
3445 ExtOut("--------------------------------------\n");
3446 ExtOut("Module Thunk heaps:\n");
3447 allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_ThunkHeap, &wasted);
3448
3449 // Module Lookup Table Heaps
3450 ExtOut("--------------------------------------\n");
3451 ExtOut("Module Lookup Table heaps:\n");
3452 allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_LookupTableHeap, &wasted);
3453 }
3454 }
3455
3456 ExtOut("--------------------------------------\n");
3457 ExtOut("Total LoaderHeap size: ");
3458 PrintHeapSize(allHeapSize, wasted);
3459 ExtOut("=======================================\n");
3460 }
3461
3462 if (showgc || !showloader)
3463 {
3464 // GC Heap
3465 DWORD dwNHeaps = 1;
3466
3467 if (!GetGcStructuresValid())
3468 {
3469 DisplayInvalidStructuresMessage();
3470 }
3471
3472 DacpGcHeapData gcheap;
3473 if (gcheap.Request(g_sos) != S_OK)
3474 {
3475 ExtOut("Error requesting GC Heap data\n");
3476 return Status;
3477 }
3478
3479 if (gcheap.bServerMode)
3480 {
3481 dwNHeaps = gcheap.HeapCount;
3482 }
3483
3484 ExtOut("Number of GC Heaps: %d\n", dwNHeaps);
3485 DWORD_PTR totalSize = 0;
3486 if (!gcheap.bServerMode)
3487 {
3488 DacpGcHeapDetails heapDetails;
3489 if (heapDetails.Request(g_sos) != S_OK)
3490 {
3491 ExtOut("Error requesting details\n");
3492 return Status;
3493 }
3494
3495 GCHeapInfo (heapDetails, totalSize);
3496 ExtOut("Total Size: ");
3497 PrintHeapSize(totalSize, 0);
3498 }
3499 else
3500 {
3501 DWORD dwAllocSize;
3502 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
3503 {
3504 ExtOut("Failed to get GCHeaps: integer overflow\n");
3505 return Status;
3506 }
3507
3508 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
3509 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
3510 {
3511 ExtOut("Failed to get GCHeaps\n");
3512 return Status;
3513 }
3514
3515 DWORD n;
3516 for (n = 0; n < dwNHeaps; n ++)
3517 {
3518 DacpGcHeapDetails heapDetails;
3519 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
3520 {
3521 ExtOut("Error requesting details\n");
3522 return Status;
3523 }
3524 ExtOut("------------------------------\n");
3525 ExtOut("Heap %d (%p)\n", n, SOS_PTR(heapAddrs[n]));
3526 DWORD_PTR heapSize = 0;
3527 GCHeapInfo (heapDetails, heapSize);
3528 totalSize += heapSize;
3529 ExtOut("Heap Size: " WIN86_8SPACES);
3530 PrintHeapSize(heapSize, 0);
3531 }
3532 }
3533 ExtOut("------------------------------\n");
3534 ExtOut("GC Heap Size: " WIN86_8SPACES);
3535 PrintHeapSize(totalSize, 0);
3536 }
3537 return Status;
3538}
3539
3540void PrintGCStat(HeapStat *inStat, const char* label=NULL)
3541{
3542 if (inStat)
3543 {
3544 bool sorted = false;
3545 try
3546 {
3547 inStat->Sort();
3548 sorted = true;
3549 inStat->Print(label);
3550 }
3551 catch(...)
3552 {
3553 ExtOut("Exception occurred while trying to %s the GC stats.\n", sorted ? "print" : "sort");
3554 }
3555
3556 inStat->Delete();
3557 }
3558}
3559
3560#ifndef FEATURE_PAL
3561
3562DECLARE_API(TraverseHeap)
3563{
3564 INIT_API();
3565 MINIDUMP_NOT_SUPPORTED();
3566
3567 BOOL bXmlFormat = FALSE;
3568 BOOL bVerify = FALSE;
3569 StringHolder Filename;
3570
3571 CMDOption option[] =
3572 { // name, vptr, type, hasValue
3573 {"-xml", &bXmlFormat, COBOOL, FALSE},
3574 {"-verify", &bVerify, COBOOL, FALSE},
3575 };
3576 CMDValue arg[] =
3577 { // vptr, type
3578 {&Filename.data, COSTRING},
3579 };
3580 size_t nArg;
3581 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
3582 {
3583 return Status;
3584 }
3585
3586 if (nArg != 1)
3587 {
3588 ExtOut("usage: HeapTraverse [-xml] filename\n");
3589 return Status;
3590 }
3591
3592 if (!g_snapshot.Build())
3593 {
3594 ExtOut("Unable to build snapshot of the garbage collector state\n");
3595 return Status;
3596 }
3597
3598 FILE* file = NULL;
3599 if (fopen_s(&file, Filename.data, "w") != 0) {
3600 ExtOut("Unable to open file\n");
3601 return Status;
3602 }
3603
3604 if (!bVerify)
3605 ExtOut("Assuming a uncorrupted GC heap. If this is a crash dump consider -verify option\n");
3606
3607 HeapTraverser traverser(bVerify != FALSE);
3608
3609 ExtOut("Writing %s format to file %s\n", bXmlFormat ? "Xml" : "CLRProfiler", Filename.data);
3610 ExtOut("Gathering types...\n");
3611
3612 // TODO: there may be a canonical list of methodtables in the runtime that we can
3613 // traverse instead of exploring the gc heap for that list. We could then simplify the
3614 // tree structure to a sorted list of methodtables, and the index is the ID.
3615
3616 // TODO: "Traversing object members" code should be generalized and shared between
3617 // !gcroot and !traverseheap. Also !dumpheap can begin using GCHeapsTraverse.
3618
3619 if (!traverser.Initialize())
3620 {
3621 ExtOut("Error initializing heap traversal\n");
3622 fclose(file);
3623 return Status;
3624 }
3625
3626 if (!traverser.CreateReport (file, bXmlFormat ? FORMAT_XML : FORMAT_CLRPROFILER))
3627 {
3628 ExtOut("Unable to write heap report\n");
3629 fclose(file);
3630 return Status;
3631 }
3632
3633 fclose(file);
3634 ExtOut("\nfile %s saved\n", Filename.data);
3635
3636 return Status;
3637}
3638
3639#endif // FEATURE_PAL
3640
3641struct PrintRuntimeTypeArgs
3642{
3643 DWORD_PTR mtOfRuntimeType;
3644 int handleFieldOffset;
3645 DacpAppDomainStoreData adstore;
3646};
3647
3648void PrintRuntimeTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token)
3649{
3650 PrintRuntimeTypeArgs *pArgs = (PrintRuntimeTypeArgs *)token;
3651
3652 if (pArgs->mtOfRuntimeType == NULL)
3653 {
3654 NameForMT_s(methodTable, g_mdName, mdNameLen);
3655
3656 if (_wcscmp(g_mdName, W("System.RuntimeType")) == 0)
3657 {
3658 pArgs->mtOfRuntimeType = methodTable;
3659 pArgs->handleFieldOffset = GetObjFieldOffset(TO_CDADDR(objAddr), TO_CDADDR(methodTable), W("m_handle"));
3660 if (pArgs->handleFieldOffset <= 0)
3661 ExtOut("Error getting System.RuntimeType.m_handle offset\n");
3662
3663 pArgs->adstore.Request(g_sos);
3664 }
3665 }
3666
3667 if ((methodTable == pArgs->mtOfRuntimeType) && (pArgs->handleFieldOffset > 0))
3668 {
3669 // Get the method table and display the information.
3670 DWORD_PTR mtPtr;
3671 if (MOVE(mtPtr, objAddr + pArgs->handleFieldOffset) == S_OK)
3672 {
3673 DMLOut(DMLObject(objAddr));
3674
3675 CLRDATA_ADDRESS appDomain = GetAppDomainForMT(mtPtr);
3676 if (appDomain != NULL)
3677 {
3678 if (appDomain == pArgs->adstore.sharedDomain)
3679 ExtOut(" %" POINTERSIZE "s", "Shared");
3680
3681 else if (appDomain == pArgs->adstore.systemDomain)
3682 ExtOut(" %" POINTERSIZE "s", "System");
3683 else
3684 DMLOut(" %s", DMLDomain(appDomain));
3685 }
3686 else
3687 {
3688 ExtOut(" %" POINTERSIZE "s", "?");
3689 }
3690
3691 NameForMT_s(mtPtr, g_mdName, mdNameLen);
3692 DMLOut(" %s %S\n", DMLMethodTable(mtPtr), g_mdName);
3693 }
3694 }
3695}
3696
3697
3698DECLARE_API(DumpRuntimeTypes)
3699{
3700 INIT_API();
3701 MINIDUMP_NOT_SUPPORTED();
3702
3703 BOOL dml = FALSE;
3704
3705 CMDOption option[] =
3706 { // name, vptr, type, hasValue
3707 {"/d", &dml, COBOOL, FALSE},
3708 };
3709
3710 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
3711 return Status;
3712
3713 EnableDMLHolder dmlHolder(dml);
3714
3715 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type Name \n",
3716 "Address", "Domain", "MT");
3717 ExtOut("------------------------------------------------------------------------------\n");
3718
3719 PrintRuntimeTypeArgs pargs;
3720 ZeroMemory(&pargs, sizeof(PrintRuntimeTypeArgs));
3721
3722 GCHeapsTraverse(PrintRuntimeTypes, (LPVOID)&pargs);
3723 return Status;
3724}
3725
3726#define MIN_FRAGMENTATIONBLOCK_BYTES (1024*512)
3727namespace sos
3728{
3729 class FragmentationBlock
3730 {
3731 public:
3732 FragmentationBlock(TADDR addr, size_t size, TADDR next, TADDR mt)
3733 : mAddress(addr), mSize(size), mNext(next), mNextMT(mt)
3734 {
3735 }
3736
3737 inline TADDR GetAddress() const
3738 {
3739 return mAddress;
3740 }
3741 inline size_t GetSize() const
3742 {
3743 return mSize;
3744 }
3745
3746 inline TADDR GetNextObject() const
3747 {
3748 return mNext;
3749 }
3750
3751 inline TADDR GetNextMT() const
3752 {
3753 return mNextMT;
3754 }
3755
3756 private:
3757 TADDR mAddress;
3758 size_t mSize;
3759 TADDR mNext;
3760 TADDR mNextMT;
3761 };
3762}
3763
3764class DumpHeapImpl
3765{
3766public:
3767 DumpHeapImpl(PCSTR args)
3768 : mStart(0), mStop(0), mMT(0), mMinSize(0), mMaxSize(~0),
3769 mStat(FALSE), mStrings(FALSE), mVerify(FALSE),
3770 mThinlock(FALSE), mShort(FALSE), mDML(FALSE),
3771 mLive(FALSE), mDead(FALSE), mType(NULL)
3772 {
3773 ArrayHolder<char> type = NULL;
3774
3775 TADDR minTemp = 0;
3776 CMDOption option[] =
3777 { // name, vptr, type, hasValue
3778 {"-mt", &mMT, COHEX, TRUE}, // dump objects with a given MethodTable
3779 {"-type", &type, COSTRING, TRUE}, // list objects of specified type
3780 {"-stat", &mStat, COBOOL, FALSE}, // dump a summary of types and the number of instances of each
3781 {"-strings", &mStrings, COBOOL, FALSE}, // dump a summary of string objects
3782 {"-verify", &mVerify, COBOOL, FALSE}, // verify heap objects (!heapverify)
3783 {"-thinlock", &mThinlock, COBOOL, FALSE},// list only thinlocks
3784 {"-short", &mShort, COBOOL, FALSE}, // list only addresses
3785 {"-min", &mMinSize, COHEX, TRUE}, // min size of objects to display
3786 {"-max", &mMaxSize, COHEX, TRUE}, // max size of objects to display
3787 {"-live", &mLive, COHEX, FALSE}, // only print live objects
3788 {"-dead", &mDead, COHEX, FALSE}, // only print dead objects
3789#ifndef FEATURE_PAL
3790 {"/d", &mDML, COBOOL, FALSE}, // Debugger Markup Language
3791#endif
3792 };
3793
3794 CMDValue arg[] =
3795 { // vptr, type
3796 {&mStart, COHEX},
3797 {&mStop, COHEX}
3798 };
3799
3800 size_t nArgs = 0;
3801 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArgs))
3802 sos::Throw<sos::Exception>("Failed to parse command line arguments.");
3803
3804 if (mStart == 0)
3805 mStart = minTemp;
3806
3807 if (mStop == 0)
3808 mStop = sos::GCHeap::HeapEnd;
3809
3810 if (type && mMT)
3811 {
3812 sos::Throw<sos::Exception>("Cannot specify both -mt and -type");
3813 }
3814
3815 if (mLive && mDead)
3816 {
3817 sos::Throw<sos::Exception>("Cannot specify both -live and -dead.");
3818 }
3819
3820 if (mMinSize > mMaxSize)
3821 {
3822 sos::Throw<sos::Exception>("wrong argument");
3823 }
3824
3825 // If the user gave us a type, convert it to unicode and clean up "type".
3826 if (type && !mStrings)
3827 {
3828 size_t iLen = strlen(type) + 1;
3829 mType = new WCHAR[iLen];
3830 MultiByteToWideChar(CP_ACP, 0, type, -1, mType, (int)iLen);
3831 }
3832 }
3833
3834 ~DumpHeapImpl()
3835 {
3836 if (mType)
3837 delete [] mType;
3838 }
3839
3840 void Run()
3841 {
3842 // enable Debugger Markup Language
3843 EnableDMLHolder dmlholder(mDML);
3844 sos::GCHeap gcheap;
3845
3846 if (!gcheap.AreGCStructuresValid())
3847 DisplayInvalidStructuresMessage();
3848
3849 if (IsMiniDumpFile())
3850 {
3851 ExtOut("In a minidump without full memory, most gc heap structures will not be valid.\n");
3852 ExtOut("If you need this functionality, get a full memory dump with \".dump /ma mydump.dmp\"\n");
3853 }
3854
3855#ifndef FEATURE_PAL
3856 if (mLive || mDead)
3857 {
3858 GCRootImpl gcroot;
3859 mLiveness = gcroot.GetLiveObjects();
3860 }
3861#endif
3862
3863 // Some of the "specialty" versions of DumpHeap have slightly
3864 // different implementations than the standard version of DumpHeap.
3865 // We seperate them out to not clutter the standard DumpHeap function.
3866 if (mShort)
3867 DumpHeapShort(gcheap);
3868 else if (mThinlock)
3869 DumpHeapThinlock(gcheap);
3870 else if (mStrings)
3871 DumpHeapStrings(gcheap);
3872 else
3873 DumpHeap(gcheap);
3874
3875 if (mVerify)
3876 ValidateSyncTable(gcheap);
3877 }
3878
3879 static bool ValidateSyncTable(sos::GCHeap &gcheap)
3880 {
3881 bool succeeded = true;
3882 for (sos::SyncBlkIterator itr; itr; ++itr)
3883 {
3884 sos::CheckInterrupt();
3885
3886 if (!itr->IsFree())
3887 {
3888 if (!sos::IsObject(itr->GetObject(), true))
3889 {
3890 ExtOut("SyncBlock %d corrupted, points to invalid object %p\n",
3891 itr->GetIndex(), SOS_PTR(itr->GetObject()));
3892 succeeded = false;
3893 }
3894 else
3895 {
3896 // Does the object header point to this syncblock index?
3897 sos::Object obj = itr->GetObject();
3898 ULONG header = 0;
3899
3900 if (!obj.TryGetHeader(header))
3901 {
3902 ExtOut("Failed to get object header for object %p while inspecting syncblock at index %d.\n",
3903 SOS_PTR(itr->GetObject()), itr->GetIndex());
3904 succeeded = false;
3905 }
3906 else
3907 {
3908 bool valid = false;
3909 if ((header & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0 && (header & BIT_SBLK_IS_HASHCODE) == 0)
3910 {
3911 ULONG index = header & MASK_SYNCBLOCKINDEX;
3912 valid = (ULONG)itr->GetIndex() == index;
3913 }
3914
3915 if (!valid)
3916 {
3917 ExtOut("Object header for %p should have a SyncBlock index of %d.\n",
3918 SOS_PTR(itr->GetObject()), itr->GetIndex());
3919 succeeded = false;
3920 }
3921 }
3922 }
3923 }
3924 }
3925
3926 return succeeded;
3927 }
3928private:
3929 DumpHeapImpl(const DumpHeapImpl &);
3930
3931 bool Verify(const sos::ObjectIterator &itr)
3932 {
3933 if (mVerify)
3934 {
3935 char buffer[1024];
3936 if (!itr.Verify(buffer, _countof(buffer)))
3937 {
3938 ExtOut(buffer);
3939 return false;
3940 }
3941 }
3942
3943 return true;
3944 }
3945
3946 bool IsCorrectType(const sos::Object &obj)
3947 {
3948 if (mMT != NULL)
3949 return mMT == obj.GetMT();
3950
3951 if (mType != NULL)
3952 {
3953 WString name = obj.GetTypeName();
3954 return _wcsstr(name.c_str(), mType) != NULL;
3955 }
3956
3957 return true;
3958 }
3959
3960 bool IsCorrectSize(const sos::Object &obj)
3961 {
3962 size_t size = obj.GetSize();
3963 return size >= mMinSize && size <= mMaxSize;
3964 }
3965
3966 bool IsCorrectLiveness(const sos::Object &obj)
3967 {
3968#ifndef FEATURE_PAL
3969 if (mLive && mLiveness.find(obj.GetAddress()) == mLiveness.end())
3970 return false;
3971
3972 if (mDead && (mLiveness.find(obj.GetAddress()) != mLiveness.end() || obj.IsFree()))
3973 return false;
3974#endif
3975 return true;
3976 }
3977
3978
3979
3980 inline void PrintHeader()
3981 {
3982 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s\n", "Address", "MT", "Size");
3983 }
3984
3985 void DumpHeap(sos::GCHeap &gcheap)
3986 {
3987 HeapStat stats;
3988
3989 // For heap fragmentation tracking.
3990 TADDR lastFreeObj = NULL;
3991 size_t lastFreeSize = 0;
3992
3993 if (!mStat)
3994 PrintHeader();
3995
3996 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3997 {
3998 if (!Verify(itr))
3999 return;
4000
4001 bool onLOH = itr.IsCurrObjectOnLOH();
4002
4003 // Check for free objects to report fragmentation
4004 if (lastFreeObj != NULL)
4005 ReportFreeObject(lastFreeObj, lastFreeSize, itr->GetAddress(), itr->GetMT());
4006
4007 if (!onLOH && itr->IsFree())
4008 {
4009 lastFreeObj = *itr;
4010 lastFreeSize = itr->GetSize();
4011 }
4012 else
4013 {
4014 lastFreeObj = NULL;
4015 }
4016
4017 if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
4018 {
4019 stats.Add((DWORD_PTR)itr->GetMT(), (DWORD)itr->GetSize());
4020 if (!mStat)
4021 DMLOut("%s %s %8d%s\n", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize(),
4022 itr->IsFree() ? " Free":" ");
4023 }
4024 }
4025
4026 if (!mStat)
4027 ExtOut("\n");
4028
4029 stats.Sort();
4030 stats.Print();
4031
4032 PrintFragmentationReport();
4033 }
4034
4035 struct StringSetEntry
4036 {
4037 StringSetEntry() : count(0), size(0)
4038 {
4039 str[0] = 0;
4040 }
4041
4042 StringSetEntry(__in_ecount(64) WCHAR tmp[64], size_t _size)
4043 : count(1), size(_size)
4044 {
4045 memcpy(str, tmp, sizeof(str));
4046 }
4047
4048 void Add(size_t _size) const
4049 {
4050 count++;
4051 size += _size;
4052 }
4053
4054 mutable size_t count;
4055 mutable size_t size;
4056 WCHAR str[64];
4057
4058 bool operator<(const StringSetEntry &rhs) const
4059 {
4060 return _wcscmp(str, rhs.str) < 0;
4061 }
4062 };
4063
4064
4065 static bool StringSetCompare(const StringSetEntry &a1, const StringSetEntry &a2)
4066 {
4067 return a1.size < a2.size;
4068 }
4069
4070 void DumpHeapStrings(sos::GCHeap &gcheap)
4071 {
4072#ifdef FEATURE_PAL
4073 ExtOut("Not implemented.\n");
4074#else
4075 const int offset = sos::Object::GetStringDataOffset();
4076 typedef std::set<StringSetEntry> Set;
4077 Set set; // A set keyed off of the string's text
4078
4079 StringSetEntry tmp; // Temp string used to keep track of the set
4080 ULONG fetched = 0;
4081
4082 TableOutput out(3, POINTERSIZE_HEX, AlignRight);
4083 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
4084 {
4085 if (IsInterrupt())
4086 break;
4087
4088 if (itr->IsString() && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
4089 {
4090 CLRDATA_ADDRESS addr = itr->GetAddress();
4091 size_t size = itr->GetSize();
4092
4093 if (!mStat)
4094 out.WriteRow(ObjectPtr(addr), Pointer(itr->GetMT()), Decimal(size));
4095
4096 // Don't bother calculating the size of the string, just read the full 64 characters of the buffer. The null
4097 // terminator we read will terminate the string.
4098 HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(addr+offset), tmp.str, sizeof(WCHAR)*(_countof(tmp.str)-1), &fetched);
4099 if (SUCCEEDED(hr))
4100 {
4101 // Ensure we null terminate the string. Note that this will not overrun the buffer as we only
4102 // wrote a max of 63 characters into the 64 character buffer.
4103 tmp.str[fetched/sizeof(WCHAR)] = 0;
4104 Set::iterator sitr = set.find(tmp);
4105 if (sitr == set.end())
4106 {
4107 tmp.size = size;
4108 tmp.count = 1;
4109 set.insert(tmp);
4110 }
4111 else
4112 {
4113 sitr->Add(size);
4114 }
4115 }
4116 }
4117 }
4118
4119 ExtOut("\n");
4120
4121 // Now flatten the set into a vector. This is much faster than keeping two sets, or using a multimap.
4122 typedef std::vector<StringSetEntry> Vect;
4123 Vect v(set.begin(), set.end());
4124 std::sort(v.begin(), v.end(), &DumpHeapImpl::StringSetCompare);
4125
4126 // Now print out the data. The call to Flatten ensures that we don't print newlines to break up the
4127 // output in strange ways.
4128 for (Vect::iterator vitr = v.begin(); vitr != v.end(); ++vitr)
4129 {
4130 if (IsInterrupt())
4131 break;
4132
4133 Flatten(vitr->str, (unsigned int)_wcslen(vitr->str));
4134 out.WriteRow(Decimal(vitr->size), Decimal(vitr->count), vitr->str);
4135 }
4136#endif // FEATURE_PAL
4137 }
4138
4139 void DumpHeapShort(sos::GCHeap &gcheap)
4140 {
4141 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
4142 {
4143 if (!Verify(itr))
4144 return;
4145
4146 if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
4147 DMLOut("%s\n", DMLObject(itr->GetAddress()));
4148 }
4149 }
4150
4151 void DumpHeapThinlock(sos::GCHeap &gcheap)
4152 {
4153 int count = 0;
4154
4155 PrintHeader();
4156 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
4157 {
4158 if (!Verify(itr))
4159 return;
4160
4161 sos::ThinLockInfo lockInfo;
4162 if (IsCorrectType(*itr) && itr->GetThinLock(lockInfo))
4163 {
4164 DMLOut("%s %s %8d", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize());
4165 ExtOut(" ThinLock owner %x (%p) Recursive %x\n", lockInfo.ThreadId,
4166 SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
4167
4168 count++;
4169 }
4170 }
4171
4172 ExtOut("Found %d objects.\n", count);
4173 }
4174
4175private:
4176 TADDR mStart,
4177 mStop,
4178 mMT,
4179 mMinSize,
4180 mMaxSize;
4181
4182 BOOL mStat,
4183 mStrings,
4184 mVerify,
4185 mThinlock,
4186 mShort,
4187 mDML,
4188 mLive,
4189 mDead;
4190
4191
4192 WCHAR *mType;
4193
4194private:
4195#if !defined(FEATURE_PAL)
4196 // Windows only
4197 std::unordered_set<TADDR> mLiveness;
4198 typedef std::list<sos::FragmentationBlock> FragmentationList;
4199 FragmentationList mFrag;
4200
4201 void InitFragmentationList()
4202 {
4203 mFrag.clear();
4204 }
4205
4206 void ReportFreeObject(TADDR addr, size_t size, TADDR next, TADDR mt)
4207 {
4208 if (size >= MIN_FRAGMENTATIONBLOCK_BYTES)
4209 mFrag.push_back(sos::FragmentationBlock(addr, size, next, mt));
4210 }
4211
4212 void PrintFragmentationReport()
4213 {
4214 if (mFrag.size() > 0)
4215 {
4216 ExtOut("Fragmented blocks larger than 0.5 MB:\n");
4217 ExtOut("%" POINTERSIZE "s %8s %16s\n", "Addr", "Size", "Followed by");
4218
4219 for (FragmentationList::const_iterator itr = mFrag.begin(); itr != mFrag.end(); ++itr)
4220 {
4221 sos::MethodTable mt = itr->GetNextMT();
4222 ExtOut("%p %6.1fMB " WIN64_8SPACES "%p %S\n",
4223 SOS_PTR(itr->GetAddress()),
4224 ((double)itr->GetSize()) / 1024.0 / 1024.0,
4225 SOS_PTR(itr->GetNextObject()),
4226 mt.GetName());
4227 }
4228 }
4229 }
4230#else
4231 void InitFragmentationList() {}
4232 void ReportFreeObject(TADDR, TADDR, size_t, TADDR) {}
4233 void PrintFragmentationReport() {}
4234#endif
4235};
4236
4237/**********************************************************************\
4238* Routine Description: *
4239* *
4240* This function dumps async state machines on GC heap, *
4241* displaying details about each async operation found. *
4242* (May not work if GC is in progress.) *
4243* *
4244\**********************************************************************/
4245
4246void ResolveContinuation(CLRDATA_ADDRESS* contAddr)
4247{
4248 // Ideally this continuation is itself an async method box.
4249 sos::Object contObj = TO_TADDR(*contAddr);
4250 if (GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("StateMachine")) == 0)
4251 {
4252 // It was something else.
4253
4254 // If it's a standard task continuation, get its task field.
4255 int offset;
4256 if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("m_task"))) != 0)
4257 {
4258 MOVE(*contAddr, contObj.GetAddress() + offset);
4259 if (sos::IsObject(*contAddr, false))
4260 {
4261 contObj = TO_TADDR(*contAddr);
4262 }
4263 }
4264 else
4265 {
4266 // If it's storing an action wrapper, try to follow to that action's target.
4267 if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("m_action"))) != 0)
4268 {
4269 MOVE(*contAddr, contObj.GetAddress() + offset);
4270 if (sos::IsObject(*contAddr, false))
4271 {
4272 contObj = TO_TADDR(*contAddr);
4273 }
4274 }
4275
4276 // If it was, or if it's storing an action, try to follow through to the action's target.
4277 if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_target"))) != 0)
4278 {
4279 MOVE(*contAddr, contObj.GetAddress() + offset);
4280 if (sos::IsObject(*contAddr, false))
4281 {
4282 contObj = TO_TADDR(*contAddr);
4283 }
4284 }
4285 }
4286
4287 // Use whatever object we ended with.
4288 *contAddr = contObj.GetAddress();
4289 }
4290}
4291
4292bool TryGetContinuation(CLRDATA_ADDRESS addr, CLRDATA_ADDRESS mt, CLRDATA_ADDRESS* contAddr)
4293{
4294 // Get the continuation field from the task.
4295 int offset = GetObjFieldOffset(addr, mt, W("m_continuationObject"));
4296 if (offset != 0)
4297 {
4298 DWORD_PTR contObjPtr;
4299 MOVE(contObjPtr, addr + offset);
4300 if (sos::IsObject(contObjPtr, false))
4301 {
4302 *contAddr = TO_CDADDR(contObjPtr);
4303 ResolveContinuation(contAddr);
4304 return true;
4305 }
4306 }
4307
4308 return false;
4309}
4310
4311struct AsyncRecord
4312{
4313 CLRDATA_ADDRESS Address;
4314 CLRDATA_ADDRESS MT;
4315 DWORD Size;
4316 CLRDATA_ADDRESS StateMachineAddr;
4317 CLRDATA_ADDRESS StateMachineMT;
4318 BOOL FilteredByOptions;
4319 BOOL IsStateMachine;
4320 BOOL IsValueType;
4321 BOOL IsTopLevel;
4322 int TaskStateFlags;
4323 int StateValue;
4324 std::vector<CLRDATA_ADDRESS> Continuations;
4325};
4326
4327bool AsyncRecordIsCompleted(AsyncRecord& ar)
4328{
4329 const int TASK_STATE_COMPLETED_MASK = 0x1600000;
4330 return (ar.TaskStateFlags & TASK_STATE_COMPLETED_MASK) != 0;
4331}
4332
4333const char* GetAsyncRecordStatusDescription(AsyncRecord& ar)
4334{
4335 const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000;
4336 const int TASK_STATE_FAULTED = 0x200000;
4337 const int TASK_STATE_CANCELED = 0x400000;
4338
4339 if ((ar.TaskStateFlags & TASK_STATE_RAN_TO_COMPLETION) != 0) return "Success";
4340 if ((ar.TaskStateFlags & TASK_STATE_FAULTED) != 0) return "Failed";
4341 if ((ar.TaskStateFlags & TASK_STATE_CANCELED) != 0) return "Canceled";
4342 return "Pending";
4343}
4344
4345void ExtOutTaskDelegateMethod(sos::Object& obj)
4346{
4347 DacpFieldDescData actionField;
4348 int offset = GetObjFieldOffset(obj.GetAddress(), obj.GetMT(), W("m_action"), TRUE, &actionField);
4349 if (offset != 0)
4350 {
4351 CLRDATA_ADDRESS actionAddr;
4352 MOVE(actionAddr, obj.GetAddress() + offset);
4353 CLRDATA_ADDRESS actionMD;
4354 if (actionAddr != NULL && TryGetMethodDescriptorForDelegate(actionAddr, &actionMD))
4355 {
4356 NameForMD_s((DWORD_PTR)actionMD, g_mdName, mdNameLen);
4357 ExtOut("(%S) ", g_mdName);
4358 }
4359 }
4360}
4361
4362void ExtOutTaskStateFlagsDescription(int stateFlags)
4363{
4364 if (stateFlags == 0) return;
4365
4366 ExtOut("State Flags: ");
4367
4368 // TaskCreationOptions.*
4369 if ((stateFlags & 0x01) != 0) ExtOut("PreferFairness ");
4370 if ((stateFlags & 0x02) != 0) ExtOut("LongRunning ");
4371 if ((stateFlags & 0x04) != 0) ExtOut("AttachedToParent ");
4372 if ((stateFlags & 0x08) != 0) ExtOut("DenyChildAttach ");
4373 if ((stateFlags & 0x10) != 0) ExtOut("HideScheduler ");
4374 if ((stateFlags & 0x40) != 0) ExtOut("RunContinuationsAsynchronously ");
4375
4376 // InternalTaskOptions.*
4377 if ((stateFlags & 0x0200) != 0) ExtOut("ContinuationTask ");
4378 if ((stateFlags & 0x0400) != 0) ExtOut("PromiseTask ");
4379 if ((stateFlags & 0x1000) != 0) ExtOut("LazyCancellation ");
4380 if ((stateFlags & 0x2000) != 0) ExtOut("QueuedByRuntime ");
4381 if ((stateFlags & 0x4000) != 0) ExtOut("DoNotDispose ");
4382
4383 // TASK_STATE_*
4384 if ((stateFlags & 0x10000) != 0) ExtOut("STARTED ");
4385 if ((stateFlags & 0x20000) != 0) ExtOut("DELEGATE_INVOKED ");
4386 if ((stateFlags & 0x40000) != 0) ExtOut("DISPOSED ");
4387 if ((stateFlags & 0x80000) != 0) ExtOut("EXCEPTIONOBSERVEDBYPARENT ");
4388 if ((stateFlags & 0x100000) != 0) ExtOut("CANCELLATIONACKNOWLEDGED ");
4389 if ((stateFlags & 0x200000) != 0) ExtOut("FAULTED ");
4390 if ((stateFlags & 0x400000) != 0) ExtOut("CANCELED ");
4391 if ((stateFlags & 0x800000) != 0) ExtOut("WAITING_ON_CHILDREN ");
4392 if ((stateFlags & 0x1000000) != 0) ExtOut("RAN_TO_COMPLETION ");
4393 if ((stateFlags & 0x2000000) != 0) ExtOut("WAITINGFORACTIVATION ");
4394 if ((stateFlags & 0x4000000) != 0) ExtOut("COMPLETION_RESERVED ");
4395 if ((stateFlags & 0x8000000) != 0) ExtOut("THREAD_WAS_ABORTED ");
4396 if ((stateFlags & 0x10000000) != 0) ExtOut("WAIT_COMPLETION_NOTIFICATION ");
4397 if ((stateFlags & 0x20000000) != 0) ExtOut("EXECUTIONCONTEXT_IS_NULL ");
4398 if ((stateFlags & 0x40000000) != 0) ExtOut("TASKSCHEDULED_WAS_FIRED ");
4399
4400 ExtOut("\n");
4401}
4402
4403DECLARE_API(DumpAsync)
4404{
4405 INIT_API();
4406 MINIDUMP_NOT_SUPPORTED();
4407 if (!g_snapshot.Build())
4408 {
4409 ExtOut("Unable to build snapshot of the garbage collector state\n");
4410 return E_FAIL;
4411 }
4412
4413 try
4414 {
4415 // Process command-line arguments.
4416 size_t nArg = 0;
4417 TADDR mt = NULL, addr = NULL;
4418 ArrayHolder<char> ansiType = NULL;
4419 ArrayHolder<WCHAR> type = NULL;
4420 BOOL dml = FALSE, includeCompleted = FALSE, includeStacks = FALSE, includeRoots = FALSE, includeAllTasks = FALSE, dumpFields = FALSE;
4421 CMDOption option[] =
4422 { // name, vptr, type, hasValue
4423 { "-addr", &addr, COHEX, TRUE }, // dump only the async object at the specified address
4424 { "-mt", &mt, COHEX, TRUE }, // dump only async objects with a given MethodTable
4425 { "-type", &ansiType, COSTRING, TRUE }, // dump only async objects that contain the specified type substring
4426 { "-tasks", &includeAllTasks, COBOOL, FALSE }, // include all tasks that can be found on the heap, not just async methods
4427 { "-completed", &includeCompleted, COBOOL, FALSE }, // include async objects that are in a completed state
4428 { "-fields", &dumpFields, COBOOL, FALSE }, // show relevant fields of found async objects
4429 { "-stacks", &includeStacks, COBOOL, FALSE }, // gather and output continuation/stack information
4430 { "-roots", &includeRoots, COBOOL, FALSE }, // gather and output GC root information
4431#ifndef FEATURE_PAL
4432 { "/d", &dml, COBOOL, FALSE }, // Debugger Markup Language
4433#endif
4434 };
4435 if (!GetCMDOption(args, option, _countof(option), NULL, 0, &nArg) || nArg != 0)
4436 {
4437 sos::Throw<sos::Exception>(
4438 "Usage: DumpAsync [-addr ObjectAddr] [-mt MethodTableAddr] [-type TypeName] [-tasks] [-completed] [-fields] [-stacks] [-roots]\n"
4439 "[-addr ObjectAddr] => Only display the async object at the specified address.\n"
4440 "[-mt MethodTableAddr] => Only display top-level async objects with the specified method table address.\n"
4441 "[-type TypeName] => Only display top-level async objects whose type name includes the specified substring.\n"
4442 "[-tasks] => Include Task and Task-derived objects, in addition to any state machine objects found.\n"
4443 "[-completed] => Include async objects that represent completed operations but that are still on the heap.\n"
4444 "[-fields] => Show the fields of state machines.\n"
4445 "[-stacks] => Gather, output, and consolidate based on continuation chains / async stacks for discovered async objects.\n"
4446 "[-roots] => Perform a gcroot on each rendered async object.\n"
4447 );
4448 }
4449 if (ansiType != NULL)
4450 {
4451 size_t ansiTypeLen = strlen(ansiType) + 1;
4452 type = new WCHAR[ansiTypeLen];
4453 MultiByteToWideChar(CP_ACP, 0, ansiType, -1, type, (int)ansiTypeLen);
4454 }
4455
4456 EnableDMLHolder dmlHolder(dml);
4457 BOOL hasTypeFilter = mt != NULL || ansiType != NULL || addr != NULL;
4458
4459 // Display a message if the heap isn't verified.
4460 sos::GCHeap gcheap;
4461 if (!gcheap.AreGCStructuresValid())
4462 {
4463 DisplayInvalidStructuresMessage();
4464 }
4465
4466 // Walk each heap object looking for async state machine objects. As we're targeting .NET Core 2.1+, all such objects
4467 // will be Task or Task-derived types.
4468 std::map<CLRDATA_ADDRESS, AsyncRecord> asyncRecords;
4469 for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr)
4470 {
4471 // Skip objects too small to be state machines or tasks, avoiding some compiler-generated caching data structures.
4472 if (itr->GetSize() <= 24)
4473 {
4474 continue;
4475 }
4476
4477 // Match only async objects.
4478 if (includeAllTasks)
4479 {
4480 // If the user has selected to include all tasks and not just async state machine boxes, we simply need to validate
4481 // that this is Task or Task-derived, and if it's not, skip it.
4482 if (!IsDerivedFrom(itr->GetMT(), W("System.Threading.Tasks.Task")))
4483 {
4484 continue;
4485 }
4486 }
4487 else
4488 {
4489 // Otherwise, we only care about AsyncStateMachineBox`1 as well as the DebugFinalizableAsyncStateMachineBox`1
4490 // that's used when certain ETW events are set.
4491 if (_wcsncmp(itr->GetTypeName(), W("System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1"), 79) != 0 &&
4492 _wcsncmp(itr->GetTypeName(), W("System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+DebugFinalizableAsyncStateMachineBox`1"), 95) != 0)
4493 {
4494 continue;
4495 }
4496 }
4497
4498 // Create an AsyncRecord to store the state for this instance. We're likely going to keep the object at this point,
4499 // though we may still discard/skip it with a few checks later; to do that, though, we'll need some of the info
4500 // gathered here, so we construct the record to store the data.
4501 AsyncRecord ar;
4502 ar.Address = itr->GetAddress();
4503 ar.MT = itr->GetMT();
4504 ar.Size = (DWORD)itr->GetSize();
4505 ar.StateMachineAddr = itr->GetAddress();
4506 ar.StateMachineMT = itr->GetMT();
4507 ar.IsValueType = false;
4508 ar.IsTopLevel = true;
4509 ar.IsStateMachine = false;
4510 ar.TaskStateFlags = 0;
4511 ar.StateValue = 0;
4512 ar.FilteredByOptions = // we process all objects to support forming proper chains, but then only display ones that match the user's request
4513 (mt == NULL || mt == itr->GetMT()) && // Match only MTs the user requested.
4514 (type == NULL || _wcsstr(itr->GetTypeName(), type) != NULL) && // Match only type name substrings the user requested.
4515 (addr == NULL || addr == itr->GetAddress()); // Match only the object at the specified address.
4516
4517 // Get the state flags for the task. This is used to determine whether async objects are completed (and thus should
4518 // be culled by default). It avoids our needing to depend on interpreting the compiler's "<>1__state" field, and also lets
4519 // us display state information for non-async state machine objects.
4520 DacpFieldDescData stateFlagsField;
4521 int offset = GetObjFieldOffset(ar.Address, ar.MT, W("m_stateFlags"), TRUE, &stateFlagsField);
4522 if (offset != 0)
4523 {
4524 MOVE(ar.TaskStateFlags, ar.Address + offset);
4525 }
4526
4527 // Get the async state machine object's StateMachine field.
4528 DacpFieldDescData stateMachineField;
4529 int stateMachineFieldOffset = GetObjFieldOffset(TO_CDADDR(itr->GetAddress()), itr->GetMT(), W("StateMachine"), TRUE, &stateMachineField);
4530 if (stateMachineFieldOffset != 0)
4531 {
4532 ar.IsStateMachine = true;
4533 ar.IsValueType = stateMachineField.Type == ELEMENT_TYPE_VALUETYPE;
4534
4535 // Get the address and method table of the state machine. While it'll generally be a struct, it is valid for it to be a
4536 // class (the C# compiler generates a class in debug builds to better support Edit-And-Continue), so we accommodate both.
4537 DacpFieldDescData stateField;
4538 int stateFieldOffset = -1;
4539 if (ar.IsValueType)
4540 {
4541 ar.StateMachineAddr = itr->GetAddress() + stateMachineFieldOffset;
4542 ar.StateMachineMT = stateMachineField.MTOfType;
4543 stateFieldOffset = GetValueFieldOffset(ar.StateMachineMT, W("<>1__state"), &stateField);
4544 }
4545 else
4546 {
4547 MOVE(ar.StateMachineAddr, itr->GetAddress() + stateMachineFieldOffset);
4548 DacpObjectData objData;
4549 if (objData.Request(g_sos, ar.StateMachineAddr) == S_OK)
4550 {
4551 ar.StateMachineMT = objData.MethodTable; // update from Canon to actual type
4552 stateFieldOffset = GetObjFieldOffset(ar.StateMachineAddr, ar.StateMachineMT, W("<>1__state"), TRUE, &stateField);
4553 }
4554 }
4555
4556 if (stateFieldOffset >= 0 && (ar.IsValueType || stateFieldOffset != 0))
4557 {
4558 MOVE(ar.StateValue, ar.StateMachineAddr + stateFieldOffset);
4559 }
4560 }
4561
4562 // If we only want to include incomplete async objects, skip this one if it's completed.
4563 if (!includeCompleted && AsyncRecordIsCompleted(ar))
4564 {
4565 continue;
4566 }
4567
4568 // If the user has asked to include "async stacks" information, resolve any continuation
4569 // that might be registered with it. This could be a single continuation, or it could
4570 // be a list of continuations in the case of the same task being awaited multiple times.
4571 CLRDATA_ADDRESS nextAddr;
4572 if (includeStacks && TryGetContinuation(itr->GetAddress(), itr->GetMT(), &nextAddr))
4573 {
4574 sos::Object contObj = TO_TADDR(nextAddr);
4575 if (_wcsncmp(contObj.GetTypeName(), W("System.Collections.Generic.List`1"), 33) == 0)
4576 {
4577 // The continuation is a List<object>. Iterate through its internal object[]
4578 // looking for non-null objects, and adding each one as a continuation.
4579 int itemsOffset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_items"));
4580 if (itemsOffset != 0)
4581 {
4582 DWORD_PTR listItemsPtr;
4583 MOVE(listItemsPtr, contObj.GetAddress() + itemsOffset);
4584 if (sos::IsObject(listItemsPtr, false))
4585 {
4586 DacpObjectData objData;
4587 if (objData.Request(g_sos, TO_CDADDR(listItemsPtr)) == S_OK && objData.ObjectType == OBJ_ARRAY)
4588 {
4589 for (int i = 0; i < objData.dwNumComponents; i++)
4590 {
4591 CLRDATA_ADDRESS elementPtr;
4592 MOVE(elementPtr, TO_CDADDR(objData.ArrayDataPtr + (i * objData.dwComponentSize)));
4593 if (elementPtr != NULL && sos::IsObject(elementPtr, false))
4594 {
4595 ResolveContinuation(&elementPtr);
4596 ar.Continuations.push_back(elementPtr);
4597 }
4598 }
4599 }
4600 }
4601 }
4602 }
4603 else
4604 {
4605 ar.Continuations.push_back(contObj.GetAddress());
4606 }
4607 }
4608
4609 // We've gathered all of the needed information for this heap object. Add it to our list of async records.
4610 asyncRecords.insert(std::pair<CLRDATA_ADDRESS, AsyncRecord>(ar.Address, ar));
4611 }
4612
4613 // As with DumpHeap, output a summary table about all of the objects we found. In contrast, though, his is based on the filtered
4614 // list of async records we gathered rather than everything on the heap.
4615 if (addr == NULL) // no point in stats if we're only targeting a single object
4616 {
4617 HeapStat stats;
4618 for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt)
4619 {
4620 if (!hasTypeFilter || arIt->second.FilteredByOptions)
4621 {
4622 stats.Add((DWORD_PTR)arIt->second.MT, (DWORD)arIt->second.Size);
4623 }
4624 }
4625 stats.Sort();
4626 stats.Print();
4627 }
4628
4629 // If the user has asked for "async stacks" and if there's not MT/type name filter, look through all of our async records
4630 // to find the "top-level" nodes that start rather than that are a part of a continuation chain. When we then iterate through
4631 // async records, we only print ones out that are still classified as top-level. We don't do this if there's a type filter
4632 // because in that case we consider those and only those objects to be top-level.
4633 if (includeStacks && !hasTypeFilter)
4634 {
4635 size_t uniqueChains = asyncRecords.size();
4636 for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt)
4637 {
4638 for (std::vector<CLRDATA_ADDRESS>::iterator contIt = arIt->second.Continuations.begin(); contIt != arIt->second.Continuations.end(); ++contIt)
4639 {
4640 std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator found = asyncRecords.find(*contIt);
4641 if (found != asyncRecords.end())
4642 {
4643 if (found->second.IsTopLevel)
4644 {
4645 found->second.IsTopLevel = false;
4646 uniqueChains--;
4647 }
4648 }
4649 }
4650 }
4651
4652 ExtOut("In %d chains.\n", uniqueChains);
4653 }
4654
4655 // Print out header for the main line of each result.
4656 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s ", "Address", "MT", "Size");
4657 if (includeCompleted) ExtOut("%8s ", "Status");
4658 ExtOut("%10s %s\n", "State", "Description");
4659
4660 // Output each top-level async record.
4661 int counter = 0;
4662 for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt)
4663 {
4664 if (!arIt->second.IsTopLevel || (hasTypeFilter && !arIt->second.FilteredByOptions))
4665 {
4666 continue;
4667 }
4668
4669 // Output the state machine's details as a single line.
4670 sos::Object obj = TO_TADDR(arIt->second.Address);
4671 DacpMethodTableData mtabledata;
4672 DacpMethodTableFieldData vMethodTableFields;
4673 if (arIt->second.IsStateMachine &&
4674 mtabledata.Request(g_sos, arIt->second.StateMachineMT) == S_OK &&
4675 vMethodTableFields.Request(g_sos, arIt->second.StateMachineMT) == S_OK &&
4676 vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
4677 {
4678 // This has a StateMachine. Output its details.
4679 sos::MethodTable mt = TO_TADDR(arIt->second.StateMachineMT);
4680 DMLOut("%s %s %8d ", DMLAsync(obj.GetAddress()), DMLDumpHeapMT(obj.GetMT()), obj.GetSize());
4681 if (includeCompleted) ExtOut("%8s ", GetAsyncRecordStatusDescription(arIt->second));
4682 ExtOut("%10d %S\n", arIt->second.StateValue, mt.GetName());
4683 if (dumpFields) DisplayFields(arIt->second.StateMachineMT, &mtabledata, &vMethodTableFields, (DWORD_PTR)arIt->second.StateMachineAddr, TRUE, arIt->second.IsValueType);
4684 }
4685 else
4686 {
4687 // This does not have a StateMachine. Output the details of the Task itself.
4688 DMLOut("%s %s %8d ", DMLAsync(obj.GetAddress()), DMLDumpHeapMT(obj.GetMT()), obj.GetSize());
4689 if (includeCompleted) ExtOut("%8s ", GetAsyncRecordStatusDescription(arIt->second));
4690 ExtOut("[%08x] %S ", arIt->second.TaskStateFlags, obj.GetTypeName());
4691 ExtOutTaskDelegateMethod(obj);
4692 ExtOut("\n");
4693 if (dumpFields) ExtOutTaskStateFlagsDescription(arIt->second.TaskStateFlags);
4694 }
4695
4696 // If we gathered any continuations for this record, output the chains now.
4697 if (includeStacks && arIt->second.Continuations.size() > 0)
4698 {
4699 ExtOut(includeAllTasks ? "Continuation chains:\n" : "Async \"stack\":\n");
4700 std::vector<std::pair<int, CLRDATA_ADDRESS>> continuationChainToExplore;
4701 continuationChainToExplore.push_back(std::pair<int, CLRDATA_ADDRESS>(1, obj.GetAddress()));
4702
4703 // Do a depth-first traversal of continuations, outputting each continuation found and then
4704 // looking in our gathered objects list for its continuations.
4705 std::set<CLRDATA_ADDRESS> seen;
4706 while (continuationChainToExplore.size() > 0)
4707 {
4708 // Pop the next continuation from the stack.
4709 std::pair<int, CLRDATA_ADDRESS> cur = continuationChainToExplore.back();
4710 continuationChainToExplore.pop_back();
4711
4712 // Get the async record for this continuation. It should be one we already know about.
4713 std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator curAsyncRecord = asyncRecords.find(cur.second);
4714 if (curAsyncRecord == asyncRecords.end())
4715 {
4716 continue;
4717 }
4718
4719 // Make sure to avoid cycles in the rare case where async records may refer to each other.
4720 if (seen.find(cur.second) != seen.end())
4721 {
4722 continue;
4723 }
4724 seen.insert(cur.second);
4725
4726 // Iterate through all continuations from this object.
4727 for (std::vector<CLRDATA_ADDRESS>::iterator contIt = curAsyncRecord->second.Continuations.begin(); contIt != curAsyncRecord->second.Continuations.end(); ++contIt)
4728 {
4729 sos::Object cont = TO_TADDR(*contIt);
4730
4731 // Print out the depth of the continuation with dots, then its address.
4732 for (int i = 0; i < cur.first; i++) ExtOut(".");
4733 DMLOut("%s ", DMLObject(cont.GetAddress()));
4734
4735 // Print out the name of the method for this task's delegate if it has one (state machines won't, but others tasks may).
4736 ExtOutTaskDelegateMethod(cont);
4737
4738 // Find the async record for this continuation, and output its name. If it's a state machine,
4739 // also output its current state value so that a user can see at a glance its status.
4740 std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator contAsyncRecord = asyncRecords.find(cont.GetAddress());
4741 if (contAsyncRecord != asyncRecords.end())
4742 {
4743 sos::MethodTable contMT = TO_TADDR(contAsyncRecord->second.StateMachineMT);
4744 if (contAsyncRecord->second.IsStateMachine) ExtOut("(%d) ", contAsyncRecord->second.StateValue);
4745 ExtOut("%S\n", contMT.GetName());
4746 }
4747 else
4748 {
4749 ExtOut("%S\n", cont.GetTypeName());
4750 }
4751
4752 // Add this continuation to the stack to explore.
4753 continuationChainToExplore.push_back(std::pair<int, CLRDATA_ADDRESS>(cur.first + 1, *contIt));
4754 }
4755 }
4756 }
4757
4758 // Finally, output gcroots, as they can serve as alternative/more detailed "async stacks", and also help to highlight
4759 // state machines that aren't being kept alive. However, they're more expensive to compute, so they're opt-in.
4760 if (includeRoots)
4761 {
4762 ExtOut("GC roots:\n");
4763 IncrementIndent();
4764 GCRootImpl gcroot;
4765 int numRoots = gcroot.PrintRootsForObject(obj.GetAddress(), FALSE, FALSE);
4766 DecrementIndent();
4767 if (numRoots == 0 && !AsyncRecordIsCompleted(arIt->second))
4768 {
4769 ExtOut("Incomplete state machine or task with 0 roots.\n");
4770 }
4771 }
4772
4773 // If we're rendering more than one line per entry, output a separator to help distinguish the entries.
4774 if (dumpFields || includeStacks || includeRoots)
4775 {
4776 ExtOut("--------------------------------------------------------------------------------\n");
4777 }
4778 }
4779
4780 return S_OK;
4781 }
4782 catch (const sos::Exception &e)
4783 {
4784 ExtOut("%s\n", e.what());
4785 return E_FAIL;
4786 }
4787}
4788
4789/**********************************************************************\
4790* Routine Description: *
4791* *
4792* This function dumps all objects on GC heap. It also displays *
4793* statistics of objects. If GC heap is corrupted, it will stop at
4794* the bad place. (May not work if GC is in progress.) *
4795* *
4796\**********************************************************************/
4797DECLARE_API(DumpHeap)
4798{
4799 INIT_API();
4800 MINIDUMP_NOT_SUPPORTED();
4801
4802 if (!g_snapshot.Build())
4803 {
4804 ExtOut("Unable to build snapshot of the garbage collector state\n");
4805 return E_FAIL;
4806 }
4807
4808 try
4809 {
4810 DumpHeapImpl dumpHeap(args);
4811 dumpHeap.Run();
4812
4813 return S_OK;
4814 }
4815 catch(const sos::Exception &e)
4816 {
4817 ExtOut("%s\n", e.what());
4818 return E_FAIL;
4819 }
4820}
4821
4822DECLARE_API(VerifyHeap)
4823{
4824 INIT_API();
4825 MINIDUMP_NOT_SUPPORTED();
4826
4827 if (!g_snapshot.Build())
4828 {
4829 ExtOut("Unable to build snapshot of the garbage collector state\n");
4830 return E_FAIL;
4831 }
4832
4833 try
4834 {
4835 bool succeeded = true;
4836 char buffer[1024];
4837 sos::GCHeap gcheap;
4838 sos::ObjectIterator itr = gcheap.WalkHeap();
4839
4840 while (itr)
4841 {
4842 if (itr.Verify(buffer, _countof(buffer)))
4843 {
4844 ++itr;
4845 }
4846 else
4847 {
4848 succeeded = false;
4849 ExtOut(buffer);
4850 itr.MoveToNextObjectCarefully();
4851 }
4852 }
4853
4854 if (!DumpHeapImpl::ValidateSyncTable(gcheap))
4855 succeeded = false;
4856
4857 if (succeeded)
4858 ExtOut("No heap corruption detected.\n");
4859
4860 return S_OK;
4861 }
4862 catch(const sos::Exception &e)
4863 {
4864 ExtOut("%s\n", e.what());
4865 return E_FAIL;
4866 }
4867}
4868
4869#ifndef FEATURE_PAL
4870
4871enum failure_get_memory
4872{
4873 fgm_no_failure = 0,
4874 fgm_reserve_segment = 1,
4875 fgm_commit_segment_beg = 2,
4876 fgm_commit_eph_segment = 3,
4877 fgm_grow_table = 4,
4878 fgm_commit_table = 5
4879};
4880
4881enum oom_reason
4882{
4883 oom_no_failure = 0,
4884 oom_budget = 1,
4885 oom_cant_commit = 2,
4886 oom_cant_reserve = 3,
4887 oom_loh = 4,
4888 oom_low_mem = 5,
4889 oom_unproductive_full_gc = 6
4890};
4891
4892static const char *const str_oom[] =
4893{
4894 "There was no managed OOM due to allocations on the GC heap", // oom_no_failure
4895 "This is likely to be a bug in GC", // oom_budget
4896 "Didn't have enough memory to commit", // oom_cant_commit
4897 "This is likely to be a bug in GC", // oom_cant_reserve
4898 "Didn't have enough memory to allocate an LOH segment", // oom_loh
4899 "Low on memory during GC", // oom_low_mem
4900 "Could not do a full GC" // oom_unproductive_full_gc
4901};
4902
4903static const char *const str_fgm[] =
4904{
4905 "There was no failure to allocate memory", // fgm_no_failure
4906 "Failed to reserve memory", // fgm_reserve_segment
4907 "Didn't have enough memory to commit beginning of the segment", // fgm_commit_segment_beg
4908 "Didn't have enough memory to commit the new ephemeral segment", // fgm_commit_eph_segment
4909 "Didn't have enough memory to grow the internal GC data structures", // fgm_grow_table
4910 "Didn't have enough memory to commit the internal GC data structures", // fgm_commit_table
4911};
4912
4913void PrintOOMInfo(DacpOomData* oomData)
4914{
4915 ExtOut("Managed OOM occurred after GC #%d (Requested to allocate %d bytes)\n",
4916 oomData->gc_index, oomData->alloc_size);
4917
4918 if ((oomData->reason == oom_budget) ||
4919 (oomData->reason == oom_cant_reserve))
4920 {
4921 // TODO: This message needs to be updated with more precious info.
4922 ExtOut("%s, please contact PSS\n", str_oom[oomData->reason]);
4923 }
4924 else
4925 {
4926 ExtOut("Reason: %s\n", str_oom[oomData->reason]);
4927 }
4928
4929 // Now print out the more detailed memory info if any.
4930 if (oomData->fgm != fgm_no_failure)
4931 {
4932 ExtOut("Detail: %s: %s (%d bytes)",
4933 (oomData->loh_p ? "LOH" : "SOH"),
4934 str_fgm[oomData->fgm],
4935 oomData->size);
4936
4937 if ((oomData->fgm == fgm_commit_segment_beg) ||
4938 (oomData->fgm == fgm_commit_eph_segment) ||
4939 (oomData->fgm == fgm_grow_table) ||
4940 (oomData->fgm == fgm_commit_table))
4941 {
4942 // If it's a commit error (fgm_grow_table can indicate a reserve
4943 // or a commit error since we make one VirtualAlloc call to
4944 // reserve and commit), we indicate the available commit
4945 // space if we recorded it.
4946 if (oomData->available_pagefile_mb)
4947 {
4948 ExtOut(" - on GC entry available commit space was %d MB",
4949 oomData->available_pagefile_mb);
4950 }
4951 }
4952
4953 ExtOut("\n");
4954 }
4955}
4956
4957DECLARE_API(AnalyzeOOM)
4958{
4959 INIT_API();
4960 MINIDUMP_NOT_SUPPORTED();
4961
4962#ifndef FEATURE_PAL
4963
4964 if (!InitializeHeapData ())
4965 {
4966 ExtOut("GC Heap not initialized yet.\n");
4967 return S_OK;
4968 }
4969
4970 BOOL bHasManagedOOM = FALSE;
4971 DacpOomData oomData;
4972 memset (&oomData, 0, sizeof(oomData));
4973 if (!IsServerBuild())
4974 {
4975 if (oomData.Request(g_sos) != S_OK)
4976 {
4977 ExtOut("Error requesting OOM data\n");
4978 return E_FAIL;
4979 }
4980 if (oomData.reason != oom_no_failure)
4981 {
4982 bHasManagedOOM = TRUE;
4983 PrintOOMInfo(&oomData);
4984 }
4985 }
4986 else
4987 {
4988 DWORD dwNHeaps = GetGcHeapCount();
4989 DWORD dwAllocSize;
4990 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
4991 {
4992 ExtOut("Failed to get GCHeaps: integer overflow\n");
4993 return Status;
4994 }
4995
4996 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
4997 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
4998 {
4999 ExtOut("Failed to get GCHeaps\n");
5000 return Status;
5001 }
5002
5003 for (DWORD n = 0; n < dwNHeaps; n ++)
5004 {
5005 if (oomData.Request(g_sos, heapAddrs[n]) != S_OK)
5006 {
5007 ExtOut("Heap %d: Error requesting OOM data\n", n);
5008 return E_FAIL;
5009 }
5010 if (oomData.reason != oom_no_failure)
5011 {
5012 if (!bHasManagedOOM)
5013 {
5014 bHasManagedOOM = TRUE;
5015 }
5016 ExtOut("---------Heap %#-2d---------\n", n);
5017 PrintOOMInfo(&oomData);
5018 }
5019 }
5020 }
5021
5022 if (!bHasManagedOOM)
5023 {
5024 ExtOut("%s\n", str_oom[oomData.reason]);
5025 }
5026
5027 return S_OK;
5028#else
5029 _ASSERTE(false);
5030 return E_FAIL;
5031#endif // FEATURE_PAL
5032}
5033
5034DECLARE_API(VerifyObj)
5035{
5036 INIT_API();
5037 MINIDUMP_NOT_SUPPORTED();
5038
5039 TADDR taddrObj = 0;
5040 TADDR taddrMT;
5041 size_t objSize;
5042
5043 BOOL bValid = FALSE;
5044 BOOL dml = FALSE;
5045
5046 CMDOption option[] =
5047 { // name, vptr, type, hasValue
5048 {"/d", &dml, COBOOL, FALSE},
5049 };
5050 CMDValue arg[] =
5051 { // vptr, type
5052 {&taddrObj, COHEX}
5053 };
5054 size_t nArg;
5055 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
5056 {
5057 return Status;
5058 }
5059
5060 EnableDMLHolder dmlHolder(dml);
5061 BOOL bContainsPointers;
5062
5063 if (FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
5064 !GetSizeEfficient(taddrObj, taddrMT, FALSE, objSize, bContainsPointers))
5065 {
5066 ExtOut("object %#p does not have valid method table\n", SOS_PTR(taddrObj));
5067 goto Exit;
5068 }
5069
5070 // we need to build g_snapshot as it is later used in GetGeneration
5071 if (!g_snapshot.Build())
5072 {
5073 ExtOut("Unable to build snapshot of the garbage collector state\n");
5074 goto Exit;
5075 }
5076 {
5077 DacpGcHeapDetails *pheapDetails = g_snapshot.GetHeap(taddrObj);
5078 bValid = VerifyObject(*pheapDetails, taddrObj, taddrMT, objSize, TRUE);
5079 }
5080
5081Exit:
5082 if (bValid)
5083 {
5084 ExtOut("object %#p is a valid object\n", SOS_PTR(taddrObj));
5085 }
5086
5087 return Status;
5088}
5089
5090void LNODisplayOutput(LPCWSTR tag, TADDR pMT, TADDR currentObj, size_t size)
5091{
5092 sos::Object obj(currentObj, pMT);
5093 DMLOut("%S %s %12d (0x%x)\t%S\n", tag, DMLObject(currentObj), size, size, obj.GetTypeName());
5094}
5095
5096DECLARE_API(ListNearObj)
5097{
5098 INIT_API();
5099 MINIDUMP_NOT_SUPPORTED();
5100
5101#if !defined(FEATURE_PAL)
5102
5103 TADDR taddrArg = 0;
5104 TADDR taddrObj = 0;
5105 // we may want to provide a more exact version of searching for the
5106 // previous object in the heap, using the brick table, instead of
5107 // looking for what may be valid method tables...
5108 //BOOL bExact;
5109 //CMDOption option[] =
5110 //{
5111 // // name, vptr, type, hasValue
5112 // {"-exact", &bExact, COBOOL, FALSE}
5113 //};
5114
5115 BOOL dml = FALSE;
5116 CMDOption option[] =
5117 { // name, vptr, type, hasValue
5118 {"/d", &dml, COBOOL, FALSE},
5119 };
5120 CMDValue arg[] =
5121 {
5122 // vptr, type
5123 {&taddrArg, COHEX}
5124 };
5125 size_t nArg;
5126 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || nArg != 1)
5127 {
5128 ExtOut("Usage: !ListNearObj <obj_address>\n");
5129 return Status;
5130 }
5131
5132 EnableDMLHolder dmlHolder(dml);
5133
5134 if (!g_snapshot.Build())
5135 {
5136 ExtOut("Unable to build snapshot of the garbage collector state\n");
5137 return Status;
5138 }
5139
5140 taddrObj = Align(taddrArg);
5141
5142 DacpGcHeapDetails *heap = g_snapshot.GetHeap(taddrArg);
5143 if (heap == NULL)
5144 {
5145 ExtOut("Address %p does not lie in the managed heap\n", SOS_PTR(taddrObj));
5146 return Status;
5147 }
5148
5149 TADDR_SEGINFO trngSeg = {0, 0, 0};
5150 TADDR_RANGE allocCtx = {0, 0};
5151 BOOL bLarge;
5152 int gen;
5153 if (!GCObjInHeap(taddrObj, *heap, trngSeg, gen, allocCtx, bLarge))
5154 {
5155 ExtOut("Failed to find the segment of the managed heap where the object %p resides\n",
5156 SOS_PTR(taddrObj));
5157 return Status;
5158 }
5159
5160 TADDR objMT = NULL;
5161 size_t objSize = 0;
5162 BOOL bObj = FALSE;
5163 TADDR taddrCur;
5164 TADDR curMT = 0;
5165 size_t curSize = 0;
5166 BOOL bCur = FALSE;
5167 TADDR taddrNxt;
5168 TADDR nxtMT = 0;
5169 size_t nxtSize = 0;
5170 BOOL bNxt = FALSE;
5171 BOOL bContainsPointers;
5172
5173 std::vector<TADDR> candidate;
5174 candidate.reserve(10);
5175
5176 // since we'll be reading back I'll prime the read cache to a buffer before the current address
5177 MOVE(taddrCur, _max(trngSeg.start, taddrObj-DT_OS_PAGE_SIZE));
5178
5179 // ===== Look for a good candidate preceeding taddrObj
5180
5181 for (taddrCur = taddrObj - sizeof(TADDR); taddrCur >= trngSeg.start; taddrCur -= sizeof(TADDR))
5182 {
5183 // currently we don't pay attention to allocation contexts. if this
5184 // proves to be an issue we need to reconsider the code below
5185 if (SUCCEEDED(GetMTOfObject(taddrCur, &curMT)) &&
5186 GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers))
5187 {
5188 // remember this as one of the possible "good" objects preceeding taddrObj
5189 candidate.push_back(taddrCur);
5190
5191 std::vector<TADDR>::iterator it =
5192 std::find(candidate.begin(), candidate.end(), taddrCur+curSize);
5193 if (it != candidate.end())
5194 {
5195 // We found a chain of two objects preceeding taddrObj. We'll
5196 // trust this is a good indication that the two objects are valid.
5197 // What is not valid is possibly the object following the second
5198 // one...
5199 taddrCur = *it;
5200 GetMTOfObject(taddrCur, &curMT);
5201 GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
5202 bCur = TRUE;
5203 break;
5204 }
5205 }
5206 }
5207
5208 if (!bCur && !candidate.empty())
5209 {
5210 // pick the closest object to taddrObj
5211 taddrCur = *(candidate.begin());
5212 GetMTOfObject(taddrCur, &curMT);
5213 GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
5214 // we have a candidate, even if not confirmed
5215 bCur = TRUE;
5216 }
5217
5218 taddrNxt = taddrObj;
5219 if (taddrArg == taddrObj)
5220 {
5221 taddrNxt += sizeof(TADDR);
5222 }
5223
5224 // ===== Now look at taddrObj
5225 if (taddrObj == taddrArg)
5226 {
5227 // only look at taddrObj if it's the same as what user passed in, meaning it's aligned.
5228 if (SUCCEEDED(GetMTOfObject(taddrObj, &objMT)) &&
5229 GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
5230 {
5231 bObj = TRUE;
5232 taddrNxt = taddrObj+objSize;
5233 }
5234 }
5235
5236 if ((taddrCur + curSize > taddrArg) && taddrCur + curSize < trngSeg.end)
5237 {
5238 if (SUCCEEDED(GetMTOfObject(taddrCur + curSize, &nxtMT)) &&
5239 GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
5240 {
5241 taddrNxt = taddrCur+curSize;
5242 }
5243 }
5244
5245 // ===== And finally move on to elements following taddrObj
5246
5247 for (; taddrNxt < trngSeg.end; taddrNxt += sizeof(TADDR))
5248 {
5249 if (SUCCEEDED(GetMTOfObject(taddrNxt, &nxtMT)) &&
5250 GetSizeEfficient(taddrNxt, nxtMT, bLarge, nxtSize, bContainsPointers))
5251 {
5252 bNxt = TRUE;
5253 break;
5254 }
5255 }
5256
5257 if (bCur)
5258 LNODisplayOutput(W("Before: "), curMT, taddrCur, curSize);
5259 else
5260 ExtOut("Before: couldn't find any object between %#p and %#p\n",
5261 SOS_PTR(trngSeg.start), SOS_PTR(taddrArg));
5262
5263 if (bObj)
5264 LNODisplayOutput(W("Current:"), objMT, taddrObj, objSize);
5265
5266 if (bNxt)
5267 LNODisplayOutput(W("After: "), nxtMT, taddrNxt, nxtSize);
5268 else
5269 ExtOut("After: couldn't find any object between %#p and %#p\n",
5270 SOS_PTR(taddrArg), SOS_PTR(trngSeg.end));
5271
5272 if (bCur && bNxt &&
5273 (((taddrCur+curSize == taddrObj) && (taddrObj+objSize == taddrNxt)) || (taddrCur+curSize == taddrNxt)))
5274 {
5275 ExtOut("Heap local consistency confirmed.\n");
5276 }
5277 else
5278 {
5279 ExtOut("Heap local consistency not confirmed.\n");
5280 }
5281
5282 return Status;
5283
5284#else
5285
5286 _ASSERTE(false);
5287 return E_FAIL;
5288
5289#endif // FEATURE_PAL
5290}
5291
5292
5293DECLARE_API(GCHeapStat)
5294{
5295 INIT_API();
5296 MINIDUMP_NOT_SUPPORTED();
5297
5298
5299#ifndef FEATURE_PAL
5300
5301 BOOL bIncUnreachable = FALSE;
5302 BOOL dml = FALSE;
5303
5304 CMDOption option[] = {
5305 // name, vptr, type, hasValue
5306 {"-inclUnrooted", &bIncUnreachable, COBOOL, FALSE},
5307 {"-iu", &bIncUnreachable, COBOOL, FALSE},
5308 {"/d", &dml, COBOOL, FALSE}
5309 };
5310
5311 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
5312 {
5313 return Status;
5314 }
5315
5316 EnableDMLHolder dmlHolder(dml);
5317 ExtOut("%-8s %12s %12s %12s %12s\n", "Heap", "Gen0", "Gen1", "Gen2", "LOH");
5318
5319 if (!IsServerBuild())
5320 {
5321 float tempf;
5322 DacpGcHeapDetails heapDetails;
5323 if (heapDetails.Request(g_sos) != S_OK)
5324 {
5325 ExtErr("Error requesting gc heap details\n");
5326 return Status;
5327 }
5328
5329 HeapUsageStat hpUsage;
5330 if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage))
5331 {
5332 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", 0,
5333 hpUsage.genUsage[0].allocd, hpUsage.genUsage[1].allocd,
5334 hpUsage.genUsage[2].allocd, hpUsage.genUsage[3].allocd);
5335 ExtOut("\nFree space: Percentage\n");
5336 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u ", 0,
5337 hpUsage.genUsage[0].freed, hpUsage.genUsage[1].freed,
5338 hpUsage.genUsage[2].freed, hpUsage.genUsage[3].freed);
5339 tempf = ((float)(hpUsage.genUsage[0].freed+hpUsage.genUsage[1].freed+hpUsage.genUsage[2].freed)) /
5340 (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
5341 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
5342 (int)(100*((float)hpUsage.genUsage[3].freed) / (hpUsage.genUsage[3].allocd)));
5343 if (bIncUnreachable)
5344 {
5345 ExtOut("\nUnrooted objects: Percentage\n");
5346 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u ", 0,
5347 hpUsage.genUsage[0].unrooted, hpUsage.genUsage[1].unrooted,
5348 hpUsage.genUsage[2].unrooted, hpUsage.genUsage[3].unrooted);
5349 tempf = ((float)(hpUsage.genUsage[0].unrooted+hpUsage.genUsage[1].unrooted+hpUsage.genUsage[2].unrooted)) /
5350 (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
5351 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
5352 (int)(100*((float)hpUsage.genUsage[3].unrooted) / (hpUsage.genUsage[3].allocd)));
5353 }
5354 }
5355 }
5356 else
5357 {
5358 float tempf;
5359 DacpGcHeapData gcheap;
5360 if (gcheap.Request(g_sos) != S_OK)
5361 {
5362 ExtErr("Error requesting GC Heap data\n");
5363 return Status;
5364 }
5365
5366 DWORD dwAllocSize;
5367 DWORD dwNHeaps = gcheap.HeapCount;
5368 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
5369 {
5370 ExtErr("Failed to get GCHeaps: integer overflow\n");
5371 return Status;
5372 }
5373
5374 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
5375 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
5376 {
5377 ExtErr("Failed to get GCHeaps\n");
5378 return Status;
5379 }
5380
5381 ArrayHolder<HeapUsageStat> hpUsage = new NOTHROW HeapUsageStat[dwNHeaps];
5382 if (hpUsage == NULL)
5383 {
5384 ReportOOM();
5385 return Status;
5386 }
5387
5388 // aggregate stats across heaps / generation
5389 GenUsageStat genUsageStat[4] = {0, 0, 0, 0};
5390
5391 for (DWORD n = 0; n < dwNHeaps; n ++)
5392 {
5393 DacpGcHeapDetails heapDetails;
5394 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
5395 {
5396 ExtErr("Error requesting gc heap details\n");
5397 return Status;
5398 }
5399
5400 if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage[n]))
5401 {
5402 for (int i = 0; i < 4; ++i)
5403 {
5404 genUsageStat[i].allocd += hpUsage[n].genUsage[i].allocd;
5405 genUsageStat[i].freed += hpUsage[n].genUsage[i].freed;
5406 if (bIncUnreachable)
5407 {
5408 genUsageStat[i].unrooted += hpUsage[n].genUsage[i].unrooted;
5409 }
5410 }
5411 }
5412 }
5413
5414 for (DWORD n = 0; n < dwNHeaps; n ++)
5415 {
5416 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", n,
5417 hpUsage[n].genUsage[0].allocd, hpUsage[n].genUsage[1].allocd,
5418 hpUsage[n].genUsage[2].allocd, hpUsage[n].genUsage[3].allocd);
5419 }
5420 ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
5421 genUsageStat[0].allocd, genUsageStat[1].allocd,
5422 genUsageStat[2].allocd, genUsageStat[3].allocd);
5423
5424 ExtOut("\nFree space: Percentage\n");
5425 for (DWORD n = 0; n < dwNHeaps; n ++)
5426 {
5427 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u ", n,
5428 hpUsage[n].genUsage[0].freed, hpUsage[n].genUsage[1].freed,
5429 hpUsage[n].genUsage[2].freed, hpUsage[n].genUsage[3].freed);
5430
5431 tempf = ((float)(hpUsage[n].genUsage[0].freed+hpUsage[n].genUsage[1].freed+hpUsage[n].genUsage[2].freed)) /
5432 (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
5433 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
5434 (int)(100*((float)hpUsage[n].genUsage[3].freed) / (hpUsage[n].genUsage[3].allocd))
5435 );
5436 }
5437 ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
5438 genUsageStat[0].freed, genUsageStat[1].freed,
5439 genUsageStat[2].freed, genUsageStat[3].freed);
5440
5441 if (bIncUnreachable)
5442 {
5443 ExtOut("\nUnrooted objects: Percentage\n");
5444 for (DWORD n = 0; n < dwNHeaps; n ++)
5445 {
5446 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u ", n,
5447 hpUsage[n].genUsage[0].unrooted, hpUsage[n].genUsage[1].unrooted,
5448 hpUsage[n].genUsage[2].unrooted, hpUsage[n].genUsage[3].unrooted);
5449
5450 tempf = ((float)(hpUsage[n].genUsage[0].unrooted+hpUsage[n].genUsage[1].unrooted+hpUsage[n].genUsage[2].unrooted)) /
5451 (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
5452 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
5453 (int)(100*((float)hpUsage[n].genUsage[3].unrooted) / (hpUsage[n].genUsage[3].allocd)));
5454 }
5455 ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
5456 genUsageStat[0].unrooted, genUsageStat[1].unrooted,
5457 genUsageStat[2].unrooted, genUsageStat[3].unrooted);
5458 }
5459
5460 }
5461
5462 return Status;
5463
5464#else
5465
5466 _ASSERTE(false);
5467 return E_FAIL;
5468
5469#endif // FEATURE_PAL
5470}
5471
5472#endif // FEATURE_PAL
5473
5474/**********************************************************************\
5475* Routine Description: *
5476* *
5477* This function dumps what is in the syncblock cache. By default *
5478* it dumps all active syncblocks. Using -all to dump all syncblocks
5479* *
5480\**********************************************************************/
5481DECLARE_API(SyncBlk)
5482{
5483 INIT_API();
5484 MINIDUMP_NOT_SUPPORTED();
5485
5486 BOOL bDumpAll = FALSE;
5487 size_t nbAsked = 0;
5488 BOOL dml = FALSE;
5489
5490 CMDOption option[] =
5491 { // name, vptr, type, hasValue
5492 {"-all", &bDumpAll, COBOOL, FALSE},
5493 {"/d", &dml, COBOOL, FALSE}
5494 };
5495 CMDValue arg[] =
5496 { // vptr, type
5497 {&nbAsked, COSIZE_T}
5498 };
5499 size_t nArg;
5500 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
5501 {
5502 return Status;
5503 }
5504
5505 EnableDMLHolder dmlHolder(dml);
5506 DacpSyncBlockData syncBlockData;
5507 if (syncBlockData.Request(g_sos,1) != S_OK)
5508 {
5509 ExtOut("Error requesting SyncBlk data\n");
5510 return Status;
5511 }
5512
5513 DWORD dwCount = syncBlockData.SyncBlockCount;
5514
5515 ExtOut("Index" WIN64_8SPACES " SyncBlock MonitorHeld Recursion Owning Thread Info" WIN64_8SPACES " SyncBlock Owner\n");
5516 ULONG freeCount = 0;
5517 ULONG CCWCount = 0;
5518 ULONG RCWCount = 0;
5519 ULONG CFCount = 0;
5520 for (DWORD nb = 1; nb <= dwCount; nb++)
5521 {
5522 if (IsInterrupt())
5523 return Status;
5524
5525 if (nbAsked && nb != nbAsked)
5526 {
5527 continue;
5528 }
5529
5530 if (syncBlockData.Request(g_sos,nb) != S_OK)
5531 {
5532 ExtOut("SyncBlock %d is invalid%s\n", nb,
5533 (nb != nbAsked) ? ", continuing..." : "");
5534 continue;
5535 }
5536
5537 BOOL bPrint = (bDumpAll || nb == nbAsked || (syncBlockData.MonitorHeld > 0 && !syncBlockData.bFree));
5538
5539 if (bPrint)
5540 {
5541 ExtOut("%5d ", nb);
5542 if (!syncBlockData.bFree || nb != nbAsked)
5543 {
5544 ExtOut("%p ", syncBlockData.SyncBlockPointer);
5545 ExtOut("%11d ", syncBlockData.MonitorHeld);
5546 ExtOut("%9d ", syncBlockData.Recursion);
5547 ExtOut("%p ", syncBlockData.HoldingThread);
5548
5549 if (syncBlockData.HoldingThread == ~0ul)
5550 {
5551 ExtOut(" orphaned ");
5552 }
5553 else if (syncBlockData.HoldingThread != NULL)
5554 {
5555 DacpThreadData Thread;
5556 if ((Status = Thread.Request(g_sos, syncBlockData.HoldingThread)) != S_OK)
5557 {
5558 ExtOut("Failed to request Thread at %p\n", syncBlockData.HoldingThread);
5559 return Status;
5560 }
5561
5562 DMLOut(DMLThreadID(Thread.osThreadId));
5563 ULONG id;
5564 if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
5565 {
5566 ExtOut("%4d ", id);
5567 }
5568 else
5569 {
5570 ExtOut(" XXX ");
5571 }
5572 }
5573 else
5574 {
5575 ExtOut(" none ");
5576 }
5577
5578 if (syncBlockData.bFree)
5579 {
5580 ExtOut(" %8d", 0); // TODO: do we need to print the free synctable list?
5581 }
5582 else
5583 {
5584 sos::Object obj = TO_TADDR(syncBlockData.Object);
5585 DMLOut(" %s %S", DMLObject(syncBlockData.Object), obj.GetTypeName());
5586 }
5587 }
5588 }
5589
5590 if (syncBlockData.bFree)
5591 {
5592 freeCount ++;
5593 if (bPrint) {
5594 ExtOut(" Free");
5595 }
5596 }
5597 else
5598 {
5599#ifdef FEATURE_COMINTEROP
5600 if (syncBlockData.COMFlags) {
5601 switch (syncBlockData.COMFlags) {
5602 case SYNCBLOCKDATA_COMFLAGS_CCW:
5603 CCWCount ++;
5604 break;
5605 case SYNCBLOCKDATA_COMFLAGS_RCW:
5606 RCWCount ++;
5607 break;
5608 case SYNCBLOCKDATA_COMFLAGS_CF:
5609 CFCount ++;
5610 break;
5611 }
5612 }
5613#endif // FEATURE_COMINTEROP
5614 }
5615
5616 if (syncBlockData.MonitorHeld > 1)
5617 {
5618 // TODO: implement this
5619 /*
5620 ExtOut(" ");
5621 DWORD_PTR pHead = (DWORD_PTR)vSyncBlock.m_Link.m_pNext;
5622 DWORD_PTR pNext = pHead;
5623 Thread vThread;
5624
5625 while (1)
5626 {
5627 if (IsInterrupt())
5628 return Status;
5629 DWORD_PTR pWaitEventLink = pNext - offsetLinkSB;
5630 WaitEventLink vWaitEventLink;
5631 vWaitEventLink.Fill(pWaitEventLink);
5632 if (!CallStatus) {
5633 break;
5634 }
5635 DWORD_PTR dwAddr = (DWORD_PTR)vWaitEventLink.m_Thread;
5636 ExtOut("%x ", dwAddr);
5637 vThread.Fill (dwAddr);
5638 if (!CallStatus) {
5639 break;
5640 }
5641 if (bPrint)
5642 DMLOut("%s,", DMLThreadID(vThread.m_OSThreadId));
5643 pNext = (DWORD_PTR)vWaitEventLink.m_LinkSB.m_pNext;
5644 if (pNext == 0)
5645 break;
5646 }
5647 */
5648 }
5649
5650 if (bPrint)
5651 ExtOut("\n");
5652 }
5653
5654 ExtOut("-----------------------------\n");
5655 ExtOut("Total %d\n", dwCount);
5656#ifdef FEATURE_COMINTEROP
5657 ExtOut("CCW %d\n", CCWCount);
5658 ExtOut("RCW %d\n", RCWCount);
5659 ExtOut("ComClassFactory %d\n", CFCount);
5660#endif
5661 ExtOut("Free %d\n", freeCount);
5662
5663 return Status;
5664}
5665
5666#ifndef FEATURE_PAL
5667
5668#ifdef FEATURE_COMINTEROP
5669struct VisitRcwArgs
5670{
5671 BOOL bDetail;
5672 UINT MTACount;
5673 UINT STACount;
5674 ULONG FTMCount;
5675};
5676
5677void VisitRcw(CLRDATA_ADDRESS RCW,CLRDATA_ADDRESS Context,CLRDATA_ADDRESS Thread, BOOL bIsFreeThreaded, LPVOID token)
5678{
5679 VisitRcwArgs *pArgs = (VisitRcwArgs *) token;
5680
5681 if (pArgs->bDetail)
5682 {
5683 if (pArgs->MTACount == 0 && pArgs->STACount == 0 && pArgs->FTMCount == 0)
5684 {
5685 // First time, print a header
5686 ExtOut("RuntimeCallableWrappers (RCW) to be cleaned:\n");
5687 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Apartment\n",
5688 "RCW", "CONTEXT", "THREAD");
5689 }
5690 LPCSTR szThreadApartment;
5691 if (bIsFreeThreaded)
5692 {
5693 szThreadApartment = "(FreeThreaded)";
5694 pArgs->FTMCount++;
5695 }
5696 else if (Thread == NULL)
5697 {
5698 szThreadApartment = "(MTA)";
5699 pArgs->MTACount++;
5700 }
5701 else
5702 {
5703 szThreadApartment = "(STA)";
5704 pArgs->STACount++;
5705 }
5706
5707 ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %9s\n",
5708 SOS_PTR(RCW),
5709 SOS_PTR(Context),
5710 SOS_PTR(Thread),
5711 szThreadApartment);
5712 }
5713}
5714
5715DECLARE_API(RCWCleanupList)
5716{
5717 INIT_API();
5718 MINIDUMP_NOT_SUPPORTED();
5719
5720 DWORD_PTR p_CleanupList = GetExpression(args);
5721
5722 VisitRcwArgs travArgs;
5723 ZeroMemory(&travArgs,sizeof(VisitRcwArgs));
5724 travArgs.bDetail = TRUE;
5725
5726 // We need to detect when !RCWCleanupList is called with an expression which evaluates to 0
5727 // (to print out an Invalid parameter message), but at the same time we need to allow an
5728 // empty argument list which would result in p_CleanupList equaling 0.
5729 if (p_CleanupList || strlen(args) == 0)
5730 {
5731 HRESULT hr = g_sos->TraverseRCWCleanupList(p_CleanupList, (VISITRCWFORCLEANUP)VisitRcw, &travArgs);
5732
5733 if (SUCCEEDED(hr))
5734 {
5735 ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
5736 ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
5737 ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
5738 }
5739 else
5740 {
5741 ExtOut("An error occurred while traversing the cleanup list.\n");
5742 }
5743 }
5744 else
5745 {
5746 ExtOut("Invalid parameter %s\n", args);
5747 }
5748
5749 return Status;
5750}
5751#endif // FEATURE_COMINTEROP
5752
5753
5754/**********************************************************************\
5755* Routine Description: *
5756* *
5757* This function is called to dump the contents of the finalizer *
5758* queue. *
5759* *
5760\**********************************************************************/
5761DECLARE_API(FinalizeQueue)
5762{
5763 INIT_API();
5764 MINIDUMP_NOT_SUPPORTED();
5765
5766 BOOL bDetail = FALSE;
5767 BOOL bAllReady = FALSE;
5768 BOOL bShort = FALSE;
5769 BOOL dml = FALSE;
5770 TADDR taddrMT = 0;
5771
5772 CMDOption option[] =
5773 { // name, vptr, type, hasValue
5774 {"-detail", &bDetail, COBOOL, FALSE},
5775 {"-allReady", &bAllReady, COBOOL, FALSE},
5776 {"-short", &bShort, COBOOL, FALSE},
5777 {"/d", &dml, COBOOL, FALSE},
5778 {"-mt", &taddrMT, COHEX, TRUE},
5779 };
5780
5781 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
5782 {
5783 return Status;
5784 }
5785
5786 EnableDMLHolder dmlHolder(dml);
5787 if (!bShort)
5788 {
5789 DacpSyncBlockCleanupData dsbcd;
5790 CLRDATA_ADDRESS sbCurrent = NULL;
5791 ULONG cleanCount = 0;
5792 while ((dsbcd.Request(g_sos,sbCurrent) == S_OK) && dsbcd.SyncBlockPointer)
5793 {
5794 if (bDetail)
5795 {
5796 if (cleanCount == 0) // print first time only
5797 {
5798 ExtOut("SyncBlocks to be cleaned by the finalizer thread:\n");
5799 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
5800 "SyncBlock", "RCW", "CCW", "ComClassFactory");
5801 }
5802
5803 ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p\n",
5804 (ULONG64) dsbcd.SyncBlockPointer,
5805 (ULONG64) dsbcd.blockRCW,
5806 (ULONG64) dsbcd.blockCCW,
5807 (ULONG64) dsbcd.blockClassFactory);
5808 }
5809
5810 cleanCount++;
5811 sbCurrent = dsbcd.nextSyncBlock;
5812 if (sbCurrent == NULL)
5813 {
5814 break;
5815 }
5816 }
5817
5818 ExtOut("SyncBlocks to be cleaned up: %d\n", cleanCount);
5819
5820#ifdef FEATURE_COMINTEROP
5821 VisitRcwArgs travArgs;
5822 ZeroMemory(&travArgs,sizeof(VisitRcwArgs));
5823 travArgs.bDetail = bDetail;
5824 g_sos->TraverseRCWCleanupList(0, (VISITRCWFORCLEANUP) VisitRcw, &travArgs);
5825 ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
5826 ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
5827 ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
5828#endif // FEATURE_COMINTEROP
5829
5830// noRCW:
5831 ExtOut("----------------------------------\n");
5832 }
5833
5834 // GC Heap
5835 DWORD dwNHeaps = GetGcHeapCount();
5836
5837 HeapStat hpStat;
5838
5839 if (!IsServerBuild())
5840 {
5841 DacpGcHeapDetails heapDetails;
5842 if (heapDetails.Request(g_sos) != S_OK)
5843 {
5844 ExtOut("Error requesting details\n");
5845 return Status;
5846 }
5847
5848 GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
5849 }
5850 else
5851 {
5852 DWORD dwAllocSize;
5853 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
5854 {
5855 ExtOut("Failed to get GCHeaps: integer overflow\n");
5856 return Status;
5857 }
5858
5859 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
5860 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
5861 {
5862 ExtOut("Failed to get GCHeaps\n");
5863 return Status;
5864 }
5865
5866 for (DWORD n = 0; n < dwNHeaps; n ++)
5867 {
5868 DacpGcHeapDetails heapDetails;
5869 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
5870 {
5871 ExtOut("Error requesting details\n");
5872 return Status;
5873 }
5874
5875 ExtOut("------------------------------\n");
5876 ExtOut("Heap %d\n", n);
5877 GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
5878 }
5879 }
5880
5881 if (!bShort)
5882 {
5883 if (bAllReady)
5884 {
5885 PrintGCStat(&hpStat, "Statistics for all finalizable objects that are no longer rooted:\n");
5886 }
5887 else
5888 {
5889 PrintGCStat(&hpStat, "Statistics for all finalizable objects (including all objects ready for finalization):\n");
5890 }
5891 }
5892
5893 return Status;
5894}
5895
5896#endif // FEATURE_PAL
5897
5898enum {
5899 // These are the values set in m_dwTransientFlags.
5900 // Note that none of these flags survive a prejit save/restore.
5901
5902 M_CRST_NOTINITIALIZED = 0x00000001, // Used to prevent destruction of garbage m_crst
5903 M_LOOKUPCRST_NOTINITIALIZED = 0x00000002,
5904
5905 SUPPORTS_UPDATEABLE_METHODS = 0x00000020,
5906 CLASSES_FREED = 0x00000040,
5907 HAS_PHONY_IL_RVAS = 0x00000080,
5908 IS_EDIT_AND_CONTINUE = 0x00000200,
5909};
5910
5911void ModuleMapTraverse(UINT index, CLRDATA_ADDRESS methodTable, LPVOID token)
5912{
5913 ULONG32 rid = (ULONG32)(size_t)token;
5914 NameForMT_s(TO_TADDR(methodTable), g_mdName, mdNameLen);
5915
5916 DMLOut("%s 0x%08x %S\n", DMLMethodTable(methodTable), (ULONG32)TokenFromRid(rid, index), g_mdName);
5917}
5918
5919
5920/**********************************************************************\
5921* Routine Description: *
5922* *
5923* This function is called to dump the contents of a Module *
5924* for a given address *
5925* *
5926\**********************************************************************/
5927DECLARE_API(DumpModule)
5928{
5929 INIT_API();
5930 MINIDUMP_NOT_SUPPORTED();
5931
5932
5933 DWORD_PTR p_ModuleAddr = NULL;
5934 BOOL bMethodTables = FALSE;
5935 BOOL dml = FALSE;
5936
5937 CMDOption option[] =
5938 { // name, vptr, type, hasValue
5939 {"-mt", &bMethodTables, COBOOL, FALSE},
5940#ifndef FEATURE_PAL
5941 {"/d", &dml, COBOOL, FALSE}
5942#endif
5943 };
5944 CMDValue arg[] =
5945 { // vptr, type
5946 {&p_ModuleAddr, COHEX}
5947 };
5948
5949 size_t nArg;
5950 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
5951 {
5952 return Status;
5953 }
5954 if (nArg != 1)
5955 {
5956 ExtOut("Usage: DumpModule [-mt] <Module Address>\n");
5957 return Status;
5958 }
5959
5960 EnableDMLHolder dmlHolder(dml);
5961 DacpModuleData module;
5962 if ((Status=module.Request(g_sos, TO_CDADDR(p_ModuleAddr)))!=S_OK)
5963 {
5964 ExtOut("Fail to fill Module %p\n", SOS_PTR(p_ModuleAddr));
5965 return Status;
5966 }
5967
5968 WCHAR FileName[MAX_LONGPATH];
5969 FileNameForModule (&module, FileName);
5970 ExtOut("Name: %S\n", FileName[0] ? FileName : W("Unknown Module"));
5971
5972 ExtOut("Attributes: ");
5973 if (module.bIsPEFile)
5974 ExtOut("PEFile ");
5975 if (module.bIsReflection)
5976 ExtOut("Reflection ");
5977 if (module.dwTransientFlags & SUPPORTS_UPDATEABLE_METHODS)
5978 ExtOut("SupportsUpdateableMethods");
5979 ExtOut("\n");
5980
5981 DMLOut("Assembly: %s\n", DMLAssembly(module.Assembly));
5982
5983 ExtOut("LoaderHeap: %p\n", SOS_PTR(module.pLookupTableHeap));
5984 ExtOut("TypeDefToMethodTableMap: %p\n", SOS_PTR(module.TypeDefToMethodTableMap));
5985 ExtOut("TypeRefToMethodTableMap: %p\n", SOS_PTR(module.TypeRefToMethodTableMap));
5986 ExtOut("MethodDefToDescMap: %p\n", SOS_PTR(module.MethodDefToDescMap));
5987 ExtOut("FieldDefToDescMap: %p\n", SOS_PTR(module.FieldDefToDescMap));
5988 ExtOut("MemberRefToDescMap: %p\n", SOS_PTR(module.MemberRefToDescMap));
5989 ExtOut("FileReferencesMap: %p\n", SOS_PTR(module.FileReferencesMap));
5990 ExtOut("AssemblyReferencesMap: %p\n", SOS_PTR(module.ManifestModuleReferencesMap));
5991
5992 if (module.ilBase && module.metadataStart)
5993 ExtOut("MetaData start address: %p (%d bytes)\n", SOS_PTR(module.metadataStart), module.metadataSize);
5994
5995 if (bMethodTables)
5996 {
5997 ExtOut("\nTypes defined in this module\n\n");
5998 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeDef", "Name");
5999
6000 ExtOut("------------------------------------------------------------------------------\n");
6001 g_sos->TraverseModuleMap(TYPEDEFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);
6002
6003 ExtOut("\nTypes referenced in this module\n\n");
6004 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeRef", "Name");
6005
6006 ExtOut("------------------------------------------------------------------------------\n");
6007 g_sos->TraverseModuleMap(TYPEREFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);
6008 }
6009
6010 return Status;
6011}
6012
6013/**********************************************************************\
6014* Routine Description: *
6015* *
6016* This function is called to dump the contents of a Domain *
6017* for a given address *
6018* *
6019\**********************************************************************/
6020DECLARE_API(DumpDomain)
6021{
6022 INIT_API();
6023 MINIDUMP_NOT_SUPPORTED();
6024
6025 DWORD_PTR p_DomainAddr = 0;
6026 BOOL dml = FALSE;
6027
6028 CMDOption option[] =
6029 { // name, vptr, type, hasValue
6030#ifndef FEATURE_PAL
6031 {"/d", &dml, COBOOL, FALSE},
6032#endif
6033 };
6034 CMDValue arg[] =
6035 { // vptr, type
6036 {&p_DomainAddr, COHEX},
6037 };
6038 size_t nArg;
6039
6040 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
6041 {
6042 return Status;
6043 }
6044
6045 EnableDMLHolder dmlHolder(dml);
6046
6047 DacpAppDomainStoreData adsData;
6048 if ((Status=adsData.Request(g_sos))!=S_OK)
6049 {
6050 ExtOut("Unable to get AppDomain information\n");
6051 return Status;
6052 }
6053
6054 if (p_DomainAddr)
6055 {
6056 DacpAppDomainData appDomain1;
6057 if ((Status=appDomain1.Request(g_sos, TO_CDADDR(p_DomainAddr)))!=S_OK)
6058 {
6059 ExtOut("Fail to fill AppDomain\n");
6060 return Status;
6061 }
6062
6063 ExtOut("--------------------------------------\n");
6064
6065 if (p_DomainAddr == adsData.sharedDomain)
6066 {
6067 DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain));
6068 }
6069 else if (p_DomainAddr == adsData.systemDomain)
6070 {
6071 DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain));
6072 }
6073 else
6074 {
6075 DMLOut("Domain %d:%s %s\n", appDomain1.dwId, (appDomain1.dwId >= 10) ? "" : " ", DMLDomain(p_DomainAddr));
6076 }
6077
6078 DomainInfo(&appDomain1);
6079 return Status;
6080 }
6081
6082 ExtOut("--------------------------------------\n");
6083 DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain));
6084 DacpAppDomainData appDomain;
6085 if ((Status=appDomain.Request(g_sos,adsData.systemDomain))!=S_OK)
6086 {
6087 ExtOut("Unable to get system domain info.\n");
6088 return Status;
6089 }
6090 DomainInfo(&appDomain);
6091
6092 if (adsData.sharedDomain != NULL)
6093 {
6094 ExtOut("--------------------------------------\n");
6095 DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain));
6096 if ((Status=appDomain.Request(g_sos, adsData.sharedDomain))!=S_OK)
6097 {
6098 ExtOut("Unable to get shared domain info\n");
6099 return Status;
6100 }
6101 DomainInfo(&appDomain);
6102 }
6103
6104 ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
6105 if (pArray==NULL)
6106 {
6107 ReportOOM();
6108 return Status;
6109 }
6110
6111 if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
6112 {
6113 ExtOut("Unable to get array of AppDomains\n");
6114 return Status;
6115 }
6116
6117 for (int n=0;n<adsData.DomainCount;n++)
6118 {
6119 if (IsInterrupt())
6120 break;
6121
6122 if ((Status=appDomain.Request(g_sos, pArray[n])) != S_OK)
6123 {
6124 ExtOut("Failed to get appdomain %p, error %lx\n", SOS_PTR(pArray[n]), Status);
6125 return Status;
6126 }
6127
6128 ExtOut("--------------------------------------\n");
6129 DMLOut("Domain %d:%s %s\n", appDomain.dwId, (appDomain.dwId >= 10) ? "" : " ", DMLDomain(pArray[n]));
6130 DomainInfo(&appDomain);
6131 }
6132
6133 return Status;
6134}
6135
6136/**********************************************************************\
6137* Routine Description: *
6138* *
6139* This function is called to dump the contents of a Assembly *
6140* for a given address *
6141* *
6142\**********************************************************************/
6143DECLARE_API(DumpAssembly)
6144{
6145 INIT_API();
6146 MINIDUMP_NOT_SUPPORTED();
6147
6148 DWORD_PTR p_AssemblyAddr = 0;
6149 BOOL dml = FALSE;
6150
6151 CMDOption option[] =
6152 { // name, vptr, type, hasValue
6153#ifndef FEATURE_PAL
6154 {"/d", &dml, COBOOL, FALSE},
6155#endif
6156 };
6157 CMDValue arg[] =
6158 { // vptr, type
6159 {&p_AssemblyAddr, COHEX},
6160 };
6161 size_t nArg;
6162
6163 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
6164 {
6165 return Status;
6166 }
6167
6168 EnableDMLHolder dmlHolder(dml);
6169
6170 if (p_AssemblyAddr == 0)
6171 {
6172 ExtOut("Invalid Assembly %s\n", args);
6173 return Status;
6174 }
6175
6176 DacpAssemblyData Assembly;
6177 if ((Status=Assembly.Request(g_sos, TO_CDADDR(p_AssemblyAddr)))!=S_OK)
6178 {
6179 ExtOut("Fail to fill Assembly\n");
6180 return Status;
6181 }
6182 DMLOut("Parent Domain: %s\n", DMLDomain(Assembly.ParentDomain));
6183 if (g_sos->GetAssemblyName(TO_CDADDR(p_AssemblyAddr), mdNameLen, g_mdName, NULL)==S_OK)
6184 ExtOut("Name: %S\n", g_mdName);
6185 else
6186 ExtOut("Name: Unknown\n");
6187
6188 AssemblyInfo(&Assembly);
6189 return Status;
6190}
6191
6192
6193String GetHostingCapabilities(DWORD hostConfig)
6194{
6195 String result;
6196
6197 bool bAnythingPrinted = false;
6198
6199#define CHK_AND_PRINT(hType,hStr) \
6200 if (hostConfig & (hType)) { \
6201 if (bAnythingPrinted) result += ", "; \
6202 result += hStr; \
6203 bAnythingPrinted = true; \
6204 }
6205
6206 CHK_AND_PRINT(CLRMEMORYHOSTED, "Memory");
6207 CHK_AND_PRINT(CLRTASKHOSTED, "Task");
6208 CHK_AND_PRINT(CLRSYNCHOSTED, "Sync");
6209 CHK_AND_PRINT(CLRTHREADPOOLHOSTED, "Threadpool");
6210 CHK_AND_PRINT(CLRIOCOMPLETIONHOSTED, "IOCompletion");
6211 CHK_AND_PRINT(CLRASSEMBLYHOSTED, "Assembly");
6212 CHK_AND_PRINT(CLRGCHOSTED, "GC");
6213 CHK_AND_PRINT(CLRSECURITYHOSTED, "Security");
6214
6215#undef CHK_AND_PRINT
6216
6217 return result;
6218}
6219
6220/**********************************************************************\
6221* Routine Description: *
6222* *
6223* This function is called to dump the managed threads *
6224* *
6225\**********************************************************************/
6226HRESULT PrintThreadsFromThreadStore(BOOL bMiniDump, BOOL bPrintLiveThreadsOnly)
6227{
6228 HRESULT Status;
6229
6230 DacpThreadStoreData ThreadStore;
6231 if ((Status = ThreadStore.Request(g_sos)) != S_OK)
6232 {
6233 Print("Failed to request ThreadStore\n");
6234 return Status;
6235 }
6236
6237 TableOutput table(2, 17);
6238
6239 table.WriteRow("ThreadCount:", Decimal(ThreadStore.threadCount));
6240 table.WriteRow("UnstartedThread:", Decimal(ThreadStore.unstartedThreadCount));
6241 table.WriteRow("BackgroundThread:", Decimal(ThreadStore.backgroundThreadCount));
6242 table.WriteRow("PendingThread:", Decimal(ThreadStore.pendingThreadCount));
6243 table.WriteRow("DeadThread:", Decimal(ThreadStore.deadThreadCount));
6244
6245 if (ThreadStore.fHostConfig & ~CLRHOSTED)
6246 {
6247 String hosting = "yes";
6248
6249 hosting += " (";
6250 hosting += GetHostingCapabilities(ThreadStore.fHostConfig);
6251 hosting += ")";
6252
6253 table.WriteRow("Hosted Runtime:", hosting);
6254 }
6255 else
6256 {
6257 table.WriteRow("Hosted Runtime:", "no");
6258 }
6259
6260 const bool hosted = (ThreadStore.fHostConfig & CLRTASKHOSTED) != 0;
6261 table.ReInit(hosted ? 12 : 11, POINTERSIZE_HEX);
6262 table.SetWidths(10, 4, 4, 4, _max(9, POINTERSIZE_HEX),
6263 8, 11, 1+POINTERSIZE_HEX*2, POINTERSIZE_HEX,
6264 5, 3, POINTERSIZE_HEX);
6265
6266 table.SetColAlignment(0, AlignRight);
6267 table.SetColAlignment(1, AlignRight);
6268 table.SetColAlignment(2, AlignRight);
6269 table.SetColAlignment(4, AlignRight);
6270
6271 table.WriteColumn(8, "Lock");
6272 table.WriteRow("", "ID", "OSID", "ThreadOBJ", "State", "GC Mode", "GC Alloc Context", "Domain", "Count", "Apt");
6273
6274 if (hosted)
6275 table.WriteColumn("Fiber");
6276
6277 table.WriteColumn("Exception");
6278
6279 DacpThreadData Thread;
6280 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
6281 while (CurThread)
6282 {
6283 if (IsInterrupt())
6284 break;
6285
6286 if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
6287 {
6288 PrintLn("Failed to request Thread at ", Pointer(CurThread));
6289 return Status;
6290 }
6291
6292 BOOL bSwitchedOutFiber = Thread.osThreadId == SWITCHED_OUT_FIBER_OSID;
6293 if (!IsKernelDebugger())
6294 {
6295 ULONG id = 0;
6296
6297 if (bSwitchedOutFiber)
6298 {
6299 table.WriteColumn(0, "<<<< ");
6300 }
6301 else if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
6302 {
6303 table.WriteColumn(0, Decimal(id));
6304 }
6305 else if (bPrintLiveThreadsOnly)
6306 {
6307 CurThread = Thread.nextThread;
6308 continue;
6309 }
6310 else
6311 {
6312 table.WriteColumn(0, "XXXX ");
6313 }
6314 }
6315
6316 table.WriteColumn(1, Decimal(Thread.corThreadId));
6317 table.WriteColumn(2, ThreadID(bSwitchedOutFiber ? 0 : Thread.osThreadId));
6318 table.WriteColumn(3, Pointer(CurThread));
6319 table.WriteColumn(4, ThreadState(Thread.state));
6320 table.WriteColumn(5, Thread.preemptiveGCDisabled == 1 ? "Cooperative" : "Preemptive");
6321 table.WriteColumnFormat(6, "%p:%p", Thread.allocContextPtr, Thread.allocContextLimit);
6322
6323 if (Thread.domain)
6324 {
6325 table.WriteColumn(7, AppDomainPtr(Thread.domain));
6326 }
6327 else
6328 {
6329 CLRDATA_ADDRESS domain = 0;
6330 if (FAILED(g_sos->GetDomainFromContext(Thread.context, &domain)))
6331 table.WriteColumn(7, "<error>");
6332 else
6333 table.WriteColumn(7, AppDomainPtr(domain));
6334 }
6335
6336 table.WriteColumn(8, Decimal(Thread.lockCount));
6337
6338 // Apartment state
6339#ifndef FEATURE_PAL
6340 DWORD_PTR OleTlsDataAddr;
6341 if (!bSwitchedOutFiber
6342 && SafeReadMemory(Thread.teb + offsetof(TEB, ReservedForOle),
6343 &OleTlsDataAddr,
6344 sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0)
6345 {
6346 DWORD AptState;
6347 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
6348 &AptState,
6349 sizeof(AptState), NULL))
6350 {
6351 if (AptState & OLETLS_APARTMENTTHREADED)
6352 table.WriteColumn(9, "STA");
6353 else if (AptState & OLETLS_MULTITHREADED)
6354 table.WriteColumn(9, "MTA");
6355 else if (AptState & OLETLS_INNEUTRALAPT)
6356 table.WriteColumn(9, "NTA");
6357 else
6358 table.WriteColumn(9, "Ukn");
6359 }
6360 else
6361 {
6362 table.WriteColumn(9, "Ukn");
6363 }
6364 }
6365 else
6366#endif // FEATURE_PAL
6367 table.WriteColumn(9, "Ukn");
6368
6369 if (hosted)
6370 table.WriteColumn(10, Thread.fiberData);
6371
6372 WString lastCol;
6373 if (CurThread == ThreadStore.finalizerThread)
6374 lastCol += W("(Finalizer) ");
6375 if (CurThread == ThreadStore.gcThread)
6376 lastCol += W("(GC) ");
6377
6378 const int TS_TPWorkerThread = 0x01000000; // is this a threadpool worker thread?
6379 const int TS_CompletionPortThread = 0x08000000; // is this is a completion port thread?
6380
6381 if (Thread.state & TS_TPWorkerThread)
6382 lastCol += W("(Threadpool Worker) ");
6383 else if (Thread.state & TS_CompletionPortThread)
6384 lastCol += W("(Threadpool Completion Port) ");
6385
6386
6387 TADDR taLTOH;
6388 if (Thread.lastThrownObjectHandle && SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
6389 &taLTOH, sizeof(taLTOH), NULL) && taLTOH)
6390 {
6391 TADDR taMT;
6392 if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
6393 {
6394 if (NameForMT_s(taMT, g_mdName, mdNameLen))
6395 lastCol += WString(g_mdName) + W(" ") + ExceptionPtr(taLTOH);
6396 else
6397 lastCol += WString(W("<Invalid Object> (")) + Pointer(taLTOH) + W(")");
6398
6399 // Print something if there are nested exceptions on the thread
6400 if (Thread.firstNestedException)
6401 lastCol += W(" (nested exceptions)");
6402 }
6403 }
6404
6405 table.WriteColumn(lastCol);
6406 CurThread = Thread.nextThread;
6407 }
6408
6409 return Status;
6410}
6411
6412#ifndef FEATURE_PAL
6413HRESULT PrintSpecialThreads()
6414{
6415 Print("\n");
6416
6417 DWORD dwCLRTLSDataIndex = 0;
6418 HRESULT Status = g_sos->GetTLSIndex(&dwCLRTLSDataIndex);
6419
6420 if (!SUCCEEDED (Status))
6421 {
6422 Print("Failed to retrieve Tls Data index\n");
6423 return Status;
6424 }
6425
6426
6427 ULONG ulOriginalThreadID = 0;
6428 Status = g_ExtSystem->GetCurrentThreadId (&ulOriginalThreadID);
6429 if (!SUCCEEDED (Status))
6430 {
6431 Print("Failed to require current Thread ID\n");
6432 return Status;
6433 }
6434
6435 ULONG ulTotalThreads = 0;
6436 Status = g_ExtSystem->GetNumberThreads (&ulTotalThreads);
6437 if (!SUCCEEDED (Status))
6438 {
6439 Print("Failed to require total thread number\n");
6440 return Status;
6441 }
6442
6443 TableOutput table(3, 4, AlignRight, 5);
6444 table.WriteRow("", "OSID", "Special thread type");
6445
6446 for (ULONG ulThread = 0; ulThread < ulTotalThreads; ulThread++)
6447 {
6448 ULONG Id = 0;
6449 ULONG SysId = 0;
6450 HRESULT threadStatus = g_ExtSystem->GetThreadIdsByIndex(ulThread, 1, &Id, &SysId);
6451 if (!SUCCEEDED (threadStatus))
6452 {
6453 PrintLn("Failed to get thread ID for thread ", Decimal(ulThread));
6454 continue;
6455 }
6456
6457 threadStatus = g_ExtSystem->SetCurrentThreadId(Id);
6458 if (!SUCCEEDED (threadStatus))
6459 {
6460 PrintLn("Failed to switch to thread ", ThreadID(SysId));
6461 continue;
6462 }
6463
6464 CLRDATA_ADDRESS cdaTeb = 0;
6465 threadStatus = g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
6466 if (!SUCCEEDED (threadStatus))
6467 {
6468 PrintLn("Failed to get Teb for Thread ", ThreadID(SysId));
6469 continue;
6470 }
6471
6472 TADDR CLRTLSDataAddr = 0;
6473
6474 TADDR tlsArrayAddr = NULL;
6475 if (!SafeReadMemory (TO_TADDR(cdaTeb) + WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer , &tlsArrayAddr, sizeof (void**), NULL))
6476 {
6477 PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
6478 continue;
6479 }
6480
6481 if (tlsArrayAddr == NULL)
6482 {
6483 continue;
6484 }
6485
6486 TADDR moduleTlsDataAddr = 0;
6487 if (!SafeReadMemory (tlsArrayAddr + sizeof (void*) * (dwCLRTLSDataIndex & 0xFFFF), &moduleTlsDataAddr, sizeof (void**), NULL))
6488 {
6489 PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
6490 continue;
6491 }
6492
6493 CLRTLSDataAddr = moduleTlsDataAddr + ((dwCLRTLSDataIndex & 0x7FFF0000) >> 16) + OFFSETOF__TLS__tls_EETlsData;
6494
6495 TADDR CLRTLSData = NULL;
6496 if (!SafeReadMemory (CLRTLSDataAddr, &CLRTLSData, sizeof (TADDR), NULL))
6497 {
6498 PrintLn("Failed to get CLR Tls data for thread ", ThreadID(SysId));
6499 continue;
6500 }
6501
6502 if (CLRTLSData == NULL)
6503 {
6504 continue;
6505 }
6506
6507 size_t ThreadType = 0;
6508 if (!SafeReadMemory (CLRTLSData + sizeof (TADDR) * TlsIdx_ThreadType, &ThreadType, sizeof (size_t), NULL))
6509 {
6510 PrintLn("Failed to get thread type info not found for thread ", ThreadID(SysId));
6511 continue;
6512 }
6513
6514 if (ThreadType == 0)
6515 {
6516 continue;
6517 }
6518
6519 table.WriteColumn(0, Decimal(Id));
6520 table.WriteColumn(1, ThreadID(SysId));
6521
6522 String type;
6523 if (ThreadType & ThreadType_GC)
6524 {
6525 type += "GC ";
6526 }
6527 if (ThreadType & ThreadType_Timer)
6528 {
6529 type += "Timer ";
6530 }
6531 if (ThreadType & ThreadType_Gate)
6532 {
6533 type += "Gate ";
6534 }
6535 if (ThreadType & ThreadType_DbgHelper)
6536 {
6537 type += "DbgHelper ";
6538 }
6539 if (ThreadType & ThreadType_Shutdown)
6540 {
6541 type += "Shutdown ";
6542 }
6543 if (ThreadType & ThreadType_DynamicSuspendEE)
6544 {
6545 type += "SuspendEE ";
6546 }
6547 if (ThreadType & ThreadType_Finalizer)
6548 {
6549 type += "Finalizer ";
6550 }
6551 if (ThreadType & ThreadType_ADUnloadHelper)
6552 {
6553 type += "ADUnloadHelper ";
6554 }
6555 if (ThreadType & ThreadType_ShutdownHelper)
6556 {
6557 type += "ShutdownHelper ";
6558 }
6559 if (ThreadType & ThreadType_Threadpool_IOCompletion)
6560 {
6561 type += "IOCompletion ";
6562 }
6563 if (ThreadType & ThreadType_Threadpool_Worker)
6564 {
6565 type += "ThreadpoolWorker ";
6566 }
6567 if (ThreadType & ThreadType_Wait)
6568 {
6569 type += "Wait ";
6570 }
6571 if (ThreadType & ThreadType_ProfAPI_Attach)
6572 {
6573 type += "ProfilingAPIAttach ";
6574 }
6575 if (ThreadType & ThreadType_ProfAPI_Detach)
6576 {
6577 type += "ProfilingAPIDetach ";
6578 }
6579
6580 table.WriteColumn(2, type);
6581 }
6582
6583 Status = g_ExtSystem->SetCurrentThreadId (ulOriginalThreadID);
6584 if (!SUCCEEDED (Status))
6585 {
6586 ExtOut("Failed to switch to original thread\n");
6587 return Status;
6588 }
6589
6590 return Status;
6591}
6592#endif //FEATURE_PAL
6593
6594HRESULT SwitchToExceptionThread()
6595{
6596 HRESULT Status;
6597
6598 DacpThreadStoreData ThreadStore;
6599 if ((Status = ThreadStore.Request(g_sos)) != S_OK)
6600 {
6601 Print("Failed to request ThreadStore\n");
6602 return Status;
6603 }
6604
6605 DacpThreadData Thread;
6606 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
6607 while (CurThread)
6608 {
6609 if (IsInterrupt())
6610 break;
6611
6612 if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
6613 {
6614 PrintLn("Failed to request Thread at ", Pointer(CurThread));
6615 return Status;
6616 }
6617
6618 TADDR taLTOH;
6619 if (Thread.lastThrownObjectHandle != NULL)
6620 {
6621 if (SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle), &taLTOH, sizeof(taLTOH), NULL))
6622 {
6623 if (taLTOH != NULL)
6624 {
6625 ULONG id;
6626 if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
6627 {
6628 if (g_ExtSystem->SetCurrentThreadId(id) == S_OK)
6629 {
6630 PrintLn("Found managed exception on thread ", ThreadID(Thread.osThreadId));
6631 break;
6632 }
6633 }
6634 }
6635 }
6636 }
6637
6638 CurThread = Thread.nextThread;
6639 }
6640
6641 return Status;
6642}
6643
6644struct ThreadStateTable
6645{
6646 unsigned int State;
6647 const char * Name;
6648};
6649static const struct ThreadStateTable ThreadStates[] =
6650{
6651 {0x1, "Thread Abort Requested"},
6652 {0x2, "GC Suspend Pending"},
6653 {0x4, "User Suspend Pending"},
6654 {0x8, "Debug Suspend Pending"},
6655 {0x10, "GC On Transitions"},
6656 {0x20, "Legal to Join"},
6657 {0x40, "Yield Requested"},
6658 {0x80, "Hijacked by the GC"},
6659 {0x100, "Blocking GC for Stack Overflow"},
6660 {0x200, "Background"},
6661 {0x400, "Unstarted"},
6662 {0x800, "Dead"},
6663 {0x1000, "CLR Owns"},
6664 {0x2000, "CoInitialized"},
6665 {0x4000, "In Single Threaded Apartment"},
6666 {0x8000, "In Multi Threaded Apartment"},
6667 {0x10000, "Reported Dead"},
6668 {0x20000, "Fully initialized"},
6669 {0x40000, "Task Reset"},
6670 {0x80000, "Sync Suspended"},
6671 {0x100000, "Debug Will Sync"},
6672 {0x200000, "Stack Crawl Needed"},
6673 {0x400000, "Suspend Unstarted"},
6674 {0x800000, "Aborted"},
6675 {0x1000000, "Thread Pool Worker Thread"},
6676 {0x2000000, "Interruptible"},
6677 {0x4000000, "Interrupted"},
6678 {0x8000000, "Completion Port Thread"},
6679 {0x10000000, "Abort Initiated"},
6680 {0x20000000, "Finalized"},
6681 {0x40000000, "Failed to Start"},
6682 {0x80000000, "Detached"},
6683};
6684
6685DECLARE_API(ThreadState)
6686{
6687 INIT_API_NODAC();
6688
6689 size_t state = GetExpression(args);
6690 int count = 0;
6691
6692 if (state)
6693 {
6694
6695 for (unsigned int i = 0; i < _countof(ThreadStates); ++i)
6696 if (state & ThreadStates[i].State)
6697 {
6698 ExtOut(" %s\n", ThreadStates[i].Name);
6699 count++;
6700 }
6701 }
6702
6703 // If we did not find any thread states, print out a message to let the user
6704 // know that the function is working correctly.
6705 if (count == 0)
6706 ExtOut(" No thread states for '%s'\n", args);
6707
6708 return Status;
6709}
6710
6711DECLARE_API(Threads)
6712{
6713 INIT_API();
6714
6715 BOOL bPrintSpecialThreads = FALSE;
6716 BOOL bPrintLiveThreadsOnly = FALSE;
6717 BOOL bSwitchToManagedExceptionThread = FALSE;
6718 BOOL dml = FALSE;
6719
6720 CMDOption option[] =
6721 { // name, vptr, type, hasValue
6722 {"-special", &bPrintSpecialThreads, COBOOL, FALSE},
6723 {"-live", &bPrintLiveThreadsOnly, COBOOL, FALSE},
6724 {"-managedexception", &bSwitchToManagedExceptionThread, COBOOL, FALSE},
6725#ifndef FEATURE_PAL
6726 {"/d", &dml, COBOOL, FALSE},
6727#endif
6728 };
6729 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
6730 {
6731 return Status;
6732 }
6733
6734 if (bSwitchToManagedExceptionThread)
6735 {
6736 return SwitchToExceptionThread();
6737 }
6738
6739 // We need to support minidumps for this command.
6740 BOOL bMiniDump = IsMiniDumpFile();
6741
6742 if (bMiniDump && bPrintSpecialThreads)
6743 {
6744 Print("Special thread information is not available in mini dumps.\n");
6745 }
6746
6747 EnableDMLHolder dmlHolder(dml);
6748
6749 try
6750 {
6751 Status = PrintThreadsFromThreadStore(bMiniDump, bPrintLiveThreadsOnly);
6752 if (!bMiniDump && bPrintSpecialThreads)
6753 {
6754#ifdef FEATURE_PAL
6755 Print("\n-special not supported.\n");
6756#else //FEATURE_PAL
6757 HRESULT Status2 = PrintSpecialThreads();
6758 if (!SUCCEEDED(Status2))
6759 Status = Status2;
6760#endif //FEATURE_PAL
6761 }
6762 }
6763 catch (sos::Exception &e)
6764 {
6765 ExtOut("%s\n", e.what());
6766 }
6767
6768 return Status;
6769}
6770
6771#ifndef FEATURE_PAL
6772/**********************************************************************\
6773* Routine Description: *
6774* *
6775* This function is called to dump the Watson Buckets. *
6776* *
6777\**********************************************************************/
6778DECLARE_API(WatsonBuckets)
6779{
6780 INIT_API();
6781
6782 // We don't need to support minidumps for this command.
6783 if (IsMiniDumpFile())
6784 {
6785 ExtOut("Not supported on mini dumps.\n");
6786 }
6787
6788 // Get the current managed thread.
6789 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
6790 DacpThreadData Thread;
6791
6792 if ((threadAddr == NULL) || ((Status = Thread.Request(g_sos, threadAddr)) != S_OK))
6793 {
6794 ExtOut("The current thread is unmanaged\n");
6795 return Status;
6796 }
6797
6798 // Get the definition of GenericModeBlock.
6799#include <msodw.h>
6800 GenericModeBlock gmb;
6801
6802 if ((Status = g_sos->GetClrWatsonBuckets(threadAddr, &gmb)) != S_OK)
6803 {
6804 ExtOut("Can't get Watson Buckets\n");
6805 return Status;
6806 }
6807
6808 ExtOut("Watson Bucket parameters:\n");
6809 ExtOut("b1: %S\n", gmb.wzP1);
6810 ExtOut("b2: %S\n", gmb.wzP2);
6811 ExtOut("b3: %S\n", gmb.wzP3);
6812 ExtOut("b4: %S\n", gmb.wzP4);
6813 ExtOut("b5: %S\n", gmb.wzP5);
6814 ExtOut("b6: %S\n", gmb.wzP6);
6815 ExtOut("b7: %S\n", gmb.wzP7);
6816 ExtOut("b8: %S\n", gmb.wzP8);
6817 ExtOut("b9: %S\n", gmb.wzP9);
6818
6819 return Status;
6820} // WatsonBuckets()
6821#endif // FEATURE_PAL
6822
6823struct PendingBreakpoint
6824{
6825 WCHAR szModuleName[MAX_LONGPATH];
6826 WCHAR szFunctionName[mdNameLen];
6827 WCHAR szFilename[MAX_LONGPATH];
6828 DWORD lineNumber;
6829 TADDR pModule;
6830 DWORD ilOffset;
6831 mdMethodDef methodToken;
6832 void SetModule(TADDR module)
6833 {
6834 pModule = module;
6835 }
6836
6837 bool ModuleMatches(TADDR compare)
6838 {
6839 return (compare == pModule);
6840 }
6841
6842 PendingBreakpoint *pNext;
6843 PendingBreakpoint() : lineNumber(0), ilOffset(0), methodToken(0), pNext(NULL)
6844 {
6845 szModuleName[0] = L'\0';
6846 szFunctionName[0] = L'\0';
6847 szFilename[0] = L'\0';
6848 }
6849};
6850
6851void IssueDebuggerBPCommand ( CLRDATA_ADDRESS addr )
6852{
6853 const int MaxBPsCached = 1024;
6854 static CLRDATA_ADDRESS alreadyPlacedBPs[MaxBPsCached];
6855 static int curLimit = 0;
6856
6857 // on ARM the debugger requires breakpoint addresses to be sanitized
6858 if (IsDbgTargetArm())
6859#ifndef FEATURE_PAL
6860 addr &= ~THUMB_CODE;
6861#else
6862 addr |= THUMB_CODE; // lldb expects thumb code bit set
6863#endif
6864
6865 // if we overflowed our cache consider all new BPs unique...
6866 BOOL bUnique = curLimit >= MaxBPsCached;
6867 if (!bUnique)
6868 {
6869 bUnique = TRUE;
6870 for (int i = 0; i < curLimit; ++i)
6871 {
6872 if (alreadyPlacedBPs[i] == addr)
6873 {
6874 bUnique = FALSE;
6875 break;
6876 }
6877 }
6878 }
6879 if (bUnique)
6880 {
6881 char buffer[64]; // sufficient for "bp <pointersize>"
6882 static WCHAR wszNameBuffer[1024]; // should be large enough
6883
6884 // get the MethodDesc name
6885 CLRDATA_ADDRESS pMD;
6886 if (g_sos->GetMethodDescPtrFromIP(addr, &pMD) != S_OK
6887 || g_sos->GetMethodDescName(pMD, 1024, wszNameBuffer, NULL) != S_OK)
6888 {
6889 wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN"));
6890 }
6891
6892#ifndef FEATURE_PAL
6893 sprintf_s(buffer, _countof(buffer), "bp %p", (void*) (size_t) addr);
6894#else
6895 sprintf_s(buffer, _countof(buffer), "breakpoint set --address 0x%p", (void*) (size_t) addr);
6896#endif
6897 ExtOut("Setting breakpoint: %s [%S]\n", buffer, wszNameBuffer);
6898 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
6899
6900 if (curLimit < MaxBPsCached)
6901 {
6902 alreadyPlacedBPs[curLimit++] = addr;
6903 }
6904 }
6905}
6906
6907class Breakpoints
6908{
6909 PendingBreakpoint* m_breakpoints;
6910public:
6911 Breakpoints()
6912 {
6913 m_breakpoints = NULL;
6914 }
6915 ~Breakpoints()
6916 {
6917 PendingBreakpoint *pCur = m_breakpoints;
6918 while(pCur)
6919 {
6920 PendingBreakpoint *pNext = pCur->pNext;
6921 delete pCur;
6922 pCur = pNext;
6923 }
6924 m_breakpoints = NULL;
6925 }
6926
6927 void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod, DWORD ilOffset)
6928 {
6929 if (!IsIn(szModule, szName, mod))
6930 {
6931 PendingBreakpoint *pNew = new PendingBreakpoint();
6932 wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule);
6933 wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
6934 pNew->SetModule(mod);
6935 pNew->ilOffset = ilOffset;
6936 pNew->pNext = m_breakpoints;
6937 m_breakpoints = pNew;
6938 }
6939 }
6940
6941 void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
6942 {
6943 if (!IsIn(methodToken, mod, ilOffset))
6944 {
6945 PendingBreakpoint *pNew = new PendingBreakpoint();
6946 wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule);
6947 wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
6948 pNew->methodToken = methodToken;
6949 pNew->SetModule(mod);
6950 pNew->ilOffset = ilOffset;
6951 pNew->pNext = m_breakpoints;
6952 m_breakpoints = pNew;
6953 }
6954 }
6955
6956 void Add(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
6957 {
6958 if (!IsIn(szFilename, lineNumber, mod))
6959 {
6960 PendingBreakpoint *pNew = new PendingBreakpoint();
6961 wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename);
6962 pNew->lineNumber = lineNumber;
6963 pNew->SetModule(mod);
6964 pNew->pNext = m_breakpoints;
6965 m_breakpoints = pNew;
6966 }
6967 }
6968
6969 void Add(__in_z LPWSTR szFilename, DWORD lineNumber, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
6970 {
6971 if (!IsIn(methodToken, mod, ilOffset))
6972 {
6973 PendingBreakpoint *pNew = new PendingBreakpoint();
6974 wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename);
6975 pNew->lineNumber = lineNumber;
6976 pNew->methodToken = methodToken;
6977 pNew->SetModule(mod);
6978 pNew->ilOffset = ilOffset;
6979 pNew->pNext = m_breakpoints;
6980 m_breakpoints = pNew;
6981 }
6982 }
6983
6984 //returns true if updates are still needed for this module, FALSE if all BPs are now bound
6985 BOOL Update(TADDR mod, BOOL isNewModule)
6986 {
6987 BOOL bNeedUpdates = FALSE;
6988 PendingBreakpoint *pCur = NULL;
6989
6990 if(isNewModule)
6991 {
6992 SymbolReader symbolReader;
6993 SymbolReader* pSymReader = &symbolReader;
6994 if(LoadSymbolsForModule(mod, &symbolReader) != S_OK)
6995 pSymReader = NULL;
6996
6997 // Get tokens for any modules that match. If there was a change,
6998 // update notifications.
6999 pCur = m_breakpoints;
7000 while(pCur)
7001 {
7002 PendingBreakpoint *pNext = pCur->pNext;
7003 ResolvePendingNonModuleBoundBreakpoint(mod, pCur, pSymReader);
7004 pCur = pNext;
7005 }
7006 }
7007
7008 pCur = m_breakpoints;
7009 while(pCur)
7010 {
7011 PendingBreakpoint *pNext = pCur->pNext;
7012 if (ResolvePendingBreakpoint(mod, pCur))
7013 {
7014 bNeedUpdates = TRUE;
7015 }
7016 pCur = pNext;
7017 }
7018 return bNeedUpdates;
7019 }
7020
7021 BOOL UpdateKnownCodeAddress(TADDR mod, CLRDATA_ADDRESS bpLocation)
7022 {
7023 PendingBreakpoint *pCur = m_breakpoints;
7024 BOOL bpSet = FALSE;
7025
7026 while(pCur)
7027 {
7028 PendingBreakpoint *pNext = pCur->pNext;
7029 if (pCur->ModuleMatches(mod))
7030 {
7031 IssueDebuggerBPCommand(bpLocation);
7032 bpSet = TRUE;
7033 break;
7034 }
7035
7036 pCur = pNext;
7037 }
7038
7039 return bpSet;
7040 }
7041
7042 void RemovePendingForModule(TADDR mod)
7043 {
7044 PendingBreakpoint *pCur = m_breakpoints;
7045 while(pCur)
7046 {
7047 PendingBreakpoint *pNext = pCur->pNext;
7048 if (pCur->ModuleMatches(mod))
7049 {
7050 // Delete the current node, and keep going
7051 Delete(pCur);
7052 }
7053
7054 pCur = pNext;
7055 }
7056 }
7057
7058 void ListBreakpoints()
7059 {
7060 PendingBreakpoint *pCur = m_breakpoints;
7061 size_t iBreakpointIndex = 1;
7062 ExtOut(SOSPrefix "bpmd pending breakpoint list\n Breakpoint index - Location, ModuleID, Method Token\n");
7063 while(pCur)
7064 {
7065 //windbg likes to format %p as always being 64 bits
7066 ULONG64 modulePtr = (ULONG64)pCur->pModule;
7067
7068 if(pCur->szModuleName[0] != L'\0')
7069 ExtOut("%d - %ws!%ws+%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset, modulePtr, pCur->methodToken);
7070 else
7071 ExtOut("%d - %ws:%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szFilename, pCur->lineNumber, modulePtr, pCur->methodToken);
7072 iBreakpointIndex++;
7073 pCur = pCur->pNext;
7074 }
7075 }
7076
7077#ifndef FEATURE_PAL
7078 void SaveBreakpoints(FILE* pFile)
7079 {
7080 PendingBreakpoint *pCur = m_breakpoints;
7081 while(pCur)
7082 {
7083 if(pCur->szModuleName[0] != L'\0')
7084 fprintf_s(pFile, "!bpmd %ws %ws %d\n", pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset);
7085 else
7086 fprintf_s(pFile, "!bpmd %ws:%d\n", pCur->szFilename, pCur->lineNumber);
7087 pCur = pCur->pNext;
7088 }
7089 }
7090#endif
7091
7092 void CleanupNotifications()
7093 {
7094#ifdef FEATURE_PAL
7095 if (m_breakpoints == NULL)
7096 {
7097 g_ExtServices->ClearExceptionCallback();
7098 }
7099#endif
7100 }
7101
7102 void ClearBreakpoint(size_t breakPointToClear)
7103 {
7104 PendingBreakpoint *pCur = m_breakpoints;
7105 size_t iBreakpointIndex = 1;
7106 while(pCur)
7107 {
7108 if (breakPointToClear == iBreakpointIndex)
7109 {
7110 ExtOut("%d - %ws, %ws, %p\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->pModule);
7111 ExtOut("Cleared\n");
7112 Delete(pCur);
7113 break;
7114 }
7115 iBreakpointIndex++;
7116 pCur = pCur->pNext;
7117 }
7118
7119 if (pCur == NULL)
7120 {
7121 ExtOut("Invalid pending breakpoint index.\n");
7122 }
7123 CleanupNotifications();
7124 }
7125
7126 void ClearAllBreakpoints()
7127 {
7128 size_t iBreakpointIndex = 1;
7129 for (PendingBreakpoint *pCur = m_breakpoints; pCur != NULL; )
7130 {
7131 PendingBreakpoint* pNext = pCur->pNext;
7132 Delete(pCur);
7133 iBreakpointIndex++;
7134 pCur = pNext;
7135 }
7136 CleanupNotifications();
7137
7138 ExtOut("All pending breakpoints cleared.\n");
7139 }
7140
7141 HRESULT LoadSymbolsForModule(TADDR mod, SymbolReader* pSymbolReader)
7142 {
7143 HRESULT Status = S_OK;
7144 ToRelease<IXCLRDataModule> pModule;
7145 IfFailRet(g_sos->GetModule(mod, &pModule));
7146
7147 ToRelease<IMetaDataImport> pMDImport = NULL;
7148 IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
7149
7150 IfFailRet(pSymbolReader->LoadSymbols(pMDImport, pModule));
7151
7152 return S_OK;
7153 }
7154
7155 HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pFilename, DWORD lineNumber, TADDR mod, SymbolReader* pSymbolReader)
7156 {
7157 HRESULT Status = S_OK;
7158 if(pSymbolReader == NULL)
7159 return S_FALSE; // no symbols, can't bind here
7160
7161 mdMethodDef methodDef;
7162 ULONG32 ilOffset;
7163 if(FAILED(Status = pSymbolReader->ResolveSequencePoint(pFilename, lineNumber, mod, &methodDef, &ilOffset)))
7164 {
7165 return S_FALSE; // not binding in a module is typical
7166 }
7167
7168 Add(pFilename, lineNumber, methodDef, mod, ilOffset);
7169 return Status;
7170 }
7171
7172 HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pModuleName, __in_z WCHAR* pMethodName, TADDR mod, DWORD ilOffset)
7173 {
7174 HRESULT Status = S_OK;
7175 char szName[mdNameLen];
7176 int numModule;
7177
7178 ToRelease<IXCLRDataModule> module;
7179 IfFailRet(g_sos->GetModule(mod, &module));
7180
7181 WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szName, mdNameLen, NULL, NULL);
7182
7183 ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(szName, &numModule);
7184 if (moduleList == NULL)
7185 {
7186 ExtOut("Failed to request module list.\n");
7187 return E_FAIL;
7188 }
7189
7190 for (int i = 0; i < numModule; i++)
7191 {
7192 // If any one entry in moduleList matches, then the current PendingBreakpoint
7193 // is the right one.
7194 if(moduleList[i] != TO_TADDR(mod))
7195 continue;
7196
7197 CLRDATA_ENUM h;
7198 if (module->StartEnumMethodDefinitionsByName(pMethodName, 0, &h) == S_OK)
7199 {
7200 IXCLRDataMethodDefinition *pMeth = NULL;
7201 while (module->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
7202 {
7203 mdMethodDef methodToken;
7204 ToRelease<IXCLRDataModule> pUnusedModule;
7205 IfFailRet(pMeth->GetTokenAndScope(&methodToken, &pUnusedModule));
7206
7207 Add(pModuleName, pMethodName, methodToken, mod, ilOffset);
7208 pMeth->Release();
7209 }
7210 module->EndEnumMethodDefinitionsByName(h);
7211 }
7212 }
7213 return S_OK;
7214 }
7215
7216 // Return TRUE if there might be more instances that will be JITTED later
7217 static BOOL ResolveMethodInstances(IXCLRDataMethodDefinition *pMeth, DWORD ilOffset)
7218 {
7219 BOOL bFoundCode = FALSE;
7220 BOOL bNeedDefer = FALSE;
7221 CLRDATA_ENUM h1;
7222
7223 if (pMeth->StartEnumInstances (NULL, &h1) == S_OK)
7224 {
7225 IXCLRDataMethodInstance *inst = NULL;
7226 while (pMeth->EnumInstance (&h1, &inst) == S_OK)
7227 {
7228 BOOL foundByIlOffset = FALSE;
7229 ULONG32 rangesNeeded = 0;
7230 if(inst->GetAddressRangesByILOffset(ilOffset, 0, &rangesNeeded, NULL) == S_OK)
7231 {
7232 ArrayHolder<CLRDATA_ADDRESS_RANGE> ranges = new NOTHROW CLRDATA_ADDRESS_RANGE[rangesNeeded];
7233 if (ranges != NULL)
7234 {
7235 if (inst->GetAddressRangesByILOffset(ilOffset, rangesNeeded, NULL, ranges) == S_OK)
7236 {
7237 for (DWORD i = 0; i < rangesNeeded; i++)
7238 {
7239 IssueDebuggerBPCommand(ranges[i].startAddress);
7240 bFoundCode = TRUE;
7241 foundByIlOffset = TRUE;
7242 }
7243 }
7244 }
7245 }
7246
7247 if (!foundByIlOffset && ilOffset == 0)
7248 {
7249 CLRDATA_ADDRESS addr = 0;
7250 if (inst->GetRepresentativeEntryAddress(&addr) == S_OK)
7251 {
7252 IssueDebuggerBPCommand(addr);
7253 bFoundCode = TRUE;
7254 }
7255 }
7256 }
7257 pMeth->EndEnumInstances (h1);
7258 }
7259
7260 // if this is a generic method we need to add a deferred bp
7261 BOOL bGeneric = FALSE;
7262 pMeth->HasClassOrMethodInstantiation(&bGeneric);
7263
7264 bNeedDefer = !bFoundCode || bGeneric;
7265 // This is down here because we only need to call SetCodeNofiication once.
7266 if (bNeedDefer)
7267 {
7268 if (pMeth->SetCodeNotification (CLRDATA_METHNOTIFY_GENERATED) != S_OK)
7269 {
7270 bNeedDefer = FALSE;
7271 ExtOut("Failed to set code notification\n");
7272 }
7273 }
7274 return bNeedDefer;
7275 }
7276
7277private:
7278 BOOL IsIn(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod)
7279 {
7280 PendingBreakpoint *pCur = m_breakpoints;
7281 while(pCur)
7282 {
7283 if (pCur->ModuleMatches(mod) &&
7284 _wcsicmp(pCur->szModuleName, szModule) == 0 &&
7285 _wcscmp(pCur->szFunctionName, szName) == 0)
7286 {
7287 return TRUE;
7288 }
7289 pCur = pCur->pNext;
7290 }
7291 return FALSE;
7292 }
7293
7294 BOOL IsIn(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
7295 {
7296 PendingBreakpoint *pCur = m_breakpoints;
7297 while(pCur)
7298 {
7299 if (pCur->ModuleMatches(mod) &&
7300 _wcsicmp(pCur->szFilename, szFilename) == 0 &&
7301 pCur->lineNumber == lineNumber)
7302 {
7303 return TRUE;
7304 }
7305 pCur = pCur->pNext;
7306 }
7307 return FALSE;
7308 }
7309
7310 BOOL IsIn(mdMethodDef token, TADDR mod, DWORD ilOffset)
7311 {
7312 PendingBreakpoint *pCur = m_breakpoints;
7313 while(pCur)
7314 {
7315 if (pCur->ModuleMatches(mod) &&
7316 pCur->methodToken == token &&
7317 pCur->ilOffset == ilOffset)
7318 {
7319 return TRUE;
7320 }
7321 pCur = pCur->pNext;
7322 }
7323 return FALSE;
7324 }
7325
7326 void Delete(PendingBreakpoint *pDelete)
7327 {
7328 PendingBreakpoint *pCur = m_breakpoints;
7329 PendingBreakpoint *pPrev = NULL;
7330 while(pCur)
7331 {
7332 if (pCur == pDelete)
7333 {
7334 if (pPrev == NULL)
7335 {
7336 m_breakpoints = pCur->pNext;
7337 }
7338 else
7339 {
7340 pPrev->pNext = pCur->pNext;
7341 }
7342 delete pCur;
7343 return;
7344 }
7345 pPrev = pCur;
7346 pCur = pCur->pNext;
7347 }
7348 }
7349
7350
7351
7352 HRESULT ResolvePendingNonModuleBoundBreakpoint(TADDR mod, PendingBreakpoint *pCur, SymbolReader* pSymbolReader)
7353 {
7354 // This function only works with pending breakpoints that are not module bound.
7355 if (pCur->pModule == NULL)
7356 {
7357 if(pCur->szModuleName[0] != L'\0')
7358 {
7359 return ResolvePendingNonModuleBoundBreakpoint(pCur->szModuleName, pCur->szFunctionName, mod, pCur->ilOffset);
7360 }
7361 else
7362 {
7363 return ResolvePendingNonModuleBoundBreakpoint(pCur->szFilename, pCur->lineNumber, mod, pSymbolReader);
7364 }
7365 }
7366 else
7367 {
7368 return S_OK;
7369 }
7370 }
7371
7372 // Returns TRUE if further instances may be jitted, FALSE if all instances are now resolved
7373 BOOL ResolvePendingBreakpoint(TADDR addr, PendingBreakpoint *pCur)
7374 {
7375 // Only go forward if the module matches the current PendingBreakpoint
7376 if (!pCur->ModuleMatches(addr))
7377 {
7378 return FALSE;
7379 }
7380
7381 ToRelease<IXCLRDataModule> mod;
7382 if (FAILED(g_sos->GetModule(addr, &mod)))
7383 {
7384 return FALSE;
7385 }
7386
7387 if(pCur->methodToken == 0)
7388 {
7389 return FALSE;
7390 }
7391
7392 ToRelease<IXCLRDataMethodDefinition> pMeth = NULL;
7393 mod->GetMethodDefinitionByToken(pCur->methodToken, &pMeth);
7394
7395 // We may not need the code notification. Maybe it was ngen'd and we
7396 // already have the method?
7397 // We can delete the current entry if ResolveMethodInstances() set all BPs
7398 return ResolveMethodInstances(pMeth, pCur->ilOffset);
7399 }
7400};
7401
7402Breakpoints g_bpoints;
7403
7404// Controls whether optimizations are disabled on module load and whether NGEN can be used
7405BOOL g_fAllowJitOptimization = TRUE;
7406
7407// Controls whether a one-shot breakpoint should be inserted the next time
7408// execution is about to enter a catch clause
7409BOOL g_stopOnNextCatch = FALSE;
7410
7411// According to the latest debuggers these callbacks will not get called
7412// unless the user (or an extension, like SOS :-)) had previously enabled
7413// clrn with "sxe clrn".
7414class CNotification : public IXCLRDataExceptionNotification5
7415{
7416 static int s_condemnedGen;
7417
7418 int m_count;
7419 int m_dbgStatus;
7420public:
7421 CNotification()
7422 : m_count(0)
7423 , m_dbgStatus(DEBUG_STATUS_NO_CHANGE)
7424 {}
7425
7426 int GetDebugStatus()
7427 {
7428 return m_dbgStatus;
7429 }
7430
7431 STDMETHODIMP QueryInterface (REFIID iid, void **ppvObject)
7432 {
7433 if (ppvObject == NULL)
7434 return E_INVALIDARG;
7435
7436 if (IsEqualIID(iid, IID_IUnknown)
7437 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification)
7438 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification2)
7439 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification3)
7440 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification4)
7441 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification5))
7442 {
7443 *ppvObject = static_cast<IXCLRDataExceptionNotification5*>(this);
7444 AddRef();
7445 return S_OK;
7446 }
7447 else
7448 return E_NOINTERFACE;
7449
7450 }
7451
7452 STDMETHODIMP_(ULONG) AddRef(void) { return ++m_count; }
7453 STDMETHODIMP_(ULONG) Release(void)
7454 {
7455 m_count--;
7456 if (m_count < 0)
7457 {
7458 m_count = 0;
7459 }
7460 return m_count;
7461 }
7462
7463
7464 /*
7465 * New code was generated or discarded for a method.:
7466 */
7467 STDMETHODIMP OnCodeGenerated(IXCLRDataMethodInstance* method)
7468 {
7469 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
7470 return S_OK;
7471 }
7472
7473 STDMETHODIMP OnCodeGenerated2(IXCLRDataMethodInstance* method, CLRDATA_ADDRESS nativeCodeLocation)
7474 {
7475 // Some method has been generated, make a breakpoint.
7476 ULONG32 len = mdNameLen;
7477 LPWSTR szModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
7478 if (method->GetName(0, mdNameLen, &len, g_mdName) == S_OK)
7479 {
7480 ToRelease<IXCLRDataModule> pMod;
7481 HRESULT hr = method->GetTokenAndScope(NULL, &pMod);
7482 if (SUCCEEDED(hr))
7483 {
7484 len = mdNameLen;
7485 if (pMod->GetName(mdNameLen, &len, szModuleName) == S_OK)
7486 {
7487 ExtOut("JITTED %S!%S\n", szModuleName, g_mdName);
7488
7489 DacpGetModuleAddress dgma;
7490 if (SUCCEEDED(dgma.Request(pMod)))
7491 {
7492 g_bpoints.UpdateKnownCodeAddress(TO_TADDR(dgma.ModulePtr), nativeCodeLocation);
7493 }
7494 else
7495 {
7496 ExtOut("Failed to request module address.\n");
7497 }
7498 }
7499 }
7500 }
7501
7502 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
7503 return S_OK;
7504 }
7505
7506 STDMETHODIMP OnCodeDiscarded(IXCLRDataMethodInstance* method)
7507 {
7508 return E_NOTIMPL;
7509 }
7510
7511 /*
7512 * The process or task reached the desired execution state.
7513 */
7514 STDMETHODIMP OnProcessExecution(ULONG32 state) { return E_NOTIMPL; }
7515 STDMETHODIMP OnTaskExecution(IXCLRDataTask* task,
7516 ULONG32 state) { return E_NOTIMPL; }
7517
7518 /*
7519 * The given module was loaded or unloaded.
7520 */
7521 STDMETHODIMP OnModuleLoaded(IXCLRDataModule* mod)
7522 {
7523 DacpGetModuleAddress dgma;
7524 if (SUCCEEDED(dgma.Request(mod)))
7525 {
7526 g_bpoints.Update(TO_TADDR(dgma.ModulePtr), TRUE);
7527 }
7528
7529 if(!g_fAllowJitOptimization)
7530 {
7531 HRESULT hr;
7532 ToRelease<IXCLRDataModule2> mod2;
7533 if(FAILED(mod->QueryInterface(__uuidof(IXCLRDataModule2), (void**) &mod2)))
7534 {
7535 ExtOut("SOS: warning, optimizations for this module could not be suppressed because this CLR version doesn't support the functionality\n");
7536 }
7537 else if(FAILED(hr = mod2->SetJITCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION)))
7538 {
7539 if(hr == CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE)
7540 ExtOut("SOS: warning, optimizations for this module could not be surpressed because an optimized prejitted image was loaded\n");
7541 else
7542 ExtOut("SOS: warning, optimizations for this module could not be surpressed hr=0x%x\n", hr);
7543 }
7544 }
7545
7546 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
7547 return S_OK;
7548 }
7549
7550 STDMETHODIMP OnModuleUnloaded(IXCLRDataModule* mod)
7551 {
7552 DacpGetModuleAddress dgma;
7553 if (SUCCEEDED(dgma.Request(mod)))
7554 {
7555 g_bpoints.RemovePendingForModule(TO_TADDR(dgma.ModulePtr));
7556 }
7557
7558 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
7559 return S_OK;
7560 }
7561
7562 /*
7563 * The given type was loaded or unloaded.
7564 */
7565 STDMETHODIMP OnTypeLoaded(IXCLRDataTypeInstance* typeInst)
7566 { return E_NOTIMPL; }
7567 STDMETHODIMP OnTypeUnloaded(IXCLRDataTypeInstance* typeInst)
7568 { return E_NOTIMPL; }
7569
7570 STDMETHODIMP OnAppDomainLoaded(IXCLRDataAppDomain* domain)
7571 { return E_NOTIMPL; }
7572 STDMETHODIMP OnAppDomainUnloaded(IXCLRDataAppDomain* domain)
7573 { return E_NOTIMPL; }
7574 STDMETHODIMP OnException(IXCLRDataExceptionState* exception)
7575 { return E_NOTIMPL; }
7576
7577 STDMETHODIMP OnGcEvent(GcEvtArgs gcEvtArgs)
7578{
7579 // by default don't stop on these notifications...
7580 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
7581
7582 IXCLRDataProcess2* idp2 = NULL;
7583 if (SUCCEEDED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
7584 {
7585 if (gcEvtArgs.typ == GC_MARK_END)
7586 {
7587 // erase notification request
7588 GcEvtArgs gea = { GC_MARK_END, { 0 } };
7589 idp2->SetGcNotification(gea);
7590
7591 s_condemnedGen = bitidx(gcEvtArgs.condemnedGeneration);
7592
7593 ExtOut("CLR notification: GC - Performing a gen %d collection. Determined surviving objects...\n", s_condemnedGen);
7594
7595 // GC_MARK_END notification means: give the user a chance to examine the debuggee
7596 m_dbgStatus = DEBUG_STATUS_BREAK;
7597 }
7598 }
7599
7600 return S_OK;
7601 }
7602
7603 /*
7604 * Catch is about to be entered
7605 */
7606 STDMETHODIMP ExceptionCatcherEnter(IXCLRDataMethodInstance* method, DWORD catcherNativeOffset)
7607 {
7608 if(g_stopOnNextCatch)
7609 {
7610 CLRDATA_ADDRESS startAddr;
7611 if(method->GetRepresentativeEntryAddress(&startAddr) == S_OK)
7612 {
7613 CHAR buffer[100];
7614#ifndef FEATURE_PAL
7615 sprintf_s(buffer, _countof(buffer), "bp /1 %p", (void*) (size_t) (startAddr+catcherNativeOffset));
7616#else
7617 sprintf_s(buffer, _countof(buffer), "breakpoint set --one-shot --address 0x%p", (void*) (size_t) (startAddr+catcherNativeOffset));
7618#endif
7619 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
7620 }
7621 g_stopOnNextCatch = FALSE;
7622 }
7623
7624 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
7625 return S_OK;
7626 }
7627
7628 static int GetCondemnedGen()
7629 {
7630 return s_condemnedGen;
7631 }
7632
7633};
7634
7635int CNotification::s_condemnedGen = -1;
7636
7637BOOL CheckCLRNotificationEvent(DEBUG_LAST_EVENT_INFO_EXCEPTION* pdle)
7638{
7639 ISOSDacInterface4 *psos4 = NULL;
7640 CLRDATA_ADDRESS arguments[3];
7641 HRESULT Status;
7642
7643 if (SUCCEEDED(Status = g_sos->QueryInterface(__uuidof(ISOSDacInterface4), (void**) &psos4)))
7644 {
7645 int count = _countof(arguments);
7646 int countNeeded = 0;
7647
7648 Status = psos4->GetClrNotification(arguments, count, &countNeeded);
7649 psos4->Release();
7650
7651 if (SUCCEEDED(Status))
7652 {
7653 memset(&pdle->ExceptionRecord, 0, sizeof(pdle->ExceptionRecord));
7654 pdle->FirstChance = TRUE;
7655 pdle->ExceptionRecord.ExceptionCode = CLRDATA_NOTIFY_EXCEPTION;
7656
7657 _ASSERTE(count <= EXCEPTION_MAXIMUM_PARAMETERS);
7658 for (int i = 0; i < count; i++)
7659 {
7660 pdle->ExceptionRecord.ExceptionInformation[i] = arguments[i];
7661 }
7662 // The rest of the ExceptionRecord isn't used by TranslateExceptionRecordToNotification
7663 return TRUE;
7664 }
7665 // No pending exception notification
7666 return FALSE;
7667 }
7668
7669 // The new DAC based interface doesn't exists so ask the debugger for the last exception
7670 // information. NOTE: this function doesn't work on xplat version when the coreclr symbols
7671 // have been stripped.
7672
7673 ULONG Type, ProcessId, ThreadId;
7674 ULONG ExtraInformationUsed;
7675 Status = g_ExtControl->GetLastEventInformation(
7676 &Type,
7677 &ProcessId,
7678 &ThreadId,
7679 pdle,
7680 sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION),
7681 &ExtraInformationUsed,
7682 NULL,
7683 0,
7684 NULL);
7685
7686 if (Status != S_OK || Type != DEBUG_EVENT_EXCEPTION)
7687 {
7688 return FALSE;
7689 }
7690
7691 if (!pdle->FirstChance || pdle->ExceptionRecord.ExceptionCode != CLRDATA_NOTIFY_EXCEPTION)
7692 {
7693 return FALSE;
7694 }
7695
7696 return TRUE;
7697}
7698
7699HRESULT HandleCLRNotificationEvent()
7700{
7701 /*
7702 * Did we get module load notification? If so, check if any in our pending list
7703 * need to be registered for jit notification.
7704 *
7705 * Did we get a jit notification? If so, check if any can be removed and
7706 * real breakpoints be set.
7707 */
7708 DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
7709 CNotification Notification;
7710
7711 if (!CheckCLRNotificationEvent(&dle))
7712 {
7713#ifndef FEATURE_PAL
7714 ExtOut("Expecting first chance CLRN exception\n");
7715 return E_FAIL;
7716#else
7717 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
7718 return S_OK;
7719#endif
7720 }
7721
7722 // Notification only needs to live for the lifetime of the call below, so it's a non-static
7723 // local.
7724 HRESULT Status = g_clrData->TranslateExceptionRecordToNotification(&dle.ExceptionRecord, &Notification);
7725 if (Status != S_OK)
7726 {
7727 ExtErr("Error processing exception notification\n");
7728 return Status;
7729 }
7730 else
7731 {
7732 switch (Notification.GetDebugStatus())
7733 {
7734 case DEBUG_STATUS_GO:
7735 case DEBUG_STATUS_GO_HANDLED:
7736 case DEBUG_STATUS_GO_NOT_HANDLED:
7737#ifndef FEATURE_PAL
7738 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "g", 0);
7739#else
7740 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
7741#endif
7742 break;
7743 default:
7744 break;
7745 }
7746 }
7747
7748 return S_OK;
7749}
7750
7751#ifndef FEATURE_PAL
7752
7753DECLARE_API(HandleCLRN)
7754{
7755 INIT_API();
7756 MINIDUMP_NOT_SUPPORTED();
7757
7758 return HandleCLRNotificationEvent();
7759}
7760
7761#else // FEATURE_PAL
7762
7763HRESULT HandleExceptionNotification(ILLDBServices *client)
7764{
7765 INIT_API();
7766 return HandleCLRNotificationEvent();
7767}
7768
7769#endif // FEATURE_PAL
7770
7771DECLARE_API(bpmd)
7772{
7773 INIT_API_NOEE();
7774 MINIDUMP_NOT_SUPPORTED();
7775 char buffer[1024];
7776
7777 if (IsDumpFile())
7778 {
7779 ExtOut(SOSPrefix "bpmd is not supported on a dump file.\n");
7780 return Status;
7781 }
7782
7783
7784 // We keep a list of managed breakpoints the user wants to set, and display pending bps
7785 // bpmd. If you call bpmd <module name> <method> we will set or update an existing bp.
7786 // bpmd acts as a feeder of breakpoints to bp when the time is right.
7787 //
7788
7789 StringHolder DllName,TypeName;
7790 int lineNumber = 0;
7791 size_t Offset = 0;
7792
7793 DWORD_PTR pMD = NULL;
7794 BOOL fNoFutureModule = FALSE;
7795 BOOL fList = FALSE;
7796 size_t clearItem = 0;
7797 BOOL fClearAll = FALSE;
7798 CMDOption option[] =
7799 { // name, vptr, type, hasValue
7800 {"-md", &pMD, COHEX, TRUE},
7801 {"-nofuturemodule", &fNoFutureModule, COBOOL, FALSE},
7802 {"-list", &fList, COBOOL, FALSE},
7803 {"-clear", &clearItem, COSIZE_T, TRUE},
7804 {"-clearall", &fClearAll, COBOOL, FALSE},
7805 };
7806 CMDValue arg[] =
7807 { // vptr, type
7808 {&DllName.data, COSTRING},
7809 {&TypeName.data, COSTRING},
7810 {&Offset, COSIZE_T},
7811 };
7812 size_t nArg;
7813 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
7814 {
7815 return Status;
7816 }
7817
7818 bool fBadParam = false;
7819 bool fIsFilename = false;
7820 int commandsParsed = 0;
7821
7822 if (pMD != NULL)
7823 {
7824 if (nArg != 0)
7825 {
7826 fBadParam = true;
7827 }
7828 commandsParsed++;
7829 }
7830 if (fList)
7831 {
7832 commandsParsed++;
7833 if (nArg != 0)
7834 {
7835 fBadParam = true;
7836 }
7837 }
7838 if (fClearAll)
7839 {
7840 commandsParsed++;
7841 if (nArg != 0)
7842 {
7843 fBadParam = true;
7844 }
7845 }
7846 if (clearItem != 0)
7847 {
7848 commandsParsed++;
7849 if (nArg != 0)
7850 {
7851 fBadParam = true;
7852 }
7853 }
7854 if (1 <= nArg && nArg <= 3)
7855 {
7856 commandsParsed++;
7857 // did we get dll and type name or file:line#? Search for a colon in the first arg
7858 // to see if it is in fact a file:line#
7859 CHAR* pColon = strchr(DllName.data, ':');
7860#ifndef FEATURE_PAL
7861 if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A, 0, NULL, NULL))) {
7862#else
7863 if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_DLL_NAME_A, 0, NULL, NULL))) {
7864#endif
7865 ExtOut("%s not loaded yet\n", MAIN_CLR_DLL_NAME_A);
7866 return Status;
7867 }
7868
7869 if(NULL != pColon)
7870 {
7871 fIsFilename = true;
7872 *pColon = '\0';
7873 pColon++;
7874 if(1 != sscanf_s(pColon, "%d", &lineNumber))
7875 {
7876 ExtOut("Unable to parse line number\n");
7877 fBadParam = true;
7878 }
7879 else if(lineNumber < 0)
7880 {
7881 ExtOut("Line number must be positive\n");
7882 fBadParam = true;
7883 }
7884 if(nArg != 1) fBadParam = 1;
7885 }
7886 }
7887
7888 if (fBadParam || (commandsParsed != 1))
7889 {
7890 ExtOut("Usage: " SOSPrefix "bpmd -md <MethodDesc pointer>\n");
7891 ExtOut("Usage: " SOSPrefix "bpmd [-nofuturemodule] <module name> <managed function name> [<il offset>]\n");
7892 ExtOut("Usage: " SOSPrefix "bpmd <filename>:<line number>\n");
7893 ExtOut("Usage: " SOSPrefix "bpmd -list\n");
7894 ExtOut("Usage: " SOSPrefix "bpmd -clear <pending breakpoint number>\n");
7895 ExtOut("Usage: " SOSPrefix "bpmd -clearall\n");
7896#ifdef FEATURE_PAL
7897 ExtOut("See \"soshelp bpmd\" for more details.\n");
7898#else
7899 ExtOut("See \"!help bpmd\" for more details.\n");
7900#endif
7901 return Status;
7902 }
7903
7904 if (fList)
7905 {
7906 g_bpoints.ListBreakpoints();
7907 return Status;
7908 }
7909 if (clearItem != 0)
7910 {
7911 g_bpoints.ClearBreakpoint(clearItem);
7912 return Status;
7913 }
7914 if (fClearAll)
7915 {
7916 g_bpoints.ClearAllBreakpoints();
7917 return Status;
7918 }
7919 // Add a breakpoint
7920 // Do we already have this breakpoint?
7921 // Or, before setting it, is the module perhaps already loaded and code
7922 // is available? If so, don't add to our pending list, just go ahead and
7923 // set the real breakpoint.
7924
7925 LPWSTR ModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
7926 LPWSTR FunctionName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
7927 LPWSTR Filename = (LPWSTR)alloca(MAX_LONGPATH * sizeof(WCHAR));
7928
7929 BOOL bNeedNotificationExceptions = FALSE;
7930
7931 if (pMD == NULL)
7932 {
7933 int numModule = 0;
7934 int numMethods = 0;
7935
7936 ArrayHolder<DWORD_PTR> moduleList = NULL;
7937
7938 if(!fIsFilename)
7939 {
7940 MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, ModuleName, mdNameLen);
7941 MultiByteToWideChar(CP_ACP, 0, TypeName.data, -1, FunctionName, mdNameLen);
7942 }
7943 else
7944 {
7945 MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, Filename, MAX_LONGPATH);
7946 }
7947
7948 // Get modules that may need a breakpoint bound
7949 if ((Status = CheckEEDll()) == S_OK)
7950 {
7951 if ((Status = LoadClrDebugDll()) != S_OK)
7952 {
7953 // if the EE is loaded but DAC isn't we should stop.
7954 DACMessage(Status);
7955 return Status;
7956 }
7957 g_bDacBroken = FALSE; \
7958
7959 // Get the module list
7960 moduleList = ModuleFromName(fIsFilename ? NULL : DllName.data, &numModule);
7961
7962 // Its OK if moduleList is NULL
7963 // There is a very normal case when checking for modules after clr is loaded
7964 // but before any AppDomains or assemblies are created
7965 // for example:
7966 // >sxe ld:clr
7967 // >g
7968 // ...
7969 // ModLoad: clr.dll
7970 // >!bpmd Foo.dll Foo.Bar
7971 }
7972 // If LoadClrDebugDll() succeeded make sure we release g_clrData
7973 ToRelease<IXCLRDataProcess> spIDP(g_clrData);
7974 ToRelease<ISOSDacInterface> spISD(g_sos);
7975 ResetGlobals();
7976
7977 // we can get here with EE not loaded => 0 modules
7978 // EE is loaded => 0 or more modules
7979 ArrayHolder<DWORD_PTR> pMDs = NULL;
7980 for (int iModule = 0; iModule < numModule; iModule++)
7981 {
7982 ToRelease<IXCLRDataModule> ModDef;
7983 if (g_sos->GetModule(moduleList[iModule], &ModDef) != S_OK)
7984 {
7985 continue;
7986 }
7987
7988 HRESULT symbolsLoaded = S_FALSE;
7989 if(!fIsFilename)
7990 {
7991 g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, moduleList[iModule], (DWORD)Offset);
7992 }
7993 else
7994 {
7995 SymbolReader symbolReader;
7996 symbolsLoaded = g_bpoints.LoadSymbolsForModule(moduleList[iModule], &symbolReader);
7997 if(symbolsLoaded == S_OK &&
7998 g_bpoints.ResolvePendingNonModuleBoundBreakpoint(Filename, lineNumber, moduleList[iModule], &symbolReader) == S_OK)
7999 {
8000 // if we have symbols then get the function name so we can lookup the MethodDescs
8001 mdMethodDef methodDefToken;
8002 ULONG32 ilOffset;
8003 if(SUCCEEDED(symbolReader.ResolveSequencePoint(Filename, lineNumber, moduleList[iModule], &methodDefToken, &ilOffset)))
8004 {
8005 ToRelease<IXCLRDataMethodDefinition> pMethodDef = NULL;
8006 if (SUCCEEDED(ModDef->GetMethodDefinitionByToken(methodDefToken, &pMethodDef)))
8007 {
8008 ULONG32 nameLen = 0;
8009 pMethodDef->GetName(0, mdNameLen, &nameLen, FunctionName);
8010
8011 // get the size of the required buffer
8012 int buffSize = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, 0, NULL, NULL);
8013
8014 TypeName.data = new NOTHROW char[buffSize];
8015 if (TypeName.data != NULL)
8016 {
8017 int bytesWritten = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, buffSize, NULL, NULL);
8018 _ASSERTE(bytesWritten == buffSize);
8019 }
8020 }
8021 }
8022 }
8023 }
8024
8025 HRESULT gotMethodDescs = GetMethodDescsFromName(moduleList[iModule], ModDef, TypeName.data, &pMDs, &numMethods);
8026 if (FAILED(gotMethodDescs) && (!fIsFilename))
8027 {
8028 // BPs via file name will enumerate through modules so there will be legitimate failures.
8029 // for module/type name we already found a match so this shouldn't fail (this is the original behavior).
8030 ExtOut("Error getting MethodDescs for module %p\n", moduleList[iModule]);
8031 return Status;
8032 }
8033
8034 // for filename+line number only print extra info if symbols for this module are loaded (it can get quite noisy otherwise).
8035 if ((!fIsFilename) || (fIsFilename && symbolsLoaded == S_OK))
8036 {
8037 for (int i = 0; i < numMethods; i++)
8038 {
8039 if (pMDs[i] == MD_NOT_YET_LOADED)
8040 {
8041 continue;
8042 }
8043 ExtOut("MethodDesc = %p\n", SOS_PTR(pMDs[i]));
8044 }
8045 }
8046
8047 if (g_bpoints.Update(moduleList[iModule], FALSE))
8048 {
8049 bNeedNotificationExceptions = TRUE;
8050 }
8051 }
8052
8053 if (!fNoFutureModule)
8054 {
8055 // add a pending breakpoint that will find future loaded modules, and
8056 // wait for the module load notification.
8057 if (!fIsFilename)
8058 {
8059 g_bpoints.Add(ModuleName, FunctionName, NULL, (DWORD)Offset);
8060 }
8061 else
8062 {
8063 g_bpoints.Add(Filename, lineNumber, NULL);
8064 }
8065 bNeedNotificationExceptions = TRUE;
8066
8067 ULONG32 flags = 0;
8068 g_clrData->GetOtherNotificationFlags(&flags);
8069 flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD);
8070 g_clrData->SetOtherNotificationFlags(flags);
8071 }
8072 }
8073 else /* We were given a MethodDesc already */
8074 {
8075 // if we've got an explicit MD, then we better have CLR and mscordacwks loaded
8076 INIT_API_EE()
8077 INIT_API_DAC();
8078
8079 DacpMethodDescData MethodDescData;
8080 ExtOut("MethodDesc = %p\n", SOS_PTR(pMD));
8081 if (MethodDescData.Request(g_sos, TO_CDADDR(pMD)) != S_OK)
8082 {
8083 ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(pMD));
8084 return Status;
8085 }
8086
8087 if (MethodDescData.bHasNativeCode)
8088 {
8089 IssueDebuggerBPCommand((size_t) MethodDescData.NativeCodeAddr);
8090 }
8091 else if (MethodDescData.bIsDynamic)
8092 {
8093#ifndef FEATURE_PAL
8094 // Dynamic methods don't have JIT notifications. This is something we must
8095 // fix in the next release. Until then, you have a cumbersome user experience.
8096 ExtOut("This DynamicMethodDesc is not yet JITTED. Placing memory breakpoint at %p\n",
8097 MethodDescData.AddressOfNativeCodeSlot);
8098
8099 sprintf_s(buffer, _countof(buffer),
8100#ifdef _TARGET_WIN64_
8101 "ba w8"
8102#else
8103 "ba w4"
8104#endif // _TARGET_WIN64_
8105
8106 " /1 %p \"bp poi(%p); g\"",
8107 (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot,
8108 (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot);
8109
8110 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
8111 if (FAILED(Status))
8112 {
8113 ExtOut("Unable to set breakpoint with IDebugControl::Execute: %x\n",Status);
8114 ExtOut("Attempted to run: %s\n", buffer);
8115 }
8116#else
8117 ExtErr("This DynamicMethodDesc is not yet JITTED %p\n", MethodDescData.AddressOfNativeCodeSlot);
8118#endif // FEATURE_PAL
8119 }
8120 else
8121 {
8122 // Must issue a pending breakpoint.
8123 if (g_sos->GetMethodDescName(pMD, mdNameLen, FunctionName, NULL) != S_OK)
8124 {
8125 ExtOut("Unable to get method name for MethodDesc %p\n", SOS_PTR(pMD));
8126 return Status;
8127 }
8128
8129 FileNameForModule ((DWORD_PTR) MethodDescData.ModulePtr, ModuleName);
8130
8131 // We didn't find code, add a breakpoint.
8132 g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, TO_TADDR(MethodDescData.ModulePtr), 0);
8133 g_bpoints.Update(TO_TADDR(MethodDescData.ModulePtr), FALSE);
8134 bNeedNotificationExceptions = TRUE;
8135 }
8136 }
8137
8138 if (bNeedNotificationExceptions)
8139 {
8140 ExtOut("Adding pending breakpoints...\n");
8141#ifndef FEATURE_PAL
8142 sprintf_s(buffer, _countof(buffer), "sxe -c \"!HandleCLRN\" clrn");
8143 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
8144#else
8145 Status = g_ExtServices->SetExceptionCallback(HandleExceptionNotification);
8146#endif // FEATURE_PAL
8147 }
8148
8149 return Status;
8150}
8151
8152#ifndef FEATURE_PAL
8153
8154/**********************************************************************\
8155* Routine Description: *
8156* *
8157* This function is called to dump the managed threadpool *
8158* *
8159\**********************************************************************/
8160DECLARE_API(ThreadPool)
8161{
8162 INIT_API();
8163 MINIDUMP_NOT_SUPPORTED();
8164
8165 DacpThreadpoolData threadpool;
8166
8167 if ((Status = threadpool.Request(g_sos)) == S_OK)
8168 {
8169 BOOL doHCDump = FALSE, doWorkItemDump = FALSE, dml = FALSE;
8170
8171 CMDOption option[] =
8172 { // name, vptr, type, hasValue
8173 {"-ti", &doHCDump, COBOOL, FALSE},
8174 {"-wi", &doWorkItemDump, COBOOL, FALSE},
8175#ifndef FEATURE_PAL
8176 {"/d", &dml, COBOOL, FALSE},
8177#endif
8178 };
8179
8180 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
8181 {
8182 return Status;
8183 }
8184
8185 EnableDMLHolder dmlHolder(dml);
8186
8187 ExtOut ("CPU utilization: %d%%\n", threadpool.cpuUtilization);
8188 ExtOut ("Worker Thread:");
8189 ExtOut (" Total: %d", threadpool.NumWorkingWorkerThreads + threadpool.NumIdleWorkerThreads + threadpool.NumRetiredWorkerThreads);
8190 ExtOut (" Running: %d", threadpool.NumWorkingWorkerThreads);
8191 ExtOut (" Idle: %d", threadpool.NumIdleWorkerThreads);
8192 ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalWorkerThreads);
8193 ExtOut (" MinLimit: %d", threadpool.MinLimitTotalWorkerThreads);
8194 ExtOut ("\n");
8195
8196 int numWorkRequests = 0;
8197 CLRDATA_ADDRESS workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
8198 DacpWorkRequestData workRequestData;
8199 while (workRequestPtr)
8200 {
8201 if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
8202 {
8203 ExtOut(" Failed to examine a WorkRequest\n");
8204 return Status;
8205 }
8206 numWorkRequests++;
8207 workRequestPtr = workRequestData.NextWorkRequest;
8208 }
8209
8210 ExtOut ("Work Request in Queue: %d\n", numWorkRequests);
8211 workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
8212 while (workRequestPtr)
8213 {
8214 if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
8215 {
8216 ExtOut(" Failed to examine a WorkRequest\n");
8217 return Status;
8218 }
8219
8220 if (workRequestData.Function == threadpool.AsyncTimerCallbackCompletionFPtr)
8221 ExtOut (" AsyncTimerCallbackCompletion TimerInfo@%p\n", SOS_PTR(workRequestData.Context));
8222 else
8223 ExtOut (" Unknown Function: %p Context: %p\n", SOS_PTR(workRequestData.Function),
8224 SOS_PTR(workRequestData.Context));
8225
8226 workRequestPtr = workRequestData.NextWorkRequest;
8227 }
8228
8229 if (doWorkItemDump && g_snapshot.Build())
8230 {
8231 // Display a message if the heap isn't verified.
8232 sos::GCHeap gcheap;
8233 if (!gcheap.AreGCStructuresValid())
8234 {
8235 DisplayInvalidStructuresMessage();
8236 }
8237
8238 // Walk every heap item looking for the global queue and local queues.
8239 ExtOut("\nQueued work items:\n%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "Queue", "Address", "Work Item");
8240 HeapStat stats;
8241 for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr)
8242 {
8243 if (_wcscmp(itr->GetTypeName(), W("System.Threading.ThreadPoolWorkQueue")) == 0)
8244 {
8245 // We found a global queue (there should be only one, given one AppDomain).
8246 // Get its workItems ConcurrentQueue<IThreadPoolWorkItem>.
8247 int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("workItems"));
8248 if (offset > 0)
8249 {
8250 DWORD_PTR workItemsConcurrentQueuePtr;
8251 MOVE(workItemsConcurrentQueuePtr, itr->GetAddress() + offset);
8252 if (sos::IsObject(workItemsConcurrentQueuePtr, false))
8253 {
8254 // We got the ConcurrentQueue. Get its head segment.
8255 sos::Object workItemsConcurrentQueue = TO_TADDR(workItemsConcurrentQueuePtr);
8256 offset = GetObjFieldOffset(workItemsConcurrentQueue.GetAddress(), workItemsConcurrentQueue.GetMT(), W("_head"));
8257 if (offset > 0)
8258 {
8259 // Now, walk from segment to segment, each of which contains an array of work items.
8260 DWORD_PTR segmentPtr;
8261 MOVE(segmentPtr, workItemsConcurrentQueue.GetAddress() + offset);
8262 while (sos::IsObject(segmentPtr, false))
8263 {
8264 sos::Object segment = TO_TADDR(segmentPtr);
8265
8266 // Get the work items array. It's an array of Slot structs, which starts with the T.
8267 offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_slots"));
8268 if (offset <= 0)
8269 {
8270 break;
8271 }
8272
8273 DWORD_PTR slotsPtr;
8274 MOVE(slotsPtr, segment.GetAddress() + offset);
8275 if (!sos::IsObject(slotsPtr, false))
8276 {
8277 break;
8278 }
8279
8280 // Walk every element in the array, outputting details on non-null work items.
8281 DacpObjectData slotsArray;
8282 if (slotsArray.Request(g_sos, TO_CDADDR(slotsPtr)) == S_OK && slotsArray.ObjectType == OBJ_ARRAY)
8283 {
8284 for (int i = 0; i < slotsArray.dwNumComponents; i++)
8285 {
8286 CLRDATA_ADDRESS workItemPtr;
8287 MOVE(workItemPtr, TO_CDADDR(slotsArray.ArrayDataPtr + (i * slotsArray.dwComponentSize))); // the item object reference is at the beginning of the Slot
8288 if (workItemPtr != NULL && sos::IsObject(workItemPtr, false))
8289 {
8290 sos::Object workItem = TO_TADDR(workItemPtr);
8291 stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize());
8292 DMLOut("%" POINTERSIZE "s %s %S", "[Global]", DMLObject(workItem.GetAddress()), workItem.GetTypeName());
8293 if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 ||
8294 (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0)
8295 {
8296 CLRDATA_ADDRESS delegatePtr;
8297 MOVE(delegatePtr, workItem.GetAddress() + offset);
8298 CLRDATA_ADDRESS md;
8299 if (TryGetMethodDescriptorForDelegate(delegatePtr, &md))
8300 {
8301 NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
8302 ExtOut(" => %S", g_mdName);
8303 }
8304 }
8305 ExtOut("\n");
8306 }
8307 }
8308 }
8309
8310 // Move to the next segment.
8311 DacpFieldDescData segmentField;
8312 offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_nextSegment"), TRUE, &segmentField);
8313 if (offset <= 0)
8314 {
8315 break;
8316 }
8317
8318 MOVE(segmentPtr, segment.GetAddress() + offset);
8319 if (segmentPtr == NULL)
8320 {
8321 break;
8322 }
8323 }
8324 }
8325 }
8326 }
8327 }
8328 else if (_wcscmp(itr->GetTypeName(), W("System.Threading.ThreadPoolWorkQueue+WorkStealingQueue")) == 0)
8329 {
8330 // We found a local queue. Get its work items array.
8331 int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("m_array"));
8332 if (offset > 0)
8333 {
8334 // Walk every element in the array, outputting details on non-null work items.
8335 DWORD_PTR workItemArrayPtr;
8336 MOVE(workItemArrayPtr, itr->GetAddress() + offset);
8337 DacpObjectData workItemArray;
8338 if (workItemArray.Request(g_sos, TO_CDADDR(workItemArrayPtr)) == S_OK && workItemArray.ObjectType == OBJ_ARRAY)
8339 {
8340 for (int i = 0; i < workItemArray.dwNumComponents; i++)
8341 {
8342 CLRDATA_ADDRESS workItemPtr;
8343 MOVE(workItemPtr, TO_CDADDR(workItemArray.ArrayDataPtr + (i * workItemArray.dwComponentSize)));
8344 if (workItemPtr != NULL && sos::IsObject(workItemPtr, false))
8345 {
8346 sos::Object workItem = TO_TADDR(workItemPtr);
8347 stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize());
8348 DMLOut("%s %s %S", DMLObject(itr->GetAddress()), DMLObject(workItem.GetAddress()), workItem.GetTypeName());
8349 if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 ||
8350 (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0)
8351 {
8352 CLRDATA_ADDRESS delegatePtr;
8353 MOVE(delegatePtr, workItem.GetAddress() + offset);
8354 CLRDATA_ADDRESS md;
8355 if (TryGetMethodDescriptorForDelegate(delegatePtr, &md))
8356 {
8357 NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
8358 ExtOut(" => %S", g_mdName);
8359 }
8360 }
8361 ExtOut("\n");
8362 }
8363 }
8364 }
8365 }
8366 }
8367 }
8368
8369 // Output a summary.
8370 stats.Sort();
8371 stats.Print();
8372 ExtOut("\n");
8373 }
8374
8375 if (doHCDump)
8376 {
8377 ExtOut ("--------------------------------------\n");
8378 ExtOut ("\nThread Injection History\n");
8379 if (threadpool.HillClimbingLogSize > 0)
8380 {
8381 static char const * const TransitionNames[] =
8382 {
8383 "Warmup",
8384 "Initializing",
8385 "RandomMove",
8386 "ClimbingMove",
8387 "ChangePoint",
8388 "Stabilizing",
8389 "Starvation",
8390 "ThreadTimedOut",
8391 "Undefined"
8392 };
8393
8394 ExtOut("\n Time Transition New #Threads #Samples Throughput\n");
8395 DacpHillClimbingLogEntry entry;
8396
8397 // get the most recent entry first, so we can calculate time offsets
8398
8399 int index = (threadpool.HillClimbingLogFirstIndex + threadpool.HillClimbingLogSize-1) % HillClimbingLogCapacity;
8400 CLRDATA_ADDRESS entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
8401 if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
8402 {
8403 ExtOut(" Failed to examine a HillClimbing log entry\n");
8404 return Status;
8405 }
8406 DWORD endTime = entry.TickCount;
8407
8408 for (int i = 0; i < threadpool.HillClimbingLogSize; i++)
8409 {
8410 index = (i + threadpool.HillClimbingLogFirstIndex) % HillClimbingLogCapacity;
8411 entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
8412
8413 if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
8414 {
8415 ExtOut(" Failed to examine a HillClimbing log entry\n");
8416 return Status;
8417 }
8418
8419 ExtOut("%8.2lf %-14s %12d %12d %11.2lf\n",
8420 (double)(int)(entry.TickCount - endTime) / 1000.0,
8421 TransitionNames[entry.Transition],
8422 entry.NewControlSetting,
8423 entry.LastHistoryCount,
8424 entry.LastHistoryMean);
8425 }
8426 }
8427 }
8428
8429 ExtOut ("--------------------------------------\n");
8430 ExtOut ("Number of Timers: %d\n", threadpool.NumTimers);
8431 ExtOut ("--------------------------------------\n");
8432
8433 ExtOut ("Completion Port Thread:");
8434 ExtOut ("Total: %d", threadpool.NumCPThreads);
8435 ExtOut (" Free: %d", threadpool.NumFreeCPThreads);
8436 ExtOut (" MaxFree: %d", threadpool.MaxFreeCPThreads);
8437 ExtOut (" CurrentLimit: %d", threadpool.CurrentLimitTotalCPThreads);
8438 ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalCPThreads);
8439 ExtOut (" MinLimit: %d", threadpool.MinLimitTotalCPThreads);
8440 ExtOut ("\n");
8441 }
8442 else
8443 {
8444 ExtOut("Failed to request ThreadpoolMgr information\n");
8445 }
8446 return Status;
8447}
8448
8449#endif // FEATURE_PAL
8450
8451DECLARE_API(FindAppDomain)
8452{
8453 INIT_API();
8454 MINIDUMP_NOT_SUPPORTED();
8455
8456 DWORD_PTR p_Object = NULL;
8457 BOOL dml = FALSE;
8458
8459 CMDOption option[] =
8460 { // name, vptr, type, hasValue
8461#ifndef FEATURE_PAL
8462 {"/d", &dml, COBOOL, FALSE},
8463#endif
8464 };
8465 CMDValue arg[] =
8466 { // vptr, type
8467 {&p_Object, COHEX},
8468 };
8469 size_t nArg;
8470
8471 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
8472 {
8473 return Status;
8474 }
8475
8476 EnableDMLHolder dmlHolder(dml);
8477
8478 if ((p_Object == 0) || !sos::IsObject(p_Object))
8479 {
8480 ExtOut("%p is not a valid object\n", SOS_PTR(p_Object));
8481 return Status;
8482 }
8483
8484 DacpAppDomainStoreData adstore;
8485 if (adstore.Request(g_sos) != S_OK)
8486 {
8487 ExtOut("Error getting AppDomain information\n");
8488 return Status;
8489 }
8490
8491 CLRDATA_ADDRESS appDomain = GetAppDomain (TO_CDADDR(p_Object));
8492
8493 if (appDomain != NULL)
8494 {
8495 DMLOut("AppDomain: %s\n", DMLDomain(appDomain));
8496 if (appDomain == adstore.sharedDomain)
8497 {
8498 ExtOut("Name: Shared Domain\n");
8499 ExtOut("ID: (shared domain)\n");
8500 }
8501 else if (appDomain == adstore.systemDomain)
8502 {
8503 ExtOut("Name: System Domain\n");
8504 ExtOut("ID: (system domain)\n");
8505 }
8506 else
8507 {
8508 DacpAppDomainData domain;
8509 if ((domain.Request(g_sos, appDomain) != S_OK) ||
8510 (g_sos->GetAppDomainName(appDomain,mdNameLen,g_mdName, NULL)!=S_OK))
8511 {
8512 ExtOut("Error getting AppDomain %p.\n", SOS_PTR(appDomain));
8513 return Status;
8514 }
8515
8516 ExtOut("Name: %S\n", (g_mdName[0]!=L'\0') ? g_mdName : W("None"));
8517 ExtOut("ID: %d\n", domain.dwId);
8518 }
8519 }
8520 else
8521 {
8522 ExtOut("The type is declared in the shared domain and other\n");
8523 ExtOut("methods of finding the AppDomain failed. Try running\n");
8524 if (IsDMLEnabled())
8525 DMLOut("<exec cmd=\"!gcroot /d %p\">!gcroot %p</exec>, and if you find a root on a\n", p_Object, p_Object);
8526 else
8527 ExtOut(SOSPrefix "gcroot %p, and if you find a root on a\n", p_Object);
8528 ExtOut("stack, check the AppDomain of that stack with " SOSThreads ".\n");
8529 ExtOut("Note that the Thread could have transitioned between\n");
8530 ExtOut("multiple AppDomains.\n");
8531 }
8532
8533 return Status;
8534}
8535
8536#ifndef FEATURE_PAL
8537
8538/**********************************************************************\
8539* Routine Description: *
8540* *
8541* This function is called to get the COM state (e.g. APT,contexe *
8542* activity. *
8543* *
8544\**********************************************************************/
8545#ifdef FEATURE_COMINTEROP
8546DECLARE_API(COMState)
8547{
8548 INIT_API();
8549 MINIDUMP_NOT_SUPPORTED();
8550
8551
8552 ULONG numThread;
8553 ULONG maxId;
8554 g_ExtSystem->GetTotalNumberThreads(&numThread,&maxId);
8555
8556 ULONG curId;
8557 g_ExtSystem->GetCurrentThreadId(&curId);
8558
8559 SIZE_T AllocSize;
8560 if (!ClrSafeInt<SIZE_T>::multiply(sizeof(ULONG), numThread, AllocSize))
8561 {
8562 ExtOut(" Error! integer overflow on numThread 0x%08x\n", numThread);
8563 return Status;
8564 }
8565 ULONG *ids = (ULONG*)alloca(AllocSize);
8566 ULONG *sysIds = (ULONG*)alloca(AllocSize);
8567 g_ExtSystem->GetThreadIdsByIndex(0,numThread,ids,sysIds);
8568#if defined(_TARGET_WIN64_)
8569 ExtOut(" ID TEB APT APTId CallerTID Context\n");
8570#else
8571 ExtOut(" ID TEB APT APTId CallerTID Context\n");
8572#endif
8573 for (ULONG i = 0; i < numThread; i ++) {
8574 g_ExtSystem->SetCurrentThreadId(ids[i]);
8575 CLRDATA_ADDRESS cdaTeb;
8576 g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
8577 ExtOut("%3d %4x %p", ids[i], sysIds[i], SOS_PTR(CDA_TO_UL64(cdaTeb)));
8578 // Apartment state
8579 TADDR OleTlsDataAddr;
8580 if (SafeReadMemory(TO_TADDR(cdaTeb) + offsetof(TEB,ReservedForOle),
8581 &OleTlsDataAddr,
8582 sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0) {
8583 DWORD AptState;
8584 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
8585 &AptState,
8586 sizeof(AptState), NULL)) {
8587 if (AptState & OLETLS_APARTMENTTHREADED) {
8588 ExtOut(" STA");
8589 }
8590 else if (AptState & OLETLS_MULTITHREADED) {
8591 ExtOut(" MTA");
8592 }
8593 else if (AptState & OLETLS_INNEUTRALAPT) {
8594 ExtOut(" NTA");
8595 }
8596 else {
8597 ExtOut(" Ukn");
8598 }
8599
8600 // Read these fields only if we were able to read anything of the SOleTlsData structure
8601 DWORD dwApartmentID;
8602 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwApartmentID),
8603 &dwApartmentID,
8604 sizeof(dwApartmentID), NULL)) {
8605 ExtOut(" %8x", dwApartmentID);
8606 }
8607 else
8608 ExtOut(" %8x", 0);
8609
8610 DWORD dwTIDCaller;
8611 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwTIDCaller),
8612 &dwTIDCaller,
8613 sizeof(dwTIDCaller), NULL)) {
8614 ExtOut(" %8x", dwTIDCaller);
8615 }
8616 else
8617 ExtOut(" %8x", 0);
8618
8619 size_t Context;
8620 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,pCurrentCtx),
8621 &Context,
8622 sizeof(Context), NULL)) {
8623 ExtOut(" %p", SOS_PTR(Context));
8624 }
8625 else
8626 ExtOut(" %p", SOS_PTR(0));
8627
8628 }
8629 else
8630 ExtOut(" Ukn");
8631 }
8632 else
8633 ExtOut(" Ukn");
8634 ExtOut("\n");
8635 }
8636
8637 g_ExtSystem->SetCurrentThreadId(curId);
8638 return Status;
8639}
8640#endif // FEATURE_COMINTEROP
8641
8642#endif // FEATURE_PAL
8643
8644BOOL traverseEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
8645{
8646 size_t methodStart = (size_t) token;
8647
8648 if (IsInterrupt())
8649 {
8650 return FALSE;
8651 }
8652
8653 ExtOut("EHHandler %d: %s ", clauseIndex, EHTypeName(pEHInfo->clauseType));
8654
8655 LPCWSTR typeName = EHTypedClauseTypeName(pEHInfo);
8656 if (typeName != NULL)
8657 {
8658 ExtOut("catch(%S) ", typeName);
8659 }
8660
8661 if (IsClonedFinally(pEHInfo))
8662 ExtOut("(cloned finally)");
8663 else if (pEHInfo->isDuplicateClause)
8664 ExtOut("(duplicate)");
8665
8666 ExtOut("\n");
8667 ExtOut("Clause: ");
8668
8669 ULONG64 addrStart = pEHInfo->tryStartOffset + methodStart;
8670 ULONG64 addrEnd = pEHInfo->tryEndOffset + methodStart;
8671
8672#ifdef _WIN64
8673 ExtOut("[%08x`%08x, %08x`%08x]",
8674 (ULONG)(addrStart >> 32), (ULONG)addrStart,
8675 (ULONG)(addrEnd >> 32), (ULONG)addrEnd);
8676#else
8677 ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
8678#endif
8679
8680 ExtOut(" [%x, %x]\n",
8681 (UINT32) pEHInfo->tryStartOffset,
8682 (UINT32) pEHInfo->tryEndOffset);
8683
8684 ExtOut("Handler: ");
8685
8686 addrStart = pEHInfo->handlerStartOffset + methodStart;
8687 addrEnd = pEHInfo->handlerEndOffset + methodStart;
8688
8689#ifdef _WIN64
8690 ExtOut("[%08x`%08x, %08x`%08x]",
8691 (ULONG)(addrStart >> 32), (ULONG)addrStart,
8692 (ULONG)(addrEnd >> 32), (ULONG)addrEnd);
8693#else
8694 ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
8695#endif
8696
8697 ExtOut(" [%x, %x]\n",
8698 (UINT32) pEHInfo->handlerStartOffset,
8699 (UINT32) pEHInfo->handlerEndOffset);
8700
8701 if (pEHInfo->clauseType == EHFilter)
8702 {
8703 ExtOut("Filter: ");
8704
8705 addrStart = pEHInfo->filterOffset + methodStart;
8706
8707#ifdef _WIN64
8708 ExtOut("[%08x`%08x]", (ULONG)(addrStart >> 32), (ULONG)addrStart);
8709#else
8710 ExtOut("[%08x]", (ULONG)addrStart);
8711#endif
8712
8713 ExtOut(" [%x]\n",
8714 (UINT32) pEHInfo->filterOffset);
8715 }
8716
8717 ExtOut("\n");
8718 return TRUE;
8719}
8720
8721DECLARE_API(EHInfo)
8722{
8723 INIT_API();
8724 MINIDUMP_NOT_SUPPORTED();
8725
8726 DWORD_PTR dwStartAddr = NULL;
8727 BOOL dml = FALSE;
8728
8729 CMDOption option[] =
8730 { // name, vptr, type, hasValue
8731 {"/d", &dml, COBOOL, FALSE},
8732 };
8733
8734 CMDValue arg[] =
8735 { // vptr, type
8736 {&dwStartAddr, COHEX},
8737 };
8738
8739 size_t nArg;
8740 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
8741 {
8742 return Status;
8743 }
8744
8745 EnableDMLHolder dmlHolder(dml);
8746 DWORD_PTR tmpAddr = dwStartAddr;
8747
8748 if (!IsMethodDesc(dwStartAddr))
8749 {
8750 JITTypes jitType;
8751 DWORD_PTR methodDesc;
8752 DWORD_PTR gcinfoAddr;
8753 IP2MethodDesc (dwStartAddr, methodDesc, jitType, gcinfoAddr);
8754 tmpAddr = methodDesc;
8755 }
8756
8757 DacpMethodDescData MD;
8758 if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
8759 {
8760 ExtOut("%p is not a MethodDesc\n", SOS_PTR(tmpAddr));
8761 return Status;
8762 }
8763
8764 if (1 == nArg && !MD.bHasNativeCode)
8765 {
8766 ExtOut("No EH info available\n");
8767 return Status;
8768 }
8769
8770 DacpCodeHeaderData codeHeaderData;
8771 if (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
8772 {
8773 ExtOut("Unable to get codeHeader information\n");
8774 return Status;
8775 }
8776
8777 DMLOut("MethodDesc: %s\n", DMLMethodDesc(MD.MethodDescPtr));
8778 DumpMDInfo(TO_TADDR(MD.MethodDescPtr));
8779
8780 ExtOut("\n");
8781 Status = g_sos->TraverseEHInfo(TO_CDADDR(MD.NativeCodeAddr), traverseEh, (LPVOID)MD.NativeCodeAddr);
8782
8783 if (Status == E_ABORT)
8784 {
8785 ExtOut("<user aborted>\n");
8786 }
8787 else if (Status != S_OK)
8788 {
8789 ExtOut("Failed to perform EHInfo traverse\n");
8790 }
8791
8792 return Status;
8793}
8794
8795/**********************************************************************\
8796* Routine Description: *
8797* *
8798* This function is called to dump the GC encoding of a managed *
8799* function. *
8800* *
8801\**********************************************************************/
8802DECLARE_API(GCInfo)
8803{
8804 INIT_API();
8805 MINIDUMP_NOT_SUPPORTED();
8806
8807
8808 TADDR taStartAddr = NULL;
8809 TADDR taGCInfoAddr;
8810 BOOL dml = FALSE;
8811
8812 CMDOption option[] =
8813 { // name, vptr, type, hasValue
8814 {"/d", &dml, COBOOL, FALSE},
8815 };
8816 CMDValue arg[] =
8817 { // vptr, type
8818 {&taStartAddr, COHEX},
8819 };
8820 size_t nArg;
8821 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
8822 {
8823 return Status;
8824 }
8825
8826 EnableDMLHolder dmlHolder(dml);
8827 TADDR tmpAddr = taStartAddr;
8828
8829 if (!IsMethodDesc(taStartAddr))
8830 {
8831 JITTypes jitType;
8832 TADDR methodDesc;
8833 TADDR gcinfoAddr;
8834 IP2MethodDesc(taStartAddr, methodDesc, jitType, gcinfoAddr);
8835 tmpAddr = methodDesc;
8836 }
8837
8838 DacpMethodDescData MD;
8839 if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
8840 {
8841 ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(taStartAddr));
8842 return Status;
8843 }
8844
8845 if (1 == nArg && !MD.bHasNativeCode)
8846 {
8847 ExtOut("No GC info available\n");
8848 return Status;
8849 }
8850
8851 DacpCodeHeaderData codeHeaderData;
8852
8853 if (
8854 // Try to get code header data from taStartAddr. This will get the code
8855 // header corresponding to the IP address, even if the function was rejitted
8856 (codeHeaderData.Request(g_sos, TO_CDADDR(taStartAddr)) != S_OK) &&
8857
8858 // If that didn't work, just try to use the code address that the MD
8859 // points to. If the function was rejitted, this will only give you the
8860 // original JITted code, but that's better than nothing
8861 (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
8862 )
8863 {
8864 // We always used to emit this (before rejit support), even if we couldn't get
8865 // the code header, so keep on doing so.
8866 ExtOut("entry point %p\n", SOS_PTR(MD.NativeCodeAddr));
8867
8868 // And now the error....
8869 ExtOut("Unable to get codeHeader information\n");
8870 return Status;
8871 }
8872
8873 // We have the code header, so use it to determine the method start
8874
8875 ExtOut("entry point %p\n", SOS_PTR(codeHeaderData.MethodStart));
8876
8877 if (codeHeaderData.JITType == TYPE_UNKNOWN)
8878 {
8879 ExtOut("unknown Jit\n");
8880 return Status;
8881 }
8882 else if (codeHeaderData.JITType == TYPE_JIT)
8883 {
8884 ExtOut("Normal JIT generated code\n");
8885 }
8886 else if (codeHeaderData.JITType == TYPE_PJIT)
8887 {
8888 ExtOut("preJIT generated code\n");
8889 }
8890
8891 taGCInfoAddr = TO_TADDR(codeHeaderData.GCInfo);
8892
8893 ExtOut("GC info %p\n", SOS_PTR(taGCInfoAddr));
8894
8895 // assume that GC encoding table is never more than
8896 // 40 + methodSize * 2
8897 int tableSize = 0;
8898 if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
8899 !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
8900 {
8901 ExtOut("<integer overflow>\n");
8902 return E_FAIL;
8903 }
8904 ArrayHolder<BYTE> table = new NOTHROW BYTE[tableSize];
8905 if (table == NULL)
8906 {
8907 ExtOut("Could not allocate memory to read the gc info.\n");
8908 return E_OUTOFMEMORY;
8909 }
8910
8911 memset(table, 0, tableSize);
8912 // We avoid using move here, because we do not want to return
8913 if (!SafeReadMemory(taGCInfoAddr, table, tableSize, NULL))
8914 {
8915 ExtOut("Could not read memory %p\n", SOS_PTR(taGCInfoAddr));
8916 return Status;
8917 }
8918
8919 // Mutable table pointer since we need to pass the appropriate
8920 // offset into the table to DumpGCTable.
8921 GCInfoToken gcInfoToken = { table, GCINFO_VERSION };
8922 unsigned int methodSize = (unsigned int)codeHeaderData.MethodSize;
8923
8924 g_targetMachine->DumpGCInfo(gcInfoToken, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/);
8925
8926 return Status;
8927}
8928
8929#if !defined(FEATURE_PAL)
8930
8931void DecodeGCTableEntry (const char *fmt, ...)
8932{
8933 GCEncodingInfo *pInfo = (GCEncodingInfo*)GetFiberData();
8934 va_list va;
8935
8936 //
8937 // Append the new data to the buffer
8938 //
8939
8940 va_start(va, fmt);
8941
8942 int cch = _vsnprintf_s(&pInfo->buf[pInfo->cch], _countof(pInfo->buf) - pInfo->cch, _countof(pInfo->buf) - pInfo->cch - 1, fmt, va);
8943 if (cch >= 0)
8944 pInfo->cch += cch;
8945
8946 va_end(va);
8947
8948 pInfo->buf[pInfo->cch] = '\0';
8949
8950 //
8951 // If there are complete lines in the buffer, decode them.
8952 //
8953
8954 for (;;)
8955 {
8956 char *pNewLine = strchr(pInfo->buf, '\n');
8957
8958 if (!pNewLine)
8959 break;
8960
8961 //
8962 // The line should start with a 16-bit (x86) or 32-bit (non-x86) hex
8963 // offset. strtoul returns ULONG_MAX or 0 on failure. 0 is a valid
8964 // offset for the first encoding, or while the last offset was 0.
8965 //
8966
8967 if (isxdigit(pInfo->buf[0]))
8968 {
8969 char *pEnd;
8970 ULONG ofs = strtoul(pInfo->buf, &pEnd, 16);
8971
8972 if ( isspace(*pEnd)
8973 && -1 != ofs
8974 && ( -1 == pInfo->ofs
8975 || 0 == pInfo->ofs
8976 || ofs > 0))
8977 {
8978 pInfo->ofs = ofs;
8979 *pNewLine = '\0';
8980
8981 SwitchToFiber(pInfo->pvMainFiber);
8982 }
8983 }
8984 else if (0 == strncmp(pInfo->buf, "Untracked:", 10))
8985 {
8986 pInfo->ofs = 0;
8987 *pNewLine = '\0';
8988
8989 SwitchToFiber(pInfo->pvMainFiber);
8990 }
8991
8992 //
8993 // Shift the remaining data to the start of the buffer
8994 //
8995
8996 strcpy_s(pInfo->buf, _countof(pInfo->buf), pNewLine+1);
8997 pInfo->cch = (int)strlen(pInfo->buf);
8998 }
8999}
9000
9001
9002VOID CALLBACK DumpGCTableFiberEntry (LPVOID pvGCEncodingInfo)
9003{
9004 GCEncodingInfo *pInfo = (GCEncodingInfo*)pvGCEncodingInfo;
9005 GCInfoToken gcInfoToken = { pInfo->table, GCINFO_VERSION };
9006 g_targetMachine->DumpGCInfo(gcInfoToken, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
9007
9008 pInfo->fDoneDecoding = true;
9009 SwitchToFiber(pInfo->pvMainFiber);
9010}
9011#endif // !FEATURE_PAL
9012
9013BOOL gatherEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
9014{
9015 SOSEHInfo *pInfo = (SOSEHInfo *) token;
9016
9017 if (pInfo == NULL)
9018 {
9019 return FALSE;
9020 }
9021
9022 if (pInfo->m_pInfos == NULL)
9023 {
9024 // First time, initialize structure
9025 pInfo->EHCount = totalClauses;
9026 pInfo->m_pInfos = new NOTHROW DACEHInfo[totalClauses];
9027 if (pInfo->m_pInfos == NULL)
9028 {
9029 ReportOOM();
9030 return FALSE;
9031 }
9032 }
9033
9034 pInfo->m_pInfos[clauseIndex] = *((DACEHInfo*)pEHInfo);
9035 return TRUE;
9036}
9037
9038
9039/**********************************************************************\
9040* Routine Description: *
9041* *
9042* This function is called to unassembly a managed function. *
9043* It tries to print symbolic info for function call, contants... *
9044* *
9045\**********************************************************************/
9046DECLARE_API(u)
9047{
9048 INIT_API();
9049 MINIDUMP_NOT_SUPPORTED();
9050
9051
9052 DWORD_PTR dwStartAddr = NULL;
9053 BOOL fWithGCInfo = FALSE;
9054 BOOL fWithEHInfo = FALSE;
9055 BOOL bSuppressLines = FALSE;
9056 BOOL bDisplayOffsets = FALSE;
9057 BOOL dml = FALSE;
9058 size_t nArg;
9059
9060 CMDOption option[] =
9061 { // name, vptr, type, hasValue
9062#ifndef FEATURE_PAL
9063 {"-gcinfo", &fWithGCInfo, COBOOL, FALSE},
9064#endif
9065 {"-ehinfo", &fWithEHInfo, COBOOL, FALSE},
9066 {"-n", &bSuppressLines, COBOOL, FALSE},
9067 {"-o", &bDisplayOffsets, COBOOL, FALSE},
9068#ifndef FEATURE_PAL
9069 {"/d", &dml, COBOOL, FALSE},
9070#endif
9071 };
9072 CMDValue arg[] =
9073 { // vptr, type
9074 {&dwStartAddr, COHEX},
9075 };
9076 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (nArg < 1))
9077 {
9078 return Status;
9079 }
9080 // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
9081 ULONG symlines = 0;
9082 if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
9083 {
9084 symlines &= SYMOPT_LOAD_LINES;
9085 }
9086 bSuppressLines = bSuppressLines || (symlines == 0);
9087
9088 EnableDMLHolder dmlHolder(dml);
9089 // dwStartAddr is either some IP address or a MethodDesc. Start off assuming it's a
9090 // MethodDesc.
9091 DWORD_PTR methodDesc = dwStartAddr;
9092 if (!IsMethodDesc(methodDesc))
9093 {
9094 // Not a methodDesc, so gotta find it ourselves
9095 DWORD_PTR tmpAddr = dwStartAddr;
9096 JITTypes jt;
9097 DWORD_PTR gcinfoAddr;
9098 IP2MethodDesc (tmpAddr, methodDesc, jt,
9099 gcinfoAddr);
9100 if (!methodDesc || jt == TYPE_UNKNOWN)
9101 {
9102 // It is not managed code.
9103 ExtOut("Unmanaged code\n");
9104 UnassemblyUnmanaged(dwStartAddr, bSuppressLines);
9105 return Status;
9106 }
9107 }
9108
9109 DacpMethodDescData MethodDescData;
9110 if ((Status=MethodDescData.Request(g_sos, TO_CDADDR(methodDesc))) != S_OK)
9111 {
9112 ExtOut("Failed to get method desc for %p.\n", SOS_PTR(dwStartAddr));
9113 return Status;
9114 }
9115
9116 if (!MethodDescData.bHasNativeCode)
9117 {
9118 ExtOut("Not jitted yet\n");
9119 return Status;
9120 }
9121
9122 // Get the appropriate code header. If we were passed an MD, then use
9123 // MethodDescData.NativeCodeAddr to find the code header; if we were passed an IP, use
9124 // that IP to find the code header. This ensures that, for rejitted functions, we
9125 // disassemble the rejit version that the user explicitly specified with their IP.
9126 DacpCodeHeaderData codeHeaderData;
9127 if (codeHeaderData.Request(
9128 g_sos,
9129 TO_CDADDR(
9130 (dwStartAddr == methodDesc) ? MethodDescData.NativeCodeAddr : dwStartAddr)
9131 ) != S_OK)
9132
9133 {
9134 ExtOut("Unable to get codeHeader information\n");
9135 return Status;
9136 }
9137
9138 if (codeHeaderData.MethodStart == 0)
9139 {
9140 ExtOut("not a valid MethodDesc\n");
9141 return Status;
9142 }
9143
9144 if (codeHeaderData.JITType == TYPE_UNKNOWN)
9145 {
9146 ExtOut("unknown Jit\n");
9147 return Status;
9148 }
9149 else if (codeHeaderData.JITType == TYPE_JIT)
9150 {
9151 ExtOut("Normal JIT generated code\n");
9152 }
9153 else if (codeHeaderData.JITType == TYPE_PJIT)
9154 {
9155 ExtOut("preJIT generated code\n");
9156 }
9157
9158 NameForMD_s(methodDesc, g_mdName, mdNameLen);
9159 ExtOut("%S\n", g_mdName);
9160 if (codeHeaderData.ColdRegionStart != NULL)
9161 {
9162 ExtOut("Begin %p, size %x. Cold region begin %p, size %x\n",
9163 SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.HotRegionSize,
9164 SOS_PTR(codeHeaderData.ColdRegionStart), codeHeaderData.ColdRegionSize);
9165 }
9166 else
9167 {
9168 ExtOut("Begin %p, size %x\n", SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.MethodSize);
9169 }
9170
9171#if !defined(FEATURE_PAL)
9172 //
9173 // Set up to mix gc info with the code if requested
9174 //
9175
9176 GCEncodingInfo gcEncodingInfo = {0};
9177
9178 // The actual GC Encoding Table, this is updated during the course of the function.
9179 gcEncodingInfo.table = NULL;
9180
9181 // The holder to make sure we clean up the memory for the table
9182 ArrayHolder<BYTE> table = NULL;
9183
9184 if (fWithGCInfo)
9185 {
9186 // assume that GC encoding table is never more than 40 + methodSize * 2
9187 int tableSize = 0;
9188 if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
9189 !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
9190 {
9191 ExtOut("<integer overflow>\n");
9192 return E_FAIL;
9193 }
9194
9195
9196 // Assign the new array to the mutable gcEncodingInfo table and to the
9197 // table ArrayHolder to clean this up when the function exits.
9198 table = gcEncodingInfo.table = new NOTHROW BYTE[tableSize];
9199
9200 if (gcEncodingInfo.table == NULL)
9201 {
9202 ExtOut("Could not allocate memory to read the gc info.\n");
9203 return E_OUTOFMEMORY;
9204 }
9205
9206 memset (gcEncodingInfo.table, 0, tableSize);
9207 // We avoid using move here, because we do not want to return
9208 if (!SafeReadMemory(TO_TADDR(codeHeaderData.GCInfo), gcEncodingInfo.table, tableSize, NULL))
9209 {
9210 ExtOut("Could not read memory %p\n", SOS_PTR(codeHeaderData.GCInfo));
9211 return Status;
9212 }
9213
9214 //
9215 // Skip the info header
9216 //
9217 gcEncodingInfo.methodSize = (unsigned int)codeHeaderData.MethodSize;
9218
9219 //
9220 // DumpGCTable will call gcPrintf for each encoding. We'd like a "give
9221 // me the next encoding" interface, but we're stuck with the callback.
9222 // To reconcile this without messing up too much code, we'll create a
9223 // fiber to dump the gc table. When we need the next gc encoding,
9224 // we'll switch to this fiber. The callback will note the next offset,
9225 // and switch back to the main fiber.
9226 //
9227
9228 gcEncodingInfo.ofs = -1;
9229 gcEncodingInfo.hotSizeToAdd = 0;
9230
9231 gcEncodingInfo.pvMainFiber = ConvertThreadToFiber(NULL);
9232 if (!gcEncodingInfo.pvMainFiber && ERROR_ALREADY_FIBER == GetLastError())
9233 gcEncodingInfo.pvMainFiber = GetCurrentFiber();
9234
9235 if (!gcEncodingInfo.pvMainFiber)
9236 return Status;
9237
9238 gcEncodingInfo.pvGCTableFiber = CreateFiber(0, DumpGCTableFiberEntry, &gcEncodingInfo);
9239 if (!gcEncodingInfo.pvGCTableFiber)
9240 return Status;
9241
9242 SwitchToFiber(gcEncodingInfo.pvGCTableFiber);
9243 }
9244#endif
9245
9246 SOSEHInfo *pInfo = NULL;
9247 if (fWithEHInfo)
9248 {
9249 pInfo = new NOTHROW SOSEHInfo;
9250 if (pInfo == NULL)
9251 {
9252 ReportOOM();
9253 }
9254 else if (g_sos->TraverseEHInfo(MethodDescData.NativeCodeAddr, gatherEh, (LPVOID)pInfo) != S_OK)
9255 {
9256 ExtOut("Failed to gather EHInfo data\n");
9257 delete pInfo;
9258 pInfo = NULL;
9259 }
9260 }
9261
9262 if (codeHeaderData.ColdRegionStart == NULL)
9263 {
9264 g_targetMachine->Unassembly (
9265 (DWORD_PTR) codeHeaderData.MethodStart,
9266 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.MethodSize,
9267 dwStartAddr,
9268 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
9269#if !defined(FEATURE_PAL)
9270 fWithGCInfo ? &gcEncodingInfo :
9271#endif
9272 NULL,
9273 pInfo,
9274 bSuppressLines,
9275 bDisplayOffsets
9276 );
9277 }
9278 else
9279 {
9280 ExtOut("Hot region:\n");
9281 g_targetMachine->Unassembly (
9282 (DWORD_PTR) codeHeaderData.MethodStart,
9283 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.HotRegionSize,
9284 dwStartAddr,
9285 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
9286#if !defined(FEATURE_PAL)
9287 fWithGCInfo ? &gcEncodingInfo :
9288#endif
9289 NULL,
9290 pInfo,
9291 bSuppressLines,
9292 bDisplayOffsets
9293 );
9294
9295 ExtOut("Cold region:\n");
9296
9297#if !defined(FEATURE_PAL)
9298 // Displaying gcinfo for a cold region requires knowing the size of
9299 // the hot region preceeding.
9300 gcEncodingInfo.hotSizeToAdd = codeHeaderData.HotRegionSize;
9301#endif
9302 g_targetMachine->Unassembly (
9303 (DWORD_PTR) codeHeaderData.ColdRegionStart,
9304 ((DWORD_PTR)codeHeaderData.ColdRegionStart) + codeHeaderData.ColdRegionSize,
9305 dwStartAddr,
9306 ((DWORD_PTR) MethodDescData.GCStressCodeCopy) + codeHeaderData.HotRegionSize,
9307#if !defined(FEATURE_PAL)
9308 fWithGCInfo ? &gcEncodingInfo :
9309#endif
9310 NULL,
9311 pInfo,
9312 bSuppressLines,
9313 bDisplayOffsets
9314 );
9315
9316 }
9317
9318 if (pInfo)
9319 {
9320 delete pInfo;
9321 pInfo = NULL;
9322 }
9323
9324#if !defined(FEATURE_PAL)
9325 if (fWithGCInfo)
9326 DeleteFiber(gcEncodingInfo.pvGCTableFiber);
9327#endif
9328
9329 return Status;
9330}
9331
9332/**********************************************************************\
9333* Routine Description: *
9334* *
9335* This function is called to dump the in-memory stress log *
9336* !DumpLog [filename] *
9337* will dump the stress log corresponding to the clr.dll *
9338* loaded in the debuggee's VAS *
9339* !DumpLog -addr <addr_of_StressLog::theLog> [filename] *
9340* will dump the stress log associated with any DLL linked *
9341* against utilcode.lib, most commonly mscordbi.dll *
9342* (e.g. !DumpLog -addr mscordbi!StressLog::theLog) *
9343* *
9344\**********************************************************************/
9345DECLARE_API(DumpLog)
9346{
9347 INIT_API_NO_RET_ON_FAILURE();
9348
9349 MINIDUMP_NOT_SUPPORTED();
9350
9351 const char* fileName = "StressLog.txt";
9352
9353 CLRDATA_ADDRESS StressLogAddress = NULL;
9354
9355 StringHolder sFileName, sLogAddr;
9356 CMDOption option[] =
9357 { // name, vptr, type, hasValue
9358 {"-addr", &sLogAddr.data, COSTRING, TRUE}
9359 };
9360 CMDValue arg[] =
9361 { // vptr, type
9362 {&sFileName.data, COSTRING}
9363 };
9364 size_t nArg;
9365 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9366 {
9367 return Status;
9368 }
9369 if (nArg > 0 && sFileName.data != NULL)
9370 {
9371 fileName = sFileName.data;
9372 }
9373
9374 // allow users to specify -addr mscordbdi!StressLog::theLog, for example.
9375 if (sLogAddr.data != NULL)
9376 {
9377 StressLogAddress = GetExpression(sLogAddr.data);
9378 }
9379
9380 if (StressLogAddress == NULL)
9381 {
9382 if (g_bDacBroken)
9383 {
9384#ifdef FEATURE_PAL
9385 ExtOut("No stress log address. DAC is broken; can't get it\n");
9386 return E_FAIL;
9387#else
9388 // Try to find stress log symbols
9389 DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
9390 StressLogAddress = dwAddr;
9391#endif
9392 }
9393 else if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
9394 {
9395 ExtOut("Unable to find stress log via DAC\n");
9396 return E_FAIL;
9397 }
9398 }
9399
9400 if (StressLogAddress == NULL)
9401 {
9402 ExtOut("Please provide the -addr argument for the address of the stress log, since no recognized runtime is loaded.\n");
9403 return E_FAIL;
9404 }
9405
9406 ExtOut("Attempting to dump Stress log to file '%s'\n", fileName);
9407
9408
9409
9410 Status = StressLog::Dump(StressLogAddress, fileName, g_ExtData);
9411
9412 if (Status == S_OK)
9413 ExtOut("SUCCESS: Stress log dumped\n");
9414 else if (Status == S_FALSE)
9415 ExtOut("No Stress log in the image, no file written\n");
9416 else
9417 ExtOut("FAILURE: Stress log not dumped\n");
9418
9419 return Status;
9420}
9421
9422#ifdef TRACE_GC
9423
9424DECLARE_API (DumpGCLog)
9425{
9426 INIT_API_NODAC();
9427 MINIDUMP_NOT_SUPPORTED();
9428
9429 if (GetEEFlavor() == UNKNOWNEE)
9430 {
9431 ExtOut("CLR not loaded\n");
9432 return Status;
9433 }
9434
9435 const char* fileName = "GCLog.txt";
9436
9437 while (isspace (*args))
9438 args ++;
9439
9440 if (*args != 0)
9441 fileName = args;
9442
9443 DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_log_buffer");
9444 moveN (dwAddr, dwAddr);
9445
9446 if (dwAddr == 0)
9447 {
9448 dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_log_buffer");
9449 moveN (dwAddr, dwAddr);
9450 if (dwAddr == 0)
9451 {
9452 ExtOut("Can't get either WKS or SVR GC's log file");
9453 return E_FAIL;
9454 }
9455 }
9456
9457 ExtOut("Dumping GC log at %08x\n", dwAddr);
9458
9459 g_bDacBroken = FALSE;
9460
9461 ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
9462
9463 Status = E_FAIL;
9464
9465 HANDLE hGCLog = CreateFileA(
9466 fileName,
9467 GENERIC_WRITE,
9468 FILE_SHARE_READ,
9469 NULL,
9470 CREATE_ALWAYS,
9471 FILE_ATTRIBUTE_NORMAL,
9472 NULL);
9473
9474 if (hGCLog == INVALID_HANDLE_VALUE)
9475 {
9476 ExtOut("failed to create file: %d\n", GetLastError());
9477 goto exit;
9478 }
9479
9480 int iLogSize = 1024*1024;
9481 BYTE* bGCLog = new NOTHROW BYTE[iLogSize];
9482 if (bGCLog == NULL)
9483 {
9484 ReportOOM();
9485 goto exit;
9486 }
9487
9488 memset (bGCLog, 0, iLogSize);
9489 if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
9490 {
9491 ExtOut("failed to read memory from %08x\n", dwAddr);
9492 }
9493
9494 int iRealLogSize = iLogSize - 1;
9495 while (iRealLogSize >= 0)
9496 {
9497 if (bGCLog[iRealLogSize] != '*')
9498 {
9499 break;
9500 }
9501
9502 iRealLogSize--;
9503 }
9504
9505 DWORD dwWritten = 0;
9506 WriteFile (hGCLog, bGCLog, iRealLogSize + 1, &dwWritten, NULL);
9507
9508 Status = S_OK;
9509
9510exit:
9511
9512 if (hGCLog != INVALID_HANDLE_VALUE)
9513 {
9514 CloseHandle (hGCLog);
9515 }
9516
9517 if (Status == S_OK)
9518 ExtOut("SUCCESS: Stress log dumped\n");
9519 else if (Status == S_FALSE)
9520 ExtOut("No Stress log in the image, no file written\n");
9521 else
9522 ExtOut("FAILURE: Stress log not dumped\n");
9523
9524 return Status;
9525}
9526#endif //TRACE_GC
9527
9528#ifndef FEATURE_PAL
9529DECLARE_API (DumpGCConfigLog)
9530{
9531 INIT_API();
9532#ifdef GC_CONFIG_DRIVEN
9533 MINIDUMP_NOT_SUPPORTED();
9534
9535 if (GetEEFlavor() == UNKNOWNEE)
9536 {
9537 ExtOut("CLR not loaded\n");
9538 return Status;
9539 }
9540
9541 const char* fileName = "GCConfigLog.txt";
9542
9543 while (isspace (*args))
9544 args ++;
9545
9546 if (*args != 0)
9547 fileName = args;
9548
9549 if (!InitializeHeapData ())
9550 {
9551 ExtOut("GC Heap not initialized yet.\n");
9552 return S_OK;
9553 }
9554
9555 BOOL fIsServerGC = IsServerBuild();
9556
9557 DWORD_PTR dwAddr = 0;
9558 DWORD_PTR dwAddrOffset = 0;
9559
9560 if (fIsServerGC)
9561 {
9562 dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer");
9563 dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer_offset");
9564 }
9565 else
9566 {
9567 dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer");
9568 dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer_offset");
9569 }
9570
9571 moveN (dwAddr, dwAddr);
9572 moveN (dwAddrOffset, dwAddrOffset);
9573
9574 if (dwAddr == 0)
9575 {
9576 ExtOut("Can't get either WKS or SVR GC's config log buffer");
9577 return E_FAIL;
9578 }
9579
9580 ExtOut("Dumping GC log at %08x\n", dwAddr);
9581
9582 g_bDacBroken = FALSE;
9583
9584 ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
9585
9586 Status = E_FAIL;
9587
9588 HANDLE hGCLog = CreateFileA(
9589 fileName,
9590 GENERIC_WRITE,
9591 FILE_SHARE_READ,
9592 NULL,
9593 OPEN_ALWAYS,
9594 FILE_ATTRIBUTE_NORMAL,
9595 NULL);
9596
9597 if (hGCLog == INVALID_HANDLE_VALUE)
9598 {
9599 ExtOut("failed to create file: %d\n", GetLastError());
9600 goto exit;
9601 }
9602
9603 {
9604 int iLogSize = (int)dwAddrOffset;
9605
9606 ArrayHolder<BYTE> bGCLog = new NOTHROW BYTE[iLogSize];
9607 if (bGCLog == NULL)
9608 {
9609 ReportOOM();
9610 goto exit;
9611 }
9612
9613 memset (bGCLog, 0, iLogSize);
9614 if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
9615 {
9616 ExtOut("failed to read memory from %08x\n", dwAddr);
9617 }
9618
9619 SetFilePointer (hGCLog, 0, 0, FILE_END);
9620 DWORD dwWritten;
9621 WriteFile (hGCLog, bGCLog, iLogSize, &dwWritten, NULL);
9622 }
9623
9624 Status = S_OK;
9625
9626exit:
9627
9628 if (hGCLog != INVALID_HANDLE_VALUE)
9629 {
9630 CloseHandle (hGCLog);
9631 }
9632
9633 if (Status == S_OK)
9634 ExtOut("SUCCESS: Stress log dumped\n");
9635 else if (Status == S_FALSE)
9636 ExtOut("No Stress log in the image, no file written\n");
9637 else
9638 ExtOut("FAILURE: Stress log not dumped\n");
9639
9640 return Status;
9641#else
9642 ExtOut("Not implemented\n");
9643 return S_OK;
9644#endif //GC_CONFIG_DRIVEN
9645}
9646#endif // FEATURE_PAL
9647
9648#ifdef GC_CONFIG_DRIVEN
9649static const char * const str_interesting_data_points[] =
9650{
9651 "pre short", // 0
9652 "post short", // 1
9653 "merged pins", // 2
9654 "converted pins", // 3
9655 "pre pin", // 4
9656 "post pin", // 5
9657 "pre and post pin", // 6
9658 "pre short padded", // 7
9659 "post short padded", // 7
9660};
9661
9662static const char * const str_heap_compact_reasons[] =
9663{
9664 "low on ephemeral space",
9665 "high fragmentation",
9666 "couldn't allocate gaps",
9667 "user specfied compact LOH",
9668 "last GC before OOM",
9669 "induced compacting GC",
9670 "fragmented gen0 (ephemeral GC)",
9671 "high memory load (ephemeral GC)",
9672 "high memory load and frag",
9673 "very high memory load and frag",
9674 "no gc mode"
9675};
9676
9677static BOOL gc_heap_compact_reason_mandatory_p[] =
9678{
9679 TRUE, //compact_low_ephemeral = 0,
9680 FALSE, //compact_high_frag = 1,
9681 TRUE, //compact_no_gaps = 2,
9682 TRUE, //compact_loh_forced = 3,
9683 TRUE, //compact_last_gc = 4
9684 TRUE, //compact_induced_compacting = 5,
9685 FALSE, //compact_fragmented_gen0 = 6,
9686 FALSE, //compact_high_mem_load = 7,
9687 TRUE, //compact_high_mem_frag = 8,
9688 TRUE, //compact_vhigh_mem_frag = 9,
9689 TRUE //compact_no_gc_mode = 10
9690};
9691
9692static const char * const str_heap_expand_mechanisms[] =
9693{
9694 "reused seg with normal fit",
9695 "reused seg with best fit",
9696 "expand promoting eph",
9697 "expand with a new seg",
9698 "no memory for a new seg",
9699 "expand in next full GC"
9700};
9701
9702static const char * const str_bit_mechanisms[] =
9703{
9704 "using mark list",
9705 "demotion"
9706};
9707
9708static const char * const str_gc_global_mechanisms[] =
9709{
9710 "concurrent GCs",
9711 "compacting GCs",
9712 "promoting GCs",
9713 "GCs that did demotion",
9714 "card bundles",
9715 "elevation logic"
9716};
9717
9718void PrintInterestingGCInfo(DacpGCInterestingInfoData* dataPerHeap)
9719{
9720 ExtOut("Interesting data points\n");
9721 size_t* data = dataPerHeap->interestingDataPoints;
9722 for (int i = 0; i < DAC_NUM_GC_DATA_POINTS; i++)
9723 {
9724 ExtOut("%20s: %d\n", str_interesting_data_points[i], data[i]);
9725 }
9726
9727 ExtOut("\nCompacting reasons\n");
9728 data = dataPerHeap->compactReasons;
9729 for (int i = 0; i < DAC_MAX_COMPACT_REASONS_COUNT; i++)
9730 {
9731 ExtOut("[%s]%35s: %d\n", (gc_heap_compact_reason_mandatory_p[i] ? "M" : "W"), str_heap_compact_reasons[i], data[i]);
9732 }
9733
9734 ExtOut("\nExpansion mechanisms\n");
9735 data = dataPerHeap->expandMechanisms;
9736 for (int i = 0; i < DAC_MAX_EXPAND_MECHANISMS_COUNT; i++)
9737 {
9738 ExtOut("%30s: %d\n", str_heap_expand_mechanisms[i], data[i]);
9739 }
9740
9741 ExtOut("\nOther mechanisms enabled\n");
9742 data = dataPerHeap->bitMechanisms;
9743 for (int i = 0; i < DAC_MAX_GC_MECHANISM_BITS_COUNT; i++)
9744 {
9745 ExtOut("%20s: %d\n", str_bit_mechanisms[i], data[i]);
9746 }
9747}
9748#endif //GC_CONFIG_DRIVEN
9749
9750DECLARE_API(DumpGCData)
9751{
9752 INIT_API();
9753
9754#ifdef GC_CONFIG_DRIVEN
9755 MINIDUMP_NOT_SUPPORTED();
9756
9757 if (!InitializeHeapData ())
9758 {
9759 ExtOut("GC Heap not initialized yet.\n");
9760 return S_OK;
9761 }
9762
9763 DacpGCInterestingInfoData interestingInfo;
9764 interestingInfo.RequestGlobal(g_sos);
9765 for (int i = 0; i < DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT; i++)
9766 {
9767 ExtOut("%-30s: %d\n", str_gc_global_mechanisms[i], interestingInfo.globalMechanisms[i]);
9768 }
9769
9770 ExtOut("\n[info per heap]\n");
9771
9772 if (!IsServerBuild())
9773 {
9774 if (interestingInfo.Request(g_sos) != S_OK)
9775 {
9776 ExtOut("Error requesting interesting GC info\n");
9777 return E_FAIL;
9778 }
9779
9780 PrintInterestingGCInfo(&interestingInfo);
9781 }
9782 else
9783 {
9784 DWORD dwNHeaps = GetGcHeapCount();
9785 DWORD dwAllocSize;
9786 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
9787 {
9788 ExtOut("Failed to get GCHeaps: integer overflow\n");
9789 return Status;
9790 }
9791
9792 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
9793 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
9794 {
9795 ExtOut("Failed to get GCHeaps\n");
9796 return Status;
9797 }
9798
9799 for (DWORD n = 0; n < dwNHeaps; n ++)
9800 {
9801 if (interestingInfo.Request(g_sos, heapAddrs[n]) != S_OK)
9802 {
9803 ExtOut("Heap %d: Error requesting interesting GC info\n", n);
9804 return E_FAIL;
9805 }
9806
9807 ExtOut("--------info for heap %d--------\n", n);
9808 PrintInterestingGCInfo(&interestingInfo);
9809 ExtOut("\n");
9810 }
9811 }
9812
9813 return S_OK;
9814#else
9815 ExtOut("Not implemented\n");
9816 return S_OK;
9817#endif //GC_CONFIG_DRIVEN
9818}
9819
9820#ifndef FEATURE_PAL
9821/**********************************************************************\
9822* Routine Description: *
9823* *
9824* This function is called to dump the build number and type of the *
9825* mscoree.dll *
9826* *
9827\**********************************************************************/
9828DECLARE_API (EEVersion)
9829{
9830 INIT_API();
9831
9832 EEFLAVOR eef = GetEEFlavor();
9833 if (eef == UNKNOWNEE) {
9834 ExtOut("CLR not loaded\n");
9835 return Status;
9836 }
9837
9838 if (g_ExtSymbols2) {
9839 VS_FIXEDFILEINFO version;
9840
9841 BOOL ret = GetEEVersion(&version);
9842
9843 if (ret)
9844 {
9845 if (version.dwFileVersionMS != (DWORD)-1)
9846 {
9847 ExtOut("%u.%u.%u.%u",
9848 HIWORD(version.dwFileVersionMS),
9849 LOWORD(version.dwFileVersionMS),
9850 HIWORD(version.dwFileVersionLS),
9851 LOWORD(version.dwFileVersionLS));
9852 if (version.dwFileFlags & VS_FF_DEBUG)
9853 {
9854 ExtOut(" Checked or debug build");
9855 }
9856 else
9857 {
9858 BOOL fRet = IsRetailBuild ((size_t)moduleInfo[eef].baseAddr);
9859
9860 if (fRet)
9861 ExtOut(" retail");
9862 else
9863 ExtOut(" free");
9864 }
9865
9866 ExtOut("\n");
9867 }
9868 }
9869 }
9870
9871 if (!InitializeHeapData ())
9872 ExtOut("GC Heap not initialized, so GC mode is not determined yet.\n");
9873 else if (IsServerBuild())
9874 ExtOut("Server mode with %d gc heaps\n", GetGcHeapCount());
9875 else
9876 ExtOut("Workstation mode\n");
9877
9878 if (!GetGcStructuresValid())
9879 {
9880 ExtOut("In plan phase of garbage collection\n");
9881 }
9882
9883 // Print SOS version
9884 VS_FIXEDFILEINFO sosVersion;
9885 if (GetSOSVersion(&sosVersion))
9886 {
9887 if (sosVersion.dwFileVersionMS != (DWORD)-1)
9888 {
9889 ExtOut("SOS Version: %u.%u.%u.%u",
9890 HIWORD(sosVersion.dwFileVersionMS),
9891 LOWORD(sosVersion.dwFileVersionMS),
9892 HIWORD(sosVersion.dwFileVersionLS),
9893 LOWORD(sosVersion.dwFileVersionLS));
9894 if (sosVersion.dwFileFlags & VS_FF_DEBUG)
9895 {
9896 ExtOut(" Checked or debug build");
9897 }
9898 else
9899 {
9900 ExtOut(" retail build");
9901 }
9902
9903 ExtOut("\n");
9904 }
9905 }
9906 return Status;
9907}
9908#endif // FEATURE_PAL
9909
9910#ifndef FEATURE_PAL
9911/**********************************************************************\
9912* Routine Description: *
9913* *
9914* This function is called to print the environment setting for *
9915* the current process. *
9916* *
9917\**********************************************************************/
9918DECLARE_API (ProcInfo)
9919{
9920 INIT_API();
9921 MINIDUMP_NOT_SUPPORTED();
9922
9923 if (IsDumpFile())
9924 {
9925 ExtOut("!ProcInfo is not supported on a dump file.\n");
9926 return Status;
9927 }
9928
9929#define INFO_ENV 0x00000001
9930#define INFO_TIME 0x00000002
9931#define INFO_MEM 0x00000004
9932#define INFO_ALL 0xFFFFFFFF
9933
9934 DWORD fProcInfo = INFO_ALL;
9935
9936 if (_stricmp (args, "-env") == 0) {
9937 fProcInfo = INFO_ENV;
9938 }
9939
9940 if (_stricmp (args, "-time") == 0) {
9941 fProcInfo = INFO_TIME;
9942 }
9943
9944 if (_stricmp (args, "-mem") == 0) {
9945 fProcInfo = INFO_MEM;
9946 }
9947
9948 if (fProcInfo & INFO_ENV) {
9949 ExtOut("---------------------------------------\n");
9950 ExtOut("Environment\n");
9951 ULONG64 pPeb;
9952 g_ExtSystem->GetCurrentProcessPeb(&pPeb);
9953
9954 static ULONG Offset_ProcessParam = -1;
9955 static ULONG Offset_Environment = -1;
9956 if (Offset_ProcessParam == -1)
9957 {
9958 ULONG TypeId;
9959 ULONG64 NtDllBase;
9960 if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
9961 &NtDllBase)))
9962 {
9963 if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB", &TypeId)))
9964 {
9965 if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
9966 "ProcessParameters", &Offset_ProcessParam)))
9967 Offset_ProcessParam = -1;
9968 }
9969 if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "_RTL_USER_PROCESS_PARAMETERS", &TypeId)))
9970 {
9971 if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
9972 "Environment", &Offset_Environment)))
9973 Offset_Environment = -1;
9974 }
9975 }
9976 }
9977 // We can not get it from PDB. Use the fixed one.
9978 if (Offset_ProcessParam == -1)
9979 Offset_ProcessParam = offsetof (DT_PEB, ProcessParameters);
9980
9981 if (Offset_Environment == -1)
9982 Offset_Environment = offsetof (DT_RTL_USER_PROCESS_PARAMETERS, Environment);
9983
9984
9985 ULONG64 addr = pPeb + Offset_ProcessParam;
9986 DWORD_PTR value;
9987 g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
9988 addr = value + Offset_Environment;
9989 g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
9990
9991 static WCHAR buffer[DT_OS_PAGE_SIZE/2];
9992 ULONG readBytes = DT_OS_PAGE_SIZE;
9993 ULONG64 Page;
9994 if ((g_ExtData->ReadDebuggerData( DEBUG_DATA_MmPageSize, &Page, sizeof(Page), NULL)) == S_OK
9995 && Page > 0)
9996 {
9997 ULONG uPageSize = (ULONG)(ULONG_PTR)Page;
9998 if (readBytes > uPageSize) {
9999 readBytes = uPageSize;
10000 }
10001 }
10002 addr = value;
10003 while (1) {
10004 if (IsInterrupt())
10005 return Status;
10006 if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &buffer, readBytes, NULL)))
10007 break;
10008 addr += readBytes;
10009 WCHAR *pt = buffer;
10010 WCHAR *end = pt;
10011 while (pt < &buffer[DT_OS_PAGE_SIZE/2]) {
10012 end = _wcschr (pt, L'\0');
10013 if (end == NULL) {
10014 char format[20];
10015 sprintf_s (format,_countof (format), "%dS", &buffer[DT_OS_PAGE_SIZE/2] - pt);
10016 ExtOut(format, pt);
10017 break;
10018 }
10019 else if (end == pt) {
10020 break;
10021 }
10022 ExtOut("%S\n", pt);
10023 pt = end + 1;
10024 }
10025 if (end == pt) {
10026 break;
10027 }
10028 }
10029 }
10030
10031 HANDLE hProcess = INVALID_HANDLE_VALUE;
10032 if (fProcInfo & (INFO_TIME | INFO_MEM)) {
10033 ULONG64 handle;
10034 g_ExtSystem->GetCurrentProcessHandle(&handle);
10035 hProcess = (HANDLE)handle;
10036 }
10037
10038 if (!IsDumpFile() && fProcInfo & INFO_TIME) {
10039 FILETIME CreationTime;
10040 FILETIME ExitTime;
10041 FILETIME KernelTime;
10042 FILETIME UserTime;
10043
10044 typedef BOOL (WINAPI *FntGetProcessTimes)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
10045 static FntGetProcessTimes pFntGetProcessTimes = (FntGetProcessTimes)-1;
10046 if (pFntGetProcessTimes == (FntGetProcessTimes)-1) {
10047 HINSTANCE hstat = LoadLibrary ("Kernel32.dll");
10048 if (hstat != 0)
10049 {
10050 pFntGetProcessTimes = (FntGetProcessTimes)GetProcAddress (hstat, "GetProcessTimes");
10051 FreeLibrary (hstat);
10052 }
10053 else
10054 pFntGetProcessTimes = NULL;
10055 }
10056
10057 if (pFntGetProcessTimes && pFntGetProcessTimes (hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime)) {
10058 ExtOut("---------------------------------------\n");
10059 ExtOut("Process Times\n");
10060 static const char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
10061 "Oct", "Nov", "Dec"};
10062 SYSTEMTIME SystemTime;
10063 FILETIME LocalFileTime;
10064 if (FileTimeToLocalFileTime (&CreationTime,&LocalFileTime)
10065 && FileTimeToSystemTime (&LocalFileTime,&SystemTime)) {
10066 ExtOut("Process Started at: %4d %s %2d %d:%d:%d.%02d\n",
10067 SystemTime.wYear, Month[SystemTime.wMonth-1], SystemTime.wDay,
10068 SystemTime.wHour, SystemTime.wMinute,
10069 SystemTime.wSecond, SystemTime.wMilliseconds/10);
10070 }
10071
10072 DWORD nDay = 0;
10073 DWORD nHour = 0;
10074 DWORD nMin = 0;
10075 DWORD nSec = 0;
10076 DWORD nHundred = 0;
10077
10078 ULONG64 totalTime;
10079
10080 totalTime = KernelTime.dwLowDateTime + (((ULONG64)KernelTime.dwHighDateTime) << 32);
10081 nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
10082 totalTime %= 24*3600*10000000ui64;
10083 nHour = (DWORD)(totalTime/(3600*10000000ui64));
10084 totalTime %= 3600*10000000ui64;
10085 nMin = (DWORD)(totalTime/(60*10000000));
10086 totalTime %= 60*10000000;
10087 nSec = (DWORD)(totalTime/10000000);
10088 totalTime %= 10000000;
10089 nHundred = (DWORD)(totalTime/100000);
10090 ExtOut("Kernel CPU time : %d days %02d:%02d:%02d.%02d\n",
10091 nDay, nHour, nMin, nSec, nHundred);
10092
10093 DWORD sDay = nDay;
10094 DWORD sHour = nHour;
10095 DWORD sMin = nMin;
10096 DWORD sSec = nSec;
10097 DWORD sHundred = nHundred;
10098
10099 totalTime = UserTime.dwLowDateTime + (((ULONG64)UserTime.dwHighDateTime) << 32);
10100 nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
10101 totalTime %= 24*3600*10000000ui64;
10102 nHour = (DWORD)(totalTime/(3600*10000000ui64));
10103 totalTime %= 3600*10000000ui64;
10104 nMin = (DWORD)(totalTime/(60*10000000));
10105 totalTime %= 60*10000000;
10106 nSec = (DWORD)(totalTime/10000000);
10107 totalTime %= 10000000;
10108 nHundred = (DWORD)(totalTime/100000);
10109 ExtOut("User CPU time : %d days %02d:%02d:%02d.%02d\n",
10110 nDay, nHour, nMin, nSec, nHundred);
10111
10112 sDay += nDay;
10113 sHour += nHour;
10114 sMin += nMin;
10115 sSec += nSec;
10116 sHundred += nHundred;
10117 if (sHundred >= 100) {
10118 sSec += sHundred/100;
10119 sHundred %= 100;
10120 }
10121 if (sSec >= 60) {
10122 sMin += sSec/60;
10123 sSec %= 60;
10124 }
10125 if (sMin >= 60) {
10126 sHour += sMin/60;
10127 sMin %= 60;
10128 }
10129 if (sHour >= 24) {
10130 sDay += sHour/24;
10131 sHour %= 24;
10132 }
10133 ExtOut("Total CPU time : %d days %02d:%02d:%02d.%02d\n",
10134 sDay, sHour, sMin, sSec, sHundred);
10135 }
10136 }
10137
10138 if (!IsDumpFile() && fProcInfo & INFO_MEM) {
10139 typedef
10140 NTSTATUS
10141 (NTAPI
10142 *FntNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
10143
10144 static FntNtQueryInformationProcess pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)-1;
10145 if (pFntNtQueryInformationProcess == (FntNtQueryInformationProcess)-1) {
10146 HINSTANCE hstat = LoadLibrary ("ntdll.dll");
10147 if (hstat != 0)
10148 {
10149 pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)GetProcAddress (hstat, "NtQueryInformationProcess");
10150 FreeLibrary (hstat);
10151 }
10152 else
10153 pFntNtQueryInformationProcess = NULL;
10154 }
10155 VM_COUNTERS memory;
10156 if (pFntNtQueryInformationProcess &&
10157 NT_SUCCESS (pFntNtQueryInformationProcess (hProcess,ProcessVmCounters,&memory,sizeof(memory),NULL))) {
10158 ExtOut("---------------------------------------\n");
10159 ExtOut("Process Memory\n");
10160 ExtOut("WorkingSetSize: %8d KB PeakWorkingSetSize: %8d KB\n",
10161 memory.WorkingSetSize/1024, memory.PeakWorkingSetSize/1024);
10162 ExtOut("VirtualSize: %8d KB PeakVirtualSize: %8d KB\n",
10163 memory.VirtualSize/1024, memory.PeakVirtualSize/1024);
10164 ExtOut("PagefileUsage: %8d KB PeakPagefileUsage: %8d KB\n",
10165 memory.PagefileUsage/1024, memory.PeakPagefileUsage/1024);
10166 }
10167
10168 MEMORYSTATUS memstat;
10169 GlobalMemoryStatus (&memstat);
10170 ExtOut("---------------------------------------\n");
10171 ExtOut("%ld percent of memory is in use.\n\n",
10172 memstat.dwMemoryLoad);
10173 ExtOut("Memory Availability (Numbers in MB)\n\n");
10174 ExtOut(" %8s %8s\n", "Total", "Avail");
10175 ExtOut("Physical Memory %8d %8d\n", memstat.dwTotalPhys/1024/1024, memstat.dwAvailPhys/1024/1024);
10176 ExtOut("Page File %8d %8d\n", memstat.dwTotalPageFile/1024/1024, memstat.dwAvailPageFile/1024/1024);
10177 ExtOut("Virtual Memory %8d %8d\n", memstat.dwTotalVirtual/1024/1024, memstat.dwAvailVirtual/1024/1024);
10178 }
10179
10180 return Status;
10181}
10182#endif // FEATURE_PAL
10183
10184/**********************************************************************\
10185* Routine Description: *
10186* *
10187* This function is called to find the address of EE data for a *
10188* metadata token. *
10189* *
10190\**********************************************************************/
10191DECLARE_API(Token2EE)
10192{
10193 INIT_API();
10194 MINIDUMP_NOT_SUPPORTED();
10195
10196 StringHolder DllName;
10197 ULONG64 token = 0;
10198 BOOL dml = FALSE;
10199
10200 CMDOption option[] =
10201 { // name, vptr, type, hasValue
10202#ifndef FEATURE_PAL
10203 {"/d", &dml, COBOOL, FALSE},
10204#endif
10205 };
10206
10207 CMDValue arg[] =
10208 { // vptr, type
10209 {&DllName.data, COSTRING},
10210 {&token, COHEX}
10211 };
10212
10213 size_t nArg;
10214 if (!GetCMDOption(args,option, _countof(option), arg, _countof(arg), &nArg))
10215 {
10216 return Status;
10217 }
10218 if (nArg!=2)
10219 {
10220 ExtOut("Usage: !Token2EE module_name mdToken\n");
10221 ExtOut(" You can pass * for module_name to search all modules.\n");
10222 return Status;
10223 }
10224
10225 EnableDMLHolder dmlHolder(dml);
10226 int numModule;
10227 ArrayHolder<DWORD_PTR> moduleList = NULL;
10228
10229 if (strcmp(DllName.data, "*") == 0)
10230 {
10231 moduleList = ModuleFromName(NULL, &numModule);
10232 }
10233 else
10234 {
10235 moduleList = ModuleFromName(DllName.data, &numModule);
10236 }
10237
10238 if (moduleList == NULL)
10239 {
10240 ExtOut("Failed to request module list.\n");
10241 }
10242 else
10243 {
10244 for (int i = 0; i < numModule; i ++)
10245 {
10246 if (IsInterrupt())
10247 break;
10248
10249 if (i > 0)
10250 {
10251 ExtOut("--------------------------------------\n");
10252 }
10253
10254 DWORD_PTR dwAddr = moduleList[i];
10255 WCHAR FileName[MAX_LONGPATH];
10256 FileNameForModule(dwAddr, FileName);
10257
10258 // We'd like a short form for this output
10259 LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W);
10260 if (pszFilename == NULL)
10261 {
10262 pszFilename = FileName;
10263 }
10264 else
10265 {
10266 pszFilename++; // skip past the last "\" character
10267 }
10268
10269 DMLOut("Module: %s\n", DMLModule(dwAddr));
10270 ExtOut("Assembly: %S\n", pszFilename);
10271
10272 GetInfoFromModule(dwAddr, (ULONG)token);
10273 }
10274 }
10275
10276 return Status;
10277}
10278
10279/**********************************************************************\
10280* Routine Description: *
10281* *
10282* This function is called to find the address of EE data for a *
10283* metadata token. *
10284* *
10285\**********************************************************************/
10286DECLARE_API(Name2EE)
10287{
10288 INIT_API();
10289 MINIDUMP_NOT_SUPPORTED();
10290
10291 StringHolder DllName, TypeName;
10292 BOOL dml = FALSE;
10293
10294 CMDOption option[] =
10295 { // name, vptr, type, hasValue
10296#ifndef FEATURE_PAL
10297 {"/d", &dml, COBOOL, FALSE},
10298#endif
10299 };
10300
10301 CMDValue arg[] =
10302 { // vptr, type
10303 {&DllName.data, COSTRING},
10304 {&TypeName.data, COSTRING}
10305 };
10306 size_t nArg;
10307
10308 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
10309 {
10310 return Status;
10311 }
10312
10313 EnableDMLHolder dmlHolder(dml);
10314
10315 if (nArg == 1)
10316 {
10317 // The input may be in the form <modulename>!<type>
10318 // If so, do some surgery on the input params.
10319
10320 // There should be only 1 ! character
10321 LPSTR pszSeperator = strchr (DllName.data, '!');
10322 if (pszSeperator != NULL)
10323 {
10324 if (strchr (pszSeperator + 1, '!') == NULL)
10325 {
10326 size_t capacity_TypeName_data = strlen(pszSeperator + 1) + 1;
10327 TypeName.data = new NOTHROW char[capacity_TypeName_data];
10328 if (TypeName.data)
10329 {
10330 // get the type name,
10331 strcpy_s (TypeName.data, capacity_TypeName_data, pszSeperator + 1);
10332 // and truncate DllName
10333 *pszSeperator = '\0';
10334
10335 // Do some extra validation
10336 if (strlen (DllName.data) >= 1 &&
10337 strlen (TypeName.data) > 1)
10338 {
10339 nArg = 2;
10340 }
10341 }
10342 }
10343 }
10344 }
10345
10346 if (nArg != 2)
10347 {
10348 ExtOut("Usage: " SOSPrefix "name2ee module_name item_name\n");
10349 ExtOut(" or " SOSPrefix "name2ee module_name!item_name\n");
10350 ExtOut(" use * for module_name to search all loaded modules\n");
10351 ExtOut("Examples: " SOSPrefix "name2ee mscorlib.dll System.String.ToString\n");
10352 ExtOut(" " SOSPrefix "name2ee *!System.String\n");
10353 return Status;
10354 }
10355
10356 int numModule;
10357 ArrayHolder<DWORD_PTR> moduleList = NULL;
10358 if (strcmp(DllName.data, "*") == 0)
10359 {
10360 moduleList = ModuleFromName(NULL, &numModule);
10361 }
10362 else
10363 {
10364 moduleList = ModuleFromName(DllName.data, &numModule);
10365 }
10366
10367
10368 if (moduleList == NULL)
10369 {
10370 ExtOut("Failed to request module list.\n", DllName.data);
10371 }
10372 else
10373 {
10374 for (int i = 0; i < numModule; i ++)
10375 {
10376 if (IsInterrupt())
10377 break;
10378
10379 if (i > 0)
10380 {
10381 ExtOut("--------------------------------------\n");
10382 }
10383
10384 DWORD_PTR dwAddr = moduleList[i];
10385 WCHAR FileName[MAX_LONGPATH];
10386 FileNameForModule (dwAddr, FileName);
10387
10388 // We'd like a short form for this output
10389 LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W);
10390 if (pszFilename == NULL)
10391 {
10392 pszFilename = FileName;
10393 }
10394 else
10395 {
10396 pszFilename++; // skip past the last "\" character
10397 }
10398
10399 DMLOut("Module: %s\n", DMLModule(dwAddr));
10400 ExtOut("Assembly: %S\n", pszFilename);
10401 GetInfoFromName(dwAddr, TypeName.data);
10402 }
10403 }
10404
10405 return Status;
10406}
10407
10408
10409#ifndef FEATURE_PAL
10410DECLARE_API(PathTo)
10411{
10412 INIT_API();
10413 MINIDUMP_NOT_SUPPORTED();
10414
10415 DWORD_PTR root = NULL;
10416 DWORD_PTR target = NULL;
10417 BOOL dml = FALSE;
10418 size_t nArg;
10419
10420 CMDOption option[] =
10421 { // name, vptr, type, hasValue
10422 {"/d", &dml, COBOOL, FALSE},
10423 };
10424 CMDValue arg[] =
10425 { // vptr, type
10426 {&root, COHEX},
10427 {&target, COHEX},
10428 };
10429 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
10430 {
10431 return Status;
10432 }
10433
10434 if (root == 0 || target == 0)
10435 {
10436 ExtOut("Invalid argument %s\n", args);
10437 return Status;
10438 }
10439
10440 GCRootImpl gcroot;
10441 bool result = gcroot.PrintPathToObject(root, target);
10442
10443 if (!result)
10444 ExtOut("Did not find a path from %p to %p.\n", SOS_PTR(root), SOS_PTR(target));
10445
10446 return Status;
10447}
10448#endif
10449
10450
10451
10452/**********************************************************************\
10453* Routine Description: *
10454* *
10455* This function finds all roots (on stack or in handles) for a *
10456* given object. *
10457* *
10458\**********************************************************************/
10459DECLARE_API(GCRoot)
10460{
10461 INIT_API();
10462 MINIDUMP_NOT_SUPPORTED();
10463
10464 BOOL bNoStacks = FALSE;
10465 DWORD_PTR obj = NULL;
10466 BOOL dml = FALSE;
10467 BOOL all = FALSE;
10468 size_t nArg;
10469
10470 CMDOption option[] =
10471 { // name, vptr, type, hasValue
10472 {"-nostacks", &bNoStacks, COBOOL, FALSE},
10473 {"-all", &all, COBOOL, FALSE},
10474#ifndef FEATURE_PAL
10475 {"/d", &dml, COBOOL, FALSE},
10476#endif
10477 };
10478 CMDValue arg[] =
10479
10480 { // vptr, type
10481 {&obj, COHEX}
10482 };
10483 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
10484 {
10485 return Status;
10486 }
10487 if (obj == 0)
10488 {
10489 ExtOut("Invalid argument %s\n", args);
10490 return Status;
10491 }
10492
10493 EnableDMLHolder dmlHolder(dml);
10494 GCRootImpl gcroot;
10495 int i = gcroot.PrintRootsForObject(obj, all == TRUE, bNoStacks == TRUE);
10496
10497 if (IsInterrupt())
10498 ExtOut("Interrupted, data may be incomplete.\n");
10499
10500 if (all)
10501 ExtOut("Found %d roots.\n", i);
10502 else
10503 ExtOut("Found %d unique roots (run '" SOSPrefix "gcroot -all' to see all roots).\n", i);
10504
10505 return Status;
10506}
10507
10508DECLARE_API(GCWhere)
10509{
10510 INIT_API();
10511 MINIDUMP_NOT_SUPPORTED();
10512
10513 BOOL dml = FALSE;
10514 BOOL bGetBrick;
10515 BOOL bGetCard;
10516 TADDR taddrObj = 0;
10517 size_t nArg;
10518
10519 CMDOption option[] =
10520 { // name, vptr, type, hasValue
10521 {"-brick", &bGetBrick, COBOOL, FALSE},
10522 {"-card", &bGetCard, COBOOL, FALSE},
10523 {"/d", &dml, COBOOL, FALSE},
10524 };
10525 CMDValue arg[] =
10526 { // vptr, type
10527 {&taddrObj, COHEX}
10528 };
10529 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
10530 {
10531 return Status;
10532 }
10533
10534 EnableDMLHolder dmlHolder(dml);
10535 // Obtain allocation context for each managed thread.
10536 AllocInfo allocInfo;
10537 allocInfo.Init();
10538
10539 TADDR_SEGINFO trngSeg = { 0, 0, 0 };
10540 TADDR_RANGE allocCtx = { 0, 0 };
10541 int gen = -1;
10542 BOOL bLarge = FALSE;
10543 BOOL bFound = FALSE;
10544
10545 size_t size = 0;
10546 if (sos::IsObject(taddrObj))
10547 {
10548 TADDR taddrMT;
10549 BOOL bContainsPointers;
10550 if(FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
10551 !GetSizeEfficient(taddrObj, taddrMT, FALSE, size, bContainsPointers))
10552 {
10553 ExtWarn("Couldn't get size for object %#p: possible heap corruption.\n",
10554 SOS_PTR(taddrObj));
10555 }
10556 }
10557
10558 if (!IsServerBuild())
10559 {
10560 DacpGcHeapDetails heapDetails;
10561 if (heapDetails.Request(g_sos) != S_OK)
10562 {
10563 ExtOut("Error requesting gc heap details\n");
10564 return Status;
10565 }
10566
10567 if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
10568 {
10569 ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated " WIN64_8SPACES " size\n");
10570 ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n",
10571 SOS_PTR(taddrObj), gen, 0, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size);
10572 bFound = TRUE;
10573 }
10574 }
10575 else
10576 {
10577 DacpGcHeapData gcheap;
10578 if (gcheap.Request(g_sos) != S_OK)
10579 {
10580 ExtOut("Error requesting GC Heap data\n");
10581 return Status;
10582 }
10583
10584 DWORD dwAllocSize;
10585 DWORD dwNHeaps = gcheap.HeapCount;
10586 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
10587 {
10588 ExtOut("Failed to get GCHeaps: integer overflow\n");
10589 return Status;
10590 }
10591
10592 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
10593 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
10594 {
10595 ExtOut("Failed to get GCHeaps\n");
10596 return Status;
10597 }
10598
10599 for (DWORD n = 0; n < dwNHeaps; n ++)
10600 {
10601 DacpGcHeapDetails heapDetails;
10602 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
10603 {
10604 ExtOut("Error requesting details\n");
10605 return Status;
10606 }
10607
10608 if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
10609 {
10610 ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated" WIN64_8SPACES " size\n");
10611 ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n",
10612 SOS_PTR(taddrObj), gen, n, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size);
10613 bFound = TRUE;
10614 break;
10615 }
10616 }
10617 }
10618
10619 if (!bFound)
10620 {
10621 ExtOut("Address %#p not found in the managed heap.\n", SOS_PTR(taddrObj));
10622 }
10623
10624 return Status;
10625}
10626
10627#ifndef FEATURE_PAL
10628
10629DECLARE_API(FindRoots)
10630{
10631#ifndef FEATURE_PAL
10632 INIT_API();
10633 MINIDUMP_NOT_SUPPORTED();
10634
10635 if (IsDumpFile())
10636 {
10637 ExtOut("!FindRoots is not supported on a dump file.\n");
10638 return Status;
10639 }
10640
10641 LONG_PTR gen = -100; // initialized outside the legal range: [-1, 2]
10642 StringHolder sgen;
10643 TADDR taObj = NULL;
10644 BOOL dml = FALSE;
10645 size_t nArg;
10646
10647 CMDOption option[] =
10648 { // name, vptr, type, hasValue
10649 {"-gen", &sgen.data, COSTRING, TRUE},
10650 {"/d", &dml, COBOOL, FALSE},
10651 };
10652 CMDValue arg[] =
10653 { // vptr, type
10654 {&taObj, COHEX}
10655 };
10656 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
10657 {
10658 return Status;
10659 }
10660
10661 EnableDMLHolder dmlHolder(dml);
10662 if (sgen.data != NULL)
10663 {
10664 if (_stricmp(sgen.data, "any") == 0)
10665 {
10666 gen = -1;
10667 }
10668 else
10669 {
10670 gen = GetExpression(sgen.data);
10671 }
10672 }
10673 if ((gen < -1 || gen > 2) && (taObj == 0))
10674 {
10675 ExtOut("Incorrect options. Usage:\n\t!FindRoots -gen <N>\n\t\twhere N is 0, 1, 2, or \"any\". OR\n\t!FindRoots <obj>\n");
10676 return Status;
10677 }
10678
10679 if (gen >= -1 && gen <= 2)
10680 {
10681 IXCLRDataProcess2* idp2 = NULL;
10682 if (FAILED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
10683 {
10684 ExtOut("Your version of the runtime/DAC do not support this command.\n");
10685 return Status;
10686 }
10687
10688 // Request GC_MARK_END notifications from debuggee
10689 GcEvtArgs gea = { GC_MARK_END, { ((gen == -1) ? 7 : (1 << gen)) } };
10690 idp2->SetGcNotification(gea);
10691 // ... and register the notification handler
10692 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
10693 // the above notification is removed in CNotification::OnGcEvent()
10694 }
10695 else
10696 {
10697 // verify that the last event in the debugger was indeed a CLRN exception
10698 DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
10699 CNotification Notification;
10700
10701 if (!CheckCLRNotificationEvent(&dle))
10702 {
10703 ExtOut("The command !FindRoots can only be used after the debugger stopped on a CLRN GC notification.\n");
10704 ExtOut("At this time !GCRoot should be used instead.\n");
10705 return Status;
10706 }
10707 // validate argument
10708 if (!g_snapshot.Build())
10709 {
10710 ExtOut("Unable to build snapshot of the garbage collector state\n");
10711 return Status;
10712 }
10713
10714 if (g_snapshot.GetHeap(taObj) == NULL)
10715 {
10716 ExtOut("Address %#p is not in the managed heap.\n", SOS_PTR(taObj));
10717 return Status;
10718 }
10719
10720 int ogen = g_snapshot.GetGeneration(taObj);
10721 if (ogen > CNotification::GetCondemnedGen())
10722 {
10723 DMLOut("Object %s will survive this collection:\n\tgen(%#p) = %d > %d = condemned generation.\n",
10724 DMLObject(taObj), SOS_PTR(taObj), ogen, CNotification::GetCondemnedGen());
10725 return Status;
10726 }
10727
10728 GCRootImpl gcroot;
10729 int roots = gcroot.FindRoots(CNotification::GetCondemnedGen(), taObj);
10730
10731 ExtOut("Found %d roots.\n", roots);
10732 }
10733
10734 return Status;
10735#else
10736 return E_NOTIMPL;
10737#endif
10738}
10739
10740class GCHandleStatsForDomains
10741{
10742public:
10743 GCHandleStatsForDomains()
10744 : m_singleDomainMode(FALSE), m_numDomains(0), m_pStatistics(NULL), m_pDomainPointers(NULL), m_sharedDomainIndex(-1), m_systemDomainIndex(-1)
10745 {
10746 }
10747
10748 ~GCHandleStatsForDomains()
10749 {
10750 if (m_pStatistics)
10751 {
10752 if (m_singleDomainMode)
10753 delete m_pStatistics;
10754 else
10755 delete [] m_pStatistics;
10756 }
10757
10758 if (m_pDomainPointers)
10759 delete [] m_pDomainPointers;
10760 }
10761
10762 BOOL Init(BOOL singleDomainMode)
10763 {
10764 m_singleDomainMode = singleDomainMode;
10765 if (m_singleDomainMode)
10766 {
10767 m_numDomains = 1;
10768 m_pStatistics = new NOTHROW GCHandleStatistics();
10769 if (m_pStatistics == NULL)
10770 return FALSE;
10771 }
10772 else
10773 {
10774 DacpAppDomainStoreData adsData;
10775 if (adsData.Request(g_sos) != S_OK)
10776 return FALSE;
10777
10778 LONG numSpecialDomains = (adsData.sharedDomain != NULL) ? 2 : 1;
10779 m_numDomains = adsData.DomainCount + numSpecialDomains;
10780 ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[m_numDomains];
10781 if (pArray == NULL)
10782 return FALSE;
10783
10784 int i = 0;
10785 if (adsData.sharedDomain != NULL)
10786 {
10787 pArray[i++] = adsData.sharedDomain;
10788 }
10789
10790 pArray[i] = adsData.systemDomain;
10791
10792 m_sharedDomainIndex = i - 1; // The m_sharedDomainIndex is set to -1 if there is no shared domain
10793 m_systemDomainIndex = i;
10794
10795 if (g_sos->GetAppDomainList(adsData.DomainCount, pArray+numSpecialDomains, NULL) != S_OK)
10796 return FALSE;
10797
10798 m_pDomainPointers = pArray.Detach();
10799 m_pStatistics = new NOTHROW GCHandleStatistics[m_numDomains];
10800 if (m_pStatistics == NULL)
10801 return FALSE;
10802 }
10803
10804 return TRUE;
10805 }
10806
10807 GCHandleStatistics *LookupStatistics(CLRDATA_ADDRESS appDomainPtr) const
10808 {
10809 if (m_singleDomainMode)
10810 {
10811 // You can pass NULL appDomainPtr if you are in singleDomainMode
10812 return m_pStatistics;
10813 }
10814 else
10815 {
10816 for (int i=0; i < m_numDomains; i++)
10817 if (m_pDomainPointers[i] == appDomainPtr)
10818 return m_pStatistics + i;
10819 }
10820
10821 return NULL;
10822 }
10823
10824
10825 GCHandleStatistics *GetStatistics(int appDomainIndex) const
10826 {
10827 SOS_Assert(appDomainIndex >= 0);
10828 SOS_Assert(appDomainIndex < m_numDomains);
10829
10830 return m_singleDomainMode ? m_pStatistics : m_pStatistics + appDomainIndex;
10831 }
10832
10833 int GetNumDomains() const
10834 {
10835 return m_numDomains;
10836 }
10837
10838 CLRDATA_ADDRESS GetDomain(int index) const
10839 {
10840 SOS_Assert(index >= 0);
10841 SOS_Assert(index < m_numDomains);
10842 return m_pDomainPointers[index];
10843 }
10844
10845 int GetSharedDomainIndex()
10846 {
10847 return m_sharedDomainIndex;
10848 }
10849
10850 int GetSystemDomainIndex()
10851 {
10852 return m_systemDomainIndex;
10853 }
10854
10855private:
10856 BOOL m_singleDomainMode;
10857 int m_numDomains;
10858 GCHandleStatistics *m_pStatistics;
10859 CLRDATA_ADDRESS *m_pDomainPointers;
10860 int m_sharedDomainIndex;
10861 int m_systemDomainIndex;
10862};
10863
10864class GCHandlesImpl
10865{
10866public:
10867 GCHandlesImpl(PCSTR args)
10868 : mPerDomain(FALSE), mStat(FALSE), mDML(FALSE), mType((int)~0)
10869 {
10870 ArrayHolder<char> type = NULL;
10871 CMDOption option[] =
10872 {
10873 {"-perdomain", &mPerDomain, COBOOL, FALSE},
10874 {"-stat", &mStat, COBOOL, FALSE},
10875 {"-type", &type, COSTRING, TRUE},
10876 {"/d", &mDML, COBOOL, FALSE},
10877 };
10878
10879 if (!GetCMDOption(args,option,_countof(option),NULL,0,NULL))
10880 sos::Throw<sos::Exception>("Failed to parse command line arguments.");
10881
10882 if (type != NULL)
10883 if (_stricmp(type, "Pinned") == 0)
10884 mType = HNDTYPE_PINNED;
10885 else if (_stricmp(type, "RefCounted") == 0)
10886 mType = HNDTYPE_REFCOUNTED;
10887 else if (_stricmp(type, "WeakShort") == 0)
10888 mType = HNDTYPE_WEAK_SHORT;
10889 else if (_stricmp(type, "WeakLong") == 0)
10890 mType = HNDTYPE_WEAK_LONG;
10891 else if (_stricmp(type, "Strong") == 0)
10892 mType = HNDTYPE_STRONG;
10893 else if (_stricmp(type, "Variable") == 0)
10894 mType = HNDTYPE_VARIABLE;
10895 else if (_stricmp(type, "AsyncPinned") == 0)
10896 mType = HNDTYPE_ASYNCPINNED;
10897 else if (_stricmp(type, "SizedRef") == 0)
10898 mType = HNDTYPE_SIZEDREF;
10899 else if (_stricmp(type, "Dependent") == 0)
10900 mType = HNDTYPE_DEPENDENT;
10901 else if (_stricmp(type, "WeakWinRT") == 0)
10902 mType = HNDTYPE_WEAK_WINRT;
10903 else
10904 sos::Throw<sos::Exception>("Unknown handle type '%s'.", type.GetPtr());
10905 }
10906
10907 void Run()
10908 {
10909 EnableDMLHolder dmlHolder(mDML);
10910
10911 mOut.ReInit(6, POINTERSIZE_HEX, AlignRight);
10912 mOut.SetWidths(5, POINTERSIZE_HEX, 11, POINTERSIZE_HEX, 8, POINTERSIZE_HEX);
10913 mOut.SetColAlignment(1, AlignLeft);
10914
10915 if (mHandleStat.Init(!mPerDomain) == FALSE)
10916 sos::Throw<sos::Exception>("Error getting per-appdomain handle information");
10917
10918 if (!mStat)
10919 mOut.WriteRow("Handle", "Type", "Object", "Size", "Data", "Type");
10920
10921 WalkHandles();
10922
10923 for (int i=0; (i < mHandleStat.GetNumDomains()) && !IsInterrupt(); i++)
10924 {
10925 GCHandleStatistics *pStats = mHandleStat.GetStatistics(i);
10926
10927 if (mPerDomain)
10928 {
10929 Print( "------------------------------------------------------------------------------\n");
10930 Print("GC Handle Statistics for AppDomain ", AppDomainPtr(mHandleStat.GetDomain(i)));
10931
10932 if (i == mHandleStat.GetSharedDomainIndex())
10933 Print(" (Shared Domain)\n");
10934 else if (i == mHandleStat.GetSystemDomainIndex())
10935 Print(" (System Domain)\n");
10936 else
10937 Print("\n");
10938 }
10939
10940 if (!mStat)
10941 Print("\n");
10942 PrintGCStat(&pStats->hs);
10943
10944 // Don't print handle stats if the user has filtered by type. All handles will be the same
10945 // type, and the total count will be displayed by PrintGCStat.
10946 if (mType == (unsigned int)~0)
10947 {
10948 Print("\n");
10949 PrintGCHandleStats(pStats);
10950 }
10951 }
10952 }
10953
10954private:
10955 void WalkHandles()
10956 {
10957 ToRelease<ISOSHandleEnum> handles;
10958 if (FAILED(g_sos->GetHandleEnum(&handles)))
10959 {
10960 if (IsMiniDumpFile())
10961 sos::Throw<sos::Exception>("Unable to display GC handles.\nA minidump without full memory may not have this information.");
10962 else
10963 sos::Throw<sos::Exception>("Failed to walk the handle table.");
10964 }
10965
10966 // GCC can't handle stacks which are too large.
10967#ifndef FEATURE_PAL
10968 SOSHandleData data[256];
10969#else
10970 SOSHandleData data[4];
10971#endif
10972
10973 unsigned int fetched = 0;
10974 HRESULT hr = S_OK;
10975 do
10976 {
10977 if (FAILED(hr = handles->Next(_countof(data), data, &fetched)))
10978 {
10979 ExtOut("Error %x while walking the handle table.\n", hr);
10980 break;
10981 }
10982
10983 WalkHandles(data, fetched);
10984 } while (_countof(data) == fetched);
10985 }
10986
10987 void WalkHandles(SOSHandleData data[], unsigned int count)
10988 {
10989 for (unsigned int i = 0; i < count; ++i)
10990 {
10991 sos::CheckInterrupt();
10992
10993 if (mType != (unsigned int)~0 && mType != data[i].Type)
10994 continue;
10995
10996 GCHandleStatistics *pStats = mHandleStat.LookupStatistics(data[i].AppDomain);
10997 TADDR objAddr = 0;
10998 TADDR mtAddr = 0;
10999 size_t size = 0;
11000 const WCHAR *mtName = 0;
11001 const char *type = 0;
11002
11003 if (FAILED(MOVE(objAddr, data[i].Handle)))
11004 {
11005 objAddr = 0;
11006 mtName = W("<error>");
11007 }
11008 else
11009 {
11010 sos::Object obj(TO_TADDR(objAddr));
11011 mtAddr = obj.GetMT();
11012 if (sos::MethodTable::IsFreeMT(mtAddr))
11013 {
11014 mtName = W("<free>");
11015 }
11016 else if (!sos::MethodTable::IsValid(mtAddr))
11017 {
11018 mtName = W("<error>");
11019 }
11020 else
11021 {
11022 size = obj.GetSize();
11023 if (mType == (unsigned int)~0 || mType == data[i].Type)
11024 pStats->hs.Add(obj.GetMT(), (DWORD)size);
11025 }
11026 }
11027
11028 switch(data[i].Type)
11029 {
11030 case HNDTYPE_PINNED:
11031 type = "Pinned";
11032 if (pStats) pStats->pinnedHandleCount++;
11033 break;
11034 case HNDTYPE_REFCOUNTED:
11035 type = "RefCounted";
11036 if (pStats) pStats->refCntHandleCount++;
11037 break;
11038 case HNDTYPE_STRONG:
11039 type = "Strong";
11040 if (pStats) pStats->strongHandleCount++;
11041 break;
11042 case HNDTYPE_WEAK_SHORT:
11043 type = "WeakShort";
11044 if (pStats) pStats->weakShortHandleCount++;
11045 break;
11046 case HNDTYPE_WEAK_LONG:
11047 type = "WeakLong";
11048 if (pStats) pStats->weakLongHandleCount++;
11049 break;
11050 case HNDTYPE_ASYNCPINNED:
11051 type = "AsyncPinned";
11052 if (pStats) pStats->asyncPinnedHandleCount++;
11053 break;
11054 case HNDTYPE_VARIABLE:
11055 type = "Variable";
11056 if (pStats) pStats->variableCount++;
11057 break;
11058 case HNDTYPE_SIZEDREF:
11059 type = "SizedRef";
11060 if (pStats) pStats->sizedRefCount++;
11061 break;
11062 case HNDTYPE_DEPENDENT:
11063 type = "Dependent";
11064 if (pStats) pStats->dependentCount++;
11065 break;
11066 case HNDTYPE_WEAK_WINRT:
11067 type = "WeakWinRT";
11068 if (pStats) pStats->weakWinRTHandleCount++;
11069 break;
11070 default:
11071 DebugBreak();
11072 type = "Unknown";
11073 pStats->unknownHandleCount++;
11074 break;
11075 }
11076
11077 if (type && !mStat)
11078 {
11079 sos::MethodTable mt = mtAddr;
11080 if (mtName == 0)
11081 mtName = mt.GetName();
11082
11083 if (data[i].Type == HNDTYPE_REFCOUNTED)
11084 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Decimal(data[i].RefCount), mtName);
11085 else if (data[i].Type == HNDTYPE_DEPENDENT)
11086 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), ObjectPtr(data[i].Secondary), mtName);
11087 else if (data[i].Type == HNDTYPE_WEAK_WINRT)
11088 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Pointer(data[i].Secondary), mtName);
11089 else
11090 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), "", mtName);
11091 }
11092 }
11093 }
11094
11095 inline void PrintHandleRow(const char *text, int count)
11096 {
11097 if (count)
11098 mOut.WriteRow(text, Decimal(count));
11099 }
11100
11101 void PrintGCHandleStats(GCHandleStatistics *pStats)
11102 {
11103 Print("Handles:\n");
11104 mOut.ReInit(2, 21, AlignLeft, 4);
11105
11106 PrintHandleRow("Strong Handles:", pStats->strongHandleCount);
11107 PrintHandleRow("Pinned Handles:", pStats->pinnedHandleCount);
11108 PrintHandleRow("Async Pinned Handles:", pStats->asyncPinnedHandleCount);
11109 PrintHandleRow("Ref Count Handles:", pStats->refCntHandleCount);
11110 PrintHandleRow("Weak Long Handles:", pStats->weakLongHandleCount);
11111 PrintHandleRow("Weak Short Handles:", pStats->weakShortHandleCount);
11112 PrintHandleRow("Weak WinRT Handles:", pStats->weakWinRTHandleCount);
11113 PrintHandleRow("Variable Handles:", pStats->variableCount);
11114 PrintHandleRow("SizedRef Handles:", pStats->sizedRefCount);
11115 PrintHandleRow("Dependent Handles:", pStats->dependentCount);
11116 PrintHandleRow("Other Handles:", pStats->unknownHandleCount);
11117 }
11118
11119private:
11120 BOOL mPerDomain, mStat, mDML;
11121 unsigned int mType;
11122 TableOutput mOut;
11123 GCHandleStatsForDomains mHandleStat;
11124};
11125
11126/**********************************************************************\
11127* Routine Description: *
11128* *
11129* This function dumps GC Handle statistics *
11130* *
11131\**********************************************************************/
11132DECLARE_API(GCHandles)
11133{
11134 INIT_API();
11135 MINIDUMP_NOT_SUPPORTED();
11136
11137 try
11138 {
11139 GCHandlesImpl gchandles(args);
11140 gchandles.Run();
11141 }
11142 catch(const sos::Exception &e)
11143 {
11144 Print(e.what());
11145 }
11146
11147 return Status;
11148}
11149
11150// This is an experimental and undocumented SOS API that attempts to step through code
11151// stopping once jitted code is reached. It currently has some issues - it can take arbitrarily long
11152// to reach jitted code and canceling it is terrible. At best it doesn't cancel, at worst it
11153// kills the debugger. IsInterrupt() doesn't work nearly as nicely as one would hope :/
11154#ifndef FEATURE_PAL
11155DECLARE_API(TraceToCode)
11156{
11157 INIT_API_NOEE();
11158
11159 static ULONG64 g_clrBaseAddr = 0;
11160
11161
11162 while(true)
11163 {
11164 if (IsInterrupt())
11165 {
11166 ExtOut("Interrupted\n");
11167 return S_FALSE;
11168 }
11169
11170 ULONG64 Offset;
11171 g_ExtRegisters->GetInstructionOffset(&Offset);
11172
11173 DWORD codeType = 0;
11174 ULONG64 base = 0;
11175 CLRDATA_ADDRESS cdaStart = TO_CDADDR(Offset);
11176 DacpMethodDescData MethodDescData;
11177 if(g_ExtSymbols->GetModuleByOffset(Offset, 0, NULL, &base) == S_OK)
11178 {
11179 if(g_clrBaseAddr == 0)
11180 {
11181 g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL,
11182 &g_clrBaseAddr);
11183 }
11184 if(g_clrBaseAddr == base)
11185 {
11186 ExtOut("Compiled code in CLR\n");
11187 codeType = 4;
11188 }
11189 else
11190 {
11191 ExtOut("Compiled code in module @ 0x%I64x\n", base);
11192 codeType = 8;
11193 }
11194 }
11195 else if (g_sos != NULL || LoadClrDebugDll()==S_OK)
11196 {
11197 CLRDATA_ADDRESS addr;
11198 if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
11199 {
11200 WCHAR wszNameBuffer[1024]; // should be large enough
11201
11202 // get the MethodDesc name
11203 if ((g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK) &&
11204 _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0)
11205 {
11206 ExtOut("ILStub\n");
11207 codeType = 2;
11208 }
11209 else
11210 {
11211 ExtOut("Jitted code\n");
11212 codeType = 1;
11213 }
11214 }
11215 else
11216 {
11217 ExtOut("Not compiled or jitted, assuming stub\n");
11218 codeType = 16;
11219 }
11220 }
11221 else
11222 {
11223 // not compiled but CLR isn't loaded... some other code generator?
11224 return E_FAIL;
11225 }
11226
11227 if(codeType == 1)
11228 {
11229 return S_OK;
11230 }
11231 else
11232 {
11233 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "thr; .echo wait" ,0);
11234 if (FAILED(Status))
11235 {
11236 ExtOut("Error tracing instruction\n");
11237 return Status;
11238 }
11239 }
11240 }
11241
11242 return Status;
11243
11244}
11245#endif // FEATURE_PAL
11246
11247// This is an experimental and undocumented API that sets a debugger pseudo-register based
11248// on the type of code at the given IP. It can be used in scripts to keep stepping until certain
11249// kinds of code have been reached. Presumbably its slower than !TraceToCode but at least it
11250// cancels much better
11251#ifndef FEATURE_PAL
11252DECLARE_API(GetCodeTypeFlags)
11253{
11254 INIT_API();
11255
11256
11257 char buffer[100+mdNameLen];
11258 size_t ip;
11259 StringHolder PReg;
11260
11261 CMDValue arg[] = {
11262 // vptr, type
11263 {&ip, COSIZE_T},
11264 {&PReg.data, COSTRING}
11265 };
11266 size_t nArg;
11267 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
11268 {
11269 return Status;
11270 }
11271
11272 size_t preg = 1; // by default
11273 if (nArg == 2)
11274 {
11275 preg = GetExpression(PReg.data);
11276 if (preg > 19)
11277 {
11278 ExtOut("Pseudo-register number must be between 0 and 19\n");
11279 return Status;
11280 }
11281 }
11282
11283 sprintf_s(buffer,_countof (buffer),
11284 "r$t%d=0",
11285 preg);
11286 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
11287 if (FAILED(Status))
11288 {
11289 ExtOut("Error initialized register $t%d to zero\n", preg);
11290 return Status;
11291 }
11292
11293 ULONG64 base = 0;
11294 CLRDATA_ADDRESS cdaStart = TO_CDADDR(ip);
11295 DWORD codeType = 0;
11296 CLRDATA_ADDRESS addr;
11297 if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
11298 {
11299 WCHAR wszNameBuffer[1024]; // should be large enough
11300
11301 // get the MethodDesc name
11302 if (g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK &&
11303 _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0)
11304 {
11305 ExtOut("ILStub\n");
11306 codeType = 2;
11307 }
11308 else
11309 {
11310 ExtOut("Jitted code");
11311 codeType = 1;
11312 }
11313 }
11314 else if(g_ExtSymbols->GetModuleByOffset (ip, 0, NULL, &base) == S_OK)
11315 {
11316 ULONG64 clrBaseAddr = 0;
11317 if(SUCCEEDED(g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL, &clrBaseAddr)) && base==clrBaseAddr)
11318 {
11319 ExtOut("Compiled code in CLR");
11320 codeType = 4;
11321 }
11322 else
11323 {
11324 ExtOut("Compiled code in module @ 0x%I64x\n", base);
11325 codeType = 8;
11326 }
11327 }
11328 else
11329 {
11330 ExtOut("Not compiled or jitted, assuming stub\n");
11331 codeType = 16;
11332 }
11333
11334 sprintf_s(buffer,_countof (buffer),
11335 "r$t%d=%x",
11336 preg, codeType);
11337 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
11338 if (FAILED(Status))
11339 {
11340 ExtOut("Error setting register $t%d\n", preg);
11341 return Status;
11342 }
11343 return Status;
11344
11345}
11346#endif // FEATURE_PAL
11347
11348DECLARE_API(StopOnException)
11349{
11350 INIT_API();
11351 MINIDUMP_NOT_SUPPORTED();
11352
11353
11354 char buffer[100+mdNameLen];
11355
11356 BOOL fDerived = FALSE;
11357 BOOL fCreate1 = FALSE;
11358 BOOL fCreate2 = FALSE;
11359
11360 CMDOption option[] = {
11361 // name, vptr, type, hasValue
11362 {"-derived", &fDerived, COBOOL, FALSE}, // catch derived exceptions
11363 {"-create", &fCreate1, COBOOL, FALSE}, // create 1st chance handler
11364 {"-create2", &fCreate2, COBOOL, FALSE}, // create 2nd chance handler
11365 };
11366
11367 StringHolder TypeName,PReg;
11368
11369 CMDValue arg[] = {
11370 // vptr, type
11371 {&TypeName.data, COSTRING},
11372 {&PReg.data, COSTRING}
11373 };
11374 size_t nArg;
11375 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
11376 {
11377 return Status;
11378 }
11379 if (IsDumpFile())
11380 {
11381 ExtOut("Live debugging session required\n");
11382 return Status;
11383 }
11384 if (nArg < 1 || nArg > 2)
11385 {
11386 ExtOut("usage: StopOnException [-derived] [-create | -create2] <type name>\n");
11387 ExtOut(" [<pseudo-register number for result>]\n");
11388 ExtOut("ex: StopOnException -create System.OutOfMemoryException 1\n");
11389 return Status;
11390 }
11391
11392 size_t preg = 1; // by default
11393 if (nArg == 2)
11394 {
11395 preg = GetExpression(PReg.data);
11396 if (preg > 19)
11397 {
11398 ExtOut("Pseudo-register number must be between 0 and 19\n");
11399 return Status;
11400 }
11401 }
11402
11403 sprintf_s(buffer,_countof (buffer),
11404 "r$t%d=0",
11405 preg);
11406 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
11407 if (FAILED(Status))
11408 {
11409 ExtOut("Error initialized register $t%d to zero\n", preg);
11410 return Status;
11411 }
11412
11413 if (fCreate1 || fCreate2)
11414 {
11415 sprintf_s(buffer,_countof (buffer),
11416 "sxe %s \"!soe %s %s %d;.if(@$t%d==0) {g} .else {.echo '%s hit'}\" %x",
11417 fCreate1 ? "-c" : "-c2",
11418 fDerived ? "-derived" : "",
11419 TypeName.data,
11420 preg,
11421 preg,
11422 TypeName.data,
11423 EXCEPTION_COMPLUS
11424 );
11425
11426 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
11427 if (FAILED(Status))
11428 {
11429 ExtOut("Error setting breakpoint: %s\n", buffer);
11430 return Status;
11431 }
11432
11433 ExtOut("Breakpoint set\n");
11434 return Status;
11435 }
11436
11437 // Find the last thrown exception on this thread.
11438 // Does it match? If so, set the register.
11439 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
11440 DacpThreadData Thread;
11441
11442 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
11443 {
11444 ExtOut("The current thread is unmanaged\n");
11445 return Status;
11446 }
11447
11448 TADDR taLTOH;
11449 if (!SafeReadMemory(Thread.lastThrownObjectHandle,
11450 &taLTOH,
11451 sizeof(taLTOH), NULL))
11452 {
11453 ExtOut("There is no current managed exception on this thread\n");
11454 return Status;
11455 }
11456
11457 if (taLTOH)
11458 {
11459 LPWSTR typeNameWide = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
11460 MultiByteToWideChar(CP_ACP,0,TypeName.data,-1,typeNameWide,mdNameLen);
11461
11462 TADDR taMT;
11463 if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
11464 {
11465 NameForMT_s (taMT, g_mdName, mdNameLen);
11466 if ((_wcscmp(g_mdName,typeNameWide) == 0) ||
11467 (fDerived && IsDerivedFrom(taMT, typeNameWide)))
11468 {
11469 sprintf_s(buffer,_countof (buffer),
11470 "r$t%d=1",
11471 preg);
11472 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
11473 if (FAILED(Status))
11474 {
11475 ExtOut("Failed to execute the following command: %s\n", buffer);
11476 }
11477 }
11478 }
11479 }
11480
11481 return Status;
11482}
11483
11484/**********************************************************************\
11485* Routine Description: *
11486* *
11487* This function finds the size of an object or all roots. *
11488* *
11489\**********************************************************************/
11490DECLARE_API(ObjSize)
11491{
11492#ifndef FEATURE_PAL
11493 INIT_API();
11494 MINIDUMP_NOT_SUPPORTED();
11495
11496 BOOL dml = FALSE;
11497 StringHolder str_Object;
11498
11499
11500 CMDOption option[] =
11501 { // name, vptr, type, hasValue
11502 {"/d", &dml, COBOOL, FALSE},
11503 };
11504 CMDValue arg[] =
11505 { // vptr, type
11506 {&str_Object.data, COSTRING}
11507 };
11508 size_t nArg;
11509 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
11510 {
11511 return Status;
11512 }
11513
11514 EnableDMLHolder dmlHolder(dml);
11515 TADDR obj = GetExpression(str_Object.data);
11516
11517 GCRootImpl gcroot;
11518 if (obj == 0)
11519 {
11520 gcroot.ObjSize();
11521 }
11522 else
11523 {
11524 if(!sos::IsObject(obj))
11525 {
11526 ExtOut("%p is not a valid object.\n", SOS_PTR(obj));
11527 return Status;
11528 }
11529
11530 size_t size = gcroot.ObjSize(obj);
11531 TADDR mt = 0;
11532 MOVE(mt, obj);
11533 sos::MethodTable methodTable = mt;
11534 ExtOut("sizeof(%p) = %d (0x%x) bytes (%S)\n", SOS_PTR(obj), size, size, methodTable.GetName());
11535 }
11536 return Status;
11537#else
11538 return E_NOTIMPL;
11539#endif
11540
11541}
11542
11543#ifndef FEATURE_PAL
11544// For FEATURE_PAL, MEMORY_BASIC_INFORMATION64 doesn't exist yet. TODO?
11545DECLARE_API(GCHandleLeaks)
11546{
11547 INIT_API();
11548 MINIDUMP_NOT_SUPPORTED();
11549
11550 ExtOut("-------------------------------------------------------------------------------\n");
11551 ExtOut("GCHandleLeaks will report any GCHandles that couldn't be found in memory. \n");
11552 ExtOut("Strong and Pinned GCHandles are reported at this time. You can safely abort the\n");
11553 ExtOut("memory scan with Control-C or Control-Break. \n");
11554 ExtOut("-------------------------------------------------------------------------------\n");
11555
11556 static DWORD_PTR array[2000];
11557 UINT i;
11558 BOOL dml = FALSE;
11559
11560 CMDOption option[] =
11561 { // name, vptr, type, hasValue
11562 {"/d", &dml, COBOOL, FALSE},
11563 };
11564
11565 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
11566 {
11567 return Status;
11568 }
11569
11570 EnableDMLHolder dmlHolder(dml);
11571
11572 UINT iFinal = FindAllPinnedAndStrong(array,sizeof(array)/sizeof(DWORD_PTR));
11573 ExtOut("Found %d handles:\n",iFinal);
11574 for (i=1;i<=iFinal;i++)
11575 {
11576 ExtOut("%p\t", SOS_PTR(array[i-1]));
11577 if ((i % 4) == 0)
11578 ExtOut("\n");
11579 }
11580
11581 ExtOut("\nSearching memory\n");
11582 // Now search memory for this:
11583 DWORD_PTR buffer[1024];
11584 ULONG64 memCur = 0x0;
11585 BOOL bAbort = FALSE;
11586
11587 //find out memory used by stress log
11588 StressLogMem stressLog;
11589 CLRDATA_ADDRESS StressLogAddress = NULL;
11590 if (LoadClrDebugDll() != S_OK)
11591 {
11592 // Try to find stress log symbols
11593 DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
11594 StressLogAddress = dwAddr;
11595 g_bDacBroken = TRUE;
11596 }
11597 else
11598 {
11599 if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
11600 {
11601 ExtOut("Unable to find stress log via DAC\n");
11602 }
11603 g_bDacBroken = FALSE;
11604 }
11605
11606 if (stressLog.Init (StressLogAddress, g_ExtData))
11607 {
11608 ExtOut("Reference found in stress log will be ignored\n");
11609 }
11610 else
11611 {
11612 ExtOut("Failed to read whole or part of stress log, some references may come from stress log\n");
11613 }
11614
11615
11616 while (!bAbort)
11617 {
11618 NTSTATUS status;
11619 MEMORY_BASIC_INFORMATION64 memInfo;
11620
11621 status = g_ExtData2->QueryVirtual(UL64_TO_CDA(memCur), &memInfo);
11622
11623 if( !NT_SUCCESS(status) )
11624 {
11625 break;
11626 }
11627
11628 if (memInfo.State == MEM_COMMIT)
11629 {
11630 for (ULONG64 memIter = memCur; memIter < (memCur + memInfo.RegionSize); memIter+=sizeof(buffer))
11631 {
11632 if (IsInterrupt())
11633 {
11634 ExtOut("Quitting at %p due to user abort\n", SOS_PTR(memIter));
11635 bAbort = TRUE;
11636 break;
11637 }
11638
11639 if ((memIter % 0x10000000)==0x0)
11640 {
11641 ExtOut("Searching %p...\n", SOS_PTR(memIter));
11642 }
11643
11644 ULONG size = 0;
11645 HRESULT ret;
11646 ret = g_ExtData->ReadVirtual(UL64_TO_CDA(memIter), buffer, sizeof(buffer), &size);
11647 if (ret == S_OK)
11648 {
11649 for (UINT x=0;x<1024;x++)
11650 {
11651 DWORD_PTR value = buffer[x];
11652 // We don't care about the low bit. Also, the GCHandle class turns on the
11653 // low bit for pinned handles, so without the statement below, we wouldn't
11654 // notice pinned handles.
11655 value = value & ~1;
11656 for (i=0;i<iFinal;i++)
11657 {
11658 ULONG64 addrInDebugee = (ULONG64)memIter+(x*sizeof(DWORD_PTR));
11659 if ((array[i] & ~1) == value)
11660 {
11661 if (stressLog.IsInStressLog (addrInDebugee))
11662 {
11663 ExtOut("Found %p in stress log at location %p, reference not counted\n", SOS_PTR(value), addrInDebugee);
11664 }
11665 else
11666 {
11667 ExtOut("Found %p at location %p\n", SOS_PTR(value), addrInDebugee);
11668 array[i] |= 0x1;
11669 }
11670 }
11671 }
11672 }
11673 }
11674 else
11675 {
11676 if (size > 0)
11677 {
11678 ExtOut("only read %x bytes at %p\n", size, SOS_PTR(memIter));
11679 }
11680 }
11681 }
11682 }
11683
11684 memCur += memInfo.RegionSize;
11685 }
11686
11687 int numNotFound = 0;
11688 for (i=0;i<iFinal;i++)
11689 {
11690 if ((array[i] & 0x1) == 0)
11691 {
11692 numNotFound++;
11693 // ExtOut("WARNING: %p not found\n", SOS_PTR(array[i]));
11694 }
11695 }
11696
11697 if (numNotFound > 0)
11698 {
11699 ExtOut("------------------------------------------------------------------------------\n");
11700 ExtOut("Some handles were not found. If the number of not-found handles grows over the\n");
11701 ExtOut("lifetime of your application, you may have a GCHandle leak. This will cause \n");
11702 ExtOut("the GC Heap to grow larger as objects are being kept alive, referenced only \n");
11703 ExtOut("by the orphaned handle. If the number doesn't grow over time, note that there \n");
11704 ExtOut("may be some noise in this output, as an unmanaged application may be storing \n");
11705 ExtOut("the handle in a non-standard way, perhaps with some bits flipped. The memory \n");
11706 ExtOut("scan wouldn't be able to find those. \n");
11707 ExtOut("------------------------------------------------------------------------------\n");
11708
11709 ExtOut("Didn't find %d handles:\n", numNotFound);
11710 int numPrinted=0;
11711 for (i=0;i<iFinal;i++)
11712 {
11713 if ((array[i] & 0x1) == 0)
11714 {
11715 numPrinted++;
11716 ExtOut("%p\t", SOS_PTR(array[i]));
11717 if ((numPrinted % 4) == 0)
11718 ExtOut("\n");
11719 }
11720 }
11721 ExtOut("\n");
11722 }
11723 else
11724 {
11725 ExtOut("------------------------------------------------------------------------------\n");
11726 ExtOut("All handles found");
11727 if (bAbort)
11728 ExtOut(" even though you aborted.\n");
11729 else
11730 ExtOut(".\n");
11731 ExtOut("A leak may still exist because in a general scan of process memory SOS can't \n");
11732 ExtOut("differentiate between garbage and valid structures, so you may have false \n");
11733 ExtOut("positives. If you still suspect a leak, use this function over time to \n");
11734 ExtOut("identify a possible trend. \n");
11735 ExtOut("------------------------------------------------------------------------------\n");
11736 }
11737
11738 return Status;
11739}
11740#endif // FEATURE_PAL
11741
11742#endif // FEATURE_PAL
11743
11744class ClrStackImplWithICorDebug
11745{
11746private:
11747 static HRESULT DereferenceAndUnboxValue(ICorDebugValue * pValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = NULL)
11748 {
11749 HRESULT Status = S_OK;
11750 *ppOutputValue = NULL;
11751 if(pIsNull != NULL) *pIsNull = FALSE;
11752
11753 ToRelease<ICorDebugReferenceValue> pReferenceValue;
11754 Status = pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue);
11755 if (SUCCEEDED(Status))
11756 {
11757 BOOL isNull = FALSE;
11758 IfFailRet(pReferenceValue->IsNull(&isNull));
11759 if(!isNull)
11760 {
11761 ToRelease<ICorDebugValue> pDereferencedValue;
11762 IfFailRet(pReferenceValue->Dereference(&pDereferencedValue));
11763 return DereferenceAndUnboxValue(pDereferencedValue, ppOutputValue);
11764 }
11765 else
11766 {
11767 if(pIsNull != NULL) *pIsNull = TRUE;
11768 *ppOutputValue = pValue;
11769 (*ppOutputValue)->AddRef();
11770 return S_OK;
11771 }
11772 }
11773
11774 ToRelease<ICorDebugBoxValue> pBoxedValue;
11775 Status = pValue->QueryInterface(IID_ICorDebugBoxValue, (LPVOID*) &pBoxedValue);
11776 if (SUCCEEDED(Status))
11777 {
11778 ToRelease<ICorDebugObjectValue> pUnboxedValue;
11779 IfFailRet(pBoxedValue->GetObject(&pUnboxedValue));
11780 return DereferenceAndUnboxValue(pUnboxedValue, ppOutputValue);
11781 }
11782 *ppOutputValue = pValue;
11783 (*ppOutputValue)->AddRef();
11784 return S_OK;
11785 }
11786
11787 static BOOL ShouldExpandVariable(__in_z WCHAR* varToExpand, __in_z WCHAR* currentExpansion)
11788 {
11789 if(currentExpansion == NULL || varToExpand == NULL) return FALSE;
11790
11791 size_t varToExpandLen = _wcslen(varToExpand);
11792 size_t currentExpansionLen = _wcslen(currentExpansion);
11793 if(currentExpansionLen > varToExpandLen) return FALSE;
11794 if(currentExpansionLen < varToExpandLen && varToExpand[currentExpansionLen] != L'.') return FALSE;
11795 if(_wcsncmp(currentExpansion, varToExpand, currentExpansionLen) != 0) return FALSE;
11796
11797 return TRUE;
11798 }
11799
11800 static BOOL IsEnum(ICorDebugValue * pInputValue)
11801 {
11802 ToRelease<ICorDebugValue> pValue;
11803 if(FAILED(DereferenceAndUnboxValue(pInputValue, &pValue, NULL))) return FALSE;
11804
11805 WCHAR baseTypeName[mdNameLen];
11806 ToRelease<ICorDebugValue2> pValue2;
11807 ToRelease<ICorDebugType> pType;
11808 ToRelease<ICorDebugType> pBaseType;
11809
11810 if(FAILED(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2))) return FALSE;
11811 if(FAILED(pValue2->GetExactType(&pType))) return FALSE;
11812 if(FAILED(pType->GetBase(&pBaseType)) || pBaseType == NULL) return FALSE;
11813 if(FAILED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen))) return FALSE;
11814
11815 return (_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0);
11816 }
11817
11818 static HRESULT AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
11819 {
11820 bool isFirst = true;
11821 ToRelease<ICorDebugTypeEnum> pTypeEnum;
11822 if(SUCCEEDED(pType->EnumerateTypeParameters(&pTypeEnum)))
11823 {
11824 ULONG numTypes = 0;
11825 ToRelease<ICorDebugType> pCurrentTypeParam;
11826
11827 while(SUCCEEDED(pTypeEnum->Next(1, &pCurrentTypeParam, &numTypes)))
11828 {
11829 if(numTypes == 0) break;
11830
11831 if(isFirst)
11832 {
11833 isFirst = false;
11834 wcsncat_s(typeName, typeNameLen, W("&lt;"), typeNameLen);
11835 }
11836 else wcsncat_s(typeName, typeNameLen, W(","), typeNameLen);
11837
11838 WCHAR typeParamName[mdNameLen];
11839 typeParamName[0] = L'\0';
11840 GetTypeOfValue(pCurrentTypeParam, typeParamName, mdNameLen);
11841 wcsncat_s(typeName, typeNameLen, typeParamName, typeNameLen);
11842 }
11843 if(!isFirst)
11844 wcsncat_s(typeName, typeNameLen, W("&gt;"), typeNameLen);
11845 }
11846
11847 return S_OK;
11848 }
11849
11850 static HRESULT GetTypeOfValue(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
11851 {
11852 HRESULT Status = S_OK;
11853
11854 CorElementType corElemType;
11855 IfFailRet(pType->GetType(&corElemType));
11856
11857 switch (corElemType)
11858 {
11859 //List of unsupported CorElementTypes:
11860 //ELEMENT_TYPE_END = 0x0,
11861 //ELEMENT_TYPE_VAR = 0x13, // a class type variable VAR <U1>
11862 //ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
11863 //ELEMENT_TYPE_TYPEDBYREF = 0x16, // TYPEDREF (it takes no args) a typed referece to some other type
11864 //ELEMENT_TYPE_MVAR = 0x1e, // a method type variable MVAR <U1>
11865 //ELEMENT_TYPE_CMOD_REQD = 0x1F, // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef>
11866 //ELEMENT_TYPE_CMOD_OPT = 0x20, // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
11867 //ELEMENT_TYPE_INTERNAL = 0x21, // INTERNAL <typehandle>
11868 //ELEMENT_TYPE_MAX = 0x22, // first invalid element type
11869 //ELEMENT_TYPE_MODIFIER = 0x40,
11870 //ELEMENT_TYPE_SENTINEL = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs
11871 //ELEMENT_TYPE_PINNED = 0x05 | ELEMENT_TYPE_MODIFIER,
11872 //ELEMENT_TYPE_R4_HFA = 0x06 | ELEMENT_TYPE_MODIFIER, // used only internally for R4 HFA types
11873 //ELEMENT_TYPE_R8_HFA = 0x07 | ELEMENT_TYPE_MODIFIER, // used only internally for R8 HFA types
11874 default:
11875 swprintf_s(typeName, typeNameLen, W("(Unhandled CorElementType: 0x%x)\0"), corElemType);
11876 break;
11877
11878 case ELEMENT_TYPE_VALUETYPE:
11879 case ELEMENT_TYPE_CLASS:
11880 {
11881 //Defaults in case we fail...
11882 if(corElemType == ELEMENT_TYPE_VALUETYPE) swprintf_s(typeName, typeNameLen, W("struct\0"));
11883 else swprintf_s(typeName, typeNameLen, W("class\0"));
11884
11885 mdTypeDef typeDef;
11886 ToRelease<ICorDebugClass> pClass;
11887 if(SUCCEEDED(pType->GetClass(&pClass)) && SUCCEEDED(pClass->GetToken(&typeDef)))
11888 {
11889 ToRelease<ICorDebugModule> pModule;
11890 IfFailRet(pClass->GetModule(&pModule));
11891
11892 ToRelease<IUnknown> pMDUnknown;
11893 ToRelease<IMetaDataImport> pMD;
11894 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11895 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11896
11897 if(SUCCEEDED(NameForToken_s(TokenFromRid(typeDef, mdtTypeDef), pMD, g_mdName, mdNameLen, false)))
11898 swprintf_s(typeName, typeNameLen, W("%s\0"), g_mdName);
11899 }
11900 AddGenericArgs(pType, typeName, typeNameLen);
11901 }
11902 break;
11903 case ELEMENT_TYPE_VOID:
11904 swprintf_s(typeName, typeNameLen, W("void\0"));
11905 break;
11906 case ELEMENT_TYPE_BOOLEAN:
11907 swprintf_s(typeName, typeNameLen, W("bool\0"));
11908 break;
11909 case ELEMENT_TYPE_CHAR:
11910 swprintf_s(typeName, typeNameLen, W("char\0"));
11911 break;
11912 case ELEMENT_TYPE_I1:
11913 swprintf_s(typeName, typeNameLen, W("signed byte\0"));
11914 break;
11915 case ELEMENT_TYPE_U1:
11916 swprintf_s(typeName, typeNameLen, W("byte\0"));
11917 break;
11918 case ELEMENT_TYPE_I2:
11919 swprintf_s(typeName, typeNameLen, W("short\0"));
11920 break;
11921 case ELEMENT_TYPE_U2:
11922 swprintf_s(typeName, typeNameLen, W("unsigned short\0"));
11923 break;
11924 case ELEMENT_TYPE_I4:
11925 swprintf_s(typeName, typeNameLen, W("int\0"));
11926 break;
11927 case ELEMENT_TYPE_U4:
11928 swprintf_s(typeName, typeNameLen, W("unsigned int\0"));
11929 break;
11930 case ELEMENT_TYPE_I8:
11931 swprintf_s(typeName, typeNameLen, W("long\0"));
11932 break;
11933 case ELEMENT_TYPE_U8:
11934 swprintf_s(typeName, typeNameLen, W("unsigned long\0"));
11935 break;
11936 case ELEMENT_TYPE_R4:
11937 swprintf_s(typeName, typeNameLen, W("float\0"));
11938 break;
11939 case ELEMENT_TYPE_R8:
11940 swprintf_s(typeName, typeNameLen, W("double\0"));
11941 break;
11942 case ELEMENT_TYPE_OBJECT:
11943 swprintf_s(typeName, typeNameLen, W("object\0"));
11944 break;
11945 case ELEMENT_TYPE_STRING:
11946 swprintf_s(typeName, typeNameLen, W("string\0"));
11947 break;
11948 case ELEMENT_TYPE_I:
11949 swprintf_s(typeName, typeNameLen, W("IntPtr\0"));
11950 break;
11951 case ELEMENT_TYPE_U:
11952 swprintf_s(typeName, typeNameLen, W("UIntPtr\0"));
11953 break;
11954 case ELEMENT_TYPE_SZARRAY:
11955 case ELEMENT_TYPE_ARRAY:
11956 case ELEMENT_TYPE_BYREF:
11957 case ELEMENT_TYPE_PTR:
11958 {
11959 ToRelease<ICorDebugType> pFirstParameter;
11960 if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)))
11961 GetTypeOfValue(pFirstParameter, typeName, typeNameLen);
11962 else
11963 swprintf_s(typeName, typeNameLen, W("<unknown>\0"));
11964
11965 switch(corElemType)
11966 {
11967 case ELEMENT_TYPE_SZARRAY:
11968 wcsncat_s(typeName, typeNameLen, W("[]\0"), typeNameLen);
11969 return S_OK;
11970 case ELEMENT_TYPE_ARRAY:
11971 {
11972 ULONG32 rank = 0;
11973 pType->GetRank(&rank);
11974 wcsncat_s(typeName, typeNameLen, W("["), typeNameLen);
11975 for(ULONG32 i = 0; i < rank - 1; i++)
11976 {
11977 //
11978 wcsncat_s(typeName, typeNameLen, W(","), typeNameLen);
11979 }
11980 wcsncat_s(typeName, typeNameLen, W("]\0"), typeNameLen);
11981 }
11982 return S_OK;
11983 case ELEMENT_TYPE_BYREF:
11984 wcsncat_s(typeName, typeNameLen, W("&\0"), typeNameLen);
11985 return S_OK;
11986 case ELEMENT_TYPE_PTR:
11987 wcsncat_s(typeName, typeNameLen, W("*\0"), typeNameLen);
11988 return S_OK;
11989 default:
11990 // note we can never reach here as this is a nested switch
11991 // and corElemType can only be one of the values above
11992 break;
11993 }
11994 }
11995 break;
11996 case ELEMENT_TYPE_FNPTR:
11997 swprintf_s(typeName, typeNameLen, W("*(...)\0"));
11998 break;
11999 case ELEMENT_TYPE_TYPEDBYREF:
12000 swprintf_s(typeName, typeNameLen, W("typedbyref\0"));
12001 break;
12002 }
12003 return S_OK;
12004 }
12005
12006 static HRESULT GetTypeOfValue(ICorDebugValue * pValue, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
12007 {
12008 HRESULT Status = S_OK;
12009
12010 CorElementType corElemType;
12011 IfFailRet(pValue->GetType(&corElemType));
12012
12013 ToRelease<ICorDebugType> pType;
12014 ToRelease<ICorDebugValue2> pValue2;
12015 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugValue2, (void**) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
12016 return GetTypeOfValue(pType, typeName, typeNameLen);
12017 else
12018 swprintf_s(typeName, typeNameLen, W("<unknown>\0"));
12019
12020 return S_OK;
12021 }
12022
12023 static HRESULT PrintEnumValue(ICorDebugValue* pInputValue, BYTE* enumValue)
12024 {
12025 HRESULT Status = S_OK;
12026
12027 ToRelease<ICorDebugValue> pValue;
12028 IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, NULL));
12029
12030 mdTypeDef currentTypeDef;
12031 ToRelease<ICorDebugClass> pClass;
12032 ToRelease<ICorDebugValue2> pValue2;
12033 ToRelease<ICorDebugType> pType;
12034 ToRelease<ICorDebugModule> pModule;
12035 IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
12036 IfFailRet(pValue2->GetExactType(&pType));
12037 IfFailRet(pType->GetClass(&pClass));
12038 IfFailRet(pClass->GetModule(&pModule));
12039 IfFailRet(pClass->GetToken(&currentTypeDef));
12040
12041 ToRelease<IUnknown> pMDUnknown;
12042 ToRelease<IMetaDataImport> pMD;
12043 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
12044 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
12045
12046
12047 //First, we need to figure out the underlying enum type so that we can correctly type cast the raw values of each enum constant
12048 //We get that from the non-static field of the enum variable (I think the field is called __value or something similar)
12049 ULONG numFields = 0;
12050 HCORENUM fEnum = NULL;
12051 mdFieldDef fieldDef;
12052 CorElementType enumUnderlyingType = ELEMENT_TYPE_END;
12053 while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
12054 {
12055 DWORD fieldAttr = 0;
12056 PCCOR_SIGNATURE pSignatureBlob = NULL;
12057 ULONG sigBlobLength = 0;
12058 if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, NULL, 0, NULL, &fieldAttr, &pSignatureBlob, &sigBlobLength, NULL, NULL, NULL)))
12059 {
12060 if((fieldAttr & fdStatic) == 0)
12061 {
12062 CorSigUncompressCallingConv(pSignatureBlob);
12063 enumUnderlyingType = CorSigUncompressElementType(pSignatureBlob);
12064 break;
12065 }
12066 }
12067 }
12068 pMD->CloseEnum(fEnum);
12069
12070
12071 //Now that we know the underlying enum type, let's decode the enum variable into OR-ed, human readable enum contants
12072 fEnum = NULL;
12073 bool isFirst = true;
12074 ULONG64 remainingValue = *((ULONG64*)enumValue);
12075 while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
12076 {
12077 ULONG nameLen = 0;
12078 DWORD fieldAttr = 0;
12079 WCHAR mdName[mdNameLen];
12080 UVCP_CONSTANT pRawValue = NULL;
12081 ULONG rawValueLength = 0;
12082 if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, &pRawValue, &rawValueLength)))
12083 {
12084 DWORD enumValueRequiredAttributes = fdPublic | fdStatic | fdLiteral | fdHasDefault;
12085 if((fieldAttr & enumValueRequiredAttributes) != enumValueRequiredAttributes)
12086 continue;
12087
12088 ULONG64 currentConstValue = 0;
12089 switch (enumUnderlyingType)
12090 {
12091 case ELEMENT_TYPE_CHAR:
12092 case ELEMENT_TYPE_I1:
12093 currentConstValue = (ULONG64)(*((CHAR*)pRawValue));
12094 break;
12095 case ELEMENT_TYPE_U1:
12096 currentConstValue = (ULONG64)(*((BYTE*)pRawValue));
12097 break;
12098 case ELEMENT_TYPE_I2:
12099 currentConstValue = (ULONG64)(*((SHORT*)pRawValue));
12100 break;
12101 case ELEMENT_TYPE_U2:
12102 currentConstValue = (ULONG64)(*((USHORT*)pRawValue));
12103 break;
12104 case ELEMENT_TYPE_I4:
12105 currentConstValue = (ULONG64)(*((INT32*)pRawValue));
12106 break;
12107 case ELEMENT_TYPE_U4:
12108 currentConstValue = (ULONG64)(*((UINT32*)pRawValue));
12109 break;
12110 case ELEMENT_TYPE_I8:
12111 currentConstValue = (ULONG64)(*((LONG*)pRawValue));
12112 break;
12113 case ELEMENT_TYPE_U8:
12114 currentConstValue = (ULONG64)(*((ULONG*)pRawValue));
12115 break;
12116 case ELEMENT_TYPE_I:
12117 currentConstValue = (ULONG64)(*((int*)pRawValue));
12118 break;
12119 case ELEMENT_TYPE_U:
12120 case ELEMENT_TYPE_R4:
12121 case ELEMENT_TYPE_R8:
12122 // Technically U and the floating-point ones are options in the CLI, but not in the CLS or C#, so these are NYI
12123 default:
12124 currentConstValue = 0;
12125 }
12126
12127 if((currentConstValue == remainingValue) || ((currentConstValue != 0) && ((currentConstValue & remainingValue) == currentConstValue)))
12128 {
12129 remainingValue &= ~currentConstValue;
12130 if(isFirst)
12131 {
12132 ExtOut(" = %S", mdName);
12133 isFirst = false;
12134 }
12135 else ExtOut(" | %S", mdName);
12136 }
12137 }
12138 }
12139 pMD->CloseEnum(fEnum);
12140
12141 return S_OK;
12142 }
12143
12144 static HRESULT PrintStringValue(ICorDebugValue * pValue)
12145 {
12146 HRESULT Status;
12147
12148 ToRelease<ICorDebugStringValue> pStringValue;
12149 IfFailRet(pValue->QueryInterface(IID_ICorDebugStringValue, (LPVOID*) &pStringValue));
12150
12151 ULONG32 cchValue;
12152 IfFailRet(pStringValue->GetLength(&cchValue));
12153 cchValue++; // Allocate one more for null terminator
12154
12155 CQuickString quickString;
12156 quickString.Alloc(cchValue);
12157
12158 ULONG32 cchValueReturned;
12159 IfFailRet(pStringValue->GetString(
12160 cchValue,
12161 &cchValueReturned,
12162 quickString.String()));
12163
12164 ExtOut(" = \"%S\"\n", quickString.String());
12165
12166 return S_OK;
12167 }
12168
12169 static HRESULT PrintSzArrayValue(ICorDebugValue * pValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
12170 {
12171 HRESULT Status = S_OK;
12172
12173 ToRelease<ICorDebugArrayValue> pArrayValue;
12174 IfFailRet(pValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue));
12175
12176 ULONG32 nRank;
12177 IfFailRet(pArrayValue->GetRank(&nRank));
12178 if (nRank != 1)
12179 {
12180 return E_UNEXPECTED;
12181 }
12182
12183 ULONG32 cElements;
12184 IfFailRet(pArrayValue->GetCount(&cElements));
12185
12186 if (cElements == 0) ExtOut(" (empty)\n");
12187 else if (cElements == 1) ExtOut(" (1 element)\n");
12188 else ExtOut(" (%d elements)\n", cElements);
12189
12190 if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
12191 size_t currentExpansionLen = _wcslen(currentExpansion);
12192
12193 for (ULONG32 i=0; i < cElements; i++)
12194 {
12195 for(int j = 0; j <= indent; j++) ExtOut(" ");
12196 currentExpansion[currentExpansionLen] = L'\0';
12197 swprintf_s(currentExpansion, mdNameLen, W("%s.[%d]\0"), currentExpansion, i);
12198
12199 bool printed = false;
12200 CorElementType corElemType;
12201 ToRelease<ICorDebugType> pFirstParameter;
12202 ToRelease<ICorDebugValue2> pValue2;
12203 ToRelease<ICorDebugType> pType;
12204 if(SUCCEEDED(pArrayValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
12205 {
12206 if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)) && SUCCEEDED(pFirstParameter->GetType(&corElemType)))
12207 {
12208 switch(corElemType)
12209 {
12210 //If the array element is something that we can expand with !clrstack, show information about the type of this element
12211 case ELEMENT_TYPE_VALUETYPE:
12212 case ELEMENT_TYPE_CLASS:
12213 case ELEMENT_TYPE_SZARRAY:
12214 {
12215 WCHAR typeOfElement[mdNameLen];
12216 GetTypeOfValue(pFirstParameter, typeOfElement, mdNameLen);
12217 DMLOut(" |- %s = %S", DMLManagedVar(currentExpansion, currentFrame, i), typeOfElement);
12218 printed = true;
12219 }
12220 break;
12221 default:
12222 break;
12223 }
12224 }
12225 }
12226 if(!printed) DMLOut(" |- %s", DMLManagedVar(currentExpansion, currentFrame, i));
12227
12228 ToRelease<ICorDebugValue> pElementValue;
12229 IfFailRet(pArrayValue->GetElementAtPosition(i, &pElementValue));
12230 IfFailRet(PrintValue(pElementValue, pILFrame, pMD, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame));
12231 }
12232
12233 return S_OK;
12234 }
12235
12236 static HRESULT PrintValue(ICorDebugValue * pInputValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
12237 {
12238 HRESULT Status = S_OK;
12239
12240 BOOL isNull = TRUE;
12241 ToRelease<ICorDebugValue> pValue;
12242 IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
12243
12244 if(isNull)
12245 {
12246 ExtOut(" = null\n");
12247 return S_OK;
12248 }
12249
12250 ULONG32 cbSize;
12251 IfFailRet(pValue->GetSize(&cbSize));
12252 ArrayHolder<BYTE> rgbValue = new NOTHROW BYTE[cbSize];
12253 if (rgbValue == NULL)
12254 {
12255 ReportOOM();
12256 return E_OUTOFMEMORY;
12257 }
12258
12259 memset(rgbValue.GetPtr(), 0, cbSize * sizeof(BYTE));
12260
12261 CorElementType corElemType;
12262 IfFailRet(pValue->GetType(&corElemType));
12263 if (corElemType == ELEMENT_TYPE_STRING)
12264 {
12265 return PrintStringValue(pValue);
12266 }
12267
12268 if (corElemType == ELEMENT_TYPE_SZARRAY)
12269 {
12270 return PrintSzArrayValue(pValue, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
12271 }
12272
12273 ToRelease<ICorDebugGenericValue> pGenericValue;
12274 IfFailRet(pValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue));
12275 IfFailRet(pGenericValue->GetValue((LPVOID) &(rgbValue[0])));
12276
12277 if(IsEnum(pValue))
12278 {
12279 Status = PrintEnumValue(pValue, rgbValue);
12280 ExtOut("\n");
12281 return Status;
12282 }
12283
12284 switch (corElemType)
12285 {
12286 default:
12287 ExtOut(" (Unhandled CorElementType: 0x%x)\n", corElemType);
12288 break;
12289
12290 case ELEMENT_TYPE_PTR:
12291 ExtOut(" = <pointer>\n");
12292 break;
12293
12294 case ELEMENT_TYPE_FNPTR:
12295 {
12296 CORDB_ADDRESS addr = 0;
12297 ToRelease<ICorDebugReferenceValue> pReferenceValue = NULL;
12298 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue)))
12299 pReferenceValue->GetValue(&addr);
12300 ExtOut(" = <function pointer 0x%x>\n", addr);
12301 }
12302 break;
12303
12304 case ELEMENT_TYPE_VALUETYPE:
12305 case ELEMENT_TYPE_CLASS:
12306 CORDB_ADDRESS addr;
12307 if(SUCCEEDED(pValue->GetAddress(&addr)))
12308 {
12309 ExtOut(" @ 0x%I64x\n", addr);
12310 }
12311 else
12312 {
12313 ExtOut("\n");
12314 }
12315 ProcessFields(pValue, NULL, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
12316 break;
12317
12318 case ELEMENT_TYPE_BOOLEAN:
12319 ExtOut(" = %s\n", rgbValue[0] == 0 ? "false" : "true");
12320 break;
12321
12322 case ELEMENT_TYPE_CHAR:
12323 ExtOut(" = '%C'\n", *(WCHAR *) &(rgbValue[0]));
12324 break;
12325
12326 case ELEMENT_TYPE_I1:
12327 ExtOut(" = %d\n", *(char*) &(rgbValue[0]));
12328 break;
12329
12330 case ELEMENT_TYPE_U1:
12331 ExtOut(" = %d\n", *(unsigned char*) &(rgbValue[0]));
12332 break;
12333
12334 case ELEMENT_TYPE_I2:
12335 ExtOut(" = %hd\n", *(short*) &(rgbValue[0]));
12336 break;
12337
12338 case ELEMENT_TYPE_U2:
12339 ExtOut(" = %hu\n", *(unsigned short*) &(rgbValue[0]));
12340 break;
12341
12342 case ELEMENT_TYPE_I:
12343 ExtOut(" = %d\n", *(int*) &(rgbValue[0]));
12344 break;
12345
12346 case ELEMENT_TYPE_U:
12347 ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0]));
12348 break;
12349
12350 case ELEMENT_TYPE_I4:
12351 ExtOut(" = %d\n", *(int*) &(rgbValue[0]));
12352 break;
12353
12354 case ELEMENT_TYPE_U4:
12355 ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0]));
12356 break;
12357
12358 case ELEMENT_TYPE_I8:
12359 ExtOut(" = %I64d\n", *(__int64*) &(rgbValue[0]));
12360 break;
12361
12362 case ELEMENT_TYPE_U8:
12363 ExtOut(" = %I64u\n", *(unsigned __int64*) &(rgbValue[0]));
12364 break;
12365
12366 case ELEMENT_TYPE_R4:
12367 ExtOut(" = %f\n", (double) *(float*) &(rgbValue[0]));
12368 break;
12369
12370 case ELEMENT_TYPE_R8:
12371 ExtOut(" = %f\n", *(double*) &(rgbValue[0]));
12372 break;
12373
12374 case ELEMENT_TYPE_OBJECT:
12375 ExtOut(" = object\n");
12376 break;
12377
12378 // TODO: The following corElementTypes are not yet implemented here. Array
12379 // might be interesting to add, though the others may be of rather limited use:
12380 // ELEMENT_TYPE_ARRAY = 0x14, // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ...
12381 //
12382 // ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
12383 }
12384
12385 return S_OK;
12386 }
12387
12388 static HRESULT PrintParameters(BOOL bParams, BOOL bLocals, IMetaDataImport * pMD, mdTypeDef typeDef, mdMethodDef methodDef, ICorDebugILFrame * pILFrame, ICorDebugModule * pModule, __in_z WCHAR* varToExpand, int currentFrame)
12389 {
12390 HRESULT Status = S_OK;
12391
12392 ULONG cParams = 0;
12393 ToRelease<ICorDebugValueEnum> pParamEnum;
12394 IfFailRet(pILFrame->EnumerateArguments(&pParamEnum));
12395 IfFailRet(pParamEnum->GetCount(&cParams));
12396 if (cParams > 0 && bParams)
12397 {
12398 DWORD methAttr = 0;
12399 IfFailRet(pMD->GetMethodProps(methodDef, NULL, NULL, 0, NULL, &methAttr, NULL, NULL, NULL, NULL));
12400
12401 ExtOut("\nPARAMETERS:\n");
12402 for (ULONG i=0; i < cParams; i++)
12403 {
12404 ULONG paramNameLen = 0;
12405 mdParamDef paramDef;
12406 WCHAR paramName[mdNameLen] = W("\0");
12407
12408 if(i == 0 && (methAttr & mdStatic) == 0)
12409 swprintf_s(paramName, mdNameLen, W("this\0"));
12410 else
12411 {
12412 int idx = ((methAttr & mdStatic) == 0)? i : (i + 1);
12413 if(SUCCEEDED(pMD->GetParamForMethodIndex(methodDef, idx, &paramDef)))
12414 pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, &paramNameLen, NULL, NULL, NULL, NULL);
12415 }
12416 if(_wcslen(paramName) == 0)
12417 swprintf_s(paramName, mdNameLen, W("param_%d\0"), i);
12418
12419 ToRelease<ICorDebugValue> pValue;
12420 ULONG cArgsFetched;
12421 Status = pParamEnum->Next(1, &pValue, &cArgsFetched);
12422
12423 if (FAILED(Status))
12424 {
12425 ExtOut(" + (Error 0x%x retrieving parameter '%S')\n", Status, paramName);
12426 continue;
12427 }
12428
12429 if (Status == S_FALSE)
12430 {
12431 break;
12432 }
12433
12434 WCHAR typeName[mdNameLen] = W("\0");
12435 GetTypeOfValue(pValue, typeName, mdNameLen);
12436 DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
12437
12438 ToRelease<ICorDebugReferenceValue> pRefValue;
12439 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
12440 {
12441 BOOL bIsNull = TRUE;
12442 pRefValue->IsNull(&bIsNull);
12443 if(bIsNull)
12444 {
12445 ExtOut(" = null\n");
12446 continue;
12447 }
12448 }
12449
12450 WCHAR currentExpansion[mdNameLen];
12451 swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName);
12452 if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
12453 ExtOut(" + (Error 0x%x printing parameter %d)\n", Status, i);
12454 }
12455 }
12456 else if (cParams == 0 && bParams)
12457 ExtOut("\nPARAMETERS: (none)\n");
12458
12459 ULONG cLocals = 0;
12460 ToRelease<ICorDebugValueEnum> pLocalsEnum;
12461 IfFailRet(pILFrame->EnumerateLocalVariables(&pLocalsEnum));
12462 IfFailRet(pLocalsEnum->GetCount(&cLocals));
12463 if (cLocals > 0 && bLocals)
12464 {
12465 bool symbolsAvailable = false;
12466 SymbolReader symReader;
12467 if(SUCCEEDED(symReader.LoadSymbols(pMD, pModule)))
12468 symbolsAvailable = true;
12469 ExtOut("\nLOCALS:\n");
12470 for (ULONG i=0; i < cLocals; i++)
12471 {
12472 ULONG paramNameLen = 0;
12473 WCHAR paramName[mdNameLen] = W("\0");
12474
12475 ToRelease<ICorDebugValue> pValue;
12476 if(symbolsAvailable)
12477 {
12478 Status = symReader.GetNamedLocalVariable(pILFrame, i, paramName, mdNameLen, &pValue);
12479 }
12480 else
12481 {
12482 ULONG cArgsFetched;
12483 Status = pLocalsEnum->Next(1, &pValue, &cArgsFetched);
12484 }
12485 if(_wcslen(paramName) == 0)
12486 swprintf_s(paramName, mdNameLen, W("local_%d\0"), i);
12487
12488 if (FAILED(Status))
12489 {
12490 ExtOut(" + (Error 0x%x retrieving local variable '%S')\n", Status, paramName);
12491 continue;
12492 }
12493
12494 if (Status == S_FALSE)
12495 {
12496 break;
12497 }
12498
12499 WCHAR typeName[mdNameLen] = W("\0");
12500 GetTypeOfValue(pValue, typeName, mdNameLen);
12501 DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
12502
12503 ToRelease<ICorDebugReferenceValue> pRefValue = NULL;
12504 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
12505 {
12506 BOOL bIsNull = TRUE;
12507 pRefValue->IsNull(&bIsNull);
12508 if(bIsNull)
12509 {
12510 ExtOut(" = null\n");
12511 continue;
12512 }
12513 }
12514
12515 WCHAR currentExpansion[mdNameLen];
12516 swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName);
12517 if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
12518 ExtOut(" + (Error 0x%x printing local variable %d)\n", Status, i);
12519 }
12520 }
12521 else if (cLocals == 0 && bLocals)
12522 ExtOut("\nLOCALS: (none)\n");
12523
12524 if(bParams || bLocals)
12525 ExtOut("\n");
12526
12527 return S_OK;
12528 }
12529
12530 static HRESULT ProcessFields(ICorDebugValue* pInputValue, ICorDebugType* pTypeCast, ICorDebugILFrame * pILFrame, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
12531 {
12532 if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
12533 size_t currentExpansionLen = _wcslen(currentExpansion);
12534
12535 HRESULT Status = S_OK;
12536
12537 BOOL isNull = FALSE;
12538 ToRelease<ICorDebugValue> pValue;
12539 IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
12540
12541 if(isNull) return S_OK;
12542
12543 mdTypeDef currentTypeDef;
12544 ToRelease<ICorDebugClass> pClass;
12545 ToRelease<ICorDebugValue2> pValue2;
12546 ToRelease<ICorDebugType> pType;
12547 ToRelease<ICorDebugModule> pModule;
12548 IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
12549 if(pTypeCast == NULL)
12550 IfFailRet(pValue2->GetExactType(&pType));
12551 else
12552 {
12553 pType = pTypeCast;
12554 pType->AddRef();
12555 }
12556 IfFailRet(pType->GetClass(&pClass));
12557 IfFailRet(pClass->GetModule(&pModule));
12558 IfFailRet(pClass->GetToken(&currentTypeDef));
12559
12560 ToRelease<IUnknown> pMDUnknown;
12561 ToRelease<IMetaDataImport> pMD;
12562 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
12563 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
12564
12565 WCHAR baseTypeName[mdNameLen] = W("\0");
12566 ToRelease<ICorDebugType> pBaseType;
12567 if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen)))
12568 {
12569 if(_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0)
12570 return S_OK;
12571 else if(_wcsncmp(baseTypeName, W("System.Object"), 13) != 0 && _wcsncmp(baseTypeName, W("System.ValueType"), 16) != 0)
12572 {
12573 currentExpansion[currentExpansionLen] = W('\0');
12574 wcscat_s(currentExpansion, currentExpansionSize, W(".\0"));
12575 wcscat_s(currentExpansion, currentExpansionSize, W("[basetype]"));
12576 for(int i = 0; i < indent; i++) ExtOut(" ");
12577 DMLOut(" |- %S %s\n", baseTypeName, DMLManagedVar(currentExpansion, currentFrame, W("[basetype]")));
12578
12579 if(ShouldExpandVariable(varToExpand, currentExpansion))
12580 ProcessFields(pInputValue, pBaseType, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
12581 }
12582 }
12583
12584
12585 ULONG numFields = 0;
12586 HCORENUM fEnum = NULL;
12587 mdFieldDef fieldDef;
12588 while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
12589 {
12590 ULONG nameLen = 0;
12591 DWORD fieldAttr = 0;
12592 WCHAR mdName[mdNameLen];
12593 WCHAR typeName[mdNameLen];
12594 if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, NULL, NULL)))
12595 {
12596 currentExpansion[currentExpansionLen] = W('\0');
12597 wcscat_s(currentExpansion, currentExpansionSize, W(".\0"));
12598 wcscat_s(currentExpansion, currentExpansionSize, mdName);
12599
12600 ToRelease<ICorDebugValue> pFieldVal;
12601 if(fieldAttr & fdLiteral)
12602 {
12603 //TODO: Is it worth it??
12604 //ExtOut(" |- const %S", mdName);
12605 }
12606 else
12607 {
12608 for(int i = 0; i < indent; i++) ExtOut(" ");
12609
12610 if (fieldAttr & fdStatic)
12611 pType->GetStaticFieldValue(fieldDef, pILFrame, &pFieldVal);
12612 else
12613 {
12614 ToRelease<ICorDebugObjectValue> pObjValue;
12615 if (SUCCEEDED(pValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue)))
12616 pObjValue->GetFieldValue(pClass, fieldDef, &pFieldVal);
12617 }
12618
12619 if(pFieldVal != NULL)
12620 {
12621 typeName[0] = L'\0';
12622 GetTypeOfValue(pFieldVal, typeName, mdNameLen);
12623 DMLOut(" |- %S %s", typeName, DMLManagedVar(currentExpansion, currentFrame, mdName));
12624 PrintValue(pFieldVal, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
12625 }
12626 else if(!(fieldAttr & fdLiteral))
12627 ExtOut(" |- < unknown type > %S\n", mdName);
12628 }
12629 }
12630 }
12631 pMD->CloseEnum(fEnum);
12632 return S_OK;
12633 }
12634
12635public:
12636
12637 // This is the main worker function used if !clrstack is called with "-i" to indicate
12638 // that the public ICorDebug* should be used instead of the private DAC interface. NOTE:
12639 // Currently only bParams is supported. NOTE: This is a work in progress and the
12640 // following would be good to do:
12641 // * More thorough testing with interesting stacks, especially with transitions into
12642 // and out of managed code.
12643 // * Consider interleaving this code back into the main body of !clrstack if it turns
12644 // out that there's a lot of duplication of code between these two functions.
12645 // (Still unclear how things will look once locals is implemented.)
12646 static HRESULT ClrStackFromPublicInterface(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, __in_z WCHAR* varToExpand = NULL, int onlyShowFrame = -1)
12647 {
12648 HRESULT Status;
12649
12650 IfFailRet(InitCorDebugInterface());
12651
12652 ExtOut("\n\n\nDumping managed stack and managed variables using ICorDebug.\n");
12653 ExtOut("=============================================================================\n");
12654
12655 ToRelease<ICorDebugThread> pThread;
12656 ToRelease<ICorDebugThread3> pThread3;
12657 ToRelease<ICorDebugStackWalk> pStackWalk;
12658 ULONG ulThreadID = 0;
12659 g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID);
12660
12661 IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread));
12662 IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3));
12663 IfFailRet(pThread3->CreateStackWalk(&pStackWalk));
12664
12665 InternalFrameManager internalFrameManager;
12666 IfFailRet(internalFrameManager.Init(pThread3));
12667
12668 #if defined(_AMD64_) || defined(_ARM64_)
12669 ExtOut("%-16s %-16s %s\n", "Child SP", "IP", "Call Site");
12670 #elif defined(_X86_) || defined(_ARM_)
12671 ExtOut("%-8s %-8s %s\n", "Child SP", "IP", "Call Site");
12672 #endif
12673
12674 int currentFrame = -1;
12675
12676 for (Status = S_OK; ; Status = pStackWalk->Next())
12677 {
12678 currentFrame++;
12679
12680 if (Status == CORDBG_S_AT_END_OF_STACK)
12681 {
12682 ExtOut("Stack walk complete.\n");
12683 break;
12684 }
12685 IfFailRet(Status);
12686
12687 if (IsInterrupt())
12688 {
12689 ExtOut("<interrupted>\n");
12690 break;
12691 }
12692
12693 CROSS_PLATFORM_CONTEXT context;
12694 ULONG32 cbContextActual;
12695 if ((Status=pStackWalk->GetContext(
12696 DT_CONTEXT_FULL,
12697 sizeof(context),
12698 &cbContextActual,
12699 (BYTE *)&context))!=S_OK)
12700 {
12701 ExtOut("GetFrameContext failed: %lx\n",Status);
12702 break;
12703 }
12704
12705 // First find the info for the Frame object, if the current frame has an associated clr!Frame.
12706 CLRDATA_ADDRESS sp = GetSP(context);
12707 CLRDATA_ADDRESS ip = GetIP(context);
12708
12709 ToRelease<ICorDebugFrame> pFrame;
12710 IfFailRet(pStackWalk->GetFrame(&pFrame));
12711 if (Status == S_FALSE)
12712 {
12713 DMLOut("%p %s [NativeStackFrame]\n", SOS_PTR(sp), DMLIP(ip));
12714 continue;
12715 }
12716
12717 // TODO: What about internal frames preceding the above native stack frame?
12718 // Should I just exclude the above native stack frame from the output?
12719 // TODO: Compare caller frame (instead of current frame) against internal frame,
12720 // to deal with issues of current frame's current SP being closer to leaf than
12721 // EE Frames it pushes. By "caller" I mean not just managed caller, but the
12722 // very next non-internal frame dbi would return (native or managed). OR...
12723 // perhaps I should use GetStackRange() instead, to see if the internal frame
12724 // appears leafier than the base-part of the range of the currently iterated
12725 // stack frame? I think I like that better.
12726 _ASSERTE(pFrame != NULL);
12727 IfFailRet(internalFrameManager.PrintPrecedingInternalFrames(pFrame));
12728
12729 // Print the stack and instruction pointers.
12730 DMLOut("%p %s ", SOS_PTR(sp), DMLIP(ip));
12731
12732 ToRelease<ICorDebugRuntimeUnwindableFrame> pRuntimeUnwindableFrame;
12733 Status = pFrame->QueryInterface(IID_ICorDebugRuntimeUnwindableFrame, (LPVOID *) &pRuntimeUnwindableFrame);
12734 if (SUCCEEDED(Status))
12735 {
12736 ExtOut("[RuntimeUnwindableFrame]\n");
12737 continue;
12738 }
12739
12740 // Print the method/Frame info
12741
12742 // TODO: IS THE FOLLOWING NECESSARY, OR AM I GUARANTEED THAT ALL INTERNAL FRAMES
12743 // CAN BE FOUND VIA GetActiveInternalFrames?
12744 ToRelease<ICorDebugInternalFrame> pInternalFrame;
12745 Status = pFrame->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame);
12746 if (SUCCEEDED(Status))
12747 {
12748 // This is a clr!Frame.
12749 LPCWSTR pwszFrameName = W("TODO: Implement GetFrameName");
12750 ExtOut("[%S: p] ", pwszFrameName);
12751 }
12752
12753 // Print the frame's associated function info, if it has any.
12754 ToRelease<ICorDebugILFrame> pILFrame;
12755 HRESULT hrILFrame = pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame);
12756
12757 if (SUCCEEDED(hrILFrame))
12758 {
12759 ToRelease<ICorDebugFunction> pFunction;
12760 Status = pFrame->GetFunction(&pFunction);
12761 if (FAILED(Status))
12762 {
12763 // We're on a JITted frame, but there's no Function for it. So it must
12764 // be...
12765 ExtOut("[IL Stub or LCG]\n");
12766 continue;
12767 }
12768
12769 ToRelease<ICorDebugClass> pClass;
12770 ToRelease<ICorDebugModule> pModule;
12771 mdMethodDef methodDef;
12772 IfFailRet(pFunction->GetClass(&pClass));
12773 IfFailRet(pFunction->GetModule(&pModule));
12774 IfFailRet(pFunction->GetToken(&methodDef));
12775
12776 WCHAR wszModuleName[100];
12777 ULONG32 cchModuleNameActual;
12778 IfFailRet(pModule->GetName(_countof(wszModuleName), &cchModuleNameActual, wszModuleName));
12779
12780 ToRelease<IUnknown> pMDUnknown;
12781 ToRelease<IMetaDataImport> pMD;
12782 ToRelease<IMDInternalImport> pMDInternal;
12783 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
12784 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
12785 IfFailRet(GetMDInternalFromImport(pMD, &pMDInternal));
12786
12787 mdTypeDef typeDef;
12788 IfFailRet(pClass->GetToken(&typeDef));
12789
12790 // Note that we don't need to pretty print the class, as class name is
12791 // already printed from GetMethodName below
12792
12793 CQuickBytes functionName;
12794 // TODO: WARNING: GetMethodName() appears to include lots of unexercised
12795 // code, as evidenced by some fundamental bugs I found. It should either be
12796 // thoroughly reviewed, or some other more exercised code path to grab the
12797 // name should be used.
12798 // TODO: If we do stay with GetMethodName, it should be updated to print
12799 // generics properly. Today, it does not show generic type parameters, and
12800 // if any arguments have a generic type, those arguments are just shown as
12801 // "__Canon", even when they're value types.
12802 GetMethodName(methodDef, pMD, &functionName);
12803
12804 DMLOut(DMLManagedVar(W("-a"), currentFrame, (LPWSTR)functionName.Ptr()));
12805 ExtOut(" (%S)\n", wszModuleName);
12806
12807 if (SUCCEEDED(hrILFrame) && (bParams || bLocals))
12808 {
12809 if(onlyShowFrame == -1 || (onlyShowFrame >= 0 && currentFrame == onlyShowFrame))
12810 IfFailRet(PrintParameters(bParams, bLocals, pMD, typeDef, methodDef, pILFrame, pModule, varToExpand, currentFrame));
12811 }
12812 }
12813 }
12814 ExtOut("=============================================================================\n");
12815
12816#ifdef FEATURE_PAL
12817 // Temporary until we get a process exit notification plumbed from lldb
12818 UninitCorDebugInterface();
12819#endif
12820 return S_OK;
12821 }
12822};
12823
12824WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj)
12825{
12826 WString res;
12827
12828 if (ref.HasRegisterInformation)
12829 {
12830 WCHAR reg[32];
12831 HRESULT hr = g_sos->GetRegisterName(ref.Register, _countof(reg), reg, NULL);
12832 if (SUCCEEDED(hr))
12833 res = reg;
12834 else
12835 res = W("<unknown register>");
12836
12837 if (ref.Offset)
12838 {
12839 int offset = ref.Offset;
12840 if (offset > 0)
12841 {
12842 res += W("+");
12843 }
12844 else
12845 {
12846 res += W("-");
12847 offset = -offset;
12848 }
12849
12850 res += Hex(offset);
12851 }
12852
12853 res += W(": ");
12854 }
12855
12856 if (ref.Address)
12857 res += WString(Pointer(ref.Address));
12858
12859 if (printObj)
12860 {
12861 if (ref.Address)
12862 res += W(" -> ");
12863
12864 res += WString(ObjectPtr(ref.Object));
12865 }
12866
12867 if (ref.Flags & SOSRefPinned)
12868 {
12869 res += W(" (pinned)");
12870 }
12871
12872 if (ref.Flags & SOSRefInterior)
12873 {
12874 res += W(" (interior)");
12875 }
12876
12877 return res;
12878}
12879
12880void PrintRef(const SOSStackRefData &ref, TableOutput &out)
12881{
12882 WString res = BuildRegisterOutput(ref);
12883
12884 if (ref.Object && (ref.Flags & SOSRefInterior) == 0)
12885 {
12886 WCHAR type[128];
12887 sos::BuildTypeWithExtraInfo(TO_TADDR(ref.Object), _countof(type), type);
12888
12889 res += WString(W(" - ")) + type;
12890 }
12891
12892 out.WriteColumn(2, res);
12893}
12894
12895
12896class ClrStackImpl
12897{
12898public:
12899 static void PrintThread(ULONG osID, BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bFull, BOOL bDisplayRegVals)
12900 {
12901 // Symbols variables
12902 ULONG symlines = 0; // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
12903 if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
12904 {
12905 symlines &= SYMOPT_LOAD_LINES;
12906 }
12907
12908 if (symlines == 0)
12909 bSuppressLines = TRUE;
12910
12911 ToRelease<IXCLRDataStackWalk> pStackWalk;
12912
12913 HRESULT hr = CreateStackWalk(osID, &pStackWalk);
12914 if (FAILED(hr) || pStackWalk == NULL)
12915 {
12916 ExtOut("Failed to start stack walk: %lx\n", hr);
12917 return;
12918 }
12919
12920#ifdef DEBUG_STACK_CONTEXT
12921 PDEBUG_STACK_FRAME currentNativeFrame = NULL;
12922 ULONG numNativeFrames = 0;
12923 if (bFull)
12924 {
12925 hr = GetContextStackTrace(osID, &numNativeFrames);
12926 if (FAILED(hr))
12927 {
12928 ExtOut("Failed to get native stack frames: %lx\n", hr);
12929 return;
12930 }
12931 currentNativeFrame = &g_Frames[0];
12932 }
12933#endif // DEBUG_STACK_CONTEXT
12934
12935 unsigned int refCount = 0, errCount = 0;
12936 ArrayHolder<SOSStackRefData> pRefs = NULL;
12937 ArrayHolder<SOSStackRefError> pErrs = NULL;
12938 if (bGC && FAILED(GetGCRefs(osID, &pRefs, &refCount, &pErrs, &errCount)))
12939 refCount = 0;
12940
12941 TableOutput out(3, POINTERSIZE_HEX, AlignRight);
12942 out.WriteRow("Child SP", "IP", "Call Site");
12943
12944 do
12945 {
12946 if (IsInterrupt())
12947 {
12948 ExtOut("<interrupted>\n");
12949 break;
12950 }
12951 CLRDATA_ADDRESS ip = 0, sp = 0;
12952 hr = GetFrameLocation(pStackWalk, &ip, &sp);
12953
12954 DacpFrameData FrameData;
12955 HRESULT frameDataResult = FrameData.Request(pStackWalk);
12956 if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
12957 sp = FrameData.frameAddr;
12958
12959#ifdef DEBUG_STACK_CONTEXT
12960 while ((numNativeFrames > 0) && (currentNativeFrame->StackOffset <= sp))
12961 {
12962 if (currentNativeFrame->StackOffset != sp)
12963 {
12964 PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines);
12965 }
12966 currentNativeFrame++;
12967 numNativeFrames--;
12968 }
12969#endif // DEBUG_STACK_CONTEXT
12970
12971 // Print the stack pointer.
12972 out.WriteColumn(0, sp);
12973
12974 // Print the method/Frame info
12975 if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
12976 {
12977 // Skip the instruction pointer because it doesn't really mean anything for method frames
12978 out.WriteColumn(1, bFull ? String("") : NativePtr(ip));
12979
12980 // This is a clr!Frame.
12981 out.WriteColumn(2, GetFrameFromAddress(TO_TADDR(FrameData.frameAddr), pStackWalk, bFull));
12982
12983 // Print out gc references for the Frame.
12984 for (unsigned int i = 0; i < refCount; ++i)
12985 if (pRefs[i].Source == sp)
12986 PrintRef(pRefs[i], out);
12987
12988 // Print out an error message if we got one.
12989 for (unsigned int i = 0; i < errCount; ++i)
12990 if (pErrs[i].Source == sp)
12991 out.WriteColumn(2, "Failed to enumerate GC references.");
12992 }
12993 else
12994 {
12995 out.WriteColumn(1, InstructionPtr(ip));
12996 out.WriteColumn(2, MethodNameFromIP(ip, bSuppressLines, bFull, bFull));
12997
12998 // Print out gc references. refCount will be zero if bGC is false (or if we
12999 // failed to fetch gc reference information).
13000 for (unsigned int i = 0; i < refCount; ++i)
13001 if (pRefs[i].Source == ip && pRefs[i].StackPointer == sp)
13002 PrintRef(pRefs[i], out);
13003
13004 // Print out an error message if we got one.
13005 for (unsigned int i = 0; i < errCount; ++i)
13006 if (pErrs[i].Source == sp)
13007 out.WriteColumn(2, "Failed to enumerate GC references.");
13008
13009 if (bParams || bLocals)
13010 PrintArgsAndLocals(pStackWalk, bParams, bLocals);
13011 }
13012
13013 if (bDisplayRegVals)
13014 PrintManagedFrameContext(pStackWalk);
13015
13016 } while (pStackWalk->Next() == S_OK);
13017
13018#ifdef DEBUG_STACK_CONTEXT
13019 while (numNativeFrames > 0)
13020 {
13021 PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines);
13022 currentNativeFrame++;
13023 numNativeFrames--;
13024 }
13025#endif // DEBUG_STACK_CONTEXT
13026 }
13027
13028 static HRESULT PrintManagedFrameContext(IXCLRDataStackWalk *pStackWalk)
13029 {
13030 CROSS_PLATFORM_CONTEXT context;
13031 HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
13032 if (FAILED(hr) || hr == S_FALSE)
13033 {
13034 // GetFrameContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us.
13035 ExtOut("GetFrameContext failed: %lx\n", hr);
13036 return E_FAIL;
13037 }
13038
13039#if defined(SOS_TARGET_AMD64)
13040 String outputFormat3 = " %3s=%016x %3s=%016x %3s=%016x\n";
13041 String outputFormat2 = " %3s=%016x %3s=%016x\n";
13042 ExtOut(outputFormat3, "rsp", context.Amd64Context.Rsp, "rbp", context.Amd64Context.Rbp, "rip", context.Amd64Context.Rip);
13043 ExtOut(outputFormat3, "rax", context.Amd64Context.Rax, "rbx", context.Amd64Context.Rbx, "rcx", context.Amd64Context.Rcx);
13044 ExtOut(outputFormat3, "rdx", context.Amd64Context.Rdx, "rsi", context.Amd64Context.Rsi, "rdi", context.Amd64Context.Rdi);
13045 ExtOut(outputFormat3, "r8", context.Amd64Context.R8, "r9", context.Amd64Context.R9, "r10", context.Amd64Context.R10);
13046 ExtOut(outputFormat3, "r11", context.Amd64Context.R11, "r12", context.Amd64Context.R12, "r13", context.Amd64Context.R13);
13047 ExtOut(outputFormat2, "r14", context.Amd64Context.R14, "r15", context.Amd64Context.R15);
13048#elif defined(SOS_TARGET_X86)
13049 String outputFormat3 = " %3s=%08x %3s=%08x %3s=%08x\n";
13050 String outputFormat2 = " %3s=%08x %3s=%08x\n";
13051 ExtOut(outputFormat3, "esp", context.X86Context.Esp, "ebp", context.X86Context.Ebp, "eip", context.X86Context.Eip);
13052 ExtOut(outputFormat3, "eax", context.X86Context.Eax, "ebx", context.X86Context.Ebx, "ecx", context.X86Context.Ecx);
13053 ExtOut(outputFormat3, "edx", context.X86Context.Edx, "esi", context.X86Context.Esi, "edi", context.X86Context.Edi);
13054#elif defined(SOS_TARGET_ARM)
13055 String outputFormat3 = " %3s=%08x %3s=%08x %3s=%08x\n";
13056 String outputFormat2 = " %s=%08x %s=%08x\n";
13057 String outputFormat1 = " %s=%08x\n";
13058 ExtOut(outputFormat3, "r0", context.ArmContext.R0, "r1", context.ArmContext.R1, "r2", context.ArmContext.R2);
13059 ExtOut(outputFormat3, "r3", context.ArmContext.R3, "r4", context.ArmContext.R4, "r5", context.ArmContext.R5);
13060 ExtOut(outputFormat3, "r6", context.ArmContext.R6, "r7", context.ArmContext.R7, "r8", context.ArmContext.R8);
13061 ExtOut(outputFormat3, "r9", context.ArmContext.R9, "r10", context.ArmContext.R10, "r11", context.ArmContext.R11);
13062 ExtOut(outputFormat1, "r12", context.ArmContext.R12);
13063 ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc);
13064 ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr);
13065#elif defined(SOS_TARGET_ARM64)
13066 String outputXRegFormat3 = " x%d=%016x x%d=%016x x%d=%016x\n";
13067 String outputXRegFormat1 = " x%d=%016x\n";
13068 String outputFormat3 = " %s=%016x %s=%016x %s=%016x\n";
13069 String outputFormat2 = " %s=%08x %s=%08x\n";
13070 DWORD64 *X = context.Arm64Context.X;
13071 for (int i = 0; i < 9; i++)
13072 {
13073 ExtOut(outputXRegFormat3, i + 0, X[i + 0], i + 1, X[i + 1], i + 2, X[i + 2]);
13074 }
13075 ExtOut(outputXRegFormat1, 28, X[28]);
13076 ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc);
13077 ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr);
13078#else
13079 ExtOut("Can't display register values for this platform\n");
13080#endif
13081 return S_OK;
13082
13083 }
13084
13085 static HRESULT GetFrameLocation(IXCLRDataStackWalk *pStackWalk, CLRDATA_ADDRESS *ip, CLRDATA_ADDRESS *sp)
13086 {
13087 CROSS_PLATFORM_CONTEXT context;
13088 HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
13089 if (FAILED(hr) || hr == S_FALSE)
13090 {
13091 // GetFrameContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us.
13092 ExtOut("GetFrameContext failed: %lx\n", hr);
13093 return E_FAIL;
13094 }
13095
13096 // First find the info for the Frame object, if the current frame has an associated clr!Frame.
13097 *ip = GetIP(context);
13098 *sp = GetSP(context);
13099
13100 if (IsDbgTargetArm())
13101 *ip = *ip & ~THUMB_CODE;
13102
13103 return S_OK;
13104 }
13105
13106 static void PrintNativeStackFrame(TableOutput out, PDEBUG_STACK_FRAME frame, BOOL bSuppressLines)
13107 {
13108 char filename[MAX_LONGPATH + 1];
13109 char symbol[1024];
13110 ULONG64 displacement;
13111
13112 ULONG64 ip = frame->InstructionOffset;
13113
13114 out.WriteColumn(0, frame->StackOffset);
13115 out.WriteColumn(1, NativePtr(ip));
13116
13117 HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, _countof(symbol), NULL, &displacement);
13118 if (SUCCEEDED(hr) && symbol[0] != '\0')
13119 {
13120 String frameOutput;
13121 frameOutput += symbol;
13122
13123 if (displacement)
13124 {
13125 frameOutput += " + ";
13126 frameOutput += Decimal(displacement);
13127 }
13128
13129 if (!bSuppressLines)
13130 {
13131 ULONG line;
13132 hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, _countof(filename), NULL, NULL);
13133 if (SUCCEEDED(hr))
13134 {
13135 frameOutput += " at ";
13136 frameOutput += filename;
13137 frameOutput += ":";
13138 frameOutput += Decimal(line);
13139 }
13140 }
13141
13142 out.WriteColumn(2, frameOutput);
13143 }
13144 else
13145 {
13146 out.WriteColumn(2, "");
13147 }
13148 }
13149
13150 static void PrintCurrentThread(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals)
13151 {
13152 ULONG id = 0;
13153 ULONG osid = 0;
13154
13155 g_ExtSystem->GetCurrentThreadSystemId(&osid);
13156 ExtOut("OS Thread Id: 0x%x ", osid);
13157 g_ExtSystem->GetCurrentThreadId(&id);
13158 ExtOut("(%d)\n", id);
13159
13160 PrintThread(osid, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
13161 }
13162
13163 static void PrintAllThreads(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals)
13164 {
13165 HRESULT Status;
13166
13167 DacpThreadStoreData ThreadStore;
13168 if ((Status = ThreadStore.Request(g_sos)) != S_OK)
13169 {
13170 ExtErr("Failed to request ThreadStore\n");
13171 return;
13172 }
13173
13174 DacpThreadData Thread;
13175 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
13176 while (CurThread != 0)
13177 {
13178 if (IsInterrupt())
13179 break;
13180
13181 if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
13182 {
13183 ExtErr("Failed to request thread at %p\n", CurThread);
13184 return;
13185 }
13186 ExtOut("OS Thread Id: 0x%x\n", Thread.osThreadId);
13187 PrintThread(Thread.osThreadId, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
13188 CurThread = Thread.nextThread;
13189 }
13190 }
13191
13192private:
13193 static HRESULT CreateStackWalk(ULONG osID, IXCLRDataStackWalk **ppStackwalk)
13194 {
13195 HRESULT hr = S_OK;
13196 ToRelease<IXCLRDataTask> pTask;
13197
13198 if ((hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK)
13199 {
13200 ExtOut("Unable to walk the managed stack. The current thread is likely not a \n");
13201 ExtOut("managed thread. You can run " SOSThreads " to get a list of managed threads in\n");
13202 ExtOut("the process\n");
13203 return hr;
13204 }
13205
13206 return pTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
13207 CLRDATA_SIMPFRAME_MANAGED_METHOD |
13208 CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
13209 CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
13210 ppStackwalk);
13211 }
13212
13213 /* Prints the args and locals of for a thread's stack.
13214 * Params:
13215 * pStackWalk - the stack we are printing
13216 * bArgs - whether to print args
13217 * bLocals - whether to print locals
13218 */
13219 static void PrintArgsAndLocals(IXCLRDataStackWalk *pStackWalk, BOOL bArgs, BOOL bLocals)
13220 {
13221 ToRelease<IXCLRDataFrame> pFrame;
13222 ToRelease<IXCLRDataValue> pVal;
13223 ULONG32 argCount = 0;
13224 ULONG32 localCount = 0;
13225 HRESULT hr = S_OK;
13226
13227 hr = pStackWalk->GetFrame(&pFrame);
13228
13229 // Print arguments
13230 if (SUCCEEDED(hr) && bArgs)
13231 hr = pFrame->GetNumArguments(&argCount);
13232
13233 if (SUCCEEDED(hr) && bArgs)
13234 hr = ShowArgs(argCount, pFrame, pVal);
13235
13236 // Print locals
13237 if (SUCCEEDED(hr) && bLocals)
13238 hr = pFrame->GetNumLocalVariables(&localCount);
13239
13240 if (SUCCEEDED(hr) && bLocals)
13241 ShowLocals(localCount, pFrame, pVal);
13242
13243 ExtOut("\n");
13244 }
13245
13246
13247
13248 /* Displays the arguments to a function
13249 * Params:
13250 * argy - the number of arguments the function has
13251 * pFramey - the frame we are inspecting
13252 * pVal - a pointer to the CLRDataValue we use to query for info about the args
13253 */
13254 static HRESULT ShowArgs(ULONG32 argy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
13255 {
13256 CLRDATA_ADDRESS addr = 0;
13257 BOOL fPrintedLocation = FALSE;
13258 ULONG64 outVar = 0;
13259 ULONG32 tmp;
13260 HRESULT hr = S_OK;
13261
13262 ArrayHolder<WCHAR> argName = new NOTHROW WCHAR[mdNameLen];
13263 if (!argName)
13264 {
13265 ReportOOM();
13266 return E_FAIL;
13267 }
13268
13269 for (ULONG32 i=0; i < argy; i++)
13270 {
13271 if (i == 0)
13272 {
13273 ExtOut(" PARAMETERS:\n");
13274 }
13275
13276 hr = pFramey->GetArgumentByIndex(i,
13277 &pVal,
13278 mdNameLen,
13279 &tmp,
13280 argName);
13281
13282 if (FAILED(hr))
13283 return hr;
13284
13285 ExtOut(" ");
13286
13287 if (argName[0] != L'\0')
13288 {
13289 ExtOut("%S ", argName.GetPtr());
13290 }
13291
13292 // At times we cannot print the value of a parameter (most
13293 // common case being a non-primitive value type). In these
13294 // cases we need to print the location of the parameter,
13295 // so that we can later examine it (e.g. using !dumpvc)
13296 {
13297 bool result = SUCCEEDED(pVal->GetNumLocations(&tmp)) && tmp == 1;
13298 if (result)
13299 result = SUCCEEDED(pVal->GetLocationByIndex(0, &tmp, &addr));
13300
13301 if (result)
13302 {
13303 if (tmp == CLRDATA_VLOC_REGISTER)
13304 {
13305 ExtOut("(<CLR reg>) ");
13306 }
13307 else
13308 {
13309 ExtOut("(0x%p) ", SOS_PTR(CDA_TO_UL64(addr)));
13310 }
13311 fPrintedLocation = TRUE;
13312 }
13313 }
13314
13315 if (argName[0] != L'\0' || fPrintedLocation)
13316 {
13317 ExtOut("= ");
13318 }
13319
13320 if (HRESULT_CODE(pVal->GetBytes(0,&tmp,NULL)) == ERROR_BUFFER_OVERFLOW)
13321 {
13322 ArrayHolder<BYTE> pByte = new NOTHROW BYTE[tmp + 1];
13323 if (pByte == NULL)
13324 {
13325 ReportOOM();
13326 return E_FAIL;
13327 }
13328
13329 hr = pVal->GetBytes(tmp, &tmp, pByte);
13330
13331 if (FAILED(hr))
13332 {
13333 ExtOut("<unable to retrieve data>\n");
13334 }
13335 else
13336 {
13337 switch(tmp)
13338 {
13339 case 1: outVar = *((BYTE *)pByte.GetPtr()); break;
13340 case 2: outVar = *((short *)pByte.GetPtr()); break;
13341 case 4: outVar = *((DWORD *)pByte.GetPtr()); break;
13342 case 8: outVar = *((ULONG64 *)pByte.GetPtr()); break;
13343 default: outVar = 0;
13344 }
13345
13346 if (outVar)
13347 DMLOut("0x%s\n", DMLObject(outVar));
13348 else
13349 ExtOut("0x%p\n", SOS_PTR(outVar));
13350 }
13351
13352 }
13353 else
13354 {
13355 ExtOut("<no data>\n");
13356 }
13357
13358 pVal->Release();
13359 }
13360
13361 return S_OK;
13362 }
13363
13364
13365 /* Prints the locals of a frame.
13366 * Params:
13367 * localy - the number of locals in the frame
13368 * pFramey - the frame we are inspecting
13369 * pVal - a pointer to the CLRDataValue we use to query for info about the args
13370 */
13371 static HRESULT ShowLocals(ULONG32 localy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
13372 {
13373 for (ULONG32 i=0; i < localy; i++)
13374 {
13375 if (i == 0)
13376 ExtOut(" LOCALS:\n");
13377
13378 HRESULT hr;
13379 ExtOut(" ");
13380
13381 // local names don't work in Whidbey.
13382 hr = pFramey->GetLocalVariableByIndex(i, &pVal, mdNameLen, NULL, g_mdName);
13383 if (FAILED(hr))
13384 {
13385 return hr;
13386 }
13387
13388 ULONG32 numLocations;
13389 if (SUCCEEDED(pVal->GetNumLocations(&numLocations)) &&
13390 numLocations == 1)
13391 {
13392 ULONG32 flags;
13393 CLRDATA_ADDRESS addr;
13394 if (SUCCEEDED(pVal->GetLocationByIndex(0, &flags, &addr)))
13395 {
13396 if (flags == CLRDATA_VLOC_REGISTER)
13397 {
13398 ExtOut("<CLR reg> ");
13399 }
13400 else
13401 {
13402 ExtOut("0x%p ", SOS_PTR(CDA_TO_UL64(addr)));
13403 }
13404 }
13405
13406 // Can I get a name for the item?
13407
13408 ExtOut("= ");
13409 }
13410 ULONG32 dwSize = 0;
13411 hr = pVal->GetBytes(0, &dwSize, NULL);
13412
13413 if (HRESULT_CODE(hr) == ERROR_BUFFER_OVERFLOW)
13414 {
13415 ArrayHolder<BYTE> pByte = new NOTHROW BYTE[dwSize + 1];
13416 if (pByte == NULL)
13417 {
13418 ReportOOM();
13419 return E_FAIL;
13420 }
13421
13422 hr = pVal->GetBytes(dwSize,&dwSize,pByte);
13423
13424 if (FAILED(hr))
13425 {
13426 ExtOut("<unable to retrieve data>\n");
13427 }
13428 else
13429 {
13430 ULONG64 outVar = 0;
13431 switch(dwSize)
13432 {
13433 case 1: outVar = *((BYTE *) pByte.GetPtr()); break;
13434 case 2: outVar = *((short *) pByte.GetPtr()); break;
13435 case 4: outVar = *((DWORD *) pByte.GetPtr()); break;
13436 case 8: outVar = *((ULONG64 *) pByte.GetPtr()); break;
13437 default: outVar = 0;
13438 }
13439
13440 if (outVar)
13441 DMLOut("0x%s\n", DMLObject(outVar));
13442 else
13443 ExtOut("0x%p\n", SOS_PTR(outVar));
13444 }
13445 }
13446 else
13447 {
13448 ExtOut("<no data>\n");
13449 }
13450
13451 pVal->Release();
13452 }
13453
13454 return S_OK;
13455 }
13456
13457};
13458
13459#ifndef FEATURE_PAL
13460
13461WatchCmd g_watchCmd;
13462
13463// The grand new !Watch command, private to Apollo for now
13464DECLARE_API(Watch)
13465{
13466 INIT_API_NOEE();
13467 BOOL bExpression = FALSE;
13468 StringHolder addExpression;
13469 StringHolder aExpression;
13470 StringHolder saveName;
13471 StringHolder sName;
13472 StringHolder expression;
13473 StringHolder filterName;
13474 StringHolder renameOldName;
13475 size_t expandIndex = -1;
13476 size_t removeIndex = -1;
13477 BOOL clear = FALSE;
13478
13479 size_t nArg = 0;
13480 CMDOption option[] =
13481 { // name, vptr, type, hasValue
13482 {"-add", &addExpression.data, COSTRING, TRUE},
13483 {"-a", &aExpression.data, COSTRING, TRUE},
13484 {"-save", &saveName.data, COSTRING, TRUE},
13485 {"-s", &sName.data, COSTRING, TRUE},
13486 {"-clear", &clear, COBOOL, FALSE},
13487 {"-c", &clear, COBOOL, FALSE},
13488 {"-expand", &expandIndex, COSIZE_T, TRUE},
13489 {"-filter", &filterName.data, COSTRING, TRUE},
13490 {"-r", &removeIndex, COSIZE_T, TRUE},
13491 {"-remove", &removeIndex, COSIZE_T, TRUE},
13492 {"-rename", &renameOldName.data, COSTRING, TRUE},
13493 };
13494
13495 CMDValue arg[] =
13496 { // vptr, type
13497 {&expression.data, COSTRING}
13498 };
13499 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
13500 {
13501 return Status;
13502 }
13503
13504 if(addExpression.data != NULL || aExpression.data != NULL)
13505 {
13506 WCHAR pAddExpression[MAX_EXPRESSION];
13507 swprintf_s(pAddExpression, MAX_EXPRESSION, W("%S"), addExpression.data != NULL ? addExpression.data : aExpression.data);
13508 Status = g_watchCmd.Add(pAddExpression);
13509 }
13510 else if(removeIndex != -1)
13511 {
13512 if(removeIndex <= 0)
13513 {
13514 ExtOut("Index must be a postive decimal number\n");
13515 }
13516 else
13517 {
13518 Status = g_watchCmd.Remove((int)removeIndex);
13519 if(Status == S_OK)
13520 ExtOut("Watch expression #%d has been removed\n", removeIndex);
13521 else if(Status == S_FALSE)
13522 ExtOut("There is no watch expression with index %d\n", removeIndex);
13523 else
13524 ExtOut("Unknown failure 0x%x removing watch expression\n", Status);
13525 }
13526 }
13527 else if(saveName.data != NULL || sName.data != NULL)
13528 {
13529 WCHAR pSaveName[MAX_EXPRESSION];
13530 swprintf_s(pSaveName, MAX_EXPRESSION, W("%S"), saveName.data != NULL ? saveName.data : sName.data);
13531 Status = g_watchCmd.SaveList(pSaveName);
13532 }
13533 else if(clear)
13534 {
13535 g_watchCmd.Clear();
13536 }
13537 else if(renameOldName.data != NULL)
13538 {
13539 if(nArg != 1)
13540 {
13541 ExtOut("Must provide an old and new name. Usage: !watch -rename <old_name> <new_name>.\n");
13542 return S_FALSE;
13543 }
13544 WCHAR pOldName[MAX_EXPRESSION];
13545 swprintf_s(pOldName, MAX_EXPRESSION, W("%S"), renameOldName.data);
13546 WCHAR pNewName[MAX_EXPRESSION];
13547 swprintf_s(pNewName, MAX_EXPRESSION, W("%S"), expression.data);
13548 g_watchCmd.RenameList(pOldName, pNewName);
13549 }
13550 // print the tree, possibly with filtering and/or expansion
13551 else if(expandIndex != -1 || expression.data == NULL)
13552 {
13553 WCHAR pExpression[MAX_EXPRESSION];
13554 pExpression[0] = '\0';
13555
13556 if(expandIndex != -1)
13557 {
13558 if(expression.data != NULL)
13559 {
13560 swprintf_s(pExpression, MAX_EXPRESSION, W("%S"), expression.data);
13561 }
13562 else
13563 {
13564 ExtOut("No expression was provided. Usage !watch -expand <index> <expression>\n");
13565 return S_FALSE;
13566 }
13567 }
13568 WCHAR pFilterName[MAX_EXPRESSION];
13569 pFilterName[0] = '\0';
13570
13571 if(filterName.data != NULL)
13572 {
13573 swprintf_s(pFilterName, MAX_EXPRESSION, W("%S"), filterName.data);
13574 }
13575
13576 g_watchCmd.Print((int)expandIndex, pExpression, pFilterName);
13577 }
13578 else
13579 {
13580 ExtOut("Unrecognized argument: %s\n", expression.data);
13581 }
13582
13583 return Status;
13584}
13585
13586#endif // FEATURE_PAL
13587
13588DECLARE_API(ClrStack)
13589{
13590 INIT_API();
13591
13592 BOOL bAll = FALSE;
13593 BOOL bParams = FALSE;
13594 BOOL bLocals = FALSE;
13595 BOOL bSuppressLines = FALSE;
13596 BOOL bICorDebug = FALSE;
13597 BOOL bGC = FALSE;
13598 BOOL dml = FALSE;
13599 BOOL bFull = FALSE;
13600 BOOL bDisplayRegVals = FALSE;
13601 BOOL bAllThreads = FALSE;
13602 DWORD frameToDumpVariablesFor = -1;
13603 StringHolder cvariableName;
13604 ArrayHolder<WCHAR> wvariableName = new NOTHROW WCHAR[mdNameLen];
13605 if (wvariableName == NULL)
13606 {
13607 ReportOOM();
13608 return E_OUTOFMEMORY;
13609 }
13610
13611 memset(wvariableName, 0, sizeof(wvariableName));
13612
13613 size_t nArg = 0;
13614 CMDOption option[] =
13615 { // name, vptr, type, hasValue
13616 {"-a", &bAll, COBOOL, FALSE},
13617 {"-all", &bAllThreads, COBOOL, FALSE},
13618 {"-p", &bParams, COBOOL, FALSE},
13619 {"-l", &bLocals, COBOOL, FALSE},
13620 {"-n", &bSuppressLines, COBOOL, FALSE},
13621 {"-i", &bICorDebug, COBOOL, FALSE},
13622 {"-gc", &bGC, COBOOL, FALSE},
13623 {"-f", &bFull, COBOOL, FALSE},
13624 {"-r", &bDisplayRegVals, COBOOL, FALSE },
13625#ifndef FEATURE_PAL
13626 {"/d", &dml, COBOOL, FALSE},
13627#endif
13628 };
13629 CMDValue arg[] =
13630 { // vptr, type
13631 {&cvariableName.data, COSTRING},
13632 {&frameToDumpVariablesFor, COSIZE_T},
13633 };
13634 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
13635 {
13636 return Status;
13637 }
13638
13639 EnableDMLHolder dmlHolder(dml);
13640 if (bAll || bParams || bLocals)
13641 {
13642 // No parameter or local supports for minidump case!
13643 MINIDUMP_NOT_SUPPORTED();
13644 }
13645
13646 if (bAll)
13647 {
13648 bParams = bLocals = TRUE;
13649 }
13650
13651 if (bICorDebug)
13652 {
13653 if(nArg > 0)
13654 {
13655 bool firstParamIsNumber = true;
13656 for(DWORD i = 0; i < strlen(cvariableName.data); i++)
13657 firstParamIsNumber = firstParamIsNumber && isdigit(cvariableName.data[i]);
13658
13659 if(firstParamIsNumber && nArg == 1)
13660 {
13661 frameToDumpVariablesFor = (DWORD)GetExpression(cvariableName.data);
13662 cvariableName.data[0] = '\0';
13663 }
13664 }
13665 if(cvariableName.data != NULL && strlen(cvariableName.data) > 0)
13666 swprintf_s(wvariableName, mdNameLen, W("%S\0"), cvariableName.data);
13667
13668 if(_wcslen(wvariableName) > 0)
13669 bParams = bLocals = TRUE;
13670
13671 EnableDMLHolder dmlHolder(TRUE);
13672 return ClrStackImplWithICorDebug::ClrStackFromPublicInterface(bParams, bLocals, FALSE, wvariableName, frameToDumpVariablesFor);
13673 }
13674
13675 if (bAllThreads) {
13676 ClrStackImpl::PrintAllThreads(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
13677 }
13678 else {
13679 ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
13680 }
13681
13682 return S_OK;
13683}
13684
13685#ifndef FEATURE_PAL
13686
13687BOOL IsMemoryInfoAvailable()
13688{
13689 ULONG Class;
13690 ULONG Qualifier;
13691 g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
13692 if (Qualifier == DEBUG_DUMP_SMALL)
13693 {
13694 g_ExtControl->GetDumpFormatFlags(&Qualifier);
13695 if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0)
13696 {
13697 if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY_INFO) == 0)
13698 {
13699 return FALSE;
13700 }
13701 }
13702 }
13703 return TRUE;
13704}
13705
13706DECLARE_API( VMMap )
13707{
13708 INIT_API();
13709
13710 if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
13711 {
13712 ExtOut("!VMMap requires a full memory dump (.dump /ma) or a live process.\n");
13713 }
13714 else
13715 {
13716 vmmap();
13717 }
13718
13719 return Status;
13720} // DECLARE_API( vmmap )
13721
13722DECLARE_API( SOSFlush )
13723{
13724 INIT_API();
13725
13726 g_clrData->Flush();
13727
13728 return Status;
13729} // DECLARE_API( SOSFlush )
13730
13731DECLARE_API( VMStat )
13732{
13733 INIT_API();
13734
13735 if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
13736 {
13737 ExtOut("!VMStat requires a full memory dump (.dump /ma) or a live process.\n");
13738 }
13739 else
13740 {
13741 vmstat();
13742 }
13743
13744 return Status;
13745} // DECLARE_API( vmmap )
13746
13747/**********************************************************************\
13748* Routine Description: *
13749* *
13750* This function saves a dll to a file. *
13751* *
13752\**********************************************************************/
13753DECLARE_API(SaveModule)
13754{
13755 INIT_API();
13756 MINIDUMP_NOT_SUPPORTED();
13757
13758 StringHolder Location;
13759 DWORD_PTR moduleAddr = NULL;
13760 BOOL bIsImage;
13761
13762 CMDValue arg[] =
13763 { // vptr, type
13764 {&moduleAddr, COHEX},
13765 {&Location.data, COSTRING}
13766 };
13767 size_t nArg;
13768 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
13769 {
13770 return Status;
13771 }
13772 if (nArg != 2)
13773 {
13774 ExtOut("Usage: SaveModule <address> <file to save>\n");
13775 return Status;
13776 }
13777 if (moduleAddr == 0) {
13778 ExtOut ("Invalid arg\n");
13779 return Status;
13780 }
13781
13782 char* ptr = Location.data;
13783
13784 DWORD_PTR dllBase = 0;
13785 ULONG64 base;
13786 if (g_ExtSymbols->GetModuleByOffset(TO_CDADDR(moduleAddr),0,NULL,&base) == S_OK)
13787 {
13788 dllBase = TO_TADDR(base);
13789 }
13790 else if (IsModule(moduleAddr))
13791 {
13792 DacpModuleData module;
13793 module.Request(g_sos, TO_CDADDR(moduleAddr));
13794 dllBase = TO_TADDR(module.ilBase);
13795 if (dllBase == 0)
13796 {
13797 ExtOut ("Module does not have base address\n");
13798 return Status;
13799 }
13800 }
13801 else
13802 {
13803 ExtOut ("%p is not a Module or base address\n", SOS_PTR(moduleAddr));
13804 return Status;
13805 }
13806
13807 MEMORY_BASIC_INFORMATION64 mbi;
13808 if (FAILED(g_ExtData2->QueryVirtual(TO_CDADDR(dllBase), &mbi)))
13809 {
13810 ExtOut("Failed to retrieve information about segment %p", SOS_PTR(dllBase));
13811 return Status;
13812 }
13813
13814 // module loaded as an image or mapped as a flat file?
13815 bIsImage = (mbi.Type == MEM_IMAGE);
13816
13817 IMAGE_DOS_HEADER DosHeader;
13818 if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
13819 return S_FALSE;
13820
13821 IMAGE_NT_HEADERS Header;
13822 if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK)
13823 return S_FALSE;
13824
13825 DWORD_PTR sectionAddr = dllBase + DosHeader.e_lfanew + offsetof(IMAGE_NT_HEADERS,OptionalHeader)
13826 + Header.FileHeader.SizeOfOptionalHeader;
13827
13828 IMAGE_SECTION_HEADER section;
13829 struct MemLocation
13830 {
13831 DWORD_PTR VAAddr;
13832 DWORD_PTR VASize;
13833 DWORD_PTR FileAddr;
13834 DWORD_PTR FileSize;
13835 };
13836
13837 int nSection = Header.FileHeader.NumberOfSections;
13838 ExtOut("%u sections in file\n",nSection);
13839 MemLocation *memLoc = (MemLocation*)_alloca(nSection*sizeof(MemLocation));
13840 int indxSec = -1;
13841 int slot;
13842 for (int n = 0; n < nSection; n++)
13843 {
13844 if (g_ExtData->ReadVirtual(TO_CDADDR(sectionAddr), &section, sizeof(section), NULL) == S_OK)
13845 {
13846 for (slot = 0; slot <= indxSec; slot ++)
13847 if (section.PointerToRawData < memLoc[slot].FileAddr)
13848 break;
13849
13850 for (int k = indxSec; k >= slot; k --)
13851 memcpy(&memLoc[k+1], &memLoc[k], sizeof(MemLocation));
13852
13853 memLoc[slot].VAAddr = section.VirtualAddress;
13854 memLoc[slot].VASize = section.Misc.VirtualSize;
13855 memLoc[slot].FileAddr = section.PointerToRawData;
13856 memLoc[slot].FileSize = section.SizeOfRawData;
13857 ExtOut("section %d - VA=%x, VASize=%x, FileAddr=%x, FileSize=%x\n",
13858 n, memLoc[slot].VAAddr,memLoc[slot]. VASize,memLoc[slot].FileAddr,
13859 memLoc[slot].FileSize);
13860 indxSec ++;
13861 }
13862 else
13863 {
13864 ExtOut("Fail to read PE section info\n");
13865 return Status;
13866 }
13867 sectionAddr += sizeof(section);
13868 }
13869
13870 if (ptr[0] == '\0')
13871 {
13872 ExtOut ("File not specified\n");
13873 return Status;
13874 }
13875
13876 PCSTR file = ptr;
13877 ptr += strlen(ptr)-1;
13878 while (isspace(*ptr))
13879 {
13880 *ptr = '\0';
13881 ptr --;
13882 }
13883
13884 HANDLE hFile = CreateFileA(file,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
13885 if (hFile == INVALID_HANDLE_VALUE)
13886 {
13887 ExtOut ("Fail to create file %s\n", file);
13888 return Status;
13889 }
13890
13891 ULONG pageSize = OSPageSize();
13892 char *buffer = (char *)_alloca(pageSize);
13893 DWORD nRead;
13894 DWORD nWrite;
13895
13896 // NT PE Headers
13897 TADDR dwAddr = dllBase;
13898 TADDR dwEnd = dllBase + Header.OptionalHeader.SizeOfHeaders;
13899 while (dwAddr < dwEnd)
13900 {
13901 nRead = pageSize;
13902 if (dwEnd - dwAddr < nRead)
13903 nRead = (ULONG)(dwEnd - dwAddr);
13904
13905 if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
13906 {
13907 WriteFile(hFile,buffer,nRead,&nWrite,NULL);
13908 }
13909 else
13910 {
13911 ExtOut ("Fail to read memory\n");
13912 goto end;
13913 }
13914 dwAddr += nRead;
13915 }
13916
13917 for (slot = 0; slot <= indxSec; slot ++)
13918 {
13919 dwAddr = dllBase + (bIsImage ? memLoc[slot].VAAddr : memLoc[slot].FileAddr);
13920 dwEnd = memLoc[slot].FileSize + dwAddr - 1;
13921
13922 while (dwAddr <= dwEnd)
13923 {
13924 nRead = pageSize;
13925 if (dwEnd - dwAddr + 1 < pageSize)
13926 nRead = (ULONG)(dwEnd - dwAddr + 1);
13927
13928 if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
13929 {
13930 WriteFile(hFile,buffer,nRead,&nWrite,NULL);
13931 }
13932 else
13933 {
13934 ExtOut ("Fail to read memory\n");
13935 goto end;
13936 }
13937 dwAddr += pageSize;
13938 }
13939 }
13940end:
13941 CloseHandle (hFile);
13942 return Status;
13943}
13944
13945#ifdef _DEBUG
13946DECLARE_API(dbgout)
13947{
13948 INIT_API();
13949
13950 BOOL bOff = FALSE;
13951
13952 CMDOption option[] =
13953 { // name, vptr, type, hasValue
13954 {"-off", &bOff, COBOOL, FALSE},
13955 };
13956
13957 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
13958 {
13959 return Status;
13960 }
13961
13962 Output::SetDebugOutputEnabled(!bOff);
13963 return Status;
13964}
13965DECLARE_API(filthint)
13966{
13967 INIT_API();
13968
13969 BOOL bOff = FALSE;
13970 DWORD_PTR filter = 0;
13971
13972 CMDOption option[] =
13973 { // name, vptr, type, hasValue
13974 {"-off", &bOff, COBOOL, FALSE},
13975 };
13976 CMDValue arg[] =
13977 { // vptr, type
13978 {&filter, COHEX}
13979 };
13980 size_t nArg;
13981 if (!GetCMDOption(args, option, _countof(option),
13982 arg, _countof(arg), &nArg))
13983 {
13984 return Status;
13985 }
13986 if (bOff)
13987 {
13988 g_filterHint = 0;
13989 return Status;
13990 }
13991
13992 g_filterHint = filter;
13993 return Status;
13994}
13995#endif // _DEBUG
13996
13997#endif // FEATURE_PAL
13998
13999static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp,
14000 ULONG64 IPAddr, StringOutput& so)
14001{
14002#define DOAPPEND(str) \
14003 do { \
14004 if (!so.Append((str))) { \
14005 return E_OUTOFMEMORY; \
14006 }} while (0)
14007
14008 // Should we skip explicit frames? They are characterized by Esp = 0, && Eip = 0 or 1.
14009 // See comment in FormatGeneratedException() for explanation why on non_IA64 Eip is 1, and not 0
14010 if (!(Flags & SOS_STACKTRACE_SHOWEXPLICITFRAMES) && (Esp == 0) && (IPAddr == 1))
14011 {
14012 return S_FALSE;
14013 }
14014
14015 DacpMethodDescData MethodDescData;
14016 if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
14017 {
14018 return E_FAIL;
14019 }
14020
14021 ArrayHolder<WCHAR> wszNameBuffer = new WCHAR[MAX_LONGPATH+1];
14022
14023 if (Flags & SOS_STACKTRACE_SHOWADDRESSES)
14024 {
14025 _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("%p %p "), (void*)(size_t) Esp, (void*)(size_t) IPAddr); // _TRUNCATE
14026 DOAPPEND(wszNameBuffer);
14027 }
14028
14029 DacpModuleData dmd;
14030 BOOL bModuleNameWorked = FALSE;
14031 ULONG64 addrInModule = IPAddr;
14032 if (dmd.Request(g_sos, MethodDescData.ModulePtr) == S_OK)
14033 {
14034 CLRDATA_ADDRESS base = 0;
14035 if (g_sos->GetPEFileBase(dmd.File, &base) == S_OK)
14036 {
14037 if (base)
14038 {
14039 addrInModule = base;
14040 }
14041 }
14042 }
14043 ULONG Index;
14044 ULONG64 base;
14045 if (g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index, &base) == S_OK)
14046 {
14047 ArrayHolder<char> szModuleName = new char[MAX_LONGPATH+1];
14048 if (g_ExtSymbols->GetModuleNames(Index, base, NULL, 0, NULL, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL) == S_OK)
14049 {
14050 MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, wszNameBuffer, MAX_LONGPATH);
14051 DOAPPEND (wszNameBuffer);
14052 bModuleNameWorked = TRUE;
14053 }
14054 }
14055#ifdef FEATURE_PAL
14056 else
14057 {
14058 if (g_sos->GetPEFileName(dmd.File, MAX_LONGPATH, wszNameBuffer, NULL) == S_OK)
14059 {
14060 if (wszNameBuffer[0] != W('\0'))
14061 {
14062 WCHAR *pJustName = _wcsrchr(wszNameBuffer, DIRECTORY_SEPARATOR_CHAR_W);
14063 if (pJustName == NULL)
14064 pJustName = wszNameBuffer - 1;
14065
14066 DOAPPEND(pJustName + 1);
14067 bModuleNameWorked = TRUE;
14068 }
14069 }
14070 }
14071#endif // FEATURE_PAL
14072
14073 // Under certain circumstances DacpMethodDescData::GetMethodDescName()
14074 // returns a module qualified method name
14075 HRESULT hr = g_sos->GetMethodDescName(dwStartAddr, MAX_LONGPATH, wszNameBuffer, NULL);
14076
14077 WCHAR* pwszMethNameBegin = (hr != S_OK ? NULL : _wcschr(wszNameBuffer, L'!'));
14078 if (!bModuleNameWorked && hr == S_OK && pwszMethNameBegin != NULL)
14079 {
14080 // if we weren't able to get the module name, but GetMethodDescName returned
14081 // the module as part of the returned method name, use this data
14082 DOAPPEND(wszNameBuffer);
14083 }
14084 else
14085 {
14086 if (!bModuleNameWorked)
14087 {
14088 DOAPPEND (W("UNKNOWN"));
14089 }
14090 DOAPPEND(W("!"));
14091 if (hr == S_OK)
14092 {
14093 // the module name we retrieved above from debugger will take
14094 // precedence over the name possibly returned by GetMethodDescName()
14095 DOAPPEND(pwszMethNameBegin != NULL ? (pwszMethNameBegin+1) : (WCHAR *)wszNameBuffer);
14096 }
14097 else
14098 {
14099 DOAPPEND(W("UNKNOWN"));
14100 }
14101 }
14102
14103 ULONG64 Displacement = (IPAddr - MethodDescData.NativeCodeAddr);
14104 if (Displacement)
14105 {
14106 _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("+%#x"), Displacement); // _TRUNCATE
14107 DOAPPEND (wszNameBuffer);
14108 }
14109
14110 return S_OK;
14111#undef DOAPPEND
14112}
14113
14114BOOL AppendContext(LPVOID pTransitionContexts, size_t maxCount, size_t *pcurCount, size_t uiSizeOfContext,
14115 CROSS_PLATFORM_CONTEXT *context)
14116{
14117 if (pTransitionContexts == NULL || *pcurCount >= maxCount)
14118 {
14119 ++(*pcurCount);
14120 return FALSE;
14121 }
14122 if (uiSizeOfContext == sizeof(StackTrace_SimpleContext))
14123 {
14124 StackTrace_SimpleContext *pSimple = (StackTrace_SimpleContext *) pTransitionContexts;
14125 g_targetMachine->FillSimpleContext(&pSimple[*pcurCount], context);
14126 }
14127 else if (uiSizeOfContext == g_targetMachine->GetContextSize())
14128 {
14129 // FillTargetContext ensures we only write uiSizeOfContext bytes in pTransitionContexts
14130 // and not sizeof(CROSS_PLATFORM_CONTEXT) bytes (which would overrun).
14131 g_targetMachine->FillTargetContext(pTransitionContexts, context, (int)(*pcurCount));
14132 }
14133 else
14134 {
14135 return FALSE;
14136 }
14137 ++(*pcurCount);
14138 return TRUE;
14139}
14140
14141HRESULT CALLBACK ImplementEFNStackTrace(
14142 PDEBUG_CLIENT client,
14143 __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
14144 size_t *puiTextLength,
14145 LPVOID pTransitionContexts,
14146 size_t *puiTransitionContextCount,
14147 size_t uiSizeOfContext,
14148 DWORD Flags)
14149{
14150
14151#define DOAPPEND(str) if (!so.Append((str))) { \
14152 Status = E_OUTOFMEMORY; \
14153 goto Exit; \
14154}
14155
14156 HRESULT Status = E_FAIL;
14157 StringOutput so;
14158 size_t transitionContextCount = 0;
14159
14160 if (puiTextLength == NULL)
14161 {
14162 return E_INVALIDARG;
14163 }
14164
14165 if (pTransitionContexts)
14166 {
14167 if (puiTransitionContextCount == NULL)
14168 {
14169 return E_INVALIDARG;
14170 }
14171
14172 // Do error checking on context size
14173 if ((uiSizeOfContext != g_targetMachine->GetContextSize()) &&
14174 (uiSizeOfContext != sizeof(StackTrace_SimpleContext)))
14175 {
14176 return E_INVALIDARG;
14177 }
14178 }
14179
14180 IXCLRDataStackWalk *pStackWalk = NULL;
14181 IXCLRDataTask* Task;
14182 ULONG ThreadId;
14183
14184 if ((Status = g_ExtSystem->GetCurrentThreadSystemId(&ThreadId)) != S_OK ||
14185 (Status = g_clrData->GetTaskByOSThreadID(ThreadId, &Task)) != S_OK)
14186 {
14187 // Not a managed thread.
14188 return SOS_E_NOMANAGEDCODE;
14189 }
14190
14191 Status = Task->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
14192 CLRDATA_SIMPFRAME_MANAGED_METHOD |
14193 CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
14194 CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
14195 &pStackWalk);
14196
14197 Task->Release();
14198
14199 if (Status != S_OK)
14200 {
14201 if (Status == E_FAIL)
14202 {
14203 return SOS_E_NOMANAGEDCODE;
14204 }
14205 return Status;
14206 }
14207
14208#ifdef _TARGET_WIN64_
14209 ULONG numFrames = 0;
14210 BOOL bInNative = TRUE;
14211
14212 Status = GetContextStackTrace(ThreadId, &numFrames);
14213 if (FAILED(Status))
14214 {
14215 goto Exit;
14216 }
14217
14218 for (ULONG i = 0; i < numFrames; i++)
14219 {
14220 PDEBUG_STACK_FRAME pCur = g_Frames + i;
14221
14222 CLRDATA_ADDRESS pMD;
14223 if (g_sos->GetMethodDescPtrFromIP(pCur->InstructionOffset, &pMD) == S_OK)
14224 {
14225 if (bInNative || transitionContextCount==0)
14226 {
14227 // We only want to list one transition frame if there are multiple frames.
14228 bInNative = FALSE;
14229
14230 DOAPPEND (W("(TransitionMU)\n"));
14231 // For each transition, we need to store the context information
14232 if (puiTransitionContextCount)
14233 {
14234 // below we cast the i-th AMD64_CONTEXT to CROSS_PLATFORM_CONTEXT
14235 AppendContext (pTransitionContexts, *puiTransitionContextCount,
14236 &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_FrameContexts[i])));
14237 }
14238 else
14239 {
14240 transitionContextCount++;
14241 }
14242 }
14243
14244 Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
14245 pCur->StackOffset, pCur->InstructionOffset, so);
14246 if (FAILED(Status))
14247 {
14248 goto Exit;
14249 }
14250 else if (Status == S_OK)
14251 {
14252 DOAPPEND (W("\n"));
14253 }
14254 // for S_FALSE do not append anything
14255
14256 }
14257 else
14258 {
14259 if (!bInNative)
14260 {
14261 // We only want to list one transition frame if there are multiple frames.
14262 bInNative = TRUE;
14263
14264 DOAPPEND (W("(TransitionUM)\n"));
14265 // For each transition, we need to store the context information
14266 if (puiTransitionContextCount)
14267 {
14268 AppendContext (pTransitionContexts, *puiTransitionContextCount,
14269 &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_FrameContexts[i])));
14270 }
14271 else
14272 {
14273 transitionContextCount++;
14274 }
14275 }
14276 }
14277 }
14278
14279Exit:
14280#else // _TARGET_WIN64_
14281
14282#ifdef _DEBUG
14283 size_t prevLength = 0;
14284 static WCHAR wszNameBuffer[1024]; // should be large enough
14285 wcscpy_s(wszNameBuffer, 1024, W("Frame")); // default value
14286#endif
14287
14288 BOOL bInNative = TRUE;
14289
14290 UINT frameCount = 0;
14291 do
14292 {
14293 DacpFrameData FrameData;
14294 if ((Status = FrameData.Request(pStackWalk)) != S_OK)
14295 {
14296 goto Exit;
14297 }
14298
14299 CROSS_PLATFORM_CONTEXT context;
14300 if ((Status=pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(),
14301 NULL, (BYTE *)&context))!=S_OK)
14302 {
14303 goto Exit;
14304 }
14305
14306 ExtDbgOut ( " * Ctx[BSI]: %08x %08x %08x ", GetBP(context), GetSP(context), GetIP(context) );
14307
14308 CLRDATA_ADDRESS pMD;
14309 if (!FrameData.frameAddr)
14310 {
14311 if (bInNative || transitionContextCount==0)
14312 {
14313 // We only want to list one transition frame if there are multiple frames.
14314 bInNative = FALSE;
14315
14316 DOAPPEND (W("(TransitionMU)\n"));
14317 // For each transition, we need to store the context information
14318 if (puiTransitionContextCount)
14319 {
14320 AppendContext (pTransitionContexts, *puiTransitionContextCount,
14321 &transitionContextCount, uiSizeOfContext, &context);
14322 }
14323 else
14324 {
14325 transitionContextCount++;
14326 }
14327 }
14328
14329 // we may have a method, try to get the methoddesc
14330 if (g_sos->GetMethodDescPtrFromIP(GetIP(context), &pMD)==S_OK)
14331 {
14332 Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
14333 GetSP(context), GetIP(context), so);
14334 if (FAILED(Status))
14335 {
14336 goto Exit;
14337 }
14338 else if (Status == S_OK)
14339 {
14340 DOAPPEND (W("\n"));
14341 }
14342 // for S_FALSE do not append anything
14343 }
14344 }
14345 else
14346 {
14347#ifdef _DEBUG
14348 if (Output::IsDebugOutputEnabled())
14349 {
14350 DWORD_PTR vtAddr;
14351 MOVE(vtAddr, TO_TADDR(FrameData.frameAddr));
14352 if (g_sos->GetFrameName(TO_CDADDR(vtAddr), 1024, wszNameBuffer, NULL) == S_OK)
14353 ExtDbgOut("[%ls: %08x] ", wszNameBuffer, FrameData.frameAddr);
14354 else
14355 ExtDbgOut("[Frame: %08x] ", FrameData.frameAddr);
14356 }
14357#endif
14358 if (!bInNative)
14359 {
14360 // We only want to list one transition frame if there are multiple frames.
14361 bInNative = TRUE;
14362
14363 DOAPPEND (W("(TransitionUM)\n"));
14364 // For each transition, we need to store the context information
14365 if (puiTransitionContextCount)
14366 {
14367 AppendContext (pTransitionContexts, *puiTransitionContextCount,
14368 &transitionContextCount, uiSizeOfContext, &context);
14369 }
14370 else
14371 {
14372 transitionContextCount++;
14373 }
14374 }
14375 }
14376
14377#ifdef _DEBUG
14378 if (so.Length() > prevLength)
14379 {
14380 ExtDbgOut ( "%ls", so.String()+prevLength );
14381 prevLength = so.Length();
14382 }
14383 else
14384 ExtDbgOut ( "\n" );
14385#endif
14386
14387 }
14388 while ((frameCount++) < MAX_STACK_FRAMES && pStackWalk->Next()==S_OK);
14389
14390 Status = S_OK;
14391
14392Exit:
14393#endif // _TARGET_WIN64_
14394
14395 if (pStackWalk)
14396 {
14397 pStackWalk->Release();
14398 pStackWalk = NULL;
14399 }
14400
14401 // We have finished. Does the user want to copy this data to a buffer?
14402 if (Status == S_OK)
14403 {
14404 if(wszTextOut)
14405 {
14406 // They want at least partial output
14407 wcsncpy_s (wszTextOut, *puiTextLength, so.String(), *puiTextLength-1); // _TRUNCATE
14408 }
14409 else
14410 {
14411 *puiTextLength = _wcslen (so.String()) + 1;
14412 }
14413
14414 if (puiTransitionContextCount)
14415 {
14416 *puiTransitionContextCount = transitionContextCount;
14417 }
14418 }
14419
14420 return Status;
14421}
14422
14423#ifdef FEATURE_PAL
14424#define PAL_TRY_NAKED PAL_CPP_TRY
14425#define PAL_EXCEPT_NAKED(disp) PAL_CPP_CATCH_ALL
14426#define PAL_ENDTRY_NAKED PAL_CPP_ENDTRY
14427#endif
14428
14429// TODO: Convert PAL_TRY_NAKED to something that works on the Mac.
14430HRESULT CALLBACK ImplementEFNStackTraceTry(
14431 PDEBUG_CLIENT client,
14432 __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
14433 size_t *puiTextLength,
14434 LPVOID pTransitionContexts,
14435 size_t *puiTransitionContextCount,
14436 size_t uiSizeOfContext,
14437 DWORD Flags)
14438{
14439 HRESULT Status = E_FAIL;
14440
14441 PAL_TRY_NAKED
14442 {
14443 Status = ImplementEFNStackTrace(client, wszTextOut, puiTextLength,
14444 pTransitionContexts, puiTransitionContextCount,
14445 uiSizeOfContext, Flags);
14446 }
14447 PAL_EXCEPT_NAKED (EXCEPTION_EXECUTE_HANDLER)
14448 {
14449 }
14450 PAL_ENDTRY_NAKED
14451
14452 return Status;
14453}
14454
14455// See sos_stacktrace.h for the contract with the callers regarding the LPVOID arguments.
14456HRESULT CALLBACK _EFN_StackTrace(
14457 PDEBUG_CLIENT client,
14458 __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
14459 size_t *puiTextLength,
14460 __out_bcount_opt(uiSizeOfContext*(*puiTransitionContextCount)) LPVOID pTransitionContexts,
14461 size_t *puiTransitionContextCount,
14462 size_t uiSizeOfContext,
14463 DWORD Flags)
14464{
14465 INIT_API();
14466
14467 Status = ImplementEFNStackTraceTry(client, wszTextOut, puiTextLength,
14468 pTransitionContexts, puiTransitionContextCount,
14469 uiSizeOfContext, Flags);
14470
14471 return Status;
14472}
14473
14474
14475BOOL FormatFromRemoteString(DWORD_PTR strObjPointer, __out_ecount(cchString) PWSTR wszBuffer, ULONG cchString)
14476{
14477 BOOL bRet = FALSE;
14478
14479 wszBuffer[0] = L'\0';
14480
14481 DacpObjectData objData;
14482 if (objData.Request(g_sos, TO_CDADDR(strObjPointer))!=S_OK)
14483 {
14484 return bRet;
14485 }
14486
14487 strobjInfo stInfo;
14488
14489 if (MOVE(stInfo, strObjPointer) != S_OK)
14490 {
14491 return bRet;
14492 }
14493
14494 DWORD dwBufLength = 0;
14495 if (!ClrSafeInt<DWORD>::addition(stInfo.m_StringLength, 1, dwBufLength))
14496 {
14497 ExtOut("<integer overflow>\n");
14498 return bRet;
14499 }
14500
14501 LPWSTR pwszBuf = new NOTHROW WCHAR[dwBufLength];
14502 if (pwszBuf == NULL)
14503 {
14504 return bRet;
14505 }
14506
14507 if (g_sos->GetObjectStringData(TO_CDADDR(strObjPointer), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK)
14508 {
14509 delete [] pwszBuf;
14510 return bRet;
14511 }
14512
14513 // String is in format
14514 // <SP><SP><SP>at <function name>(args,...)\n
14515 // ...
14516 // Parse and copy just <function name>(args,...)
14517
14518 LPWSTR pwszPointer = pwszBuf;
14519
14520 WCHAR PSZSEP[] = W(" at ");
14521
14522 UINT Length = 0;
14523 while(1)
14524 {
14525 if (_wcsncmp(pwszPointer, PSZSEP, _countof(PSZSEP)-1) != 0)
14526 {
14527 delete [] pwszBuf;
14528 return bRet;
14529 }
14530
14531 pwszPointer += _wcslen(PSZSEP);
14532 LPWSTR nextPos = _wcsstr(pwszPointer, PSZSEP);
14533 if (nextPos == NULL)
14534 {
14535 // Done! Note that we are leaving the function before we add the last
14536 // line of stack trace to the output string. This is on purpose because
14537 // this string needs to be merged with a real trace, and the last line
14538 // of the trace will be common to the real trace.
14539 break;
14540 }
14541 WCHAR c = *nextPos;
14542 *nextPos = L'\0';
14543
14544 // Buffer is calculated for sprintf below (" %p %p %S\n");
14545 WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2];
14546
14547 // Note that we don't add a newline because we have this embedded in wszLineBuffer
14548 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %p %p %s"), (void*)(size_t)-1, (void*)(size_t)-1, pwszPointer);
14549 Length += (UINT)_wcslen(wszLineBuffer);
14550
14551 if (wszBuffer)
14552 {
14553 wcsncat_s(wszBuffer, cchString, wszLineBuffer, _TRUNCATE);
14554 }
14555
14556 *nextPos = c;
14557 // Move to the next line.
14558 pwszPointer = nextPos;
14559 }
14560
14561 delete [] pwszBuf;
14562
14563 // Return TRUE only if the stack string had any information that was successfully parsed.
14564 // (Length > 0) is a good indicator of that.
14565 bRet = (Length > 0);
14566 return bRet;
14567}
14568
14569HRESULT AppendExceptionInfo(CLRDATA_ADDRESS cdaObj,
14570 __out_ecount(cchString) PWSTR wszStackString,
14571 ULONG cchString,
14572 BOOL bNestedCase) // If bNestedCase is TRUE, the last frame of the computed stack is left off
14573{
14574 DacpObjectData objData;
14575 if (objData.Request(g_sos, cdaObj) != S_OK)
14576 {
14577 return E_FAIL;
14578 }
14579
14580 // Make sure it is an exception object, and get the MT of Exception
14581 CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
14582 if (exceptionMT == NULL)
14583 {
14584 return E_INVALIDARG;
14585 }
14586
14587 // First try to get exception object data using ISOSDacInterface2
14588 DacpExceptionObjectData excData;
14589 BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, cdaObj));
14590
14591 int iOffset;
14592 // Is there a _remoteStackTraceString? We'll want to prepend that data.
14593 // We only have string data, so IP/SP info has to be set to -1.
14594 DWORD_PTR strPointer;
14595 if (bGotExcData)
14596 {
14597 strPointer = TO_TADDR(excData.RemoteStackTraceString);
14598 }
14599 else
14600 {
14601 iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_remoteStackTraceString"));
14602 MOVE (strPointer, TO_TADDR(cdaObj) + iOffset);
14603 }
14604 if (strPointer)
14605 {
14606 WCHAR *pwszBuffer = new NOTHROW WCHAR[cchString];
14607 if (pwszBuffer == NULL)
14608 {
14609 return E_OUTOFMEMORY;
14610 }
14611
14612 if (FormatFromRemoteString(strPointer, pwszBuffer, cchString))
14613 {
14614 // Prepend this stuff to the string for the user
14615 wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
14616 }
14617 delete[] pwszBuffer;
14618 }
14619
14620 BOOL bAsync = bGotExcData ? IsAsyncException(excData)
14621 : IsAsyncException(cdaObj, objData.MethodTable);
14622
14623 DWORD_PTR arrayPtr;
14624 if (bGotExcData)
14625 {
14626 arrayPtr = TO_TADDR(excData.StackTrace);
14627 }
14628 else
14629 {
14630 iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_stackTrace"));
14631 MOVE (arrayPtr, TO_TADDR(cdaObj) + iOffset);
14632 }
14633
14634 if (arrayPtr)
14635 {
14636 DWORD arrayLen;
14637 MOVE (arrayLen, arrayPtr + sizeof(DWORD_PTR));
14638
14639 if (arrayLen)
14640 {
14641#ifdef _TARGET_WIN64_
14642 DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
14643#else
14644 DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD);
14645#endif // _TARGET_WIN64_
14646 size_t stackTraceSize = 0;
14647 MOVE (stackTraceSize, dataPtr); // data length is stored at the beginning of the array in this case
14648
14649 DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
14650 dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
14651
14652 if (stackTraceSize != 0)
14653 {
14654 size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, bNestedCase);
14655 WCHAR *pwszBuffer = new NOTHROW WCHAR[iLength + 1];
14656 if (pwszBuffer)
14657 {
14658 FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer, iLength + 1, bAsync, bNestedCase);
14659 wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
14660 delete[] pwszBuffer;
14661 }
14662 else
14663 {
14664 return E_OUTOFMEMORY;
14665 }
14666 }
14667 }
14668 }
14669 return S_OK;
14670}
14671
14672HRESULT ImplementEFNGetManagedExcepStack(
14673 CLRDATA_ADDRESS cdaStackObj,
14674 __out_ecount(cchString) PWSTR wszStackString,
14675 ULONG cchString)
14676{
14677 HRESULT Status = E_FAIL;
14678
14679 if (wszStackString == NULL || cchString == 0)
14680 {
14681 return E_INVALIDARG;
14682 }
14683
14684 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
14685 DacpThreadData Thread;
14686 BOOL bCanUseThreadContext = TRUE;
14687
14688 ZeroMemory(&Thread, sizeof(DacpThreadData));
14689
14690 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
14691 {
14692 // The current thread is unmanaged
14693 bCanUseThreadContext = FALSE;
14694 }
14695
14696 if (cdaStackObj == NULL)
14697 {
14698 if (!bCanUseThreadContext)
14699 {
14700 return E_INVALIDARG;
14701 }
14702
14703 TADDR taLTOH = NULL;
14704 if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
14705 &taLTOH,
14706 sizeof(taLTOH), NULL)) || (taLTOH==NULL))
14707 {
14708 return Status;
14709 }
14710 else
14711 {
14712 cdaStackObj = TO_CDADDR(taLTOH);
14713 }
14714 }
14715
14716 // Put the stack trace header on
14717 AddExceptionHeader(wszStackString, cchString);
14718
14719 // First is there a nested exception?
14720 if (bCanUseThreadContext && Thread.firstNestedException)
14721 {
14722 CLRDATA_ADDRESS obj = 0, next = 0;
14723 CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
14724 do
14725 {
14726 Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
14727
14728 // deal with the inability to read a nested exception gracefully
14729 if (Status != S_OK)
14730 {
14731 break;
14732 }
14733
14734 Status = AppendExceptionInfo(obj, wszStackString, cchString, TRUE);
14735 currentNested = next;
14736 }
14737 while(currentNested != NULL);
14738 }
14739
14740 Status = AppendExceptionInfo(cdaStackObj, wszStackString, cchString, FALSE);
14741
14742 return Status;
14743}
14744
14745// TODO: Enable this when ImplementEFNStackTraceTry is fixed.
14746// This function, like VerifyDAC, exists for the purpose of testing
14747// hard-to-get-to SOS APIs.
14748DECLARE_API(VerifyStackTrace)
14749{
14750 INIT_API();
14751
14752 BOOL bVerifyManagedExcepStack = FALSE;
14753 CMDOption option[] =
14754 { // name, vptr, type, hasValue
14755 {"-ManagedExcepStack", &bVerifyManagedExcepStack, COBOOL, FALSE},
14756 };
14757
14758 if (!GetCMDOption(args, option, _countof(option), NULL,0,NULL))
14759 {
14760 return Status;
14761 }
14762
14763 if (bVerifyManagedExcepStack)
14764 {
14765 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
14766 DacpThreadData Thread;
14767
14768 TADDR taExc = NULL;
14769 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
14770 {
14771 ExtOut("The current thread is unmanaged\n");
14772 return Status;
14773 }
14774
14775 TADDR taLTOH = NULL;
14776 if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
14777 &taLTOH,
14778 sizeof(taLTOH), NULL)) || (taLTOH == NULL))
14779 {
14780 ExtOut("There is no current managed exception on this thread\n");
14781 return Status;
14782 }
14783 else
14784 {
14785 taExc = taLTOH;
14786 }
14787
14788 const SIZE_T cchStr = 4096;
14789 WCHAR *wszStr = (WCHAR *)alloca(cchStr * sizeof(WCHAR));
14790 if (ImplementEFNGetManagedExcepStack(TO_CDADDR(taExc), wszStr, cchStr) != S_OK)
14791 {
14792 ExtOut("Error!\n");
14793 return Status;
14794 }
14795
14796 ExtOut("_EFN_GetManagedExcepStack(%P, wszStr, sizeof(wszStr)) returned:\n", SOS_PTR(taExc));
14797 ExtOut("%S\n", wszStr);
14798
14799 if (ImplementEFNGetManagedExcepStack((ULONG64)NULL, wszStr, cchStr) != S_OK)
14800 {
14801 ExtOut("Error!\n");
14802 return Status;
14803 }
14804
14805 ExtOut("_EFN_GetManagedExcepStack(NULL, wszStr, sizeof(wszStr)) returned:\n");
14806 ExtOut("%S\n", wszStr);
14807 }
14808 else
14809 {
14810 size_t textLength = 0;
14811 size_t contextLength = 0;
14812 Status = ImplementEFNStackTraceTry(client,
14813 NULL,
14814 &textLength,
14815 NULL,
14816 &contextLength,
14817 0,
14818 0);
14819
14820 if (Status != S_OK)
14821 {
14822 ExtOut("Error: %lx\n", Status);
14823 return Status;
14824 }
14825
14826 ExtOut("Number of characters requested: %d\n", textLength);
14827 WCHAR *wszBuffer = new NOTHROW WCHAR[textLength + 1];
14828 if (wszBuffer == NULL)
14829 {
14830 ReportOOM();
14831 return Status;
14832 }
14833
14834 // For the transition contexts buffer the callers are expected to allocate
14835 // contextLength * sizeof(TARGET_CONTEXT), and not
14836 // contextLength * sizeof(CROSS_PLATFORM_CONTEXT). See sos_stacktrace.h for
14837 // details.
14838 LPBYTE pContexts = new NOTHROW BYTE[contextLength * g_targetMachine->GetContextSize()];
14839
14840 if (pContexts == NULL)
14841 {
14842 ReportOOM();
14843 delete[] wszBuffer;
14844 return Status;
14845 }
14846
14847 Status = ImplementEFNStackTrace(client,
14848 wszBuffer,
14849 &textLength,
14850 pContexts,
14851 &contextLength,
14852 g_targetMachine->GetContextSize(),
14853 0);
14854
14855 if (Status != S_OK)
14856 {
14857 ExtOut("Error: %lx\n", Status);
14858 delete[] wszBuffer;
14859 delete [] pContexts;
14860 return Status;
14861 }
14862
14863 ExtOut("%S\n", wszBuffer);
14864
14865 ExtOut("Context information:\n");
14866 if (IsDbgTargetX86())
14867 {
14868 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14869 "Ebp", "Esp", "Eip");
14870 }
14871 else if (IsDbgTargetAmd64())
14872 {
14873 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14874 "Rbp", "Rsp", "Rip");
14875 }
14876 else if (IsDbgTargetArm())
14877 {
14878 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14879 "FP", "SP", "PC");
14880 }
14881 else
14882 {
14883 ExtOut("Unsupported platform");
14884 delete [] pContexts;
14885 delete[] wszBuffer;
14886 return S_FALSE;
14887 }
14888
14889 for (size_t j=0; j < contextLength; j++)
14890 {
14891 CROSS_PLATFORM_CONTEXT *pCtx = (CROSS_PLATFORM_CONTEXT*)(pContexts + j*g_targetMachine->GetContextSize());
14892 ExtOut("%p %p %p\n", GetBP(*pCtx), GetSP(*pCtx), GetIP(*pCtx));
14893 }
14894
14895 delete [] pContexts;
14896
14897 StackTrace_SimpleContext *pSimple = new NOTHROW StackTrace_SimpleContext[contextLength];
14898 if (pSimple == NULL)
14899 {
14900 ReportOOM();
14901 delete[] wszBuffer;
14902 return Status;
14903 }
14904
14905 Status = ImplementEFNStackTrace(client,
14906 wszBuffer,
14907 &textLength,
14908 pSimple,
14909 &contextLength,
14910 sizeof(StackTrace_SimpleContext),
14911 0);
14912
14913 if (Status != S_OK)
14914 {
14915 ExtOut("Error: %lx\n", Status);
14916 delete[] wszBuffer;
14917 delete [] pSimple;
14918 return Status;
14919 }
14920
14921 ExtOut("Simple Context information:\n");
14922 if (IsDbgTargetX86())
14923 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14924 "Ebp", "Esp", "Eip");
14925 else if (IsDbgTargetAmd64())
14926 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14927 "Rbp", "Rsp", "Rip");
14928 else if (IsDbgTargetArm())
14929 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14930 "FP", "SP", "PC");
14931 else
14932 {
14933 ExtOut("Unsupported platform");
14934 delete[] wszBuffer;
14935 delete [] pSimple;
14936 return S_FALSE;
14937 }
14938 for (size_t j=0; j < contextLength; j++)
14939 {
14940 ExtOut("%p %p %p\n", SOS_PTR(pSimple[j].FrameOffset),
14941 SOS_PTR(pSimple[j].StackOffset),
14942 SOS_PTR(pSimple[j].InstructionOffset));
14943 }
14944 delete [] pSimple;
14945 delete[] wszBuffer;
14946 }
14947
14948 return Status;
14949}
14950
14951#ifndef FEATURE_PAL
14952
14953// This is an internal-only Apollo extension to de-optimize the code
14954DECLARE_API(SuppressJitOptimization)
14955{
14956 INIT_API_NOEE();
14957 MINIDUMP_NOT_SUPPORTED();
14958
14959 StringHolder onOff;
14960 CMDValue arg[] =
14961 { // vptr, type
14962 {&onOff.data, COSTRING},
14963 };
14964 size_t nArg;
14965 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
14966 {
14967 return E_FAIL;
14968 }
14969
14970 if(nArg == 1 && (_stricmp(onOff.data, "On") == 0))
14971 {
14972 // if CLR is already loaded, try to change the flags now
14973 if(CheckEEDll() == S_OK)
14974 {
14975 SetNGENCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION);
14976 }
14977
14978 if(!g_fAllowJitOptimization)
14979 ExtOut("JIT optimization is already suppressed\n");
14980 else
14981 {
14982 g_fAllowJitOptimization = FALSE;
14983 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
14984 ExtOut("JIT optimization will be suppressed\n");
14985 }
14986
14987
14988 }
14989 else if(nArg == 1 && (_stricmp(onOff.data, "Off") == 0))
14990 {
14991 // if CLR is already loaded, try to change the flags now
14992 if(CheckEEDll() == S_OK)
14993 {
14994 SetNGENCompilerFlags(CORDEBUG_JIT_DEFAULT);
14995 }
14996
14997 if(g_fAllowJitOptimization)
14998 ExtOut("JIT optimization is already permitted\n");
14999 else
15000 {
15001 g_fAllowJitOptimization = TRUE;
15002 ExtOut("JIT optimization will be permitted\n");
15003 }
15004 }
15005 else
15006 {
15007 ExtOut("Usage: !SuppressJitOptimization <on|off>\n");
15008 }
15009
15010 return S_OK;
15011}
15012
15013// Uses ICorDebug to set the state of desired NGEN compiler flags. This can suppress pre-jitted optimized
15014// code
15015HRESULT SetNGENCompilerFlags(DWORD flags)
15016{
15017 HRESULT hr;
15018
15019 ToRelease<ICorDebugProcess2> proc2;
15020 if(FAILED(hr = InitCorDebugInterface()))
15021 {
15022 ExtOut("SOS: warning, prejitted code optimizations could not be changed. Failed to load ICorDebug HR = 0x%x\n", hr);
15023 }
15024 else if(FAILED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess2), (void**) &proc2)))
15025 {
15026 if(flags != CORDEBUG_JIT_DEFAULT)
15027 {
15028 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
15029 }
15030 else
15031 {
15032 hr = S_OK;
15033 }
15034 }
15035 else if(FAILED(hr = proc2->SetDesiredNGENCompilerFlags(flags)))
15036 {
15037 // Versions of CLR that don't have SetDesiredNGENCompilerFlags DAC-ized will return E_FAIL.
15038 // This was first supported in the clr_triton branch around 4/1/12, Apollo release
15039 // It will likely be supported in desktop CLR during Dev12
15040 if(hr == E_FAIL)
15041 {
15042 if(flags != CORDEBUG_JIT_DEFAULT)
15043 {
15044 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
15045 }
15046 else
15047 {
15048 hr = S_OK;
15049 }
15050 }
15051 else if(hr == CORDBG_E_NGEN_NOT_SUPPORTED)
15052 {
15053 if(flags != CORDEBUG_JIT_DEFAULT)
15054 {
15055 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support NGEN\n");
15056 }
15057 else
15058 {
15059 hr = S_OK;
15060 }
15061 }
15062 else if(hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS)
15063 {
15064 DWORD currentFlags = 0;
15065 if(FAILED(hr = proc2->GetDesiredNGENCompilerFlags(&currentFlags)))
15066 {
15067 ExtOut("SOS: warning, prejitted code optimizations could not be changed. GetDesiredNGENCompilerFlags failed hr=0x%x\n", hr);
15068 }
15069 else if(currentFlags != flags)
15070 {
15071 ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. This setting is fixed once CLR starts\n");
15072 }
15073 else
15074 {
15075 hr = S_OK;
15076 }
15077 }
15078 else
15079 {
15080 ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. SetDesiredNGENCompilerFlags hr = 0x%x\n", hr);
15081 }
15082 }
15083
15084 return hr;
15085}
15086
15087
15088// This is an internal-only Apollo extension to save breakpoint/watch state
15089DECLARE_API(SaveState)
15090{
15091 INIT_API_NOEE();
15092 MINIDUMP_NOT_SUPPORTED();
15093
15094 StringHolder filePath;
15095 CMDValue arg[] =
15096 { // vptr, type
15097 {&filePath.data, COSTRING},
15098 };
15099 size_t nArg;
15100 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
15101 {
15102 return E_FAIL;
15103 }
15104
15105 if(nArg == 0)
15106 {
15107 ExtOut("Usage: !SaveState <file_path>\n");
15108 }
15109
15110 FILE* pFile;
15111 errno_t error = fopen_s(&pFile, filePath.data, "w");
15112 if(error != 0)
15113 {
15114 ExtOut("Failed to open file %s, error=0x%x\n", filePath.data, error);
15115 return E_FAIL;
15116 }
15117
15118 g_bpoints.SaveBreakpoints(pFile);
15119 g_watchCmd.SaveListToFile(pFile);
15120
15121 fclose(pFile);
15122 ExtOut("Session breakpoints and watch expressions saved to %s\n", filePath.data);
15123 return S_OK;
15124}
15125
15126#endif // FEATURE_PAL
15127
15128DECLARE_API(StopOnCatch)
15129{
15130 INIT_API();
15131 MINIDUMP_NOT_SUPPORTED();
15132
15133 g_stopOnNextCatch = TRUE;
15134 ULONG32 flags = 0;
15135 g_clrData->GetOtherNotificationFlags(&flags);
15136 flags |= CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER;
15137 g_clrData->SetOtherNotificationFlags(flags);
15138 ExtOut("Debuggee will break the next time a managed exception is caught during execution\n");
15139 return S_OK;
15140}
15141
15142// This is an undocumented SOS extension command intended to help test SOS
15143// It causes the Dml output to be printed to the console uninterpretted so
15144// that a test script can read the commands which are hidden in the markup
15145DECLARE_API(ExposeDML)
15146{
15147 Output::SetDMLExposed(true);
15148 return S_OK;
15149}
15150
15151// According to kksharma the Windows debuggers always sign-extend
15152// arguments when calling externally, therefore StackObjAddr
15153// conforms to CLRDATA_ADDRESS contract.
15154HRESULT CALLBACK
15155_EFN_GetManagedExcepStack(
15156 PDEBUG_CLIENT client,
15157 ULONG64 StackObjAddr,
15158 __out_ecount (cbString) PSTR szStackString,
15159 ULONG cbString
15160 )
15161{
15162 INIT_API();
15163
15164 ArrayHolder<WCHAR> tmpStr = new NOTHROW WCHAR[cbString];
15165 if (tmpStr == NULL)
15166 {
15167 ReportOOM();
15168 return E_OUTOFMEMORY;
15169 }
15170
15171 if (FAILED(Status = ImplementEFNGetManagedExcepStack(StackObjAddr, tmpStr, cbString)))
15172 {
15173 return Status;
15174 }
15175
15176 if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmpStr, -1, szStackString, cbString, NULL, NULL) == 0)
15177 {
15178 return E_FAIL;
15179 }
15180
15181 return S_OK;
15182}
15183
15184// same as _EFN_GetManagedExcepStack, but returns the stack as a wide string.
15185HRESULT CALLBACK
15186_EFN_GetManagedExcepStackW(
15187 PDEBUG_CLIENT client,
15188 ULONG64 StackObjAddr,
15189 __out_ecount(cchString) PWSTR wszStackString,
15190 ULONG cchString
15191 )
15192{
15193 INIT_API();
15194
15195 return ImplementEFNGetManagedExcepStack(StackObjAddr, wszStackString, cchString);
15196}
15197
15198// According to kksharma the Windows debuggers always sign-extend
15199// arguments when calling externally, therefore objAddr
15200// conforms to CLRDATA_ADDRESS contract.
15201HRESULT CALLBACK
15202_EFN_GetManagedObjectName(
15203 PDEBUG_CLIENT client,
15204 ULONG64 objAddr,
15205 __out_ecount (cbName) PSTR szName,
15206 ULONG cbName
15207 )
15208{
15209 INIT_API ();
15210
15211 if (!sos::IsObject(objAddr, false))
15212 {
15213 return E_INVALIDARG;
15214 }
15215
15216 sos::Object obj = TO_TADDR(objAddr);
15217
15218 if (WideCharToMultiByte(CP_ACP, 0, obj.GetTypeName(), (int) (_wcslen(obj.GetTypeName()) + 1),
15219 szName, cbName, NULL, NULL) == 0)
15220 {
15221 return E_FAIL;
15222 }
15223 return S_OK;
15224}
15225
15226// According to kksharma the Windows debuggers always sign-extend
15227// arguments when calling externally, therefore objAddr
15228// conforms to CLRDATA_ADDRESS contract.
15229HRESULT CALLBACK
15230_EFN_GetManagedObjectFieldInfo(
15231 PDEBUG_CLIENT client,
15232 ULONG64 objAddr,
15233 __out_ecount (mdNameLen) PSTR szFieldName,
15234 PULONG64 pValue,
15235 PULONG pOffset
15236 )
15237{
15238 INIT_API();
15239 DacpObjectData objData;
15240 LPWSTR fieldName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
15241
15242 if (szFieldName == NULL || *szFieldName == '\0' ||
15243 objAddr == NULL)
15244 {
15245 return E_FAIL;
15246 }
15247
15248 if (pOffset == NULL && pValue == NULL)
15249 {
15250 // One of these needs to be valid
15251 return E_FAIL;
15252 }
15253
15254 if (FAILED(objData.Request(g_sos, objAddr)))
15255 {
15256 return E_FAIL;
15257 }
15258
15259 MultiByteToWideChar(CP_ACP,0,szFieldName,-1,fieldName,mdNameLen);
15260
15261 int iOffset = GetObjFieldOffset (objAddr, objData.MethodTable, fieldName);
15262 if (iOffset <= 0)
15263 {
15264 return E_FAIL;
15265 }
15266
15267 if (pOffset)
15268 {
15269 *pOffset = (ULONG) iOffset;
15270 }
15271
15272 if (pValue)
15273 {
15274 if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(objAddr + iOffset), pValue, sizeof(ULONG64), NULL)))
15275 {
15276 return E_FAIL;
15277 }
15278 }
15279
15280 return S_OK;
15281}
15282
15283#ifdef FEATURE_PAL
15284
15285#ifdef CREATE_DUMP_SUPPORTED
15286#include <dumpcommon.h>
15287#include "datatarget.h"
15288extern bool CreateDumpForSOS(const char* programPath, const char* dumpPathTemplate, pid_t pid, MINIDUMP_TYPE minidumpType, ICLRDataTarget* dataTarget);
15289extern bool g_diagnostics;
15290#endif // CREATE_DUMP_SUPPORTED
15291
15292DECLARE_API(CreateDump)
15293{
15294 INIT_API();
15295#ifdef CREATE_DUMP_SUPPORTED
15296 StringHolder sFileName;
15297 BOOL normal = FALSE;
15298 BOOL withHeap = FALSE;
15299 BOOL triage = FALSE;
15300 BOOL full = FALSE;
15301 BOOL diag = FALSE;
15302
15303 size_t nArg = 0;
15304 CMDOption option[] =
15305 { // name, vptr, type, hasValue
15306 {"-n", &normal, COBOOL, FALSE},
15307 {"-h", &withHeap, COBOOL, FALSE},
15308 {"-t", &triage, COBOOL, FALSE},
15309 {"-f", &full, COBOOL, FALSE},
15310 {"-d", &diag, COBOOL, FALSE},
15311 };
15312 CMDValue arg[] =
15313 { // vptr, type
15314 {&sFileName.data, COSTRING}
15315 };
15316 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
15317 {
15318 return E_FAIL;
15319 }
15320 MINIDUMP_TYPE minidumpType = MiniDumpWithPrivateReadWriteMemory;
15321 ULONG pid = 0;
15322 g_ExtSystem->GetCurrentProcessId(&pid);
15323
15324 if (full)
15325 {
15326 minidumpType = MiniDumpWithFullMemory;
15327 }
15328 else if (withHeap)
15329 {
15330 minidumpType = MiniDumpWithPrivateReadWriteMemory;
15331 }
15332 else if (triage)
15333 {
15334 minidumpType = MiniDumpFilterTriage;
15335 }
15336 else if (normal)
15337 {
15338 minidumpType = MiniDumpNormal;
15339 }
15340 g_diagnostics = diag;
15341
15342 const char* programPath = g_ExtServices->GetCoreClrDirectory();
15343 const char* dumpPathTemplate = "/tmp/coredump.%d";
15344 ToRelease<ICLRDataTarget> dataTarget = new DataTarget();
15345 dataTarget->AddRef();
15346
15347 if (sFileName.data != nullptr)
15348 {
15349 dumpPathTemplate = sFileName.data;
15350 }
15351 if (!CreateDumpForSOS(programPath, dumpPathTemplate, pid, minidumpType, dataTarget))
15352 {
15353 Status = E_FAIL;
15354 }
15355#else // CREATE_DUMP_SUPPORTED
15356 ExtErr("CreateDump not supported on this platform\n");
15357#endif // CREATE_DUMP_SUPPORTED
15358 return Status;
15359}
15360
15361#endif // FEATURE_PAL
15362
15363void PrintHelp (__in_z LPCSTR pszCmdName)
15364{
15365 static LPSTR pText = NULL;
15366
15367 if (pText == NULL) {
15368#ifndef FEATURE_PAL
15369 HGLOBAL hResource = NULL;
15370 HRSRC hResInfo = FindResource (g_hInstance, TEXT ("DOCUMENTATION"), TEXT ("TEXT"));
15371 if (hResInfo) hResource = LoadResource (g_hInstance, hResInfo);
15372 if (hResource) pText = (LPSTR) LockResource (hResource);
15373 if (pText == NULL)
15374 {
15375 ExtOut("Error loading documentation resource\n");
15376 return;
15377 }
15378#else
15379 int err = PAL_InitializeDLL();
15380 if(err != 0)
15381 {
15382 ExtOut("Error initializing PAL\n");
15383 return;
15384 }
15385 char lpFilename[MAX_LONGPATH + 12]; // + 12 to make enough room for strcat function.
15386 strcpy_s(lpFilename, _countof(lpFilename), g_ExtServices->GetCoreClrDirectory());
15387 strcat_s(lpFilename, _countof(lpFilename), "sosdocsunix.txt");
15388
15389 HANDLE hSosDocFile = CreateFileA(lpFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
15390 if (hSosDocFile == INVALID_HANDLE_VALUE) {
15391 ExtOut("Error finding documentation file\n");
15392 return;
15393 }
15394
15395 HANDLE hMappedSosDocFile = CreateFileMappingA(hSosDocFile, NULL, PAGE_READONLY, 0, 0, NULL);
15396 CloseHandle(hSosDocFile);
15397 if (hMappedSosDocFile == NULL) {
15398 ExtOut("Error mapping documentation file\n");
15399 return;
15400 }
15401
15402 pText = (LPSTR)MapViewOfFile(hMappedSosDocFile, FILE_MAP_READ, 0, 0, 0);
15403 CloseHandle(hMappedSosDocFile);
15404 if (pText == NULL)
15405 {
15406 ExtOut("Error loading documentation file\n");
15407 return;
15408 }
15409#endif
15410 }
15411
15412 // Find our line in the text file
15413 char searchString[MAX_LONGPATH];
15414 sprintf_s(searchString, _countof(searchString), "COMMAND: %s.", pszCmdName);
15415
15416 LPSTR pStart = strstr(pText, searchString);
15417 LPSTR pEnd = NULL;
15418 if (!pStart)
15419 {
15420 ExtOut("Documentation for %s not found.\n", pszCmdName);
15421 return;
15422 }
15423
15424 // Go to the end of this line:
15425 pStart = strchr(pStart, '\n');
15426 if (!pStart)
15427 {
15428 ExtOut("Expected newline in documentation resource.\n");
15429 return;
15430 }
15431
15432 // Bypass the newline that pStart points to and setup pEnd for the loop below. We set
15433 // pEnd to be the old pStart since we add one to it when we call strstr.
15434 pEnd = pStart++;
15435
15436 // Find the first occurrence of \\ followed by an \r or an \n on a line by itself.
15437 do
15438 {
15439 pEnd = strstr(pEnd+1, "\\\\");
15440 } while (pEnd && ((pEnd[-1] != '\r' && pEnd[-1] != '\n') || (pEnd[3] != '\r' && pEnd[3] != '\n')));
15441
15442 if (pEnd)
15443 {
15444 // We have found a \\ followed by a \r or \n. Do not print out the character pEnd points
15445 // to, as this will be the first \ (this is why we don't add one to the second parameter).
15446 ExtOut("%.*s", pEnd - pStart, pStart);
15447 }
15448 else
15449 {
15450 // If pEnd is false then we have run to the end of the document. However, we did find
15451 // the command to print, so we should simply print to the end of the file. We'll add
15452 // an extra newline here in case the file does not contain one.
15453 ExtOut("%s\n", pStart);
15454 }
15455}
15456
15457/**********************************************************************\
15458* Routine Description: *
15459* *
15460* This function displays the commands available in strike and the *
15461* arguments passed into each.
15462* *
15463\**********************************************************************/
15464DECLARE_API(Help)
15465{
15466 // Call extension initialization functions directly, because we don't need the DAC dll to be initialized to get help.
15467 HRESULT Status;
15468 __ExtensionCleanUp __extensionCleanUp;
15469 if ((Status = ExtQuery(client)) != S_OK) return Status;
15470 ControlC = FALSE;
15471
15472 StringHolder commandName;
15473 CMDValue arg[] =
15474 {
15475 {&commandName.data, COSTRING}
15476 };
15477 size_t nArg;
15478 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
15479 {
15480 return Status;
15481 }
15482
15483 ExtOut("-------------------------------------------------------------------------------\n");
15484
15485 if (nArg == 1)
15486 {
15487 // Convert commandName to lower-case
15488 LPSTR curChar = commandName.data;
15489 while (*curChar != '\0')
15490 {
15491 if ( ((unsigned) *curChar <= 0x7F) && isupper(*curChar))
15492 {
15493 *curChar = (CHAR) tolower(*curChar);
15494 }
15495 curChar++;
15496 }
15497
15498 // Strip off leading "!" if the user put that.
15499 curChar = commandName.data;
15500 if (*curChar == '!')
15501 curChar++;
15502
15503 PrintHelp (curChar);
15504 }
15505 else
15506 {
15507 PrintHelp ("contents");
15508 }
15509
15510 return S_OK;
15511}
15512
15513#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_)
15514
15515static BOOL
15516ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size)
15517{
15518 ULONG fetched;
15519 HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(address), buffer, size, &fetched);
15520 return SUCCEEDED(hr);
15521}
15522
15523static BOOL
15524GetStackFrame(CONTEXT* context, ULONG numNativeFrames)
15525{
15526 KNONVOLATILE_CONTEXT_POINTERS contextPointers;
15527 memset(&contextPointers, 0, sizeof(contextPointers));
15528
15529 ULONG64 baseAddress;
15530 HRESULT hr = g_ExtSymbols->GetModuleByOffset(context->Rip, 0, NULL, &baseAddress);
15531 if (FAILED(hr))
15532 {
15533 PDEBUG_STACK_FRAME frame = &g_Frames[0];
15534 for (int i = 0; i < numNativeFrames; i++, frame++) {
15535 if (frame->InstructionOffset == context->Rip)
15536 {
15537 if ((i + 1) >= numNativeFrames) {
15538 return FALSE;
15539 }
15540 memcpy(context, &(g_FrameContexts[i + 1]), sizeof(*context));
15541 return TRUE;
15542 }
15543 }
15544 return FALSE;
15545 }
15546 if (!PAL_VirtualUnwindOutOfProc(context, &contextPointers, baseAddress, ReadMemoryAdapter))
15547 {
15548 return FALSE;
15549 }
15550 return TRUE;
15551}
15552
15553static BOOL
15554UnwindStackFrames(ULONG32 osThreadId)
15555{
15556 ULONG numNativeFrames = 0;
15557 HRESULT hr = GetContextStackTrace(osThreadId, &numNativeFrames);
15558 if (FAILED(hr))
15559 {
15560 return FALSE;
15561 }
15562 CONTEXT context;
15563 memset(&context, 0, sizeof(context));
15564 context.ContextFlags = CONTEXT_FULL;
15565
15566 hr = g_ExtSystem->GetThreadContextById(osThreadId, CONTEXT_FULL, sizeof(context), (PBYTE)&context);
15567 if (FAILED(hr))
15568 {
15569 return FALSE;
15570 }
15571 TableOutput out(3, POINTERSIZE_HEX, AlignRight);
15572 out.WriteRow("RSP", "RIP", "Call Site");
15573
15574 DEBUG_STACK_FRAME nativeFrame;
15575 memset(&nativeFrame, 0, sizeof(nativeFrame));
15576
15577 do
15578 {
15579 if (context.Rip == 0)
15580 {
15581 break;
15582 }
15583 nativeFrame.InstructionOffset = context.Rip;
15584 nativeFrame.ReturnOffset = context.Rip;
15585 nativeFrame.FrameOffset = context.Rbp;
15586 nativeFrame.StackOffset = context.Rsp;
15587 ClrStackImpl::PrintNativeStackFrame(out, &nativeFrame, FALSE);
15588
15589 } while (GetStackFrame(&context, numNativeFrames));
15590
15591 return TRUE;
15592}
15593
15594#endif // FEATURE_PAL && _TARGET_AMD64_
15595