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.
11 // ===========================================================================
13 // ===========================================================================
16 // 09/07/99 Microsoft Created
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.
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.
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
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.
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 instaces 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).
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.
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 // -----------------------------------------------------------------------------------------------
63 #define DO_NOT_DISABLE_RAND //this is a standalone tool, and can use rand()
71 #endif // !FEATURE_PAL
74 #include "platformspecific.h"
95 #define STRESS_LOG_READONLY
96 #include "stresslog.h"
102 #include "cordebug.h"
103 #include "dacprivate.h"
104 #include "corexcep.h"
106 #define CORHANDLE_MASK 0x1
107 #define SWITCHED_OUT_FIBER_OSID 0xbaadf00d;
109 #define DEFINE_EXT_GLOBALS
114 #include "predeftlsslot.h"
116 #include "hillclimbing.h"
122 #include "ExpressionNode.h"
123 #include "WatchCmd.h"
131 typedef struct _VM_COUNTERS {
132 SIZE_T PeakVirtualSize;
134 ULONG PageFaultCount;
135 SIZE_T PeakWorkingSetSize;
136 SIZE_T WorkingSetSize;
137 SIZE_T QuotaPeakPagedPoolUsage;
138 SIZE_T QuotaPagedPoolUsage;
139 SIZE_T QuotaPeakNonPagedPoolUsage;
140 SIZE_T QuotaNonPagedPoolUsage;
141 SIZE_T PagefileUsage;
142 SIZE_T PeakPagefileUsage;
144 typedef VM_COUNTERS *PVM_COUNTERS;
146 const PROCESSINFOCLASS ProcessVmCounters = static_cast<PROCESSINFOCLASS>(3);
148 #endif // !FEATURE_PAL
151 BOOL ControlC = FALSE;
153 IMetaDataDispenserEx *pDisp = NULL;
154 WCHAR g_mdName[mdNameLen];
157 HMODULE g_hInstance = NULL;
160 #endif // !FEATURE_PAL
163 #pragma warning(disable:4244) // conversion from 'unsigned int' to 'unsigned short', possible loss of data
164 #pragma warning(disable:4189) // local variable is initialized but not referenced
170 #define SOSPrefix "!"
173 #if defined _X86_ && !defined FEATURE_PAL
174 // disable FPO for X86 builds
175 #pragma optimize("y", off)
181 #pragma warning(default:4244)
182 #pragma warning(default:4189)
187 #endif // FEATURE_PAL
190 #define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
196 #define MINIDUMP_NOT_SUPPORTED()
198 #else // !FEATURE_PAL
200 #define MINIDUMP_NOT_SUPPORTED() \
201 if (IsMiniDumpFile()) \
203 ExtOut("This command is not supported in a minidump without full memory\n"); \
204 ExtOut("To try the command anyway, run !MinidumpMode 0\n"); \
208 #define NOTHROW (std::nothrow)
210 #include "safemath.h"
212 DECLARE_API (MinidumpMode)
223 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
229 // Print status of current mode
230 ExtOut("Current mode: %s - unsafe minidump commands are %s.\n",
231 g_InMinidumpSafeMode ? "1" : "0",
232 g_InMinidumpSafeMode ? "disabled" : "enabled");
236 if (Value != 0 && Value != 1)
238 ExtOut("Mode must be 0 or 1\n");
242 g_InMinidumpSafeMode = (BOOL) Value;
243 ExtOut("Unsafe minidump commands are %s.\n",
244 g_InMinidumpSafeMode ? "disabled" : "enabled");
250 #endif // FEATURE_PAL
252 /**********************************************************************\
253 * Routine Description: *
255 * This function is called to get the MethodDesc for a given eip *
257 \**********************************************************************/
261 MINIDUMP_NOT_SUPPORTED();
266 { // name, vptr, type, hasValue
268 {"/d", &dml, COBOOL, FALSE},
277 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
281 EnableDMLHolder dmlHolder(dml);
285 ExtOut("%s is not IP\n", args);
289 CLRDATA_ADDRESS cdaStart = TO_CDADDR(IP);
293 if ((Status = g_sos->GetMethodDescPtrFromIP(cdaStart, &pMD)) != S_OK)
295 ExtOut("Failed to request MethodData, not in JIT code range\n");
299 DMLOut("MethodDesc: %s\n", DMLMethodDesc(pMD));
300 DumpMDInfo(TO_TADDR(pMD), cdaStart, FALSE /* fStackTraceFormat */);
302 WCHAR filename[MAX_LONGPATH];
304 // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
306 if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
308 symlines &= SYMOPT_LOAD_LINES;
312 SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, _countof(filename))))
314 ExtOut("Source file: %S @ %d\n", filename, linenum);
320 // (MAX_STACK_FRAMES is also used by x86 to prevent infinite loops in _EFN_StackTrace)
321 #define MAX_STACK_FRAMES 1000
323 #ifdef _TARGET_WIN64_
325 // I use a global set of frames for stack walking on win64 because the debugger's
326 // GetStackTrace function doesn't provide a way to find out the total size of a stackwalk,
327 // and I'd like to have a reasonably big maximum without overflowing the stack by declaring
328 // the buffer locally and I also want to get a managed trace in a low memory environment
329 // (so no dynamic allocation if possible).
330 DEBUG_STACK_FRAME g_Frames[MAX_STACK_FRAMES];
331 AMD64_CONTEXT g_X64FrameContexts[MAX_STACK_FRAMES];
334 GetContextStackTrace(ULONG osThreadId, PULONG pnumFrames)
336 PDEBUG_CONTROL4 debugControl4;
339 // Do we have advanced capability?
340 if ((hr = g_ExtControl->QueryInterface(__uuidof(IDebugControl4), (void **)&debugControl4)) == S_OK)
343 g_ExtSystem->GetCurrentThreadId(&oldId);
345 if ((hr = g_ExtSystem->GetThreadIdBySystemId(osThreadId, &id)) != S_OK) {
348 g_ExtSystem->SetCurrentThreadId(id);
350 // GetContextStackTrace fills g_X64FrameContexts as an array of
351 // contexts packed as target architecture contexts. We cannot
352 // safely cast this as an array of CROSS_PLATFORM_CONTEXT, since
353 // sizeof(CROSS_PLATFORM_CONTEXT) != sizeof(TGT_CONTEXT)
354 hr = debugControl4->GetContextStackTrace(
360 MAX_STACK_FRAMES*g_targetMachine->GetContextSize(),
361 g_targetMachine->GetContextSize(),
364 g_ExtSystem->SetCurrentThreadId(oldId);
365 debugControl4->Release();
370 #endif // _TARGET_WIN64_
372 /**********************************************************************\
373 * Routine Description: *
375 * This function displays the stack trace. It looks at each DWORD *
376 * on stack. If the DWORD is a return address, the symbol name or
377 * managed function name is displayed. *
379 \**********************************************************************/
380 void DumpStackInternal(DumpStackFlag *pDSFlag)
382 ReloadSymbolWithLineInfo();
385 g_ExtRegisters->GetStackOffset (&StackOffset);
386 if (pDSFlag->top == 0) {
387 pDSFlag->top = TO_TADDR(StackOffset);
390 while (g_ExtData->ReadVirtual(TO_CDADDR(pDSFlag->top), &value, sizeof(size_t), NULL) != S_OK) {
393 pDSFlag->top = NextOSPageAddress(pDSFlag->top);
397 if (pDSFlag->end == 0) {
398 // Find the current stack range
402 g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
403 if (SafeReadMemory(TO_TADDR(dwTebAddr), &teb, sizeof(NT_TIB), NULL))
405 if (pDSFlag->top > TO_TADDR(teb.StackLimit)
406 && pDSFlag->top <= TO_TADDR(teb.StackBase))
408 if (pDSFlag->end == 0 || pDSFlag->end > TO_TADDR(teb.StackBase))
409 pDSFlag->end = TO_TADDR(teb.StackBase);
413 #endif // FEATURE_PAL
415 if (pDSFlag->end == 0)
417 ExtOut("TEB information is not available so a stack size of 0xFFFF is assumed\n");
418 pDSFlag->end = pDSFlag->top + 0xFFFF;
421 if (pDSFlag->end < pDSFlag->top)
423 ExtOut("Wrong option: stack selection wrong\n");
427 DumpStackWorker(*pDSFlag);
430 #if defined(FEATURE_PAL) && defined(_TARGET_WIN64_)
431 static BOOL UnwindStackFrames(ULONG32 osThreadId);
434 DECLARE_API(DumpStack)
436 INIT_API_NO_RET_ON_FAILURE();
438 MINIDUMP_NOT_SUPPORTED();
440 DumpStackFlag DSFlag;
441 DSFlag.fEEonly = FALSE;
442 DSFlag.fSuppressSrcInfo = FALSE;
448 CMDOption option[] = {
449 // name, vptr, type, hasValue
450 {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
451 {"-n", &DSFlag.fSuppressSrcInfo, COBOOL, FALSE},
452 {"-unwind", &unwind, COBOOL, FALSE},
454 {"/d", &dml, COBOOL, FALSE}
459 {&DSFlag.top, COHEX},
463 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
466 // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
468 if (!DSFlag.fSuppressSrcInfo && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
470 symlines &= SYMOPT_LOAD_LINES;
472 DSFlag.fSuppressSrcInfo = DSFlag.fSuppressSrcInfo || (symlines == 0);
474 EnableDMLHolder enabledml(dml);
476 ULONG sysId = 0, id = 0;
477 g_ExtSystem->GetCurrentThreadSystemId(&sysId);
478 ExtOut("OS Thread Id: 0x%x ", sysId);
479 g_ExtSystem->GetCurrentThreadId(&id);
480 ExtOut("(%d)\n", id);
482 #if defined(FEATURE_PAL) && defined(_TARGET_WIN64_)
485 UnwindStackFrames(sysId);
490 DumpStackInternal(&DSFlag);
496 /**********************************************************************\
497 * Routine Description: *
499 * This function displays the stack trace for threads that EE knows *
500 * from ThreadStore. *
502 \**********************************************************************/
503 DECLARE_API (EEStack)
507 MINIDUMP_NOT_SUPPORTED();
509 DumpStackFlag DSFlag;
510 DSFlag.fEEonly = FALSE;
511 DSFlag.fSuppressSrcInfo = FALSE;
515 BOOL bShortList = FALSE;
518 { // name, vptr, type, hasValue
519 {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
520 {"-short", &bShortList, COBOOL, FALSE},
522 {"/d", &dml, COBOOL, FALSE}
526 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
531 EnableDMLHolder enableDML(dml);
534 g_ExtSystem->GetCurrentThreadId(&Tid);
536 DacpThreadStoreData ThreadStore;
537 if ((Status = ThreadStore.Request(g_sos)) != S_OK)
539 ExtOut("Failed to request ThreadStore\n");
543 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
549 DacpThreadData Thread;
550 if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
552 ExtOut("Failed to request Thread at %p\n", CurThread);
557 if (g_ExtSystem->GetThreadIdBySystemId (Thread.osThreadId, &id) != S_OK)
559 CurThread = Thread.nextThread;
563 ExtOut("---------------------------------------------\n");
564 ExtOut("Thread %3d\n", id);
568 #define TS_Hijacked 0x00000080
574 else if ((Thread.lockCount > 0) || (Thread.state & TS_Hijacked))
576 // TODO: bring back || (int)vThread.m_pFrame != -1 {
582 g_ExtRegisters->GetInstructionOffset (&IP);
586 IP2MethodDesc (TO_TADDR(IP), methodDesc, jitType, gcinfoAddr);
595 g_ExtSystem->SetCurrentThreadId(id);
598 DumpStackInternal(&DSFlag);
601 CurThread = Thread.nextThread;
604 g_ExtSystem->SetCurrentThreadId(Tid);
608 HRESULT DumpStackObjectsRaw(size_t nArg, __in_z LPSTR exprBottom, __in_z LPSTR exprTop, BOOL bVerify)
611 size_t StackBottom = 0;
615 g_ExtRegisters->GetStackOffset(&StackOffset);
617 StackTop = TO_TADDR(StackOffset);
621 StackTop = GetExpression(exprTop);
624 ExtOut("wrong option: %s\n", exprTop);
630 StackBottom = GetExpression(exprBottom);
631 if (StackBottom == 0)
633 ExtOut("wrong option: %s\n", exprBottom);
642 HRESULT hr = g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
643 if (SUCCEEDED(hr) && SafeReadMemory (TO_TADDR(dwTebAddr), &teb, sizeof (NT_TIB), NULL))
645 if (StackTop > TO_TADDR(teb.StackLimit) && StackTop <= TO_TADDR(teb.StackBase))
647 if (StackBottom == 0 || StackBottom > TO_TADDR(teb.StackBase))
648 StackBottom = TO_TADDR(teb.StackBase);
653 if (StackBottom == 0)
654 StackBottom = StackTop + 0xFFFF;
656 if (StackBottom < StackTop)
658 ExtOut("Wrong option: stack selection wrong\n");
662 // We can use the gc snapshot to eliminate object addresses that are
663 // not on the gc heap.
664 if (!g_snapshot.Build())
666 ExtOut("Unable to determine bounds of gc heap\n");
672 g_ExtSystem->GetCurrentThreadSystemId (&id);
673 ExtOut("OS Thread Id: 0x%x ", id);
674 g_ExtSystem->GetCurrentThreadId (&id);
675 ExtOut("(%d)\n", id);
677 DumpStackObjectsHelper(StackTop, StackBottom, bVerify);
681 /**********************************************************************\
682 * Routine Description: *
684 * This function is called to dump the address and name of all *
685 * Managed Objects on the stack. *
687 \**********************************************************************/
688 DECLARE_API(DumpStackObjects)
691 MINIDUMP_NOT_SUPPORTED();
692 StringHolder exprTop, exprBottom;
694 BOOL bVerify = FALSE;
697 { // name, vptr, type, hasValue
698 {"-verify", &bVerify, COBOOL, FALSE},
700 {"/d", &dml, COBOOL, FALSE}
705 {&exprTop.data, COSTRING},
706 {&exprBottom.data, COSTRING}
710 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
715 EnableDMLHolder enableDML(dml);
717 return DumpStackObjectsRaw(nArg, exprBottom.data, exprTop.data, bVerify);
720 /**********************************************************************\
721 * Routine Description: *
723 * This function is called to dump the contents of a MethodDesc *
724 * for a given address *
726 \**********************************************************************/
730 MINIDUMP_NOT_SUPPORTED();
732 DWORD_PTR dwStartAddr = NULL;
736 { // name, vptr, type, hasValue
738 {"/d", &dml, COBOOL, FALSE},
743 {&dwStartAddr, COHEX},
747 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
752 EnableDMLHolder dmlHolder(dml);
754 DumpMDInfo(dwStartAddr);
759 BOOL GatherDynamicInfo(TADDR DynamicMethodObj, DacpObjectData *codeArray,
760 DacpObjectData *tokenArray, TADDR *ptokenArrayAddr)
764 DacpObjectData objData; // temp object
766 if (codeArray == NULL || tokenArray == NULL)
769 if (objData.Request(g_sos, TO_CDADDR(DynamicMethodObj)) != S_OK)
772 iOffset = GetObjFieldOffset(DynamicMethodObj, objData.MethodTable, W("m_resolver"));
777 if (FAILED(MOVE(resolverPtr, DynamicMethodObj + iOffset)))
780 if (objData.Request(g_sos, TO_CDADDR(resolverPtr)) != S_OK)
783 iOffset = GetObjFieldOffset(resolverPtr, objData.MethodTable, W("m_code"));
788 if (FAILED(MOVE(codePtr, resolverPtr + iOffset)))
791 if (codeArray->Request(g_sos, TO_CDADDR(codePtr)) != S_OK)
794 if (codeArray->dwComponentSize != 1)
797 // We also need the resolution table
798 iOffset = GetObjFieldOffset (resolverPtr, objData.MethodTable, W("m_scope"));
803 if (FAILED(MOVE(scopePtr, resolverPtr + iOffset)))
806 if (objData.Request(g_sos, TO_CDADDR(scopePtr)) != S_OK)
809 iOffset = GetObjFieldOffset (scopePtr, objData.MethodTable, W("m_tokens"));
814 if (FAILED(MOVE(tokensPtr, scopePtr + iOffset)))
817 if (objData.Request(g_sos, TO_CDADDR(tokensPtr)) != S_OK)
820 iOffset = GetObjFieldOffset(tokensPtr, objData.MethodTable, W("_items"));
825 MOVE (itemsPtr, tokensPtr + iOffset);
827 *ptokenArrayAddr = itemsPtr;
829 if (tokenArray->Request(g_sos, TO_CDADDR(itemsPtr)) != S_OK)
832 bRet = TRUE; // whew.
839 MINIDUMP_NOT_SUPPORTED();
840 DWORD_PTR dwStartAddr = NULL;
841 DWORD_PTR dwDynamicMethodObj = NULL;
843 BOOL fILPointerDirectlySpecified = FALSE;
846 { // name, vptr, type, hasValue
847 {"/d", &dml, COBOOL, FALSE},
848 {"/i", &fILPointerDirectlySpecified, COBOOL, FALSE},
852 {&dwStartAddr, COHEX},
856 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
861 EnableDMLHolder dmlHolder(dml);
862 if (dwStartAddr == NULL)
864 ExtOut("Must pass a valid expression\n");
868 if (fILPointerDirectlySpecified)
870 return DecodeILFromAddress(NULL, dwStartAddr);
873 if (!g_snapshot.Build())
875 ExtOut("Unable to build snapshot of the garbage collector state\n");
879 if (g_snapshot.GetHeap(dwStartAddr) != NULL)
881 dwDynamicMethodObj = dwStartAddr;
884 if (dwDynamicMethodObj == NULL)
886 // We have been given a MethodDesc
887 DacpMethodDescData MethodDescData;
888 if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
890 ExtOut("%p is not a MethodDesc\n", SOS_PTR(dwStartAddr));
894 if (MethodDescData.bIsDynamic && MethodDescData.managedDynamicMethodObject)
896 dwDynamicMethodObj = TO_TADDR(MethodDescData.managedDynamicMethodObject);
897 if (dwDynamicMethodObj == NULL)
899 ExtOut("Unable to print IL for DynamicMethodDesc %p\n", SOS_PTR(dwDynamicMethodObj));
905 // This is not a dynamic method, print the IL for it.
908 if (dmd.Request(g_sos, MethodDescData.ModulePtr) != S_OK)
910 ExtOut("Unable to get module\n");
914 ToRelease<IMetaDataImport> pImport = MDImportForModule(&dmd);
917 ExtOut("bad import\n");
923 if (pImport->GetRVA(MethodDescData.MDToken, &pRva, &dwFlags) != S_OK)
925 ExtOut("error in import\n");
929 CLRDATA_ADDRESS ilAddrClr;
930 if (g_sos->GetILForModule(MethodDescData.ModulePtr, pRva, &ilAddrClr) != S_OK)
932 ExtOut("FindIL failed\n");
936 TADDR ilAddr = TO_TADDR(ilAddrClr);
937 IfFailRet(DecodeILFromAddress(pImport, ilAddr));
941 if (dwDynamicMethodObj != NULL)
943 // We have a DynamicMethod managed object, let us visit the town and paint.
944 DacpObjectData codeArray;
945 DacpObjectData tokenArray;
946 DWORD_PTR tokenArrayAddr;
947 if (!GatherDynamicInfo (dwDynamicMethodObj, &codeArray, &tokenArray, &tokenArrayAddr))
949 DMLOut("Error gathering dynamic info from object at %s.\n", DMLObject(dwDynamicMethodObj));
953 // Read the memory into a local buffer
954 BYTE *pArray = new NOTHROW BYTE[(SIZE_T)codeArray.dwNumComponents];
957 ExtOut("Not enough memory to read IL\n");
961 Status = g_ExtData->ReadVirtual(UL64_TO_CDA(codeArray.ArrayDataPtr), pArray, (ULONG)codeArray.dwNumComponents, NULL);
964 ExtOut("Failed to read memory\n");
969 // Now we have a local copy of the IL, and a managed array for token resolution.
970 // Visit our IL parser with this info.
971 ExtOut("This is dynamic IL. Exception info is not reported at this time.\n");
972 ExtOut("If a token is unresolved, run \"!do <addr>\" on the addr given\n");
973 ExtOut("in parenthesis. You can also look at the token table yourself, by\n");
974 ExtOut("running \"!DumpArray %p\".\n\n", SOS_PTR(tokenArrayAddr));
975 DecodeDynamicIL(pArray, (ULONG)codeArray.dwNumComponents, tokenArray);
984 DWORD_PTR dwModuleAddr,
988 // Find the length of the signature and copy it into the debugger process.
992 const ULONG cbSigInc = 256;
993 ArrayHolder<COR_SIGNATURE> pSig = new NOTHROW COR_SIGNATURE[cbSigInc];
1000 CQuickBytes sigString;
1007 if (!SafeReadMemory(TO_TADDR(dwSigAddr + cbSig), pSig + cbSig, cbSigInc, &cbCopied))
1011 sigString.ReSize(0);
1012 GetSignatureStringResults result;
1014 result = GetMethodSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
1016 result = GetSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
1018 if (GSS_ERROR == result)
1021 if (GSS_SUCCESS == result)
1024 // If we didn't get the full amount back, and we failed to parse the
1025 // signature, it's not valid because of insufficient data
1028 ExtOut("Invalid signature\n");
1033 #pragma warning(push)
1034 #pragma warning(disable:6280) // "Suppress PREFast warning about mismatch alloc/free"
1037 PCOR_SIGNATURE pSigNew = (PCOR_SIGNATURE)realloc(pSig, cbSig+cbSigInc);
1040 #pragma warning(pop)
1043 if (pSigNew == NULL)
1045 ExtOut("Out of memory\n");
1052 ExtOut("%S\n", (PCWSTR)sigString.Ptr());
1055 /**********************************************************************\
1056 * Routine Description: *
1058 * This function is called to dump a signature object. *
1060 \**********************************************************************/
1061 DECLARE_API(DumpSig)
1065 MINIDUMP_NOT_SUPPORTED();
1071 StringHolder sigExpr;
1072 StringHolder moduleExpr;
1075 {&sigExpr.data, COSTRING},
1076 {&moduleExpr.data, COSTRING}
1079 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
1085 ExtOut("!DumpSig <sigaddr> <moduleaddr>\n");
1089 DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);
1090 DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
1092 if (dwSigAddr == 0 || dwModuleAddr == 0)
1094 ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
1098 DumpSigWorker(dwSigAddr, dwModuleAddr, TRUE);
1102 /**********************************************************************\
1103 * Routine Description: *
1105 * This function is called to dump a portion of a signature object. *
1107 \**********************************************************************/
1108 DECLARE_API(DumpSigElem)
1112 MINIDUMP_NOT_SUPPORTED();
1119 StringHolder sigExpr;
1120 StringHolder moduleExpr;
1123 {&sigExpr.data, COSTRING},
1124 {&moduleExpr.data, COSTRING}
1127 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
1134 ExtOut("!DumpSigElem <sigaddr> <moduleaddr>\n");
1138 DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);
1139 DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
1141 if (dwSigAddr == 0 || dwModuleAddr == 0)
1143 ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
1147 DumpSigWorker(dwSigAddr, dwModuleAddr, FALSE);
1151 /**********************************************************************\
1152 * Routine Description: *
1154 * This function is called to dump the contents of an EEClass from *
1157 \**********************************************************************/
1158 DECLARE_API(DumpClass)
1161 MINIDUMP_NOT_SUPPORTED();
1163 DWORD_PTR dwStartAddr = 0;
1166 CMDOption option[] =
1167 { // name, vptr, type, hasValue
1169 {"/d", &dml, COBOOL, FALSE},
1174 {&dwStartAddr, COHEX}
1178 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
1185 ExtOut("Missing EEClass address\n");
1189 EnableDMLHolder dmlHolder(dml);
1191 CLRDATA_ADDRESS methodTable;
1192 if ((Status=g_sos->GetMethodTableForEEClass(TO_CDADDR(dwStartAddr), &methodTable)) != S_OK)
1194 ExtOut("Invalid EEClass address\n");
1198 DacpMethodTableData mtdata;
1199 if ((Status=mtdata.Request(g_sos, TO_CDADDR(methodTable)))!=S_OK)
1201 ExtOut("EEClass has an invalid MethodTable address\n");
1205 sos::MethodTable mt = TO_TADDR(methodTable);
1206 ExtOut("Class Name: %S\n", mt.GetName());
1208 WCHAR fileName[MAX_LONGPATH];
1209 FileNameForModule(TO_TADDR(mtdata.Module), fileName);
1210 ExtOut("mdToken: %p\n", mtdata.cl);
1211 ExtOut("File: %S\n", fileName);
1213 CLRDATA_ADDRESS ParentEEClass = NULL;
1214 if (mtdata.ParentMethodTable)
1216 DacpMethodTableData mtdataparent;
1217 if ((Status=mtdataparent.Request(g_sos, TO_CDADDR(mtdata.ParentMethodTable)))!=S_OK)
1219 ExtOut("EEClass has an invalid MethodTable address\n");
1222 ParentEEClass = mtdataparent.Class;
1225 DMLOut("Parent Class: %s\n", DMLClass(ParentEEClass));
1226 DMLOut("Module: %s\n", DMLModule(mtdata.Module));
1227 DMLOut("Method Table: %s\n", DMLMethodTable(methodTable));
1228 ExtOut("Vtable Slots: %x\n", mtdata.wNumVirtuals);
1229 ExtOut("Total Method Slots: %x\n", mtdata.wNumVtableSlots);
1230 ExtOut("Class Attributes: %x ", mtdata.dwAttrClass);
1232 if (IsTdInterface(mtdata.dwAttrClass))
1233 ExtOut("Interface, ");
1234 if (IsTdAbstract(mtdata.dwAttrClass))
1235 ExtOut("Abstract, ");
1236 if (IsTdImport(mtdata.dwAttrClass))
1237 ExtOut("ComImport, ");
1241 DacpMethodTableTransparencyData transparency;
1242 if (SUCCEEDED(transparency.Request(g_sos, methodTable)))
1244 ExtOut("Transparency: %s\n", GetTransparency(transparency));
1247 DacpMethodTableFieldData vMethodTableFields;
1248 if (SUCCEEDED(vMethodTableFields.Request(g_sos, methodTable)))
1250 ExtOut("NumInstanceFields: %x\n", vMethodTableFields.wNumInstanceFields);
1251 ExtOut("NumStaticFields: %x\n", vMethodTableFields.wNumStaticFields);
1253 if (vMethodTableFields.wNumThreadStaticFields != 0)
1255 ExtOut("NumThreadStaticFields: %x\n", vMethodTableFields.wNumThreadStaticFields);
1259 if (vMethodTableFields.wContextStaticsSize)
1261 ExtOut("ContextStaticOffset: %x\n", vMethodTableFields.wContextStaticOffset);
1262 ExtOut("ContextStaticsSize: %x\n", vMethodTableFields.wContextStaticsSize);
1266 if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1268 DisplayFields(methodTable, &mtdata, &vMethodTableFields, NULL, TRUE, FALSE);
1275 /**********************************************************************\
1276 * Routine Description: *
1278 * This function is called to dump the contents of a MethodTable *
1279 * from a given address *
1281 \**********************************************************************/
1284 DWORD_PTR dwStartAddr=0;
1285 DWORD_PTR dwOriginalAddr;
1289 MINIDUMP_NOT_SUPPORTED();
1291 BOOL bDumpMDTable = FALSE;
1294 CMDOption option[] =
1295 { // name, vptr, type, hasValue
1296 {"-MD", &bDumpMDTable, COBOOL, FALSE},
1298 {"/d", &dml, COBOOL, FALSE}
1303 {&dwStartAddr, COHEX}
1306 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
1311 EnableDMLHolder dmlHolder(dml);
1312 TableOutput table(2, 16, AlignLeft, false);
1316 Print("Missing MethodTable address\n");
1320 dwOriginalAddr = dwStartAddr;
1321 dwStartAddr = dwStartAddr&~3;
1323 if (!IsMethodTable(dwStartAddr))
1325 Print(dwOriginalAddr, " is not a MethodTable\n");
1329 DacpMethodTableData vMethTable;
1330 vMethTable.Request(g_sos, TO_CDADDR(dwStartAddr));
1332 if (vMethTable.bIsFree)
1334 Print("Free MethodTable\n");
1338 table.WriteRow("EEClass:", EEClassPtr(vMethTable.Class));
1340 table.WriteRow("Module:", ModulePtr(vMethTable.Module));
1342 sos::MethodTable mt = (TADDR)dwStartAddr;
1343 table.WriteRow("Name:", mt.GetName());
1345 WCHAR fileName[MAX_LONGPATH];
1346 FileNameForModule(TO_TADDR(vMethTable.Module), fileName);
1347 table.WriteRow("mdToken:", Pointer(vMethTable.cl));
1348 table.WriteRow("File:", fileName[0] ? fileName : W("Unknown Module"));
1350 table.WriteRow("BaseSize:", PrefixHex(vMethTable.BaseSize));
1351 table.WriteRow("ComponentSize:", PrefixHex(vMethTable.ComponentSize));
1352 table.WriteRow("Slots in VTable:", Decimal(vMethTable.wNumMethods));
1354 table.SetColWidth(0, 29);
1355 table.WriteRow("Number of IFaces in IFaceMap:", Decimal(vMethTable.wNumInterfaces));
1359 table.ReInit(4, POINTERSIZE_HEX, AlignRight);
1360 table.SetColAlignment(3, AlignLeft);
1361 table.SetColWidth(2, 6);
1363 Print("--------------------------------------\n");
1364 Print("MethodDesc Table\n");
1366 table.WriteRow("Entry", "MethodDesc", "JIT", "Name");
1368 for (DWORD n = 0; n < vMethTable.wNumMethods; n++)
1371 DWORD_PTR methodDesc=0;
1372 DWORD_PTR gcinfoAddr;
1374 CLRDATA_ADDRESS entry;
1375 if (g_sos->GetMethodTableSlot(dwStartAddr, n, &entry) != S_OK)
1377 PrintLn("<error getting slot ", Decimal(n), ">");
1381 IP2MethodDesc((DWORD_PTR)entry, methodDesc, jitType, gcinfoAddr);
1382 table.WriteColumn(0, entry);
1383 table.WriteColumn(1, MethodDescPtr(methodDesc));
1385 if (jitType == TYPE_UNKNOWN && methodDesc != NULL)
1387 // We can get a more accurate jitType from NativeCodeAddr of the methoddesc,
1388 // because the methodtable entry hasn't always been patched.
1389 DacpMethodDescData tmpMethodDescData;
1390 if (tmpMethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
1392 DacpCodeHeaderData codeHeaderData;
1393 if (codeHeaderData.Request(g_sos,tmpMethodDescData.NativeCodeAddr) == S_OK)
1395 jitType = (JITTypes) codeHeaderData.JITType;
1400 const char *pszJitType = "NONE";
1401 if (jitType == TYPE_JIT)
1403 else if (jitType == TYPE_PJIT)
1404 pszJitType = "PreJIT";
1407 DacpMethodDescData MethodDescData;
1408 if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
1411 if ((TO_TADDR(MethodDescData.NativeCodeAddr) >= TO_TADDR(moduleInfo[MSCORWKS].baseAddr)) &&
1412 ((TO_TADDR(MethodDescData.NativeCodeAddr) < TO_TADDR(moduleInfo[MSCORWKS].baseAddr + moduleInfo[MSCORWKS].size))))
1414 pszJitType = "FCALL";
1419 table.WriteColumn(2, pszJitType);
1421 NameForMD_s(methodDesc,g_mdName,mdNameLen);
1422 table.WriteColumn(3, g_mdName);
1428 extern size_t Align (size_t nbytes);
1430 HRESULT PrintVC(TADDR taMT, TADDR taObject, BOOL bPrintFields = TRUE)
1433 DacpMethodTableData mtabledata;
1434 if ((Status = mtabledata.Request(g_sos, TO_CDADDR(taMT)))!=S_OK)
1437 size_t size = mtabledata.BaseSize;
1438 if ((Status=g_sos->GetMethodTableName(TO_CDADDR(taMT), mdNameLen, g_mdName, NULL))!=S_OK)
1441 ExtOut("Name: %S\n", g_mdName);
1442 DMLOut("MethodTable: %s\n", DMLMethodTable(taMT));
1443 DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
1444 ExtOut("Size: %d(0x%x) bytes\n", size, size);
1446 FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
1447 ExtOut("File: %S\n", g_mdName[0] ? g_mdName : W("Unknown Module"));
1451 DacpMethodTableFieldData vMethodTableFields;
1452 if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(taMT)))!=S_OK)
1455 ExtOut("Fields:\n");
1457 if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1458 DisplayFields(TO_CDADDR(taMT), &mtabledata, &vMethodTableFields, taObject, TRUE, TRUE);
1464 void PrintRuntimeTypeInfo(TADDR p_rtObject, const DacpObjectData & rtObjectData)
1466 // Get the method table
1467 int iOffset = GetObjFieldOffset(p_rtObject, rtObjectData.MethodTable, W("m_handle"));
1471 if (SUCCEEDED(GetMTOfObject(p_rtObject + iOffset, &mtPtr)))
1473 sos::MethodTable mt = mtPtr;
1474 ExtOut("Type Name: %S\n", mt.GetName());
1475 DMLOut("Type MT: %s\n", DMLMethodTable(mtPtr));
1480 HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE)
1482 if (!sos::IsObject(taObj, true))
1484 ExtOut("<Note: this object has an invalid CLASS field>\n");
1487 DacpObjectData objData;
1489 if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
1491 ExtOut("Invalid object\n");
1495 if (objData.ObjectType==OBJ_FREE)
1497 ExtOut("Free Object\n");
1498 DWORD_PTR size = (DWORD_PTR)objData.Size;
1499 ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1503 sos::Object obj = taObj;
1504 ExtOut("Name: %S\n", obj.GetTypeName());
1505 DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
1508 DacpMethodTableData mtabledata;
1509 if ((Status=mtabledata.Request(g_sos,objData.MethodTable)) == S_OK)
1511 DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
1515 ExtOut("Invalid EEClass address\n");
1519 if (objData.RCW != NULL)
1521 DMLOut("RCW: %s\n", DMLRCWrapper(objData.RCW));
1523 if (objData.CCW != NULL)
1525 DMLOut("CCW: %s\n", DMLCCWrapper(objData.CCW));
1528 DWORD_PTR size = (DWORD_PTR)objData.Size;
1529 ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1531 if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType")) == 0)
1533 PrintRuntimeTypeInfo(taObj, objData);
1536 if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType+RuntimeTypeCache")) == 0)
1538 // Get the method table
1539 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("m_runtimeType"));
1543 if (MOVE(rtPtr, taObj + iOffset) == S_OK)
1545 DacpObjectData rtObjectData;
1546 if ((Status=rtObjectData.Request(g_sos, TO_CDADDR(rtPtr))) != S_OK)
1548 ExtOut("Error when reading RuntimeType field\n");
1552 PrintRuntimeTypeInfo(rtPtr, rtObjectData);
1557 if (objData.ObjectType==OBJ_ARRAY)
1559 ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s",
1560 objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
1562 IfDMLOut(" (<exec cmd=\"!DumpArray /d %p\">Print Array</exec>)", SOS_PTR(taObj));
1565 if (objData.ElementType == ELEMENT_TYPE_I1 ||
1566 objData.ElementType == ELEMENT_TYPE_U1 ||
1567 objData.ElementType == ELEMENT_TYPE_CHAR)
1569 bool wide = objData.ElementType == ELEMENT_TYPE_CHAR;
1571 // Get the size of the character array, but clamp it to a reasonable length.
1572 TADDR pos = taObj + (2 * sizeof(DWORD_PTR));
1574 moveN(num, taObj + sizeof(DWORD_PTR));
1577 DMLOut("<exec cmd=\"%s %x L%x\">Content</exec>: ", (wide) ? "dw" : "db", pos, num);
1579 ExtOut("Content: ");
1580 CharArrayContent(pos, (ULONG)(num <= 128 ? num : 128), wide);
1586 FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
1587 ExtOut("File: %S\n", g_mdName[0] ? g_mdName : W("Unknown Module"));
1590 if (objData.ObjectType == OBJ_STRING)
1593 StringObjectContent(taObj);
1596 else if (objData.ObjectType == OBJ_OBJECT)
1603 DacpMethodTableFieldData vMethodTableFields;
1604 if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(objData.MethodTable)))!=S_OK)
1607 ExtOut("Fields:\n");
1608 if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1610 DisplayFields(objData.MethodTable, &mtabledata, &vMethodTableFields, taObj, TRUE, FALSE);
1618 sos::ThinLockInfo lockInfo;
1619 if (obj.GetThinLock(lockInfo))
1621 ExtOut("ThinLock owner %x (%p), Recursive %x\n", lockInfo.ThreadId,
1622 SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
1628 BOOL IndicesInRange (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
1631 if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
1633 ExtOut("<integer underflow>\n");
1639 if (indices[i] >= bounds[i] + lowerBounds[i])
1646 indices[i] = lowerBounds[i];
1654 void ExtOutIndices (DWORD * indices, DWORD rank)
1656 for (DWORD i = 0; i < rank; i++)
1658 ExtOut("[%d]", indices[i]);
1662 size_t OffsetFromIndices (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
1664 _ASSERTE(rank >= 0);
1665 size_t multiplier = 1;
1668 if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
1670 ExtOut("<integer underflow>\n");
1676 DWORD curIndex = indices[i] - lowerBounds[i];
1677 offset += curIndex * multiplier;
1678 multiplier *= bounds[i];
1683 HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint);
1685 HRESULT PrintPermissionSet (TADDR p_PermSet)
1687 HRESULT Status = S_OK;
1689 DacpObjectData PermSetData;
1690 if ((Status=PermSetData.Request(g_sos, TO_CDADDR(p_PermSet))) != S_OK)
1692 ExtOut("Invalid object\n");
1697 sos::MethodTable mt = TO_TADDR(PermSetData.MethodTable);
1698 if (_wcscmp (W("System.Security.PermissionSet"), mt.GetName()) != 0 && _wcscmp(W("System.Security.NamedPermissionSet"), mt.GetName()) != 0)
1700 ExtOut("Invalid PermissionSet object\n");
1704 ExtOut("PermissionSet object: %p\n", SOS_PTR(p_PermSet));
1708 // Walk the fields, printing some fields in a special way.
1710 int iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, W("m_Unrestricted"));
1715 MOVE(unrestricted, p_PermSet + iOffset);
1717 ExtOut("Unrestricted: TRUE\n");
1719 ExtOut("Unrestricted: FALSE\n");
1722 iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, W("m_permSet"));
1726 MOVE(tbSetPtr, p_PermSet + iOffset);
1727 if (tbSetPtr != NULL)
1729 DacpObjectData tbSetData;
1730 if ((Status=tbSetData.Request(g_sos, TO_CDADDR(tbSetPtr))) != S_OK)
1732 ExtOut("Invalid object\n");
1736 iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, W("m_Set"));
1739 DWORD_PTR PermsArrayPtr;
1740 MOVE(PermsArrayPtr, tbSetPtr + iOffset);
1741 if (PermsArrayPtr != NULL)
1743 // Print all the permissions in the array
1744 DacpObjectData objData;
1745 if ((Status=objData.Request(g_sos, TO_CDADDR(PermsArrayPtr))) != S_OK)
1747 ExtOut("Invalid object\n");
1750 DumpArrayFlags flags;
1751 flags.bDetail = TRUE;
1752 return PrintArray(objData, flags, TRUE);
1756 iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, W("m_Obj"));
1759 DWORD_PTR PermObjPtr;
1760 MOVE(PermObjPtr, tbSetPtr + iOffset);
1761 if (PermObjPtr != NULL)
1763 // Print the permission object
1764 return PrintObj(PermObjPtr);
1776 /**********************************************************************\
1777 * Routine Description: *
1779 * This function is called to dump the contents of an object from a *
1782 \**********************************************************************/
1783 DECLARE_API(DumpArray)
1787 DumpArrayFlags flags;
1789 MINIDUMP_NOT_SUPPORTED();
1793 CMDOption option[] =
1794 { // name, vptr, type, hasValue
1795 {"-start", &flags.startIndex, COSIZE_T, TRUE},
1796 {"-length", &flags.Length, COSIZE_T, TRUE},
1797 {"-details", &flags.bDetail, COBOOL, FALSE},
1798 {"-nofields", &flags.bNoFieldsForElement, COBOOL, FALSE},
1800 {"/d", &dml, COBOOL, FALSE},
1805 {&flags.strObject, COSTRING}
1808 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
1813 EnableDMLHolder dmlHolder(dml);
1814 DWORD_PTR p_Object = GetExpression (flags.strObject);
1817 ExtOut("Invalid parameter %s\n", flags.strObject);
1821 if (!sos::IsObject(p_Object, true))
1823 ExtOut("<Note: this object has an invalid CLASS field>\n");
1826 DacpObjectData objData;
1827 if ((Status=objData.Request(g_sos, TO_CDADDR(p_Object))) != S_OK)
1829 ExtOut("Invalid object\n");
1833 if (objData.ObjectType != OBJ_ARRAY)
1835 ExtOut("Not an array, please use !DumpObj instead\n");
1838 return PrintArray(objData, flags, FALSE);
1842 HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint)
1844 HRESULT Status = S_OK;
1846 if (objData.dwRank != 1 && (flags.Length != (DWORD_PTR)-1 ||flags.startIndex != 0))
1848 ExtOut("For multi-dimension array, length and start index are supported\n");
1852 if (flags.startIndex > objData.dwNumComponents)
1854 ExtOut("Start index out of range\n");
1858 if (!flags.bDetail && flags.bNoFieldsForElement)
1860 ExtOut("-nofields has no effect unless -details is specified\n");
1864 if (!isPermSetPrint)
1866 // TODO: don't depend on this being a MethodTable
1867 NameForMT_s(TO_TADDR(objData.ElementTypeHandle), g_mdName, mdNameLen);
1869 ExtOut("Name: %S[", g_mdName);
1870 for (i = 1; i < objData.dwRank; i++)
1874 DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
1877 DacpMethodTableData mtdata;
1878 if (SUCCEEDED(mtdata.Request(g_sos, objData.MethodTable)))
1880 DMLOut("EEClass: %s\n", DMLClass(mtdata.Class));
1884 DWORD_PTR size = (DWORD_PTR)objData.Size;
1885 ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1887 ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s\n",
1888 objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
1889 DMLOut("Element Methodtable: %s\n", DMLMethodTable(objData.ElementTypeHandle));
1892 BOOL isElementValueType = IsElementValueType(objData.ElementType);
1894 DWORD dwRankAllocSize;
1895 if (!ClrSafeInt<DWORD>::multiply(sizeof(DWORD), objData.dwRank, dwRankAllocSize))
1897 ExtOut("Integer overflow on array rank\n");
1901 DWORD *lowerBounds = (DWORD *)alloca(dwRankAllocSize);
1902 if (!SafeReadMemory(objData.ArrayLowerBoundsPtr, lowerBounds, dwRankAllocSize, NULL))
1904 ExtOut("Failed to read lower bounds info from the array\n");
1908 DWORD *bounds = (DWORD *)alloca(dwRankAllocSize);
1909 if (!SafeReadMemory (objData.ArrayBoundsPtr, bounds, dwRankAllocSize, NULL))
1911 ExtOut("Failed to read bounds info from the array\n");
1915 //length is only supported for single-dimension array
1916 if (objData.dwRank == 1 && flags.Length != (DWORD_PTR)-1)
1918 bounds[0] = _min(bounds[0], (DWORD)(flags.Length + flags.startIndex) - lowerBounds[0]);
1921 DWORD *indices = (DWORD *)alloca(dwRankAllocSize);
1922 for (i = 0; i < objData.dwRank; i++)
1924 indices[i] = lowerBounds[i];
1927 //start index is only supported for single-dimension array
1928 if (objData.dwRank == 1)
1930 indices[0] = (DWORD)flags.startIndex;
1933 //Offset should be calculated by OffsetFromIndices. However because of the way
1934 //how we grow indices, incrementing offset by one happens to match indices in every iteration
1935 for (size_t offset = OffsetFromIndices (indices, lowerBounds, bounds, objData.dwRank);
1936 IndicesInRange (indices, lowerBounds, bounds, objData.dwRank);
1937 indices[objData.dwRank - 1]++, offset++)
1941 ExtOut("interrupted by user\n");
1945 TADDR elementAddress = TO_TADDR(objData.ArrayDataPtr + offset * objData.dwComponentSize);
1946 TADDR p_Element = NULL;
1947 if (isElementValueType)
1949 p_Element = elementAddress;
1951 else if (!SafeReadMemory (elementAddress, &p_Element, sizeof (p_Element), NULL))
1953 ExtOut("Failed to read element at ");
1954 ExtOutIndices(indices, objData.dwRank);
1961 ExtOutIndices(indices, objData.dwRank);
1963 if (isElementValueType)
1965 DMLOut( " %s\n", DMLValueClass(objData.MethodTable, p_Element));
1969 DMLOut(" %s\n", DMLObject(p_Element));
1972 else if (!isPermSetPrint)
1974 ExtOutIndices(indices, objData.dwRank);
1981 if (isElementValueType)
1983 PrintVC(TO_TADDR(objData.ElementTypeHandle), elementAddress, !flags.bNoFieldsForElement);
1985 else if (p_Element != NULL)
1987 PrintObj(p_Element, !flags.bNoFieldsForElement);
1996 /**********************************************************************\
1997 * Routine Description: *
1999 * This function is called to dump the contents of an object from a *
2002 \**********************************************************************/
2003 DECLARE_API(DumpObj)
2007 MINIDUMP_NOT_SUPPORTED();
2010 BOOL bNoFields = FALSE;
2012 StringHolder str_Object;
2013 CMDOption option[] =
2014 { // name, vptr, type, hasValue
2015 {"-nofields", &bNoFields, COBOOL, FALSE},
2016 {"-refs", &bRefs, COBOOL, FALSE},
2018 {"/d", &dml, COBOOL, FALSE},
2023 {&str_Object.data, COSTRING}
2026 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2031 DWORD_PTR p_Object = GetExpression(str_Object.data);
2032 EnableDMLHolder dmlHolder(dml);
2035 ExtOut("Invalid parameter %s\n", args);
2039 Status = PrintObj(p_Object, !bNoFields);
2041 if (SUCCEEDED(Status) && bRefs)
2043 ExtOut("GC Refs:\n");
2044 TableOutput out(2, POINTERSIZE_HEX, AlignRight, 4);
2045 out.WriteRow("offset", "object");
2046 for (sos::RefIterator itr(TO_TADDR(p_Object)); itr; ++itr)
2047 out.WriteRow(Hex(itr.GetOffset()), ObjectPtr(*itr));
2053 CLRDATA_ADDRESS isExceptionObj(CLRDATA_ADDRESS mtObj)
2055 // We want to follow back until we get the mt for System.Exception
2056 DacpMethodTableData dmtd;
2057 CLRDATA_ADDRESS walkMT = mtObj;
2058 while(walkMT != NULL)
2060 if (dmtd.Request(g_sos, walkMT) != S_OK)
2064 if (walkMT == g_special_usefulGlobals.ExceptionMethodTable)
2068 walkMT = dmtd.ParentMethodTable;
2073 CLRDATA_ADDRESS isSecurityExceptionObj(CLRDATA_ADDRESS mtObj)
2075 // We want to follow back until we get the mt for System.Exception
2076 DacpMethodTableData dmtd;
2077 CLRDATA_ADDRESS walkMT = mtObj;
2078 while(walkMT != NULL)
2080 if (dmtd.Request(g_sos, walkMT) != S_OK)
2084 NameForMT_s(TO_TADDR(walkMT), g_mdName, mdNameLen);
2085 if (_wcscmp(W("System.Security.SecurityException"), g_mdName) == 0)
2089 walkMT = dmtd.ParentMethodTable;
2094 // Fill the passed in buffer with a text header for generated exception information.
2095 // Returns the number of characters in the wszBuffer array on exit.
2096 // If NULL is passed for wszBuffer, just returns the number of characters needed.
2097 size_t AddExceptionHeader (__out_ecount_opt(bufferLength) WCHAR *wszBuffer, size_t bufferLength)
2099 #ifdef _TARGET_WIN64_
2100 const WCHAR *wszHeader = W(" SP IP Function\n");
2102 const WCHAR *wszHeader = W(" SP IP Function\n");
2103 #endif // _TARGET_WIN64_
2106 swprintf_s(wszBuffer, bufferLength, wszHeader);
2108 return _wcslen(wszHeader);
2111 struct StackTraceElement
2115 DWORD_PTR pFunc; // MethodDesc
2116 // TRUE if this element represents the last frame of the foreign
2117 // exception stack trace.
2118 BOOL fIsLastFrameFromForeignStackTrace;
2122 #include "sos_stacktrace.h"
2131 cs.String()[0] = L'\0';
2134 BOOL Append(__in_z LPCWSTR pszStr)
2136 size_t iInputLen = _wcslen (pszStr);
2137 size_t iCurLen = _wcslen (cs.String());
2138 if ((iCurLen + iInputLen + 1) > cs.Size())
2140 if (cs.ReSize(iCurLen + iInputLen + 1) != S_OK)
2146 wcsncat_s (cs.String(), cs.Size(), pszStr, _TRUNCATE);
2152 return _wcslen(cs.String());
2161 static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp, ULONG64 IPAddr, StringOutput& so);
2163 // Using heuristics to determine if an exception object represented an async (hardware) or a
2164 // managed exception
2165 // We need to use these heuristics when the System.Exception object is not the active exception
2166 // on some thread, but it's something found somewhere on the managed heap.
2168 // uses the MapWin32FaultToCOMPlusException to figure out how we map async exceptions
2169 // to managed exceptions and their HRESULTs
2170 static const HRESULT AsyncHResultValues[] =
2172 COR_E_ARITHMETIC, // kArithmeticException
2173 COR_E_OVERFLOW, // kOverflowException
2174 COR_E_DIVIDEBYZERO, // kDivideByZeroException
2175 COR_E_FORMAT, // kFormatException
2176 COR_E_NULLREFERENCE, // kNullReferenceException
2177 E_POINTER, // kAccessViolationException
2178 // the EE is raising the next exceptions more often than the OS will raise an async
2179 // exception for these conditions, so in general treat these as Synchronous
2180 // COR_E_INDEXOUTOFRANGE, // kIndexOutOfRangeException
2181 // COR_E_OUTOFMEMORY, // kOutOfMemoryException
2182 // COR_E_STACKOVERFLOW, // kStackOverflowException
2183 COR_E_DATAMISALIGNED, // kDataMisalignedException
2186 BOOL IsAsyncException(TADDR taObj, TADDR mtObj)
2188 // by default we'll treat exceptions as synchronous
2189 UINT32 xcode = EXCEPTION_COMPLUS;
2190 int iOffset = GetObjFieldOffset (taObj, mtObj, W("_xcode"));
2193 HRESULT hr = MOVE(xcode, taObj + iOffset);
2196 xcode = EXCEPTION_COMPLUS;
2201 if (xcode == EXCEPTION_COMPLUS)
2204 iOffset = GetObjFieldOffset (taObj, mtObj, W("_HResult"));
2207 HRESULT hr = MOVE(ehr, taObj + iOffset);
2210 xcode = EXCEPTION_COMPLUS;
2213 for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
2215 if (ehr == AsyncHResultValues[idx])
2224 return xcode != EXCEPTION_COMPLUS;
2227 // Overload that mirrors the code above when the ExceptionObjectData was already retrieved from LS
2228 BOOL IsAsyncException(const DacpExceptionObjectData & excData)
2230 if (excData.XCode != EXCEPTION_COMPLUS)
2233 HRESULT ehr = excData.HResult;
2234 for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
2236 if (ehr == AsyncHResultValues[idx])
2246 #define SOS_STACKTRACE_SHOWEXPLICITFRAMES 0x00000002
2247 size_t FormatGeneratedException (DWORD_PTR dataPtr,
2249 __out_ecount_opt(bufferLength) WCHAR *wszBuffer,
2250 size_t bufferLength,
2252 BOOL bNestedCase = FALSE,
2253 BOOL bLineNumbers = FALSE)
2255 UINT count = bytes / sizeof(StackTraceElement);
2258 if (wszBuffer && bufferLength > 0)
2260 wszBuffer[0] = L'\0';
2263 // Buffer is calculated for sprintf below (" %p %p %S\n");
2264 WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2 + MAX_LONGPATH + 8];
2273 // If we are computing the call stack for a nested exception, we
2274 // don't want to print the last frame, because the outer exception
2275 // will have that frame.
2279 for (UINT i = 0; i < count; i++)
2281 StackTraceElement ste;
2282 MOVE (ste, dataPtr + i*sizeof(StackTraceElement));
2284 // ste.ip must be adjusted because of an ancient workaround in the exception
2285 // infrastructure. The workaround is that the exception needs to have
2286 // an ip address that will map to the line number where the exception was thrown.
2287 // (It doesn't matter that it's not a valid instruction). (see /vm/excep.cpp)
2289 // This "counterhack" is not 100% accurate
2290 // The biggest issue is that !PrintException must work with exception objects
2291 // that may not be currently active; as a consequence we cannot rely on the
2292 // state of some "current thread" to infer whether the IP values stored in
2293 // the exception object have been adjusted or not. If we could, we may examine
2294 // the topmost "Frame" and make the decision based on whether it's a
2295 // FaultingExceptionFrame or not.
2296 // 1. On IA64 the IP values are never adjusted by the EE so there's nothing
2299 // (a) if the exception was an async (hardware) exception add 1 to all
2300 // IP values in the exception object
2301 // (b) if the exception was a managed exception (either raised by the
2302 // EE or thrown by managed code) do not adjust any IP values
2304 // (a) if the exception was an async (hardware) exception add 1 to
2305 // all but the topmost IP value in the exception object
2306 // (b) if the exception was a managed exception (either raised by
2307 // the EE or thrown by managed code) add 1 to all IP values in
2308 // the exception object
2309 #if defined(_TARGET_AMD64_)
2314 #elif defined(_TARGET_X86_)
2315 if (IsDbgTargetX86() && (!bAsync || i != 0))
2319 #endif // defined(_TARGET_AMD64_) || defined(_TARGET__X86_)
2322 HRESULT Status = DumpMDInfoBuffer(ste.pFunc, SOS_STACKTRACE_SHOWADDRESSES|SOS_STACKTRACE_SHOWEXPLICITFRAMES, ste.sp, ste.ip, so);
2324 // If DumpMDInfoBuffer failed (due to out of memory or missing metadata),
2325 // or did not update so (when ste is an explicit frames), do not update wszBuffer
2328 WCHAR filename[MAX_LONGPATH] = W("");
2331 SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, _countof(filename))))
2333 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s [%s @ %d]\n"), so.String(), filename, linenum);
2337 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s\n"), so.String());
2340 Length += _wcslen(wszLineBuffer);
2344 wcsncat_s(wszBuffer, bufferLength, wszLineBuffer, _TRUNCATE);
2352 // ExtOut has an internal limit for the string size
2353 void SosExtOutLargeString(__inout_z __inout_ecount_opt(len) WCHAR * pwszLargeString, size_t len)
2355 const size_t chunkLen = 2048;
2357 WCHAR *pwsz = pwszLargeString; // beginning of a chunk
2358 size_t count = len/chunkLen;
2359 // write full chunks
2360 for (size_t idx = 0; idx < count; ++idx)
2362 WCHAR *pch = pwsz + chunkLen; // after the chunk
2363 // zero terminate the chunk
2369 // restore whacked char
2372 // advance to next chunk
2380 HRESULT FormatException(TADDR taObj, BOOL bLineNumbers = FALSE)
2382 HRESULT Status = S_OK;
2384 DacpObjectData objData;
2385 if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
2387 ExtOut("Invalid object\n");
2391 // Make sure it is an exception object, and get the MT of Exception
2392 CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
2393 if (exceptionMT == NULL)
2395 ExtOut("Not a valid exception object\n");
2399 DMLOut("Exception object: %s\n", DMLObject(taObj));
2401 if (NameForMT_s(TO_TADDR(objData.MethodTable), g_mdName, mdNameLen))
2403 ExtOut("Exception type: %S\n", g_mdName);
2407 ExtOut("Exception type: <Unknown>\n");
2412 // First try to get exception object data using ISOSDacInterface2
2413 DacpExceptionObjectData excData;
2414 BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, TO_CDADDR(taObj)));
2416 // Walk the fields, printing some fields in a special way.
2417 // HR, InnerException, Message, StackTrace, StackTraceString
2423 taMsg = TO_TADDR(excData.Message);
2427 int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_message"));
2430 MOVE (taMsg, taObj + iOffset);
2434 ExtOut("Message: ");
2437 StringObjectContent(taMsg);
2445 TADDR taInnerExc = 0;
2448 taInnerExc = TO_TADDR(excData.InnerException);
2452 int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_innerException"));
2455 MOVE (taInnerExc, taObj + iOffset);
2459 ExtOut("InnerException: ");
2463 if (SUCCEEDED(GetMTOfObject(taInnerExc, &taMT)))
2465 NameForMT_s(taMT, g_mdName, mdNameLen);
2466 ExtOut("%S, ", g_mdName);
2468 DMLOut("Use <exec cmd=\"!PrintException /d %p\">!PrintException %p</exec> to see more.\n", taInnerExc, taInnerExc);
2470 ExtOut("Use !PrintException %p to see more.\n", SOS_PTR(taInnerExc));
2474 ExtOut("<invalid MethodTable of inner exception>");
2483 BOOL bAsync = bGotExcData ? IsAsyncException(excData)
2484 : IsAsyncException(taObj, TO_TADDR(objData.MethodTable));
2487 TADDR taStackTrace = 0;
2490 taStackTrace = TO_TADDR(excData.StackTrace);
2494 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTrace"));
2497 MOVE(taStackTrace, taObj + iOffset);
2501 ExtOut("StackTrace (generated):\n");
2505 HRESULT hr = MOVE(arrayLen, taStackTrace + sizeof(DWORD_PTR));
2507 if (arrayLen != 0 && hr == S_OK)
2509 #ifdef _TARGET_WIN64_
2510 DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
2512 DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD);
2513 #endif // _TARGET_WIN64_
2514 size_t stackTraceSize = 0;
2515 MOVE (stackTraceSize, dataPtr);
2517 DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
2518 dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
2520 if (stackTraceSize == 0)
2522 ExtOut("Unable to decipher generated stack trace\n");
2526 size_t iHeaderLength = AddExceptionHeader (NULL, 0);
2527 size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, FALSE, bLineNumbers);
2528 WCHAR *pwszBuffer = new NOTHROW WCHAR[iHeaderLength + iLength + 1];
2531 AddExceptionHeader(pwszBuffer, iHeaderLength + 1);
2532 FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer + iHeaderLength, iLength + 1, bAsync, FALSE, bLineNumbers);
2533 SosExtOutLargeString(pwszBuffer, iHeaderLength + iLength + 1);
2534 delete[] pwszBuffer;
2541 ExtOut("<Not Available>\n");
2551 TADDR taStackString;
2554 taStackString = TO_TADDR(excData.StackTraceString);
2558 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTraceString"));
2559 MOVE (taStackString, taObj + iOffset);
2562 ExtOut("StackTraceString: ");
2565 StringObjectContent(taStackString);
2566 ExtOut("\n\n"); // extra newline looks better
2578 hResult = excData.HResult;
2582 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_HResult"));
2583 MOVE (hResult, taObj + iOffset);
2586 ExtOut("HResult: %lx\n", hResult);
2589 if (isSecurityExceptionObj(objData.MethodTable) != NULL)
2591 // We have a SecurityException Object: print out the debugString if present
2592 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("m_debugString"));
2595 TADDR taDebugString;
2596 MOVE (taDebugString, taObj + iOffset);
2600 ExtOut("SecurityException Message: ");
2601 StringObjectContent(taDebugString);
2602 ExtOut("\n\n"); // extra newline looks better
2610 DECLARE_API(PrintException)
2615 BOOL bShowNested = FALSE;
2616 BOOL bLineNumbers = FALSE;
2618 StringHolder strObject;
2619 CMDOption option[] =
2620 { // name, vptr, type, hasValue
2621 {"-nested", &bShowNested, COBOOL, FALSE},
2622 {"-lines", &bLineNumbers, COBOOL, FALSE},
2623 {"-l", &bLineNumbers, COBOOL, FALSE},
2624 {"-ccw", &bCCW, COBOOL, FALSE},
2626 {"/d", &dml, COBOOL, FALSE}
2631 {&strObject, COSTRING}
2634 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2641 if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
2643 symlines &= SYMOPT_LOAD_LINES;
2647 ExtOut("In order for the option -lines to enable display of source information\n"
2648 "the debugger must be configured to load the line number information from\n"
2649 "the symbol files. Use the \".lines; .reload\" command to achieve this.\n");
2651 bLineNumbers = FALSE;
2655 EnableDMLHolder dmlHolder(dml);
2656 DWORD_PTR p_Object = NULL;
2661 ExtOut("No CCW pointer specified\n");
2665 // Look at the last exception object on this thread
2667 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
2668 DacpThreadData Thread;
2670 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
2672 ExtOut("The current thread is unmanaged\n");
2676 DWORD_PTR dwAddr = NULL;
2677 if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
2679 sizeof(dwAddr), NULL)) || (dwAddr==NULL))
2681 ExtOut("There is no current managed exception on this thread\n");
2690 p_Object = GetExpression(strObject.data);
2695 ExtOut("Invalid CCW pointer %s\n", args);
2699 ExtOut("Invalid exception object %s\n", args);
2706 // check if the address is a CCW pointer and then
2707 // get the exception object from it
2708 DacpCCWData ccwData;
2709 if (ccwData.Request(g_sos, p_Object) == S_OK)
2711 p_Object = TO_TADDR(ccwData.managedObject);
2718 FormatException(p_Object, bLineNumbers);
2721 // Are there nested exceptions?
2722 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
2723 DacpThreadData Thread;
2725 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
2727 ExtOut("The current thread is unmanaged\n");
2731 if (Thread.firstNestedException)
2735 ExtOut("There are nested exceptions on this thread. Run with -nested for details\n");
2739 CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
2742 CLRDATA_ADDRESS obj = 0, next = 0;
2743 Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
2747 ExtOut("Error retrieving nested exception info %p\n", SOS_PTR(currentNested));
2753 ExtOut("<aborted>\n");
2757 ExtOut("\nNested exception -------------------------------------------------------------\n");
2758 Status = FormatException((DWORD_PTR) obj, bLineNumbers);
2764 currentNested = next;
2766 while(currentNested != NULL);
2771 /**********************************************************************\
2772 * Routine Description: *
2774 * This function is called to dump the contents of an object from a *
2777 \**********************************************************************/
2781 MINIDUMP_NOT_SUPPORTED();
2783 DWORD_PTR p_MT = NULL;
2784 DWORD_PTR p_Object = NULL;
2787 CMDOption option[] =
2788 { // name, vptr, type, hasValue
2790 {"/d", &dml, COBOOL, FALSE}
2799 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2804 EnableDMLHolder dmlHolder(dml);
2807 ExtOut("Usage: !DumpVC <Method Table> <Value object start addr>\n");
2811 if (!IsMethodTable(p_MT))
2813 ExtOut("Not a managed object\n");
2817 return PrintVC(p_MT, p_Object);
2822 #ifdef FEATURE_COMINTEROP
2824 DECLARE_API(DumpRCW)
2829 StringHolder strObject;
2831 CMDOption option[] =
2832 { // name, vptr, type, hasValue
2833 {"/d", &dml, COBOOL, FALSE}
2837 {&strObject, COSTRING}
2840 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2845 EnableDMLHolder dmlHolder(dml);
2848 ExtOut("Missing RCW address\n");
2853 DWORD_PTR p_RCW = GetExpression(strObject.data);
2856 ExtOut("Invalid RCW %s\n", args);
2860 DacpRCWData rcwData;
2861 if ((Status = rcwData.Request(g_sos, p_RCW)) != S_OK)
2863 ExtOut("Error requesting RCW data\n");
2867 if (FAILED(rcwData.IsDCOMProxy(g_sos, p_RCW, &isDCOMProxy)))
2869 isDCOMProxy = FALSE;
2872 DMLOut("Managed object: %s\n", DMLObject(rcwData.managedObject));
2873 DMLOut("Creating thread: %p\n", SOS_PTR(rcwData.creatorThread));
2874 ExtOut("IUnknown pointer: %p\n", SOS_PTR(rcwData.unknownPointer));
2875 ExtOut("COM Context: %p\n", SOS_PTR(rcwData.ctxCookie));
2876 ExtOut("Managed ref count: %d\n", rcwData.refCount);
2877 ExtOut("IUnknown V-table pointer : %p (captured at RCW creation time)\n", SOS_PTR(rcwData.vtablePtr));
2879 ExtOut("Flags: %s%s%s%s%s%s%s%s\n",
2880 (rcwData.isDisconnected? "IsDisconnected " : ""),
2881 (rcwData.supportsIInspectable? "SupportsIInspectable " : ""),
2882 (rcwData.isAggregated? "IsAggregated " : ""),
2883 (rcwData.isContained? "IsContained " : ""),
2884 (rcwData.isJupiterObject? "IsJupiterObject " : ""),
2885 (rcwData.isFreeThreaded? "IsFreeThreaded " : ""),
2886 (rcwData.identityPointer == TO_CDADDR(p_RCW)? "IsUnique " : ""),
2887 (isDCOMProxy ? "IsDCOMProxy " : "")
2890 // Jupiter data hidden by default
2891 if (rcwData.isJupiterObject)
2893 ExtOut("IJupiterObject: %p\n", SOS_PTR(rcwData.jupiterObject));
2896 ExtOut("COM interface pointers:\n");
2898 ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[rcwData.interfaceCount];
2905 if ((Status = g_sos->GetRCWInterfaces(p_RCW, rcwData.interfaceCount, pArray, NULL)) != S_OK)
2907 ExtOut("Error requesting COM interface pointers\n");
2911 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "Context", "MT");
2912 for (int i = 0; i < rcwData.interfaceCount; i++)
2914 // Ignore any NULL MethodTable interface cache. At this point only IJupiterObject
2915 // is saved as NULL MethodTable at first slot, and we've already printed outs its
2917 if (pArray[i].methodTable == NULL)
2920 NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
2922 DMLOut("%p %p %s %S\n", SOS_PTR(pArray[i].interfacePtr), SOS_PTR(pArray[i].comContext), DMLMethodTable(pArray[i].methodTable), g_mdName);
2930 DECLARE_API(DumpCCW)
2935 StringHolder strObject;
2937 CMDOption option[] =
2938 { // name, vptr, type, hasValue
2939 {"/d", &dml, COBOOL, FALSE}
2943 {&strObject, COSTRING}
2946 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2951 EnableDMLHolder dmlHolder(dml);
2954 ExtOut("Missing CCW address\n");
2959 DWORD_PTR p_CCW = GetExpression(strObject.data);
2962 ExtOut("Invalid CCW %s\n", args);
2966 DacpCCWData ccwData;
2967 if ((Status = ccwData.Request(g_sos, p_CCW)) != S_OK)
2969 ExtOut("Error requesting CCW data\n");
2973 if (ccwData.ccwAddress != p_CCW)
2974 ExtOut("CCW: %p\n", SOS_PTR(ccwData.ccwAddress));
2976 DMLOut("Managed object: %s\n", DMLObject(ccwData.managedObject));
2977 ExtOut("Outer IUnknown: %p\n", SOS_PTR(ccwData.outerIUnknown));
2978 ExtOut("Ref count: %d%s\n", ccwData.refCount, ccwData.isNeutered ? " (NEUTERED)" : "");
2979 ExtOut("Flags: %s%s\n",
2980 (ccwData.isExtendsCOMObject? "IsExtendsCOMObject " : ""),
2981 (ccwData.isAggregated? "IsAggregated " : "")
2984 // Jupiter information hidden by default
2985 if (ccwData.jupiterRefCount > 0)
2987 ExtOut("Jupiter ref count: %d%s%s%s%s\n",
2988 ccwData.jupiterRefCount,
2989 (ccwData.isPegged || ccwData.isGlobalPegged) ? ", Pegged by" : "",
2990 ccwData.isPegged ? " Jupiter " : "",
2991 (ccwData.isPegged && ccwData.isGlobalPegged) ? "&" : "",
2992 ccwData.isGlobalPegged ? " CLR " : ""
2996 ExtOut("RefCounted Handle: %p%s\n",
2997 SOS_PTR(ccwData.handle),
2998 (ccwData.hasStrongRef ? " (STRONG)" : " (WEAK)"));
3000 ExtOut("COM interface pointers:\n");
3002 ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[ccwData.interfaceCount];
3009 if ((Status = g_sos->GetCCWInterfaces(p_CCW, ccwData.interfaceCount, pArray, NULL)) != S_OK)
3011 ExtOut("Error requesting COM interface pointers\n");
3015 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "MT", "Type");
3016 for (int i = 0; i < ccwData.interfaceCount; i++)
3018 if (pArray[i].methodTable == NULL)
3020 wcscpy_s(g_mdName, mdNameLen, W("IDispatch/IUnknown"));
3024 NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
3027 DMLOut("%p %s %S\n", pArray[i].interfacePtr, DMLMethodTable(pArray[i].methodTable), g_mdName);
3035 #endif // FEATURE_COMINTEROP
3038 /**********************************************************************\
3039 * Routine Description: *
3041 * This function is called to dump the contents of a PermissionSet *
3042 * from a given address. *
3044 \**********************************************************************/
3046 COMMAND: dumppermissionset.
3047 !DumpPermissionSet <PermissionSet object address>
3049 This command allows you to examine a PermissionSet object. Note that you can
3050 also use DumpObj such an object in greater detail. DumpPermissionSet attempts
3051 to extract all the relevant information from a PermissionSet that you might be
3052 interested in when performing Code Access Security (CAS) related debugging.
3054 Here is a simple PermissionSet object:
3056 0:000> !DumpPermissionSet 014615f4
3057 PermissionSet object: 014615f4
3060 Note that this is an unrestricted PermissionSet object that does not contain
3061 any individual permissions.
3063 Here is another example of a PermissionSet object, one that is not unrestricted
3064 and contains a single permission:
3066 0:003> !DumpPermissionSet 01469fa8
3067 PermissionSet object: 01469fa8
3069 Name: System.Security.Permissions.ReflectionPermission
3070 MethodTable: 5b731308
3073 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.
3074 0.0__b77a5c561934e089\mscorlib.dll)
3077 MT Field Offset Type VT Attr Value Name
3078 5b73125c 4001d66 4 System.Int32 0 instance 2 m_flags
3080 Here is another example of an unrestricted PermissionSet, one that contains
3081 several permissions. The numbers in parentheses before each Permission object
3082 represents the index of that Permission in the PermissionSet.
3084 0:003> !DumpPermissionSet 01467bd8
3085 PermissionSet object: 01467bd8
3088 Name: System.Security.Permissions.FileDialogPermission
3089 MethodTable: 5b73023c
3092 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
3094 MT Field Offset Type VT Attr Value Name
3095 5b730190 4001cc2 4 System.Int32 0 instance 1 access
3097 Name: System.Security.Permissions.ReflectionPermission
3098 MethodTable: 5b731308
3101 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
3103 MT Field Offset Type VT Attr Value Name
3104 5b73125c 4001d66 4 System.Int32 0 instance 0 m_flags
3106 Name: System.Diagnostics.EventLogPermission
3107 MethodTable: 569841c4
3109 Size: 28(0x1c) bytes
3110 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3112 MT Field Offset Type VT Attr Value Name
3113 5b6d65d4 4003078 4 System.Object[] 0 instance 0146c190 tagNames
3114 5b6c9ed8 4003079 8 System.Type 0 instance 0146c17c permissionAccessType
3115 5b6cd928 400307a 10 System.Boolean 0 instance 0 isUnrestricted
3116 5b6c45f8 400307b c ...ections.Hashtable 0 instance 0146c1a4 rootTable
3117 5b6c090c 4003077 bfc System.String 0 static 00000000 computerName
3118 56984434 40030e7 14 ...onEntryCollection 0 instance 00000000 innerCollection
3120 Name: System.Net.WebPermission
3121 MethodTable: 5696dfc4
3123 Size: 20(0x14) bytes
3124 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3126 MT Field Offset Type VT Attr Value Name
3127 5b6cd928 400238e c System.Boolean 0 instance 0 m_Unrestricted
3128 5b6cd928 400238f d System.Boolean 0 instance 0 m_UnrestrictedConnect
3129 5b6cd928 4002390 e System.Boolean 0 instance 0 m_UnrestrictedAccept
3130 5b6c639c 4002391 4 ...ections.ArrayList 0 instance 0146cf3c m_connectList
3131 5b6c639c 4002392 8 ...ections.ArrayList 0 instance 0146cf54 m_acceptList
3132 569476f8 4002393 8a4 ...Expressions.Regex 0 static 00000000 s_MatchAllRegex
3134 Name: System.Net.DnsPermission
3135 MethodTable: 56966408
3138 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3140 MT Field Offset Type VT Attr Value Name
3141 5b6cd928 4001d2c 4 System.Boolean 0 instance 1 m_noRestriction
3143 Name: System.Web.AspNetHostingPermission
3144 MethodTable: 569831bc
3147 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3149 MT Field Offset Type VT Attr Value Name
3150 56983090 4003074 4 System.Int32 0 instance 600 _level
3152 Name: System.Net.NetworkInformation.NetworkInformationPermission
3153 MethodTable: 5697ac70
3155 Size: 16(0x10) bytes
3156 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3158 MT Field Offset Type VT Attr Value Name
3159 5697ab38 4002c34 4 System.Int32 0 instance 0 access
3160 5b6cd928 4002c35 8 System.Boolean 0 instance 0 unrestricted
3163 The abbreviation !dps can be used for brevity.
3167 DECLARE_API(DumpPermissionSet)
3170 MINIDUMP_NOT_SUPPORTED();
3172 DWORD_PTR p_Object = NULL;
3179 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
3185 ExtOut("Usage: !DumpPermissionSet <PermissionSet object addr>\n");
3190 return PrintPermissionSet(p_Object);
3195 void GCPrintGenerationInfo(DacpGcHeapDetails &heap);
3196 void GCPrintSegmentInfo(DacpGcHeapDetails &heap, DWORD_PTR &total_size);
3198 #endif // FEATURE_PAL
3200 void DisplayInvalidStructuresMessage()
3202 ExtOut("The garbage collector data structures are not in a valid state for traversal.\n");
3203 ExtOut("It is either in the \"plan phase,\" where objects are being moved around, or\n");
3204 ExtOut("we are at the initialization or shutdown of the gc heap. Commands related to \n");
3205 ExtOut("displaying, finding or traversing objects as well as gc heap segments may not \n");
3206 ExtOut("work properly. !dumpheap and !verifyheap may incorrectly complain of heap \n");
3207 ExtOut("consistency errors.\n");
3210 /**********************************************************************\
3211 * Routine Description: *
3213 * This function dumps GC heap size. *
3215 \**********************************************************************/
3219 MINIDUMP_NOT_SUPPORTED();
3222 BOOL showgc = FALSE;
3223 BOOL showloader = FALSE;
3225 CMDOption option[] =
3226 { // name, vptr, type, hasValue
3227 {"-gc", &showgc, COBOOL, FALSE},
3228 {"-loader", &showloader, COBOOL, FALSE},
3229 {"/d", &dml, COBOOL, FALSE},
3232 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
3237 EnableDMLHolder dmlHolder(dml);
3238 if (showloader || !showgc)
3241 DWORD_PTR allHeapSize = 0;
3242 DWORD_PTR wasted = 0;
3243 DacpAppDomainStoreData adsData;
3244 if ((Status=adsData.Request(g_sos))!=S_OK)
3246 ExtOut("Unable to get AppDomain information\n");
3250 // The first one is the system domain.
3251 ExtOut("Loader Heap:\n");
3252 IfFailRet(PrintDomainHeapInfo("System Domain", adsData.systemDomain, &allHeapSize, &wasted));
3253 IfFailRet(PrintDomainHeapInfo("Shared Domain", adsData.sharedDomain, &allHeapSize, &wasted));
3255 ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
3263 if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
3265 ExtOut("Unable to get the array of all AppDomains.\n");
3269 for (int n=0;n<adsData.DomainCount;n++)
3275 sprintf_s(domain, _countof(domain), "Domain %d", n+1);
3277 IfFailRet(PrintDomainHeapInfo(domain, pArray[n], &allHeapSize, &wasted));
3282 ExtOut("--------------------------------------\n");
3283 ExtOut("Jit code heap:\n");
3285 if (IsMiniDumpFile())
3287 ExtOut("<no information>\n");
3291 allHeapSize += JitHeapInfo();
3298 ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(NULL, &numModule);
3299 if (moduleList == NULL)
3301 ExtOut("Failed to request module list.\n");
3305 // Module Thunk Heaps
3306 ExtOut("--------------------------------------\n");
3307 ExtOut("Module Thunk heaps:\n");
3308 allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_ThunkHeap, &wasted);
3310 // Module Lookup Table Heaps
3311 ExtOut("--------------------------------------\n");
3312 ExtOut("Module Lookup Table heaps:\n");
3313 allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_LookupTableHeap, &wasted);
3317 ExtOut("--------------------------------------\n");
3318 ExtOut("Total LoaderHeap size: ");
3319 PrintHeapSize(allHeapSize, wasted);
3320 ExtOut("=======================================\n");
3323 if (showgc || !showloader)
3328 if (!GetGcStructuresValid())
3330 DisplayInvalidStructuresMessage();
3333 DacpGcHeapData gcheap;
3334 if (gcheap.Request(g_sos) != S_OK)
3336 ExtOut("Error requesting GC Heap data\n");
3340 if (gcheap.bServerMode)
3342 dwNHeaps = gcheap.HeapCount;
3345 ExtOut("Number of GC Heaps: %d\n", dwNHeaps);
3346 DWORD_PTR totalSize = 0;
3347 if (!gcheap.bServerMode)
3349 DacpGcHeapDetails heapDetails;
3350 if (heapDetails.Request(g_sos) != S_OK)
3352 ExtOut("Error requesting details\n");
3356 GCHeapInfo (heapDetails, totalSize);
3357 ExtOut("Total Size: ");
3358 PrintHeapSize(totalSize, 0);
3363 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
3365 ExtOut("Failed to get GCHeaps: integer overflow\n");
3369 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
3370 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
3372 ExtOut("Failed to get GCHeaps\n");
3377 for (n = 0; n < dwNHeaps; n ++)
3379 DacpGcHeapDetails heapDetails;
3380 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
3382 ExtOut("Error requesting details\n");
3385 ExtOut("------------------------------\n");
3386 ExtOut("Heap %d (%p)\n", n, SOS_PTR(heapAddrs[n]));
3387 DWORD_PTR heapSize = 0;
3388 GCHeapInfo (heapDetails, heapSize);
3389 totalSize += heapSize;
3390 ExtOut("Heap Size: " WIN86_8SPACES);
3391 PrintHeapSize(heapSize, 0);
3394 ExtOut("------------------------------\n");
3395 ExtOut("GC Heap Size: " WIN86_8SPACES);
3396 PrintHeapSize(totalSize, 0);
3401 void PrintGCStat(HeapStat *inStat, const char* label=NULL)
3405 bool sorted = false;
3410 inStat->Print(label);
3414 ExtOut("Exception occurred while trying to %s the GC stats.\n", sorted ? "print" : "sort");
3423 DECLARE_API(TraverseHeap)
3426 MINIDUMP_NOT_SUPPORTED();
3428 BOOL bXmlFormat = FALSE;
3429 BOOL bVerify = FALSE;
3430 StringHolder Filename;
3432 CMDOption option[] =
3433 { // name, vptr, type, hasValue
3434 {"-xml", &bXmlFormat, COBOOL, FALSE},
3435 {"-verify", &bVerify, COBOOL, FALSE},
3439 {&Filename.data, COSTRING},
3442 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
3449 ExtOut("usage: HeapTraverse [-xml] filename\n");
3453 if (!g_snapshot.Build())
3455 ExtOut("Unable to build snapshot of the garbage collector state\n");
3460 if (fopen_s(&file, Filename.data, "w") != 0) {
3461 ExtOut("Unable to open file\n");
3466 ExtOut("Assuming a uncorrupted GC heap. If this is a crash dump consider -verify option\n");
3468 HeapTraverser traverser(bVerify != FALSE);
3470 ExtOut("Writing %s format to file %s\n", bXmlFormat ? "Xml" : "CLRProfiler", Filename.data);
3471 ExtOut("Gathering types...\n");
3473 // TODO: there may be a canonical list of methodtables in the runtime that we can
3474 // traverse instead of exploring the gc heap for that list. We could then simplify the
3475 // tree structure to a sorted list of methodtables, and the index is the ID.
3477 // TODO: "Traversing object members" code should be generalized and shared between
3478 // !gcroot and !traverseheap. Also !dumpheap can begin using GCHeapsTraverse.
3480 if (!traverser.Initialize())
3482 ExtOut("Error initializing heap traversal\n");
3487 if (!traverser.CreateReport (file, bXmlFormat ? FORMAT_XML : FORMAT_CLRPROFILER))
3489 ExtOut("Unable to write heap report\n");
3495 ExtOut("\nfile %s saved\n", Filename.data);
3500 #endif // FEATURE_PAL
3502 struct PrintRuntimeTypeArgs
3504 DWORD_PTR mtOfRuntimeType;
3505 int handleFieldOffset;
3506 DacpAppDomainStoreData adstore;
3509 void PrintRuntimeTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token)
3511 PrintRuntimeTypeArgs *pArgs = (PrintRuntimeTypeArgs *)token;
3513 if (pArgs->mtOfRuntimeType == NULL)
3515 NameForMT_s(methodTable, g_mdName, mdNameLen);
3517 if (_wcscmp(g_mdName, W("System.RuntimeType")) == 0)
3519 pArgs->mtOfRuntimeType = methodTable;
3520 pArgs->handleFieldOffset = GetObjFieldOffset(objAddr, methodTable, W("m_handle"));
3521 if (pArgs->handleFieldOffset <= 0)
3522 ExtOut("Error getting System.RuntimeType.m_handle offset\n");
3524 pArgs->adstore.Request(g_sos);
3528 if ((methodTable == pArgs->mtOfRuntimeType) && (pArgs->handleFieldOffset > 0))
3530 // Get the method table and display the information.
3532 if (MOVE(mtPtr, objAddr + pArgs->handleFieldOffset) == S_OK)
3534 DMLOut(DMLObject(objAddr));
3536 CLRDATA_ADDRESS appDomain = GetAppDomainForMT(mtPtr);
3537 if (appDomain != NULL)
3539 if (appDomain == pArgs->adstore.sharedDomain)
3540 ExtOut(" %" POINTERSIZE "s", "Shared");
3542 else if (appDomain == pArgs->adstore.systemDomain)
3543 ExtOut(" %" POINTERSIZE "s", "System");
3545 DMLOut(" %s", DMLDomain(appDomain));
3549 ExtOut(" %" POINTERSIZE "s", "?");
3552 NameForMT_s(mtPtr, g_mdName, mdNameLen);
3553 DMLOut(" %s %S\n", DMLMethodTable(mtPtr), g_mdName);
3559 DECLARE_API(DumpRuntimeTypes)
3562 MINIDUMP_NOT_SUPPORTED();
3566 CMDOption option[] =
3567 { // name, vptr, type, hasValue
3568 {"/d", &dml, COBOOL, FALSE},
3571 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
3574 EnableDMLHolder dmlHolder(dml);
3576 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type Name \n",
3577 "Address", "Domain", "MT");
3578 ExtOut("------------------------------------------------------------------------------\n");
3580 PrintRuntimeTypeArgs pargs;
3581 ZeroMemory(&pargs, sizeof(PrintRuntimeTypeArgs));
3583 GCHeapsTraverse(PrintRuntimeTypes, (LPVOID)&pargs);
3587 #define MIN_FRAGMENTATIONBLOCK_BYTES (1024*512)
3590 class FragmentationBlock
3593 FragmentationBlock(TADDR addr, size_t size, TADDR next, TADDR mt)
3594 : mAddress(addr), mSize(size), mNext(next), mNextMT(mt)
3598 inline TADDR GetAddress() const
3602 inline size_t GetSize() const
3607 inline TADDR GetNextObject() const
3612 inline TADDR GetNextMT() const
3628 DumpHeapImpl(PCSTR args)
3629 : mStart(0), mStop(0), mMT(0), mMinSize(0), mMaxSize(~0),
3630 mStat(FALSE), mStrings(FALSE), mVerify(FALSE),
3631 mThinlock(FALSE), mShort(FALSE), mDML(FALSE),
3632 mLive(FALSE), mDead(FALSE), mType(NULL)
3634 ArrayHolder<char> type = NULL;
3637 CMDOption option[] =
3638 { // name, vptr, type, hasValue
3639 {"-mt", &mMT, COHEX, TRUE}, // dump objects with a given MethodTable
3640 {"-type", &type, COSTRING, TRUE}, // list objects of specified type
3641 {"-stat", &mStat, COBOOL, FALSE}, // dump a summary of types and the number of instances of each
3642 {"-strings", &mStrings, COBOOL, FALSE}, // dump a summary of string objects
3643 {"-verify", &mVerify, COBOOL, FALSE}, // verify heap objects (!heapverify)
3644 {"-thinlock", &mThinlock, COBOOL, FALSE},// list only thinlocks
3645 {"-short", &mShort, COBOOL, FALSE}, // list only addresses
3646 {"-min", &mMinSize, COHEX, TRUE}, // min size of objects to display
3647 {"-max", &mMaxSize, COHEX, TRUE}, // max size of objects to display
3648 {"-live", &mLive, COHEX, FALSE}, // only print live objects
3649 {"-dead", &mDead, COHEX, FALSE}, // only print dead objects
3651 {"/d", &mDML, COBOOL, FALSE}, // Debugger Markup Language
3662 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArgs))
3663 sos::Throw<sos::Exception>("Failed to parse command line arguments.");
3669 mStop = sos::GCHeap::HeapEnd;
3673 sos::Throw<sos::Exception>("Cannot specify both -mt and -type");
3678 sos::Throw<sos::Exception>("Cannot specify both -live and -dead.");
3681 if (mMinSize > mMaxSize)
3683 sos::Throw<sos::Exception>("wrong argument");
3686 // If the user gave us a type, convert it to unicode and clean up "type".
3687 if (type && !mStrings)
3689 size_t iLen = strlen(type) + 1;
3690 mType = new WCHAR[iLen];
3691 MultiByteToWideChar(CP_ACP, 0, type, -1, mType, (int)iLen);
3703 // enable Debugger Markup Language
3704 EnableDMLHolder dmlholder(mDML);
3707 if (!gcheap.AreGCStructuresValid())
3708 DisplayInvalidStructuresMessage();
3710 if (IsMiniDumpFile())
3712 ExtOut("In a minidump without full memory, most gc heap structures will not be valid.\n");
3713 ExtOut("If you need this functionality, get a full memory dump with \".dump /ma mydump.dmp\"\n");
3720 mLiveness = gcroot.GetLiveObjects();
3724 // Some of the "specialty" versions of DumpHeap have slightly
3725 // different implementations than the standard version of DumpHeap.
3726 // We seperate them out to not clutter the standard DumpHeap function.
3728 DumpHeapShort(gcheap);
3730 DumpHeapThinlock(gcheap);
3732 DumpHeapStrings(gcheap);
3737 ValidateSyncTable(gcheap);
3740 static bool ValidateSyncTable(sos::GCHeap &gcheap)
3742 bool succeeded = true;
3743 for (sos::SyncBlkIterator itr; itr; ++itr)
3745 sos::CheckInterrupt();
3749 if (!sos::IsObject(itr->GetObject(), true))
3751 ExtOut("SyncBlock %d corrupted, points to invalid object %p\n",
3752 itr->GetIndex(), SOS_PTR(itr->GetObject()));
3757 // Does the object header point to this syncblock index?
3758 sos::Object obj = itr->GetObject();
3761 if (!obj.TryGetHeader(header))
3763 ExtOut("Failed to get object header for object %p while inspecting syncblock at index %d.\n",
3764 SOS_PTR(itr->GetObject()), itr->GetIndex());
3770 if ((header & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0 && (header & BIT_SBLK_IS_HASHCODE) == 0)
3772 ULONG index = header & MASK_SYNCBLOCKINDEX;
3773 valid = (ULONG)itr->GetIndex() == index;
3778 ExtOut("Object header for %p should have a SyncBlock index of %d.\n",
3779 SOS_PTR(itr->GetObject()), itr->GetIndex());
3790 DumpHeapImpl(const DumpHeapImpl &);
3792 bool Verify(const sos::ObjectIterator &itr)
3797 if (!itr.Verify(buffer, _countof(buffer)))
3807 bool IsCorrectType(const sos::Object &obj)
3810 return mMT == obj.GetMT();
3814 WString name = obj.GetTypeName();
3815 return _wcsstr(name.c_str(), mType) != NULL;
3821 bool IsCorrectSize(const sos::Object &obj)
3823 size_t size = obj.GetSize();
3824 return size >= mMinSize && size <= mMaxSize;
3827 bool IsCorrectLiveness(const sos::Object &obj)
3830 if (mLive && mLiveness.find(obj.GetAddress()) == mLiveness.end())
3833 if (mDead && (mLiveness.find(obj.GetAddress()) != mLiveness.end() || obj.IsFree()))
3841 inline void PrintHeader()
3843 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s\n", "Address", "MT", "Size");
3846 void DumpHeap(sos::GCHeap &gcheap)
3850 // For heap fragmentation tracking.
3851 TADDR lastFreeObj = NULL;
3852 size_t lastFreeSize = 0;
3857 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3862 bool onLOH = itr.IsCurrObjectOnLOH();
3864 // Check for free objects to report fragmentation
3865 if (lastFreeObj != NULL)
3866 ReportFreeObject(lastFreeObj, lastFreeSize, itr->GetAddress(), itr->GetMT());
3868 if (!onLOH && itr->IsFree())
3871 lastFreeSize = itr->GetSize();
3878 if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
3880 stats.Add((DWORD_PTR)itr->GetMT(), (DWORD)itr->GetSize());
3882 DMLOut("%s %s %8d%s\n", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize(),
3883 itr->IsFree() ? " Free":" ");
3893 PrintFragmentationReport();
3896 struct StringSetEntry
3898 StringSetEntry() : count(0), size(0)
3903 StringSetEntry(__in_ecount(64) WCHAR tmp[64], size_t _size)
3904 : count(1), size(_size)
3906 memcpy(str, tmp, sizeof(str));
3909 void Add(size_t _size) const
3915 mutable size_t count;
3916 mutable size_t size;
3919 bool operator<(const StringSetEntry &rhs) const
3921 return _wcscmp(str, rhs.str) < 0;
3926 static bool StringSetCompare(const StringSetEntry &a1, const StringSetEntry &a2)
3928 return a1.size < a2.size;
3931 void DumpHeapStrings(sos::GCHeap &gcheap)
3934 ExtOut("Not implemented.\n");
3936 const int offset = sos::Object::GetStringDataOffset();
3937 typedef std::set<StringSetEntry> Set;
3938 Set set; // A set keyed off of the string's text
3940 StringSetEntry tmp; // Temp string used to keep track of the set
3943 TableOutput out(3, POINTERSIZE_HEX, AlignRight);
3944 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3949 if (itr->IsString() && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
3951 CLRDATA_ADDRESS addr = itr->GetAddress();
3952 size_t size = itr->GetSize();
3955 out.WriteRow(ObjectPtr(addr), Pointer(itr->GetMT()), Decimal(size));
3957 // Don't bother calculating the size of the string, just read the full 64 characters of the buffer. The null
3958 // terminator we read will terminate the string.
3959 HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(addr+offset), tmp.str, sizeof(WCHAR)*(_countof(tmp.str)-1), &fetched);
3962 // Ensure we null terminate the string. Note that this will not overrun the buffer as we only
3963 // wrote a max of 63 characters into the 64 character buffer.
3964 tmp.str[fetched/sizeof(WCHAR)] = 0;
3965 Set::iterator sitr = set.find(tmp);
3966 if (sitr == set.end())
3982 // Now flatten the set into a vector. This is much faster than keeping two sets, or using a multimap.
3983 typedef std::vector<StringSetEntry> Vect;
3984 Vect v(set.begin(), set.end());
3985 std::sort(v.begin(), v.end(), &DumpHeapImpl::StringSetCompare);
3987 // Now print out the data. The call to Flatten ensures that we don't print newlines to break up the
3988 // output in strange ways.
3989 for (Vect::iterator vitr = v.begin(); vitr != v.end(); ++vitr)
3994 Flatten(vitr->str, (unsigned int)_wcslen(vitr->str));
3995 out.WriteRow(Decimal(vitr->size), Decimal(vitr->count), vitr->str);
3997 #endif // FEATURE_PAL
4000 void DumpHeapShort(sos::GCHeap &gcheap)
4002 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
4007 if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
4008 DMLOut("%s\n", DMLObject(itr->GetAddress()));
4012 void DumpHeapThinlock(sos::GCHeap &gcheap)
4017 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
4022 sos::ThinLockInfo lockInfo;
4023 if (IsCorrectType(*itr) && itr->GetThinLock(lockInfo))
4025 DMLOut("%s %s %8d", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize());
4026 ExtOut(" ThinLock owner %x (%p) Recursive %x\n", lockInfo.ThreadId,
4027 SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
4033 ExtOut("Found %d objects.\n", count);
4056 #if !defined(FEATURE_PAL)
4058 std::unordered_set<TADDR> mLiveness;
4059 typedef std::list<sos::FragmentationBlock> FragmentationList;
4060 FragmentationList mFrag;
4062 void InitFragmentationList()
4067 void ReportFreeObject(TADDR addr, size_t size, TADDR next, TADDR mt)
4069 if (size >= MIN_FRAGMENTATIONBLOCK_BYTES)
4070 mFrag.push_back(sos::FragmentationBlock(addr, size, next, mt));
4073 void PrintFragmentationReport()
4075 if (mFrag.size() > 0)
4077 ExtOut("Fragmented blocks larger than 0.5 MB:\n");
4078 ExtOut("%" POINTERSIZE "s %8s %16s\n", "Addr", "Size", "Followed by");
4080 for (FragmentationList::const_iterator itr = mFrag.begin(); itr != mFrag.end(); ++itr)
4082 sos::MethodTable mt = itr->GetNextMT();
4083 ExtOut("%p %6.1fMB " WIN64_8SPACES "%p %S\n",
4084 SOS_PTR(itr->GetAddress()),
4085 ((double)itr->GetSize()) / 1024.0 / 1024.0,
4086 SOS_PTR(itr->GetNextObject()),
4092 void InitFragmentationList() {}
4093 void ReportFreeObject(TADDR, TADDR, size_t, TADDR) {}
4094 void PrintFragmentationReport() {}
4098 /**********************************************************************\
4099 * Routine Description: *
4101 * This function dumps all objects on GC heap. It also displays *
4102 * statistics of objects. If GC heap is corrupted, it will stop at
4103 * the bad place. (May not work if GC is in progress.) *
4105 \**********************************************************************/
4106 DECLARE_API(DumpHeap)
4109 MINIDUMP_NOT_SUPPORTED();
4111 if (!g_snapshot.Build())
4113 ExtOut("Unable to build snapshot of the garbage collector state\n");
4119 DumpHeapImpl dumpHeap(args);
4124 catch(const sos::Exception &e)
4126 ExtOut("%s\n", e.what());
4131 DECLARE_API(VerifyHeap)
4134 MINIDUMP_NOT_SUPPORTED();
4136 if (!g_snapshot.Build())
4138 ExtOut("Unable to build snapshot of the garbage collector state\n");
4144 bool succeeded = true;
4147 sos::ObjectIterator itr = gcheap.WalkHeap();
4151 if (itr.Verify(buffer, _countof(buffer)))
4159 itr.MoveToNextObjectCarefully();
4163 if (!DumpHeapImpl::ValidateSyncTable(gcheap))
4167 ExtOut("No heap corruption detected.\n");
4171 catch(const sos::Exception &e)
4173 ExtOut("%s\n", e.what());
4180 enum failure_get_memory
4183 fgm_reserve_segment = 1,
4184 fgm_commit_segment_beg = 2,
4185 fgm_commit_eph_segment = 3,
4187 fgm_commit_table = 5
4194 oom_cant_commit = 2,
4195 oom_cant_reserve = 3,
4198 oom_unproductive_full_gc = 6
4201 static const char *const str_oom[] =
4203 "There was no managed OOM due to allocations on the GC heap", // oom_no_failure
4204 "This is likely to be a bug in GC", // oom_budget
4205 "Didn't have enough memory to commit", // oom_cant_commit
4206 "This is likely to be a bug in GC", // oom_cant_reserve
4207 "Didn't have enough memory to allocate an LOH segment", // oom_loh
4208 "Low on memory during GC", // oom_low_mem
4209 "Could not do a full GC" // oom_unproductive_full_gc
4212 static const char *const str_fgm[] =
4214 "There was no failure to allocate memory", // fgm_no_failure
4215 "Failed to reserve memory", // fgm_reserve_segment
4216 "Didn't have enough memory to commit beginning of the segment", // fgm_commit_segment_beg
4217 "Didn't have enough memory to commit the new ephemeral segment", // fgm_commit_eph_segment
4218 "Didn't have enough memory to grow the internal GC datastructures", // fgm_grow_table
4219 "Didn't have enough memory to commit the internal GC datastructures", // fgm_commit_table
4222 void PrintOOMInfo(DacpOomData* oomData)
4224 ExtOut("Managed OOM occurred after GC #%d (Requested to allocate %d bytes)\n",
4225 oomData->gc_index, oomData->alloc_size);
4227 if ((oomData->reason == oom_budget) ||
4228 (oomData->reason == oom_cant_reserve))
4230 // TODO: This message needs to be updated with more precious info.
4231 ExtOut("%s, please contact PSS\n", str_oom[oomData->reason]);
4235 ExtOut("Reason: %s\n", str_oom[oomData->reason]);
4238 // Now print out the more detailed memory info if any.
4239 if (oomData->fgm != fgm_no_failure)
4241 ExtOut("Detail: %s: %s (%d bytes)",
4242 (oomData->loh_p ? "LOH" : "SOH"),
4243 str_fgm[oomData->fgm],
4246 if ((oomData->fgm == fgm_commit_segment_beg) ||
4247 (oomData->fgm == fgm_commit_eph_segment) ||
4248 (oomData->fgm == fgm_grow_table) ||
4249 (oomData->fgm == fgm_commit_table))
4251 // If it's a commit error (fgm_grow_table can indicate a reserve
4252 // or a commit error since we make one VirtualAlloc call to
4253 // reserve and commit), we indicate the available commit
4254 // space if we recorded it.
4255 if (oomData->available_pagefile_mb)
4257 ExtOut(" - on GC entry available commit space was %d MB",
4258 oomData->available_pagefile_mb);
4266 DECLARE_API(AnalyzeOOM)
4269 MINIDUMP_NOT_SUPPORTED();
4273 if (!InitializeHeapData ())
4275 ExtOut("GC Heap not initialized yet.\n");
4279 BOOL bHasManagedOOM = FALSE;
4280 DacpOomData oomData;
4281 memset (&oomData, 0, sizeof(oomData));
4282 if (!IsServerBuild())
4284 if (oomData.Request(g_sos) != S_OK)
4286 ExtOut("Error requesting OOM data\n");
4289 if (oomData.reason != oom_no_failure)
4291 bHasManagedOOM = TRUE;
4292 PrintOOMInfo(&oomData);
4297 DWORD dwNHeaps = GetGcHeapCount();
4299 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
4301 ExtOut("Failed to get GCHeaps: integer overflow\n");
4305 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
4306 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
4308 ExtOut("Failed to get GCHeaps\n");
4312 for (DWORD n = 0; n < dwNHeaps; n ++)
4314 if (oomData.Request(g_sos, heapAddrs[n]) != S_OK)
4316 ExtOut("Heap %d: Error requesting OOM data\n", n);
4319 if (oomData.reason != oom_no_failure)
4321 if (!bHasManagedOOM)
4323 bHasManagedOOM = TRUE;
4325 ExtOut("---------Heap %#-2d---------\n", n);
4326 PrintOOMInfo(&oomData);
4331 if (!bHasManagedOOM)
4333 ExtOut("%s\n", str_oom[oomData.reason]);
4340 #endif // FEATURE_PAL
4343 DECLARE_API(VerifyObj)
4346 MINIDUMP_NOT_SUPPORTED();
4352 BOOL bValid = FALSE;
4355 CMDOption option[] =
4356 { // name, vptr, type, hasValue
4357 {"/d", &dml, COBOOL, FALSE},
4364 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
4369 EnableDMLHolder dmlHolder(dml);
4370 BOOL bContainsPointers;
4372 if (FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
4373 !GetSizeEfficient(taddrObj, taddrMT, FALSE, objSize, bContainsPointers))
4375 ExtOut("object %#p does not have valid method table\n", SOS_PTR(taddrObj));
4379 // we need to build g_snapshot as it is later used in GetGeneration
4380 if (!g_snapshot.Build())
4382 ExtOut("Unable to build snapshot of the garbage collector state\n");
4386 DacpGcHeapDetails *pheapDetails = g_snapshot.GetHeap(taddrObj);
4387 bValid = VerifyObject(*pheapDetails, taddrObj, taddrMT, objSize, TRUE);
4393 ExtOut("object %#p is a valid object\n", SOS_PTR(taddrObj));
4399 void LNODisplayOutput(LPCWSTR tag, TADDR pMT, TADDR currentObj, size_t size)
4401 sos::Object obj(currentObj, pMT);
4402 DMLOut("%S %s %12d (0x%x)\t%S\n", tag, DMLObject(currentObj), size, size, obj.GetTypeName());
4405 DECLARE_API(ListNearObj)
4408 MINIDUMP_NOT_SUPPORTED();
4410 #if !defined(FEATURE_PAL)
4414 // we may want to provide a more exact version of searching for the
4415 // previous object in the heap, using the brick table, instead of
4416 // looking for what may be valid method tables...
4418 //CMDOption option[] =
4420 // // name, vptr, type, hasValue
4421 // {"-exact", &bExact, COBOOL, FALSE}
4425 CMDOption option[] =
4426 { // name, vptr, type, hasValue
4427 {"/d", &dml, COBOOL, FALSE},
4435 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || nArg != 1)
4437 ExtOut("Usage: !ListNearObj <obj_address>\n");
4441 EnableDMLHolder dmlHolder(dml);
4443 if (!g_snapshot.Build())
4445 ExtOut("Unable to build snapshot of the garbage collector state\n");
4449 taddrObj = Align(taddrArg);
4451 DacpGcHeapDetails *heap = g_snapshot.GetHeap(taddrArg);
4454 ExtOut("Address %p does not lie in the managed heap\n", SOS_PTR(taddrObj));
4458 TADDR_SEGINFO trngSeg = {0, 0, 0};
4459 TADDR_RANGE allocCtx = {0, 0};
4462 if (!GCObjInHeap(taddrObj, *heap, trngSeg, gen, allocCtx, bLarge))
4464 ExtOut("Failed to find the segment of the managed heap where the object %p resides\n",
4480 BOOL bContainsPointers;
4482 std::vector<TADDR> candidate;
4483 candidate.reserve(10);
4485 // since we'll be reading back I'll prime the read cache to a buffer before the current address
4486 MOVE(taddrCur, _max(trngSeg.start, taddrObj-DT_OS_PAGE_SIZE));
4488 // ===== Look for a good candidate preceeding taddrObj
4490 for (taddrCur = taddrObj - sizeof(TADDR); taddrCur >= trngSeg.start; taddrCur -= sizeof(TADDR))
4492 // currently we don't pay attention to allocation contexts. if this
4493 // proves to be an issue we need to reconsider the code below
4494 if (SUCCEEDED(GetMTOfObject(taddrCur, &curMT)) &&
4495 GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers))
4497 // remember this as one of the possible "good" objects preceeding taddrObj
4498 candidate.push_back(taddrCur);
4500 std::vector<TADDR>::iterator it =
4501 std::find(candidate.begin(), candidate.end(), taddrCur+curSize);
4502 if (it != candidate.end())
4504 // We found a chain of two objects preceeding taddrObj. We'll
4505 // trust this is a good indication that the two objects are valid.
4506 // What is not valid is possibly the object following the second
4509 GetMTOfObject(taddrCur, &curMT);
4510 GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
4517 if (!bCur && !candidate.empty())
4519 // pick the closest object to taddrObj
4520 taddrCur = *(candidate.begin());
4521 GetMTOfObject(taddrCur, &curMT);
4522 GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
4523 // we have a candidate, even if not confirmed
4527 taddrNxt = taddrObj;
4528 if (taddrArg == taddrObj)
4530 taddrNxt += sizeof(TADDR);
4533 // ===== Now look at taddrObj
4534 if (taddrObj == taddrArg)
4536 // only look at taddrObj if it's the same as what user passed in, meaning it's aligned.
4537 if (SUCCEEDED(GetMTOfObject(taddrObj, &objMT)) &&
4538 GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
4541 taddrNxt = taddrObj+objSize;
4545 if ((taddrCur + curSize > taddrArg) && taddrCur + curSize < trngSeg.end)
4547 if (SUCCEEDED(GetMTOfObject(taddrCur + curSize, &nxtMT)) &&
4548 GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
4550 taddrNxt = taddrCur+curSize;
4554 // ===== And finally move on to elements following taddrObj
4556 for (; taddrNxt < trngSeg.end; taddrNxt += sizeof(TADDR))
4558 if (SUCCEEDED(GetMTOfObject(taddrNxt, &nxtMT)) &&
4559 GetSizeEfficient(taddrNxt, nxtMT, bLarge, nxtSize, bContainsPointers))
4567 LNODisplayOutput(W("Before: "), curMT, taddrCur, curSize);
4569 ExtOut("Before: couldn't find any object between %#p and %#p\n",
4570 SOS_PTR(trngSeg.start), SOS_PTR(taddrArg));
4573 LNODisplayOutput(W("Current:"), objMT, taddrObj, objSize);
4576 LNODisplayOutput(W("After: "), nxtMT, taddrNxt, nxtSize);
4578 ExtOut("After: couldn't find any object between %#p and %#p\n",
4579 SOS_PTR(taddrArg), SOS_PTR(trngSeg.end));
4582 (((taddrCur+curSize == taddrObj) && (taddrObj+objSize == taddrNxt)) || (taddrCur+curSize == taddrNxt)))
4584 ExtOut("Heap local consistency confirmed.\n");
4588 ExtOut("Heap local consistency not confirmed.\n");
4598 #endif // FEATURE_PAL
4602 DECLARE_API(GCHeapStat)
4605 MINIDUMP_NOT_SUPPORTED();
4610 BOOL bIncUnreachable = FALSE;
4613 CMDOption option[] = {
4614 // name, vptr, type, hasValue
4615 {"-inclUnrooted", &bIncUnreachable, COBOOL, FALSE},
4616 {"-iu", &bIncUnreachable, COBOOL, FALSE},
4617 {"/d", &dml, COBOOL, FALSE}
4620 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
4625 EnableDMLHolder dmlHolder(dml);
4626 ExtOut("%-8s %12s %12s %12s %12s\n", "Heap", "Gen0", "Gen1", "Gen2", "LOH");
4628 if (!IsServerBuild())
4631 DacpGcHeapDetails heapDetails;
4632 if (heapDetails.Request(g_sos) != S_OK)
4634 ExtErr("Error requesting gc heap details\n");
4638 HeapUsageStat hpUsage;
4639 if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage))
4641 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", 0,
4642 hpUsage.genUsage[0].allocd, hpUsage.genUsage[1].allocd,
4643 hpUsage.genUsage[2].allocd, hpUsage.genUsage[3].allocd);
4644 ExtOut("\nFree space: Percentage\n");
4645 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0,
4646 hpUsage.genUsage[0].freed, hpUsage.genUsage[1].freed,
4647 hpUsage.genUsage[2].freed, hpUsage.genUsage[3].freed);
4648 tempf = ((float)(hpUsage.genUsage[0].freed+hpUsage.genUsage[1].freed+hpUsage.genUsage[2].freed)) /
4649 (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
4650 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4651 (int)(100*((float)hpUsage.genUsage[3].freed) / (hpUsage.genUsage[3].allocd)));
4652 if (bIncUnreachable)
4654 ExtOut("\nUnrooted objects: Percentage\n");
4655 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0,
4656 hpUsage.genUsage[0].unrooted, hpUsage.genUsage[1].unrooted,
4657 hpUsage.genUsage[2].unrooted, hpUsage.genUsage[3].unrooted);
4658 tempf = ((float)(hpUsage.genUsage[0].unrooted+hpUsage.genUsage[1].unrooted+hpUsage.genUsage[2].unrooted)) /
4659 (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
4660 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4661 (int)(100*((float)hpUsage.genUsage[3].unrooted) / (hpUsage.genUsage[3].allocd)));
4668 DacpGcHeapData gcheap;
4669 if (gcheap.Request(g_sos) != S_OK)
4671 ExtErr("Error requesting GC Heap data\n");
4676 DWORD dwNHeaps = gcheap.HeapCount;
4677 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
4679 ExtErr("Failed to get GCHeaps: integer overflow\n");
4683 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
4684 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
4686 ExtErr("Failed to get GCHeaps\n");
4690 ArrayHolder<HeapUsageStat> hpUsage = new NOTHROW HeapUsageStat[dwNHeaps];
4691 if (hpUsage == NULL)
4697 // aggregate stats accross heaps / generation
4698 GenUsageStat genUsageStat[4] = {0, 0, 0, 0};
4700 for (DWORD n = 0; n < dwNHeaps; n ++)
4702 DacpGcHeapDetails heapDetails;
4703 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
4705 ExtErr("Error requesting gc heap details\n");
4709 if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage[n]))
4711 for (int i = 0; i < 4; ++i)
4713 genUsageStat[i].allocd += hpUsage[n].genUsage[i].allocd;
4714 genUsageStat[i].freed += hpUsage[n].genUsage[i].freed;
4715 if (bIncUnreachable)
4717 genUsageStat[i].unrooted += hpUsage[n].genUsage[i].unrooted;
4723 for (DWORD n = 0; n < dwNHeaps; n ++)
4725 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", n,
4726 hpUsage[n].genUsage[0].allocd, hpUsage[n].genUsage[1].allocd,
4727 hpUsage[n].genUsage[2].allocd, hpUsage[n].genUsage[3].allocd);
4729 ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4730 genUsageStat[0].allocd, genUsageStat[1].allocd,
4731 genUsageStat[2].allocd, genUsageStat[3].allocd);
4733 ExtOut("\nFree space: Percentage\n");
4734 for (DWORD n = 0; n < dwNHeaps; n ++)
4736 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n,
4737 hpUsage[n].genUsage[0].freed, hpUsage[n].genUsage[1].freed,
4738 hpUsage[n].genUsage[2].freed, hpUsage[n].genUsage[3].freed);
4740 tempf = ((float)(hpUsage[n].genUsage[0].freed+hpUsage[n].genUsage[1].freed+hpUsage[n].genUsage[2].freed)) /
4741 (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
4742 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4743 (int)(100*((float)hpUsage[n].genUsage[3].freed) / (hpUsage[n].genUsage[3].allocd))
4746 ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4747 genUsageStat[0].freed, genUsageStat[1].freed,
4748 genUsageStat[2].freed, genUsageStat[3].freed);
4750 if (bIncUnreachable)
4752 ExtOut("\nUnrooted objects: Percentage\n");
4753 for (DWORD n = 0; n < dwNHeaps; n ++)
4755 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n,
4756 hpUsage[n].genUsage[0].unrooted, hpUsage[n].genUsage[1].unrooted,
4757 hpUsage[n].genUsage[2].unrooted, hpUsage[n].genUsage[3].unrooted);
4759 tempf = ((float)(hpUsage[n].genUsage[0].unrooted+hpUsage[n].genUsage[1].unrooted+hpUsage[n].genUsage[2].unrooted)) /
4760 (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
4761 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4762 (int)(100*((float)hpUsage[n].genUsage[3].unrooted) / (hpUsage[n].genUsage[3].allocd)));
4764 ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4765 genUsageStat[0].unrooted, genUsageStat[1].unrooted,
4766 genUsageStat[2].unrooted, genUsageStat[3].unrooted);
4778 #endif // FEATURE_PAL
4781 /**********************************************************************\
4782 * Routine Description: *
4784 * This function dumps what is in the syncblock cache. By default *
4785 * it dumps all active syncblocks. Using -all to dump all syncblocks
4787 \**********************************************************************/
4788 DECLARE_API(SyncBlk)
4791 MINIDUMP_NOT_SUPPORTED();
4793 BOOL bDumpAll = FALSE;
4797 CMDOption option[] =
4798 { // name, vptr, type, hasValue
4799 {"-all", &bDumpAll, COBOOL, FALSE},
4800 {"/d", &dml, COBOOL, FALSE}
4804 {&nbAsked, COSIZE_T}
4807 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
4812 EnableDMLHolder dmlHolder(dml);
4813 DacpSyncBlockData syncBlockData;
4814 if (syncBlockData.Request(g_sos,1) != S_OK)
4816 ExtOut("Error requesting SyncBlk data\n");
4820 DWORD dwCount = syncBlockData.SyncBlockCount;
4822 ExtOut("Index" WIN64_8SPACES " SyncBlock MonitorHeld Recursion Owning Thread Info" WIN64_8SPACES " SyncBlock Owner\n");
4823 ULONG freeCount = 0;
4827 for (DWORD nb = 1; nb <= dwCount; nb++)
4832 if (nbAsked && nb != nbAsked)
4837 if (syncBlockData.Request(g_sos,nb) != S_OK)
4839 ExtOut("SyncBlock %d is invalid%s\n", nb,
4840 (nb != nbAsked) ? ", continuing..." : "");
4844 BOOL bPrint = (bDumpAll || nb == nbAsked || (syncBlockData.MonitorHeld > 0 && !syncBlockData.bFree));
4849 if (!syncBlockData.bFree || nb != nbAsked)
4851 ExtOut("%p ", syncBlockData.SyncBlockPointer);
4852 ExtOut("%11d ", syncBlockData.MonitorHeld);
4853 ExtOut("%9d ", syncBlockData.Recursion);
4854 ExtOut("%p ", syncBlockData.HoldingThread);
4856 if (syncBlockData.HoldingThread == ~0ul)
4858 ExtOut(" orphaned ");
4860 else if (syncBlockData.HoldingThread != NULL)
4862 DacpThreadData Thread;
4863 if ((Status = Thread.Request(g_sos, syncBlockData.HoldingThread)) != S_OK)
4865 ExtOut("Failed to request Thread at %p\n", syncBlockData.HoldingThread);
4869 DMLOut(DMLThreadID(Thread.osThreadId));
4871 if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
4885 if (syncBlockData.bFree)
4887 ExtOut(" %8d", 0); // TODO: do we need to print the free synctable list?
4891 sos::Object obj = TO_TADDR(syncBlockData.Object);
4892 DMLOut(" %s %S", DMLObject(syncBlockData.Object), obj.GetTypeName());
4897 if (syncBlockData.bFree)
4906 #ifdef FEATURE_COMINTEROP
4907 if (syncBlockData.COMFlags) {
4908 switch (syncBlockData.COMFlags) {
4909 case SYNCBLOCKDATA_COMFLAGS_CCW:
4912 case SYNCBLOCKDATA_COMFLAGS_RCW:
4915 case SYNCBLOCKDATA_COMFLAGS_CF:
4920 #endif // FEATURE_COMINTEROP
4923 if (syncBlockData.MonitorHeld > 1)
4925 // TODO: implement this
4928 DWORD_PTR pHead = (DWORD_PTR)vSyncBlock.m_Link.m_pNext;
4929 DWORD_PTR pNext = pHead;
4936 DWORD_PTR pWaitEventLink = pNext - offsetLinkSB;
4937 WaitEventLink vWaitEventLink;
4938 vWaitEventLink.Fill(pWaitEventLink);
4942 DWORD_PTR dwAddr = (DWORD_PTR)vWaitEventLink.m_Thread;
4943 ExtOut("%x ", dwAddr);
4944 vThread.Fill (dwAddr);
4949 DMLOut("%s,", DMLThreadID(vThread.m_OSThreadId));
4950 pNext = (DWORD_PTR)vWaitEventLink.m_LinkSB.m_pNext;
4961 ExtOut("-----------------------------\n");
4962 ExtOut("Total %d\n", dwCount);
4963 ExtOut("CCW %d\n", CCWCount);
4964 ExtOut("RCW %d\n", RCWCount);
4965 ExtOut("ComClassFactory %d\n", CFCount);
4966 ExtOut("Free %d\n", freeCount);
4971 #ifdef FEATURE_COMINTEROP
4980 void VisitRcw(CLRDATA_ADDRESS RCW,CLRDATA_ADDRESS Context,CLRDATA_ADDRESS Thread, BOOL bIsFreeThreaded, LPVOID token)
4982 VisitRcwArgs *pArgs = (VisitRcwArgs *) token;
4986 if (pArgs->MTACount == 0 && pArgs->STACount == 0 && pArgs->FTMCount == 0)
4988 // First time, print a header
4989 ExtOut("RuntimeCallableWrappers (RCW) to be cleaned:\n");
4990 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Apartment\n",
4991 "RCW", "CONTEXT", "THREAD");
4993 LPCSTR szThreadApartment;
4994 if (bIsFreeThreaded)
4996 szThreadApartment = "(FreeThreaded)";
4999 else if (Thread == NULL)
5001 szThreadApartment = "(MTA)";
5006 szThreadApartment = "(STA)";
5010 ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %9s\n",
5018 DECLARE_API(RCWCleanupList)
5021 MINIDUMP_NOT_SUPPORTED();
5023 DWORD_PTR p_CleanupList = GetExpression(args);
5025 VisitRcwArgs travArgs;
5026 ZeroMemory(&travArgs,sizeof(VisitRcwArgs));
5027 travArgs.bDetail = TRUE;
5029 // We need to detect when !RCWCleanupList is called with an expression which evaluates to 0
5030 // (to print out an Invalid parameter message), but at the same time we need to allow an
5031 // empty argument list which would result in p_CleanupList equaling 0.
5032 if (p_CleanupList || strlen(args) == 0)
5034 HRESULT hr = g_sos->TraverseRCWCleanupList(p_CleanupList, (VISITRCWFORCLEANUP)VisitRcw, &travArgs);
5038 ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
5039 ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
5040 ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
5044 ExtOut("An error occurred while traversing the cleanup list.\n");
5049 ExtOut("Invalid parameter %s\n", args);
5054 #endif // FEATURE_COMINTEROP
5057 /**********************************************************************\
5058 * Routine Description: *
5060 * This function is called to dump the contents of the finalizer *
5063 \**********************************************************************/
5064 DECLARE_API(FinalizeQueue)
5067 MINIDUMP_NOT_SUPPORTED();
5069 BOOL bDetail = FALSE;
5070 BOOL bAllReady = FALSE;
5071 BOOL bShort = FALSE;
5075 CMDOption option[] =
5076 { // name, vptr, type, hasValue
5077 {"-detail", &bDetail, COBOOL, FALSE},
5078 {"-allReady", &bAllReady, COBOOL, FALSE},
5079 {"-short", &bShort, COBOOL, FALSE},
5080 {"/d", &dml, COBOOL, FALSE},
5081 {"-mt", &taddrMT, COHEX, TRUE},
5084 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
5089 EnableDMLHolder dmlHolder(dml);
5092 DacpSyncBlockCleanupData dsbcd;
5093 CLRDATA_ADDRESS sbCurrent = NULL;
5094 ULONG cleanCount = 0;
5095 while ((dsbcd.Request(g_sos,sbCurrent) == S_OK) && dsbcd.SyncBlockPointer)
5099 if (cleanCount == 0) // print first time only
5101 ExtOut("SyncBlocks to be cleaned by the finalizer thread:\n");
5102 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
5103 "SyncBlock", "RCW", "CCW", "ComClassFactory");
5106 ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p\n",
5107 (ULONG64) dsbcd.SyncBlockPointer,
5108 (ULONG64) dsbcd.blockRCW,
5109 (ULONG64) dsbcd.blockCCW,
5110 (ULONG64) dsbcd.blockClassFactory);
5114 sbCurrent = dsbcd.nextSyncBlock;
5115 if (sbCurrent == NULL)
5121 ExtOut("SyncBlocks to be cleaned up: %d\n", cleanCount);
5123 #ifdef FEATURE_COMINTEROP
5124 VisitRcwArgs travArgs;
5125 ZeroMemory(&travArgs,sizeof(VisitRcwArgs));
5126 travArgs.bDetail = bDetail;
5127 g_sos->TraverseRCWCleanupList(0, (VISITRCWFORCLEANUP) VisitRcw, &travArgs);
5128 ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
5129 ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
5130 ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
5131 #endif // FEATURE_COMINTEROP
5134 ExtOut("----------------------------------\n");
5138 DWORD dwNHeaps = GetGcHeapCount();
5142 if (!IsServerBuild())
5144 DacpGcHeapDetails heapDetails;
5145 if (heapDetails.Request(g_sos) != S_OK)
5147 ExtOut("Error requesting details\n");
5151 GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
5156 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
5158 ExtOut("Failed to get GCHeaps: integer overflow\n");
5162 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
5163 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
5165 ExtOut("Failed to get GCHeaps\n");
5169 for (DWORD n = 0; n < dwNHeaps; n ++)
5171 DacpGcHeapDetails heapDetails;
5172 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
5174 ExtOut("Error requesting details\n");
5178 ExtOut("------------------------------\n");
5179 ExtOut("Heap %d\n", n);
5180 GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
5188 PrintGCStat(&hpStat, "Statistics for all finalizable objects that are no longer rooted:\n");
5192 PrintGCStat(&hpStat, "Statistics for all finalizable objects (including all objects ready for finalization):\n");
5199 #endif // FEATURE_PAL
5202 // These are the values set in m_dwTransientFlags.
5203 // Note that none of these flags survive a prejit save/restore.
5205 M_CRST_NOTINITIALIZED = 0x00000001, // Used to prevent destruction of garbage m_crst
5206 M_LOOKUPCRST_NOTINITIALIZED = 0x00000002,
5208 SUPPORTS_UPDATEABLE_METHODS = 0x00000020,
5209 CLASSES_FREED = 0x00000040,
5210 HAS_PHONY_IL_RVAS = 0x00000080,
5211 IS_EDIT_AND_CONTINUE = 0x00000200,
5214 void ModuleMapTraverse(UINT index, CLRDATA_ADDRESS methodTable, LPVOID token)
5216 ULONG32 rid = (ULONG32)(size_t)token;
5217 NameForMT_s(TO_TADDR(methodTable), g_mdName, mdNameLen);
5219 DMLOut("%s 0x%08x %S\n", DMLMethodTable(methodTable), (ULONG32)TokenFromRid(rid, index), g_mdName);
5223 /**********************************************************************\
5224 * Routine Description: *
5226 * This function is called to dump the contents of a Module *
5227 * for a given address *
5229 \**********************************************************************/
5230 DECLARE_API(DumpModule)
5233 MINIDUMP_NOT_SUPPORTED();
5236 DWORD_PTR p_ModuleAddr = NULL;
5237 BOOL bMethodTables = FALSE;
5240 CMDOption option[] =
5241 { // name, vptr, type, hasValue
5242 {"-mt", &bMethodTables, COBOOL, FALSE},
5244 {"/d", &dml, COBOOL, FALSE}
5249 {&p_ModuleAddr, COHEX}
5253 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
5259 ExtOut("Usage: DumpModule [-mt] <Module Address>\n");
5263 EnableDMLHolder dmlHolder(dml);
5264 DacpModuleData module;
5265 if ((Status=module.Request(g_sos, TO_CDADDR(p_ModuleAddr)))!=S_OK)
5267 ExtOut("Fail to fill Module %p\n", SOS_PTR(p_ModuleAddr));
5271 WCHAR FileName[MAX_LONGPATH];
5272 FileNameForModule (&module, FileName);
5273 ExtOut("Name: %S\n", FileName[0] ? FileName : W("Unknown Module"));
5275 ExtOut("Attributes: ");
5276 if (module.bIsPEFile)
5278 if (module.bIsReflection)
5279 ExtOut("Reflection ");
5280 if (module.dwTransientFlags & SUPPORTS_UPDATEABLE_METHODS)
5281 ExtOut("SupportsUpdateableMethods");
5284 DMLOut("Assembly: %s\n", DMLAssembly(module.Assembly));
5286 ExtOut("LoaderHeap: %p\n", SOS_PTR(module.pLookupTableHeap));
5287 ExtOut("TypeDefToMethodTableMap: %p\n", SOS_PTR(module.TypeDefToMethodTableMap));
5288 ExtOut("TypeRefToMethodTableMap: %p\n", SOS_PTR(module.TypeRefToMethodTableMap));
5289 ExtOut("MethodDefToDescMap: %p\n", SOS_PTR(module.MethodDefToDescMap));
5290 ExtOut("FieldDefToDescMap: %p\n", SOS_PTR(module.FieldDefToDescMap));
5291 ExtOut("MemberRefToDescMap: %p\n", SOS_PTR(module.MemberRefToDescMap));
5292 ExtOut("FileReferencesMap: %p\n", SOS_PTR(module.FileReferencesMap));
5293 ExtOut("AssemblyReferencesMap: %p\n", SOS_PTR(module.ManifestModuleReferencesMap));
5295 if (module.ilBase && module.metadataStart)
5296 ExtOut("MetaData start address: %p (%d bytes)\n", SOS_PTR(module.metadataStart), module.metadataSize);
5300 ExtOut("\nTypes defined in this module\n\n");
5301 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeDef", "Name");
5303 ExtOut("------------------------------------------------------------------------------\n");
5304 g_sos->TraverseModuleMap(TYPEDEFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);
5306 ExtOut("\nTypes referenced in this module\n\n");
5307 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeRef", "Name");
5309 ExtOut("------------------------------------------------------------------------------\n");
5310 g_sos->TraverseModuleMap(TYPEREFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);
5316 /**********************************************************************\
5317 * Routine Description: *
5319 * This function is called to dump the contents of a Domain *
5320 * for a given address *
5322 \**********************************************************************/
5323 DECLARE_API(DumpDomain)
5326 MINIDUMP_NOT_SUPPORTED();
5328 DWORD_PTR p_DomainAddr = 0;
5331 CMDOption option[] =
5332 { // name, vptr, type, hasValue
5334 {"/d", &dml, COBOOL, FALSE},
5339 {&p_DomainAddr, COHEX},
5343 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
5348 EnableDMLHolder dmlHolder(dml);
5350 DacpAppDomainStoreData adsData;
5351 if ((Status=adsData.Request(g_sos))!=S_OK)
5353 ExtOut("Unable to get AppDomain information\n");
5359 DacpAppDomainData appDomain1;
5360 if ((Status=appDomain1.Request(g_sos, TO_CDADDR(p_DomainAddr)))!=S_OK)
5362 ExtOut("Fail to fill AppDomain\n");
5366 ExtOut("--------------------------------------\n");
5368 if (p_DomainAddr == adsData.sharedDomain)
5370 DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain));
5372 else if (p_DomainAddr == adsData.systemDomain)
5374 DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain));
5378 DMLOut("Domain %d:%s %s\n", appDomain1.dwId, (appDomain1.dwId >= 10) ? "" : " ", DMLDomain(p_DomainAddr));
5381 DomainInfo(&appDomain1);
5385 ExtOut("--------------------------------------\n");
5386 DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain));
5387 DacpAppDomainData appDomain;
5388 if ((Status=appDomain.Request(g_sos,adsData.systemDomain))!=S_OK)
5390 ExtOut("Unable to get system domain info.\n");
5393 DomainInfo(&appDomain);
5395 ExtOut("--------------------------------------\n");
5396 DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain));
5397 if ((Status=appDomain.Request(g_sos, adsData.sharedDomain))!=S_OK)
5399 ExtOut("Unable to get shared domain info\n");
5402 DomainInfo(&appDomain);
5404 ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
5411 if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
5413 ExtOut("Unable to get array of AppDomains\n");
5417 for (int n=0;n<adsData.DomainCount;n++)
5422 if ((Status=appDomain.Request(g_sos, pArray[n])) != S_OK)
5424 ExtOut("Failed to get appdomain %p, error %lx\n", SOS_PTR(pArray[n]), Status);
5428 ExtOut("--------------------------------------\n");
5429 DMLOut("Domain %d:%s %s\n", appDomain.dwId, (appDomain.dwId >= 10) ? "" : " ", DMLDomain(pArray[n]));
5430 DomainInfo(&appDomain);
5436 /**********************************************************************\
5437 * Routine Description: *
5439 * This function is called to dump the contents of a Assembly *
5440 * for a given address *
5442 \**********************************************************************/
5443 DECLARE_API(DumpAssembly)
5446 MINIDUMP_NOT_SUPPORTED();
5448 DWORD_PTR p_AssemblyAddr = 0;
5451 CMDOption option[] =
5452 { // name, vptr, type, hasValue
5454 {"/d", &dml, COBOOL, FALSE},
5459 {&p_AssemblyAddr, COHEX},
5463 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
5468 EnableDMLHolder dmlHolder(dml);
5470 if (p_AssemblyAddr == 0)
5472 ExtOut("Invalid Assembly %s\n", args);
5476 DacpAssemblyData Assembly;
5477 if ((Status=Assembly.Request(g_sos, TO_CDADDR(p_AssemblyAddr)))!=S_OK)
5479 ExtOut("Fail to fill Assembly\n");
5482 DMLOut("Parent Domain: %s\n", DMLDomain(Assembly.ParentDomain));
5483 if (g_sos->GetAssemblyName(TO_CDADDR(p_AssemblyAddr), mdNameLen, g_mdName, NULL)==S_OK)
5484 ExtOut("Name: %S\n", g_mdName);
5486 ExtOut("Name: Unknown\n");
5488 AssemblyInfo(&Assembly);
5493 String GetHostingCapabilities(DWORD hostConfig)
5497 bool bAnythingPrinted = false;
5499 #define CHK_AND_PRINT(hType,hStr) \
5500 if (hostConfig & (hType)) { \
5501 if (bAnythingPrinted) result += ", "; \
5503 bAnythingPrinted = true; \
5506 CHK_AND_PRINT(CLRMEMORYHOSTED, "Memory");
5507 CHK_AND_PRINT(CLRTASKHOSTED, "Task");
5508 CHK_AND_PRINT(CLRSYNCHOSTED, "Sync");
5509 CHK_AND_PRINT(CLRTHREADPOOLHOSTED, "Threadpool");
5510 CHK_AND_PRINT(CLRIOCOMPLETIONHOSTED, "IOCompletion");
5511 CHK_AND_PRINT(CLRASSEMBLYHOSTED, "Assembly");
5512 CHK_AND_PRINT(CLRGCHOSTED, "GC");
5513 CHK_AND_PRINT(CLRSECURITYHOSTED, "Security");
5515 #undef CHK_AND_PRINT
5520 /**********************************************************************\
5521 * Routine Description: *
5523 * This function is called to dump the managed threads *
5525 \**********************************************************************/
5526 HRESULT PrintThreadsFromThreadStore(BOOL bMiniDump, BOOL bPrintLiveThreadsOnly)
5530 DacpThreadStoreData ThreadStore;
5531 if ((Status = ThreadStore.Request(g_sos)) != S_OK)
5533 Print("Failed to request ThreadStore\n");
5537 TableOutput table(2, 17);
5539 table.WriteRow("ThreadCount:", Decimal(ThreadStore.threadCount));
5540 table.WriteRow("UnstartedThread:", Decimal(ThreadStore.unstartedThreadCount));
5541 table.WriteRow("BackgroundThread:", Decimal(ThreadStore.backgroundThreadCount));
5542 table.WriteRow("PendingThread:", Decimal(ThreadStore.pendingThreadCount));
5543 table.WriteRow("DeadThread:", Decimal(ThreadStore.deadThreadCount));
5545 if (ThreadStore.fHostConfig & ~CLRHOSTED)
5547 String hosting = "yes";
5550 hosting += GetHostingCapabilities(ThreadStore.fHostConfig);
5553 table.WriteRow("Hosted Runtime:", hosting);
5557 table.WriteRow("Hosted Runtime:", "no");
5560 const bool hosted = (ThreadStore.fHostConfig & CLRTASKHOSTED) != 0;
5561 table.ReInit(hosted ? 12 : 11, POINTERSIZE_HEX);
5562 table.SetWidths(10, 4, 4, 4, _max(9, POINTERSIZE_HEX),
5563 8, 11, 1+POINTERSIZE_HEX*2, POINTERSIZE_HEX,
5564 5, 3, POINTERSIZE_HEX);
5566 table.SetColAlignment(0, AlignRight);
5567 table.SetColAlignment(1, AlignRight);
5568 table.SetColAlignment(2, AlignRight);
5569 table.SetColAlignment(4, AlignRight);
5571 table.WriteColumn(8, "Lock");
5572 table.WriteRow("", "ID", "OSID", "ThreadOBJ", "State", "GC Mode", "GC Alloc Context", "Domain", "Count", "Apt");
5575 table.WriteColumn("Fiber");
5577 table.WriteColumn("Exception");
5579 DacpThreadData Thread;
5580 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
5586 if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
5588 PrintLn("Failed to request Thread at ", Pointer(CurThread));
5592 BOOL bSwitchedOutFiber = Thread.osThreadId == SWITCHED_OUT_FIBER_OSID;
5593 if (!IsKernelDebugger())
5597 if (bSwitchedOutFiber)
5599 table.WriteColumn(0, "<<<< ");
5601 else if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
5603 table.WriteColumn(0, Decimal(id));
5605 else if (bPrintLiveThreadsOnly)
5607 CurThread = Thread.nextThread;
5612 table.WriteColumn(0, "XXXX ");
5616 table.WriteColumn(1, Decimal(Thread.corThreadId));
5617 table.WriteColumn(2, ThreadID(bSwitchedOutFiber ? 0 : Thread.osThreadId));
5618 table.WriteColumn(3, Pointer(CurThread));
5619 table.WriteColumn(4, ThreadState(Thread.state));
5620 table.WriteColumn(5, Thread.preemptiveGCDisabled == 1 ? "Cooperative" : "Preemptive");
5621 table.WriteColumnFormat(6, "%p:%p", Thread.allocContextPtr, Thread.allocContextLimit);
5625 table.WriteColumn(7, AppDomainPtr(Thread.domain));
5629 CLRDATA_ADDRESS domain = 0;
5630 if (FAILED(g_sos->GetDomainFromContext(Thread.context, &domain)))
5631 table.WriteColumn(7, "<error>");
5633 table.WriteColumn(7, AppDomainPtr(domain));
5636 table.WriteColumn(8, Decimal(Thread.lockCount));
5640 DWORD_PTR OleTlsDataAddr;
5641 if (!bSwitchedOutFiber
5642 && SafeReadMemory(Thread.teb + offsetof(TEB, ReservedForOle),
5644 sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0)
5647 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
5649 sizeof(AptState), NULL))
5651 if (AptState & OLETLS_APARTMENTTHREADED)
5652 table.WriteColumn(9, "STA");
5653 else if (AptState & OLETLS_MULTITHREADED)
5654 table.WriteColumn(9, "MTA");
5655 else if (AptState & OLETLS_INNEUTRALAPT)
5656 table.WriteColumn(9, "NTA");
5658 table.WriteColumn(9, "Ukn");
5662 table.WriteColumn(9, "Ukn");
5666 #endif // FEATURE_PAL
5667 table.WriteColumn(9, "Ukn");
5670 table.WriteColumn(10, Thread.fiberData);
5673 if (CurThread == ThreadStore.finalizerThread)
5674 lastCol += W("(Finalizer) ");
5675 if (CurThread == ThreadStore.gcThread)
5676 lastCol += W("(GC) ");
5678 const int TS_TPWorkerThread = 0x01000000; // is this a threadpool worker thread?
5679 const int TS_CompletionPortThread = 0x08000000; // is this is a completion port thread?
5681 if (Thread.state & TS_TPWorkerThread)
5682 lastCol += W("(Threadpool Worker) ");
5683 else if (Thread.state & TS_CompletionPortThread)
5684 lastCol += W("(Threadpool Completion Port) ");
5688 if (Thread.lastThrownObjectHandle && SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
5689 &taLTOH, sizeof(taLTOH), NULL) && taLTOH)
5692 if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
5694 if (NameForMT_s(taMT, g_mdName, mdNameLen))
5695 lastCol += WString(g_mdName) + W(" ") + ExceptionPtr(taLTOH);
5697 lastCol += WString(W("<Invalid Object> (")) + Pointer(taLTOH) + W(")");
5699 // Print something if there are nested exceptions on the thread
5700 if (Thread.firstNestedException)
5701 lastCol += W(" (nested exceptions)");
5705 table.WriteColumn(lastCol);
5706 CurThread = Thread.nextThread;
5713 HRESULT PrintSpecialThreads()
5717 DWORD dwCLRTLSDataIndex = 0;
5718 HRESULT Status = g_sos->GetTLSIndex(&dwCLRTLSDataIndex);
5720 if (!SUCCEEDED (Status))
5722 Print("Failed to retrieve Tls Data index\n");
5727 ULONG ulOriginalThreadID = 0;
5728 Status = g_ExtSystem->GetCurrentThreadId (&ulOriginalThreadID);
5729 if (!SUCCEEDED (Status))
5731 Print("Failed to require current Thread ID\n");
5735 ULONG ulTotalThreads = 0;
5736 Status = g_ExtSystem->GetNumberThreads (&ulTotalThreads);
5737 if (!SUCCEEDED (Status))
5739 Print("Failed to require total thread number\n");
5743 TableOutput table(3, 4, AlignRight, 5);
5744 table.WriteRow("", "OSID", "Special thread type");
5746 for (ULONG ulThread = 0; ulThread < ulTotalThreads; ulThread++)
5750 HRESULT threadStatus = g_ExtSystem->GetThreadIdsByIndex(ulThread, 1, &Id, &SysId);
5751 if (!SUCCEEDED (threadStatus))
5753 PrintLn("Failed to get thread ID for thread ", Decimal(ulThread));
5757 threadStatus = g_ExtSystem->SetCurrentThreadId(Id);
5758 if (!SUCCEEDED (threadStatus))
5760 PrintLn("Failed to switch to thread ", ThreadID(SysId));
5764 CLRDATA_ADDRESS cdaTeb = 0;
5765 threadStatus = g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
5766 if (!SUCCEEDED (threadStatus))
5768 PrintLn("Failed to get Teb for Thread ", ThreadID(SysId));
5772 TADDR CLRTLSDataAddr = 0;
5774 #ifdef FEATURE_IMPLICIT_TLS
5775 TADDR tlsArrayAddr = NULL;
5776 if (!SafeReadMemory (TO_TADDR(cdaTeb) + WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer , &tlsArrayAddr, sizeof (void**), NULL))
5778 PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
5782 TADDR moduleTlsDataAddr = 0;
5784 if (!SafeReadMemory (tlsArrayAddr + sizeof (void*) * dwCLRTLSDataIndex, &moduleTlsDataAddr, sizeof (void**), NULL))
5786 PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
5790 CLRTLSDataAddr = moduleTlsDataAddr + OFFSETOF__TLS__tls_EETlsData;
5792 if (dwCLRTLSDataIndex < TLS_MINIMUM_AVAILABLE)
5794 CLRTLSDataAddr = TO_TADDR(cdaTeb) + offsetof(TEB, TlsSlots) + sizeof (void*) * dwCLRTLSDataIndex;
5798 //if TLS index is bigger than TLS_MINIMUM_AVAILABLE, the TLS slot lives in ExpansionSlots
5799 TADDR TebExpsionAddr = NULL;
5800 if (!SafeReadMemory (TO_TADDR(cdaTeb) + offsetof(TEB, TlsExpansionSlots) , &TebExpsionAddr, sizeof (void**), NULL))
5802 PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
5806 if (TebExpsionAddr == NULL)
5811 CLRTLSDataAddr = TebExpsionAddr + sizeof (void*) * (dwCLRTLSDataIndex - TLS_MINIMUM_AVAILABLE);
5813 #endif // FEATURE_IMPLICIT_TLS
5815 TADDR CLRTLSData = NULL;
5816 if (!SafeReadMemory (CLRTLSDataAddr, &CLRTLSData, sizeof (TADDR), NULL))
5818 PrintLn("Failed to get CLR Tls data for thread ", ThreadID(SysId));
5822 if (CLRTLSData == NULL)
5827 size_t ThreadType = 0;
5828 if (!SafeReadMemory (CLRTLSData + sizeof (TADDR) * TlsIdx_ThreadType, &ThreadType, sizeof (size_t), NULL))
5830 PrintLn("Failed to get thread type info not found for thread ", ThreadID(SysId));
5834 if (ThreadType == 0)
5839 table.WriteColumn(0, Decimal(Id));
5840 table.WriteColumn(1, ThreadID(SysId));
5843 if (ThreadType & ThreadType_GC)
5847 if (ThreadType & ThreadType_Timer)
5851 if (ThreadType & ThreadType_Gate)
5855 if (ThreadType & ThreadType_DbgHelper)
5857 type += "DbgHelper ";
5859 if (ThreadType & ThreadType_Shutdown)
5861 type += "Shutdown ";
5863 if (ThreadType & ThreadType_DynamicSuspendEE)
5865 type += "SuspendEE ";
5867 if (ThreadType & ThreadType_Finalizer)
5869 type += "Finalizer ";
5871 if (ThreadType & ThreadType_ADUnloadHelper)
5873 type += "ADUnloadHelper ";
5875 if (ThreadType & ThreadType_ShutdownHelper)
5877 type += "ShutdownHelper ";
5879 if (ThreadType & ThreadType_Threadpool_IOCompletion)
5881 type += "IOCompletion ";
5883 if (ThreadType & ThreadType_Threadpool_Worker)
5885 type += "ThreadpoolWorker ";
5887 if (ThreadType & ThreadType_Wait)
5891 if (ThreadType & ThreadType_ProfAPI_Attach)
5893 type += "ProfilingAPIAttach ";
5895 if (ThreadType & ThreadType_ProfAPI_Detach)
5897 type += "ProfilingAPIDetach ";
5900 table.WriteColumn(2, type);
5903 Status = g_ExtSystem->SetCurrentThreadId (ulOriginalThreadID);
5904 if (!SUCCEEDED (Status))
5906 ExtOut("Failed to switch to original thread\n");
5912 #endif //FEATURE_PAL
5914 HRESULT SwitchToExceptionThread()
5918 DacpThreadStoreData ThreadStore;
5919 if ((Status = ThreadStore.Request(g_sos)) != S_OK)
5921 Print("Failed to request ThreadStore\n");
5925 DacpThreadData Thread;
5926 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
5932 if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
5934 PrintLn("Failed to request Thread at ", Pointer(CurThread));
5939 if (Thread.lastThrownObjectHandle != NULL)
5941 if (SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle), &taLTOH, sizeof(taLTOH), NULL))
5946 if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
5948 if (g_ExtSystem->SetCurrentThreadId(id) == S_OK)
5950 PrintLn("Found managed exception on thread ", ThreadID(Thread.osThreadId));
5958 CurThread = Thread.nextThread;
5964 struct ThreadStateTable
5969 static const struct ThreadStateTable ThreadStates[] =
5971 {0x1, "Thread Abort Requested"},
5972 {0x2, "GC Suspend Pending"},
5973 {0x4, "User Suspend Pending"},
5974 {0x8, "Debug Suspend Pending"},
5975 {0x10, "GC On Transitions"},
5976 {0x20, "Legal to Join"},
5977 {0x40, "Yield Requested"},
5978 {0x80, "Hijacked by the GC"},
5979 {0x100, "Blocking GC for Stack Overflow"},
5980 {0x200, "Background"},
5981 {0x400, "Unstarted"},
5983 {0x1000, "CLR Owns"},
5984 {0x2000, "CoInitialized"},
5985 {0x4000, "In Single Threaded Apartment"},
5986 {0x8000, "In Multi Threaded Apartment"},
5987 {0x10000, "Reported Dead"},
5988 {0x20000, "Fully initialized"},
5989 {0x40000, "Task Reset"},
5990 {0x80000, "Sync Suspended"},
5991 {0x100000, "Debug Will Sync"},
5992 {0x200000, "Stack Crawl Needed"},
5993 {0x400000, "Suspend Unstarted"},
5994 {0x800000, "Aborted"},
5995 {0x1000000, "Thread Pool Worker Thread"},
5996 {0x2000000, "Interruptible"},
5997 {0x4000000, "Interrupted"},
5998 {0x8000000, "Completion Port Thread"},
5999 {0x10000000, "Abort Initiated"},
6000 {0x20000000, "Finalized"},
6001 {0x40000000, "Failed to Start"},
6002 {0x80000000, "Detached"},
6005 DECLARE_API(ThreadState)
6009 size_t state = GetExpression(args);
6015 for (unsigned int i = 0; i < _countof(ThreadStates); ++i)
6016 if (state & ThreadStates[i].State)
6018 ExtOut(" %s\n", ThreadStates[i].Name);
6023 // If we did not find any thread states, print out a message to let the user
6024 // know that the function is working correctly.
6026 ExtOut(" No thread states for '%s'\n", args);
6031 DECLARE_API(Threads)
6035 BOOL bPrintSpecialThreads = FALSE;
6036 BOOL bPrintLiveThreadsOnly = FALSE;
6037 BOOL bSwitchToManagedExceptionThread = FALSE;
6040 CMDOption option[] =
6041 { // name, vptr, type, hasValue
6042 {"-special", &bPrintSpecialThreads, COBOOL, FALSE},
6043 {"-live", &bPrintLiveThreadsOnly, COBOOL, FALSE},
6044 {"-managedexception", &bSwitchToManagedExceptionThread, COBOOL, FALSE},
6046 {"/d", &dml, COBOOL, FALSE},
6049 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
6054 if (bSwitchToManagedExceptionThread)
6056 return SwitchToExceptionThread();
6059 // We need to support minidumps for this command.
6060 BOOL bMiniDump = IsMiniDumpFile();
6062 if (bMiniDump && bPrintSpecialThreads)
6064 Print("Special thread information is not available in mini dumps.\n");
6067 EnableDMLHolder dmlHolder(dml);
6071 Status = PrintThreadsFromThreadStore(bMiniDump, bPrintLiveThreadsOnly);
6072 if (!bMiniDump && bPrintSpecialThreads)
6075 Print("\n-special not supported.\n");
6077 HRESULT Status2 = PrintSpecialThreads();
6078 if (!SUCCEEDED(Status2))
6080 #endif //FEATURE_PAL
6083 catch (sos::Exception &e)
6085 ExtOut("%s\n", e.what());
6092 /**********************************************************************\
6093 * Routine Description: *
6095 * This function is called to dump the Watson Buckets. *
6097 \**********************************************************************/
6098 DECLARE_API(WatsonBuckets)
6102 // We don't need to support minidumps for this command.
6103 if (IsMiniDumpFile())
6105 ExtOut("Not supported on mini dumps.\n");
6108 // Get the current managed thread.
6109 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
6110 DacpThreadData Thread;
6112 if ((threadAddr == NULL) || ((Status = Thread.Request(g_sos, threadAddr)) != S_OK))
6114 ExtOut("The current thread is unmanaged\n");
6118 // Get the definition of GenericModeBlock.
6120 GenericModeBlock gmb;
6122 if ((Status = g_sos->GetClrWatsonBuckets(threadAddr, &gmb)) != S_OK)
6124 ExtOut("Can't get Watson Buckets\n");
6128 ExtOut("Watson Bucket parameters:\n");
6129 ExtOut("b1: %S\n", gmb.wzP1);
6130 ExtOut("b2: %S\n", gmb.wzP2);
6131 ExtOut("b3: %S\n", gmb.wzP3);
6132 ExtOut("b4: %S\n", gmb.wzP4);
6133 ExtOut("b5: %S\n", gmb.wzP5);
6134 ExtOut("b6: %S\n", gmb.wzP6);
6135 ExtOut("b7: %S\n", gmb.wzP7);
6136 ExtOut("b8: %S\n", gmb.wzP8);
6137 ExtOut("b9: %S\n", gmb.wzP9);
6140 } // WatsonBuckets()
6141 #endif // FEATURE_PAL
6143 struct PendingBreakpoint
6145 WCHAR szModuleName[MAX_LONGPATH];
6146 WCHAR szFunctionName[mdNameLen];
6147 WCHAR szFilename[MAX_LONGPATH];
6151 mdMethodDef methodToken;
6152 void SetModule(TADDR module)
6157 bool ModuleMatches(TADDR compare)
6159 return (compare == pModule);
6162 PendingBreakpoint *pNext;
6163 PendingBreakpoint() : lineNumber(0), ilOffset(0), methodToken(0), pNext(NULL)
6165 szModuleName[0] = L'\0';
6166 szFunctionName[0] = L'\0';
6167 szFilename[0] = L'\0';
6171 void IssueDebuggerBPCommand ( CLRDATA_ADDRESS addr )
6173 const int MaxBPsCached = 1024;
6174 static CLRDATA_ADDRESS alreadyPlacedBPs[MaxBPsCached];
6175 static int curLimit = 0;
6177 // on ARM the debugger requires breakpoint addresses to be sanitized
6178 if (IsDbgTargetArm())
6180 addr &= ~THUMB_CODE;
6182 addr |= THUMB_CODE; // lldb expects thumb code bit set
6185 // if we overflowed our cache consider all new BPs unique...
6186 BOOL bUnique = curLimit >= MaxBPsCached;
6190 for (int i = 0; i < curLimit; ++i)
6192 if (alreadyPlacedBPs[i] == addr)
6201 char buffer[64]; // sufficient for "bp <pointersize>"
6202 static WCHAR wszNameBuffer[1024]; // should be large enough
6204 // get the MethodDesc name
6205 CLRDATA_ADDRESS pMD;
6206 if (g_sos->GetMethodDescPtrFromIP(addr, &pMD) != S_OK
6207 || g_sos->GetMethodDescName(pMD, 1024, wszNameBuffer, NULL) != S_OK)
6209 wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN"));
6213 sprintf_s(buffer, _countof(buffer), "bp %p", (void*) (size_t) addr);
6215 sprintf_s(buffer, _countof(buffer), "breakpoint set --address 0x%p", (void*) (size_t) addr);
6217 ExtOut("Setting breakpoint: %s [%S]\n", buffer, wszNameBuffer);
6218 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
6220 if (curLimit < MaxBPsCached)
6222 alreadyPlacedBPs[curLimit++] = addr;
6229 PendingBreakpoint* m_breakpoints;
6233 m_breakpoints = NULL;
6237 PendingBreakpoint *pCur = m_breakpoints;
6240 PendingBreakpoint *pNext = pCur->pNext;
6244 m_breakpoints = NULL;
6247 void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod, DWORD ilOffset)
6249 if (!IsIn(szModule, szName, mod))
6251 PendingBreakpoint *pNew = new PendingBreakpoint();
6252 wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule);
6253 wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
6254 pNew->SetModule(mod);
6255 pNew->ilOffset = ilOffset;
6256 pNew->pNext = m_breakpoints;
6257 m_breakpoints = pNew;
6261 void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
6263 if (!IsIn(methodToken, mod, ilOffset))
6265 PendingBreakpoint *pNew = new PendingBreakpoint();
6266 wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule);
6267 wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
6268 pNew->methodToken = methodToken;
6269 pNew->SetModule(mod);
6270 pNew->ilOffset = ilOffset;
6271 pNew->pNext = m_breakpoints;
6272 m_breakpoints = pNew;
6276 void Add(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
6278 if (!IsIn(szFilename, lineNumber, mod))
6280 PendingBreakpoint *pNew = new PendingBreakpoint();
6281 wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename);
6282 pNew->lineNumber = lineNumber;
6283 pNew->SetModule(mod);
6284 pNew->pNext = m_breakpoints;
6285 m_breakpoints = pNew;
6289 void Add(__in_z LPWSTR szFilename, DWORD lineNumber, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
6291 if (!IsIn(methodToken, mod, ilOffset))
6293 PendingBreakpoint *pNew = new PendingBreakpoint();
6294 wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename);
6295 pNew->lineNumber = lineNumber;
6296 pNew->methodToken = methodToken;
6297 pNew->SetModule(mod);
6298 pNew->ilOffset = ilOffset;
6299 pNew->pNext = m_breakpoints;
6300 m_breakpoints = pNew;
6304 //returns true if updates are still needed for this module, FALSE if all BPs are now bound
6305 BOOL Update(TADDR mod, BOOL isNewModule)
6307 BOOL bNeedUpdates = FALSE;
6308 PendingBreakpoint *pCur = NULL;
6312 SymbolReader symbolReader;
6313 SymbolReader* pSymReader = &symbolReader;
6314 if(LoadSymbolsForModule(mod, &symbolReader) != S_OK)
6317 // Get tokens for any modules that match. If there was a change,
6318 // update notifications.
6319 pCur = m_breakpoints;
6322 PendingBreakpoint *pNext = pCur->pNext;
6323 ResolvePendingNonModuleBoundBreakpoint(mod, pCur, pSymReader);
6328 pCur = m_breakpoints;
6331 PendingBreakpoint *pNext = pCur->pNext;
6332 if (ResolvePendingBreakpoint(mod, pCur))
6334 bNeedUpdates = TRUE;
6338 return bNeedUpdates;
6341 void RemovePendingForModule(TADDR mod)
6343 PendingBreakpoint *pCur = m_breakpoints;
6346 PendingBreakpoint *pNext = pCur->pNext;
6347 if (pCur->ModuleMatches(mod))
6349 // Delete the current node, and keep going
6357 void ListBreakpoints()
6359 PendingBreakpoint *pCur = m_breakpoints;
6360 size_t iBreakpointIndex = 1;
6361 ExtOut(SOSPrefix "bpmd pending breakpoint list\n Breakpoint index - Location, ModuleID, Method Token\n");
6364 //windbg likes to format %p as always being 64 bits
6365 ULONG64 modulePtr = (ULONG64)pCur->pModule;
6367 if(pCur->szModuleName[0] != L'\0')
6368 ExtOut("%d - %ws!%ws+%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset, modulePtr, pCur->methodToken);
6370 ExtOut("%d - %ws:%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szFilename, pCur->lineNumber, modulePtr, pCur->methodToken);
6377 void SaveBreakpoints(FILE* pFile)
6379 PendingBreakpoint *pCur = m_breakpoints;
6382 if(pCur->szModuleName[0] != L'\0')
6383 fprintf_s(pFile, "!bpmd %ws %ws %d\n", pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset);
6385 fprintf_s(pFile, "!bpmd %ws:%d\n", pCur->szFilename, pCur->lineNumber);
6391 void CleanupNotifications()
6394 if (m_breakpoints == NULL)
6396 g_ExtServices->ClearExceptionCallback();
6401 void ClearBreakpoint(size_t breakPointToClear)
6403 PendingBreakpoint *pCur = m_breakpoints;
6404 size_t iBreakpointIndex = 1;
6407 if (breakPointToClear == iBreakpointIndex)
6409 ExtOut("%d - %ws, %ws, %p\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->pModule);
6410 ExtOut("Cleared\n");
6420 ExtOut("Invalid pending breakpoint index.\n");
6422 CleanupNotifications();
6425 void ClearAllBreakpoints()
6427 size_t iBreakpointIndex = 1;
6428 for (PendingBreakpoint *pCur = m_breakpoints; pCur != NULL; )
6430 PendingBreakpoint* pNext = pCur->pNext;
6435 CleanupNotifications();
6437 ExtOut("All pending breakpoints cleared.\n");
6440 HRESULT LoadSymbolsForModule(TADDR mod, SymbolReader* pSymbolReader)
6442 HRESULT Status = S_OK;
6443 ToRelease<IXCLRDataModule> pModule;
6444 IfFailRet(g_sos->GetModule(mod, &pModule));
6446 ToRelease<IMetaDataImport> pMDImport = NULL;
6447 IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
6449 IfFailRet(pSymbolReader->LoadSymbols(pMDImport, pModule));
6454 HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pFilename, DWORD lineNumber, TADDR mod, SymbolReader* pSymbolReader)
6456 HRESULT Status = S_OK;
6457 if(pSymbolReader == NULL)
6458 return S_FALSE; // no symbols, can't bind here
6460 mdMethodDef methodDef;
6462 if(FAILED(Status = pSymbolReader->ResolveSequencePoint(pFilename, lineNumber, mod, &methodDef, &ilOffset)))
6464 return S_FALSE; // not binding in a module is typical
6467 Add(pFilename, lineNumber, methodDef, mod, ilOffset);
6471 HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pModuleName, __in_z WCHAR* pMethodName, TADDR mod, DWORD ilOffset)
6473 HRESULT Status = S_OK;
6474 char szName[mdNameLen];
6477 ToRelease<IXCLRDataModule> module;
6478 IfFailRet(g_sos->GetModule(mod, &module));
6480 WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szName, mdNameLen, NULL, NULL);
6482 ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(szName, &numModule);
6483 if (moduleList == NULL)
6485 ExtOut("Failed to request module list.\n");
6489 for (int i = 0; i < numModule; i++)
6491 // If any one entry in moduleList matches, then the current PendingBreakpoint
6492 // is the right one.
6493 if(moduleList[i] != TO_TADDR(mod))
6497 if (module->StartEnumMethodDefinitionsByName(pMethodName, 0, &h) == S_OK)
6499 IXCLRDataMethodDefinition *pMeth = NULL;
6500 while (module->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
6502 mdMethodDef methodToken;
6503 ToRelease<IXCLRDataModule> pUnusedModule;
6504 IfFailRet(pMeth->GetTokenAndScope(&methodToken, &pUnusedModule));
6506 Add(pModuleName, pMethodName, methodToken, mod, ilOffset);
6509 module->EndEnumMethodDefinitionsByName(h);
6515 // Return TRUE if there might be more instances that will be JITTED later
6516 static BOOL ResolveMethodInstances(IXCLRDataMethodDefinition *pMeth, DWORD ilOffset)
6518 BOOL bFoundCode = FALSE;
6519 BOOL bNeedDefer = FALSE;
6522 if (pMeth->StartEnumInstances (NULL, &h1) == S_OK)
6524 IXCLRDataMethodInstance *inst = NULL;
6525 while (pMeth->EnumInstance (&h1, &inst) == S_OK)
6527 BOOL foundByIlOffset = FALSE;
6528 ULONG32 rangesNeeded = 0;
6529 if(inst->GetAddressRangesByILOffset(ilOffset, 0, &rangesNeeded, NULL) == S_OK)
6531 ArrayHolder<CLRDATA_ADDRESS_RANGE> ranges = new NOTHROW CLRDATA_ADDRESS_RANGE[rangesNeeded];
6534 if (inst->GetAddressRangesByILOffset(ilOffset, rangesNeeded, NULL, ranges) == S_OK)
6536 for (DWORD i = 0; i < rangesNeeded; i++)
6538 IssueDebuggerBPCommand(ranges[i].startAddress);
6540 foundByIlOffset = TRUE;
6546 if (!foundByIlOffset && ilOffset == 0)
6548 CLRDATA_ADDRESS addr = 0;
6549 if (inst->GetRepresentativeEntryAddress(&addr) == S_OK)
6551 IssueDebuggerBPCommand(addr);
6556 pMeth->EndEnumInstances (h1);
6559 // if this is a generic method we need to add a defered bp
6560 BOOL bGeneric = FALSE;
6561 pMeth->HasClassOrMethodInstantiation(&bGeneric);
6563 bNeedDefer = !bFoundCode || bGeneric;
6564 // This is down here because we only need to call SetCodeNofiication once.
6567 if (pMeth->SetCodeNotification (CLRDATA_METHNOTIFY_GENERATED) != S_OK)
6570 ExtOut("Failed to set code notification\n");
6577 BOOL IsIn(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod)
6579 PendingBreakpoint *pCur = m_breakpoints;
6582 if (pCur->ModuleMatches(mod) &&
6583 _wcsicmp(pCur->szModuleName, szModule) == 0 &&
6584 _wcscmp(pCur->szFunctionName, szName) == 0)
6593 BOOL IsIn(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
6595 PendingBreakpoint *pCur = m_breakpoints;
6598 if (pCur->ModuleMatches(mod) &&
6599 _wcsicmp(pCur->szFilename, szFilename) == 0 &&
6600 pCur->lineNumber == lineNumber)
6609 BOOL IsIn(mdMethodDef token, TADDR mod, DWORD ilOffset)
6611 PendingBreakpoint *pCur = m_breakpoints;
6614 if (pCur->ModuleMatches(mod) &&
6615 pCur->methodToken == token &&
6616 pCur->ilOffset == ilOffset)
6625 void Delete(PendingBreakpoint *pDelete)
6627 PendingBreakpoint *pCur = m_breakpoints;
6628 PendingBreakpoint *pPrev = NULL;
6631 if (pCur == pDelete)
6635 m_breakpoints = pCur->pNext;
6639 pPrev->pNext = pCur->pNext;
6651 HRESULT ResolvePendingNonModuleBoundBreakpoint(TADDR mod, PendingBreakpoint *pCur, SymbolReader* pSymbolReader)
6653 // This function only works with pending breakpoints that are not module bound.
6654 if (pCur->pModule == NULL)
6656 if(pCur->szModuleName[0] != L'\0')
6658 return ResolvePendingNonModuleBoundBreakpoint(pCur->szModuleName, pCur->szFunctionName, mod, pCur->ilOffset);
6662 return ResolvePendingNonModuleBoundBreakpoint(pCur->szFilename, pCur->lineNumber, mod, pSymbolReader);
6671 // Returns TRUE if further instances may be jitted, FALSE if all instances are now resolved
6672 BOOL ResolvePendingBreakpoint(TADDR addr, PendingBreakpoint *pCur)
6674 // Only go forward if the module matches the current PendingBreakpoint
6675 if (!pCur->ModuleMatches(addr))
6680 ToRelease<IXCLRDataModule> mod;
6681 if (FAILED(g_sos->GetModule(addr, &mod)))
6686 if(pCur->methodToken == 0)
6691 ToRelease<IXCLRDataMethodDefinition> pMeth = NULL;
6692 mod->GetMethodDefinitionByToken(pCur->methodToken, &pMeth);
6694 // We may not need the code notification. Maybe it was ngen'd and we
6695 // already have the method?
6696 // We can delete the current entry if ResolveMethodInstances() set all BPs
6697 return ResolveMethodInstances(pMeth, pCur->ilOffset);
6701 Breakpoints g_bpoints;
6703 // Controls whether optimizations are disabled on module load and whether NGEN can be used
6704 BOOL g_fAllowJitOptimization = TRUE;
6706 // Controls whether a one-shot breakpoint should be inserted the next time
6707 // execution is about to enter a catch clause
6708 BOOL g_stopOnNextCatch = FALSE;
6710 // According to the latest debuggers these callbacks will not get called
6711 // unless the user (or an extension, like SOS :-)) had previously enabled
6712 // clrn with "sxe clrn".
6713 class CNotification : public IXCLRDataExceptionNotification4
6715 static int s_condemnedGen;
6722 , m_dbgStatus(DEBUG_STATUS_NO_CHANGE)
6725 int GetDebugStatus()
6730 STDMETHODIMP QueryInterface (REFIID iid, void **ppvObject)
6732 if (ppvObject == NULL)
6733 return E_INVALIDARG;
6735 if (IsEqualIID(iid, IID_IUnknown)
6736 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification)
6737 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification2)
6738 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification3)
6739 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification4))
6741 *ppvObject = static_cast<IXCLRDataExceptionNotification4*>(this);
6746 return E_NOINTERFACE;
6750 STDMETHODIMP_(ULONG) AddRef(void) { return ++m_count; }
6751 STDMETHODIMP_(ULONG) Release(void)
6763 * New code was generated or discarded for a method.:
6765 STDMETHODIMP OnCodeGenerated(IXCLRDataMethodInstance* method)
6767 // Some method has been generated, make a breakpoint and remove it.
6768 ULONG32 len = mdNameLen;
6769 LPWSTR szModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
6770 if (method->GetName(0, mdNameLen, &len, g_mdName) == S_OK)
6772 ToRelease<IXCLRDataModule> pMod;
6773 HRESULT hr = method->GetTokenAndScope(NULL, &pMod);
6777 if (pMod->GetName(mdNameLen, &len, szModuleName) == S_OK)
6779 ExtOut("JITTED %S!%S\n", szModuleName, g_mdName);
6781 // Add breakpoint, perhaps delete pending breakpoint
6782 DacpGetModuleAddress dgma;
6783 if (SUCCEEDED(dgma.Request(pMod)))
6785 g_bpoints.Update(TO_TADDR(dgma.ModulePtr), FALSE);
6789 ExtOut("Failed to request module address.\n");
6795 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6799 STDMETHODIMP OnCodeDiscarded(IXCLRDataMethodInstance* method)
6805 * The process or task reached the desired execution state.
6807 STDMETHODIMP OnProcessExecution(ULONG32 state) { return E_NOTIMPL; }
6808 STDMETHODIMP OnTaskExecution(IXCLRDataTask* task,
6809 ULONG32 state) { return E_NOTIMPL; }
6812 * The given module was loaded or unloaded.
6814 STDMETHODIMP OnModuleLoaded(IXCLRDataModule* mod)
6816 DacpGetModuleAddress dgma;
6817 if (SUCCEEDED(dgma.Request(mod)))
6819 g_bpoints.Update(TO_TADDR(dgma.ModulePtr), TRUE);
6822 if(!g_fAllowJitOptimization)
6825 ToRelease<IXCLRDataModule2> mod2;
6826 if(FAILED(mod->QueryInterface(__uuidof(IXCLRDataModule2), (void**) &mod2)))
6828 ExtOut("SOS: warning, optimizations for this module could not be suppressed because this CLR version doesn't support the functionality\n");
6830 else if(FAILED(hr = mod2->SetJITCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION)))
6832 if(hr == CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE)
6833 ExtOut("SOS: warning, optimizations for this module could not be surpressed because an optimized prejitted image was loaded\n");
6835 ExtOut("SOS: warning, optimizations for this module could not be surpressed hr=0x%x\n", hr);
6839 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6843 STDMETHODIMP OnModuleUnloaded(IXCLRDataModule* mod)
6845 DacpGetModuleAddress dgma;
6846 if (SUCCEEDED(dgma.Request(mod)))
6848 g_bpoints.RemovePendingForModule(TO_TADDR(dgma.ModulePtr));
6851 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6856 * The given type was loaded or unloaded.
6858 STDMETHODIMP OnTypeLoaded(IXCLRDataTypeInstance* typeInst)
6859 { return E_NOTIMPL; }
6860 STDMETHODIMP OnTypeUnloaded(IXCLRDataTypeInstance* typeInst)
6861 { return E_NOTIMPL; }
6863 STDMETHODIMP OnAppDomainLoaded(IXCLRDataAppDomain* domain)
6864 { return E_NOTIMPL; }
6865 STDMETHODIMP OnAppDomainUnloaded(IXCLRDataAppDomain* domain)
6866 { return E_NOTIMPL; }
6867 STDMETHODIMP OnException(IXCLRDataExceptionState* exception)
6868 { return E_NOTIMPL; }
6870 STDMETHODIMP OnGcEvent(GcEvtArgs gcEvtArgs)
6872 // by default don't stop on these notifications...
6873 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6875 IXCLRDataProcess2* idp2 = NULL;
6876 if (SUCCEEDED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
6878 if (gcEvtArgs.typ == GC_MARK_END)
6880 // erase notification request
6881 GcEvtArgs gea = { GC_MARK_END, { 0 } };
6882 idp2->SetGcNotification(gea);
6884 s_condemnedGen = bitidx(gcEvtArgs.condemnedGeneration);
6886 ExtOut("CLR notification: GC - Performing a gen %d collection. Determined surviving objects...\n", s_condemnedGen);
6888 // GC_MARK_END notification means: give the user a chance to examine the debuggee
6889 m_dbgStatus = DEBUG_STATUS_BREAK;
6897 * Catch is about to be entered
6899 STDMETHODIMP ExceptionCatcherEnter(IXCLRDataMethodInstance* method, DWORD catcherNativeOffset)
6901 if(g_stopOnNextCatch)
6903 CLRDATA_ADDRESS startAddr;
6904 if(method->GetRepresentativeEntryAddress(&startAddr) == S_OK)
6908 sprintf_s(buffer, _countof(buffer), "bp /1 %p", (void*) (size_t) (startAddr+catcherNativeOffset));
6910 sprintf_s(buffer, _countof(buffer), "breakpoint set --one-shot --address 0x%p", (void*) (size_t) (startAddr+catcherNativeOffset));
6912 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
6914 g_stopOnNextCatch = FALSE;
6917 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6921 static int GetCondemnedGen()
6923 return s_condemnedGen;
6928 int CNotification::s_condemnedGen = -1;
6930 BOOL CheckCLRNotificationEvent(DEBUG_LAST_EVENT_INFO_EXCEPTION* pdle)
6932 ISOSDacInterface4 *psos4 = NULL;
6933 CLRDATA_ADDRESS arguments[3];
6936 if (SUCCEEDED(Status = g_sos->QueryInterface(__uuidof(ISOSDacInterface4), (void**) &psos4)))
6938 int count = _countof(arguments);
6939 int countNeeded = 0;
6941 Status = psos4->GetClrNotification(arguments, count, &countNeeded);
6944 if (SUCCEEDED(Status))
6946 memset(&pdle->ExceptionRecord, 0, sizeof(pdle->ExceptionRecord));
6947 pdle->FirstChance = TRUE;
6948 pdle->ExceptionRecord.ExceptionCode = CLRDATA_NOTIFY_EXCEPTION;
6950 _ASSERTE(count <= EXCEPTION_MAXIMUM_PARAMETERS);
6951 for (int i = 0; i < count; i++)
6953 pdle->ExceptionRecord.ExceptionInformation[i] = arguments[i];
6955 // The rest of the ExceptionRecord isn't used by TranslateExceptionRecordToNotification
6958 // No pending exception notification
6962 // The new DAC based interface doesn't exists so ask the debugger for the last exception
6963 // information. NOTE: this function doesn't work on xplat version when the coreclr symbols
6964 // have been stripped.
6966 ULONG Type, ProcessId, ThreadId;
6967 ULONG ExtraInformationUsed;
6968 Status = g_ExtControl->GetLastEventInformation(
6973 sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION),
6974 &ExtraInformationUsed,
6979 if (Status != S_OK || Type != DEBUG_EVENT_EXCEPTION)
6984 if (!pdle->FirstChance || pdle->ExceptionRecord.ExceptionCode != CLRDATA_NOTIFY_EXCEPTION)
6992 HRESULT HandleCLRNotificationEvent()
6995 * Did we get module load notification? If so, check if any in our pending list
6996 * need to be registered for jit notification.
6998 * Did we get a jit notification? If so, check if any can be removed and
6999 * real breakpoints be set.
7001 DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
7002 CNotification Notification;
7004 if (!CheckCLRNotificationEvent(&dle))
7007 ExtOut("Expecting first chance CLRN exception\n");
7010 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
7015 // Notification only needs to live for the lifetime of the call below, so it's a non-static
7017 HRESULT Status = g_clrData->TranslateExceptionRecordToNotification(&dle.ExceptionRecord, &Notification);
7020 ExtErr("Error processing exception notification\n");
7025 switch (Notification.GetDebugStatus())
7027 case DEBUG_STATUS_GO:
7028 case DEBUG_STATUS_GO_HANDLED:
7029 case DEBUG_STATUS_GO_NOT_HANDLED:
7031 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "g", 0);
7033 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
7046 DECLARE_API(HandleCLRN)
7049 MINIDUMP_NOT_SUPPORTED();
7051 return HandleCLRNotificationEvent();
7054 #else // FEATURE_PAL
7056 HRESULT HandleExceptionNotification(ILLDBServices *client)
7059 return HandleCLRNotificationEvent();
7062 #endif // FEATURE_PAL
7067 MINIDUMP_NOT_SUPPORTED();
7073 ExtOut(SOSPrefix "bpmd is not supported on a dump file.\n");
7078 // We keep a list of managed breakpoints the user wants to set, and display pending bps
7079 // bpmd. If you call bpmd <module name> <method> we will set or update an existing bp.
7080 // bpmd acts as a feeder of breakpoints to bp when the time is right.
7083 StringHolder DllName,TypeName;
7087 DWORD_PTR pMD = NULL;
7088 BOOL fNoFutureModule = FALSE;
7090 size_t clearItem = 0;
7091 BOOL fClearAll = FALSE;
7092 CMDOption option[] =
7093 { // name, vptr, type, hasValue
7094 {"-md", &pMD, COHEX, TRUE},
7095 {"-nofuturemodule", &fNoFutureModule, COBOOL, FALSE},
7096 {"-list", &fList, COBOOL, FALSE},
7097 {"-clear", &clearItem, COSIZE_T, TRUE},
7098 {"-clearall", &fClearAll, COBOOL, FALSE},
7102 {&DllName.data, COSTRING},
7103 {&TypeName.data, COSTRING},
7104 {&Offset, COSIZE_T},
7107 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
7112 bool fBadParam = false;
7113 bool fIsFilename = false;
7114 int commandsParsed = 0;
7148 if (1 <= nArg && nArg <= 3)
7151 // did we get dll and type name or file:line#? Search for a colon in the first arg
7152 // to see if it is in fact a file:line#
7153 CHAR* pColon = strchr(DllName.data, ':');
7155 if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A, 0, NULL, NULL))) {
7157 if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_DLL_NAME_A, 0, NULL, NULL))) {
7159 ExtOut("%s not loaded yet\n", MAIN_CLR_DLL_NAME_A);
7168 if(1 != sscanf_s(pColon, "%d", &lineNumber))
7170 ExtOut("Unable to parse line number\n");
7173 else if(lineNumber < 0)
7175 ExtOut("Line number must be positive\n");
7178 if(nArg != 1) fBadParam = 1;
7182 if (fBadParam || (commandsParsed != 1))
7184 ExtOut("Usage: " SOSPrefix "bpmd -md <MethodDesc pointer>\n");
7185 ExtOut("Usage: " SOSPrefix "bpmd [-nofuturemodule] <module name> <managed function name> [<il offset>]\n");
7186 ExtOut("Usage: " SOSPrefix "bpmd <filename>:<line number>\n");
7187 ExtOut("Usage: " SOSPrefix "bpmd -list\n");
7188 ExtOut("Usage: " SOSPrefix "bpmd -clear <pending breakpoint number>\n");
7189 ExtOut("Usage: " SOSPrefix "bpmd -clearall\n");
7191 ExtOut("See \"soshelp bpmd\" for more details.\n");
7193 ExtOut("See \"!help bpmd\" for more details.\n");
7200 g_bpoints.ListBreakpoints();
7205 g_bpoints.ClearBreakpoint(clearItem);
7210 g_bpoints.ClearAllBreakpoints();
7214 // Do we already have this breakpoint?
7215 // Or, before setting it, is the module perhaps already loaded and code
7216 // is available? If so, don't add to our pending list, just go ahead and
7217 // set the real breakpoint.
7219 LPWSTR ModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
7220 LPWSTR FunctionName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
7221 LPWSTR Filename = (LPWSTR)alloca(MAX_LONGPATH * sizeof(WCHAR));
7223 BOOL bNeedNotificationExceptions = FALSE;
7230 ArrayHolder<DWORD_PTR> moduleList = NULL;
7234 MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, ModuleName, mdNameLen);
7235 MultiByteToWideChar(CP_ACP, 0, TypeName.data, -1, FunctionName, mdNameLen);
7239 MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, Filename, MAX_LONGPATH);
7242 // Get modules that may need a breakpoint bound
7243 if ((Status = CheckEEDll()) == S_OK)
7245 if ((Status = LoadClrDebugDll()) != S_OK)
7247 // if the EE is loaded but DAC isn't we should stop.
7251 g_bDacBroken = FALSE; \
7253 // Get the module list
7254 moduleList = ModuleFromName(fIsFilename ? NULL : DllName.data, &numModule);
7256 // Its OK if moduleList is NULL
7257 // There is a very normal case when checking for modules after clr is loaded
7258 // but before any AppDomains or assemblies are created
7264 // >!bpmd Foo.dll Foo.Bar
7266 // If LoadClrDebugDll() succeeded make sure we release g_clrData
7267 ToRelease<IXCLRDataProcess> spIDP(g_clrData);
7268 ToRelease<ISOSDacInterface> spISD(g_sos);
7271 // we can get here with EE not loaded => 0 modules
7272 // EE is loaded => 0 or more modules
7273 ArrayHolder<DWORD_PTR> pMDs = NULL;
7274 for (int iModule = 0; iModule < numModule; iModule++)
7276 ToRelease<IXCLRDataModule> ModDef;
7277 if (g_sos->GetModule(moduleList[iModule], &ModDef) != S_OK)
7282 HRESULT symbolsLoaded = S_FALSE;
7285 g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, moduleList[iModule], (DWORD)Offset);
7289 SymbolReader symbolReader;
7290 symbolsLoaded = g_bpoints.LoadSymbolsForModule(moduleList[iModule], &symbolReader);
7291 if(symbolsLoaded == S_OK &&
7292 g_bpoints.ResolvePendingNonModuleBoundBreakpoint(Filename, lineNumber, moduleList[iModule], &symbolReader) == S_OK)
7294 // if we have symbols then get the function name so we can lookup the MethodDescs
7295 mdMethodDef methodDefToken;
7297 if(SUCCEEDED(symbolReader.ResolveSequencePoint(Filename, lineNumber, moduleList[iModule], &methodDefToken, &ilOffset)))
7299 ToRelease<IXCLRDataMethodDefinition> pMethodDef = NULL;
7300 if (SUCCEEDED(ModDef->GetMethodDefinitionByToken(methodDefToken, &pMethodDef)))
7302 ULONG32 nameLen = 0;
7303 pMethodDef->GetName(0, mdNameLen, &nameLen, FunctionName);
7305 // get the size of the required buffer
7306 int buffSize = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, 0, NULL, NULL);
7308 TypeName.data = new NOTHROW char[buffSize];
7309 if (TypeName.data != NULL)
7311 int bytesWritten = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, buffSize, NULL, NULL);
7312 _ASSERTE(bytesWritten == buffSize);
7319 HRESULT gotMethodDescs = GetMethodDescsFromName(moduleList[iModule], ModDef, TypeName.data, &pMDs, &numMethods);
7320 if (FAILED(gotMethodDescs) && (!fIsFilename))
7322 // BPs via file name will enumerate through modules so there will be legitimate failures.
7323 // for module/type name we already found a match so this shouldn't fail (this is the original behavior).
7324 ExtOut("Error getting MethodDescs for module %p\n", moduleList[iModule]);
7328 // for filename+line number only print extra info if symbols for this module are loaded (it can get quite noisy otherwise).
7329 if ((!fIsFilename) || (fIsFilename && symbolsLoaded == S_OK))
7331 for (int i = 0; i < numMethods; i++)
7333 if (pMDs[i] == MD_NOT_YET_LOADED)
7337 ExtOut("MethodDesc = %p\n", SOS_PTR(pMDs[i]));
7341 if (g_bpoints.Update(moduleList[iModule], FALSE))
7343 bNeedNotificationExceptions = TRUE;
7347 if (!fNoFutureModule)
7349 // add a pending breakpoint that will find future loaded modules, and
7350 // wait for the module load notification.
7353 g_bpoints.Add(ModuleName, FunctionName, NULL, (DWORD)Offset);
7357 g_bpoints.Add(Filename, lineNumber, NULL);
7359 bNeedNotificationExceptions = TRUE;
7362 g_clrData->GetOtherNotificationFlags(&flags);
7363 flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD);
7364 g_clrData->SetOtherNotificationFlags(flags);
7367 else /* We were given a MethodDesc already */
7369 // if we've got an explicit MD, then we better have CLR and mscordacwks loaded
7373 DacpMethodDescData MethodDescData;
7374 ExtOut("MethodDesc = %p\n", SOS_PTR(pMD));
7375 if (MethodDescData.Request(g_sos, TO_CDADDR(pMD)) != S_OK)
7377 ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(pMD));
7381 if (MethodDescData.bHasNativeCode)
7383 IssueDebuggerBPCommand((size_t) MethodDescData.NativeCodeAddr);
7385 else if (MethodDescData.bIsDynamic)
7388 // Dynamic methods don't have JIT notifications. This is something we must
7389 // fix in the next release. Until then, you have a cumbersome user experience.
7390 ExtOut("This DynamicMethodDesc is not yet JITTED. Placing memory breakpoint at %p\n",
7391 MethodDescData.AddressOfNativeCodeSlot);
7393 sprintf_s(buffer, _countof(buffer),
7394 #ifdef _TARGET_WIN64_
7398 #endif // _TARGET_WIN64_
7400 " /1 %p \"bp poi(%p); g\"",
7401 (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot,
7402 (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot);
7404 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
7407 ExtOut("Unable to set breakpoint with IDebugControl::Execute: %x\n",Status);
7408 ExtOut("Attempted to run: %s\n", buffer);
7411 ExtErr("This DynamicMethodDesc is not yet JITTED %p\n", MethodDescData.AddressOfNativeCodeSlot);
7412 #endif // FEATURE_PAL
7416 // Must issue a pending breakpoint.
7417 if (g_sos->GetMethodDescName(pMD, mdNameLen, FunctionName, NULL) != S_OK)
7419 ExtOut("Unable to get method name for MethodDesc %p\n", SOS_PTR(pMD));
7423 FileNameForModule ((DWORD_PTR) MethodDescData.ModulePtr, ModuleName);
7425 // We didn't find code, add a breakpoint.
7426 g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, TO_TADDR(MethodDescData.ModulePtr), 0);
7427 g_bpoints.Update(TO_TADDR(MethodDescData.ModulePtr), FALSE);
7428 bNeedNotificationExceptions = TRUE;
7432 if (bNeedNotificationExceptions)
7434 ExtOut("Adding pending breakpoints...\n");
7436 sprintf_s(buffer, _countof(buffer), "sxe -c \"!HandleCLRN\" clrn");
7437 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
7439 Status = g_ExtServices->SetExceptionCallback(HandleExceptionNotification);
7440 #endif // FEATURE_PAL
7448 /**********************************************************************\
7449 * Routine Description: *
7451 * This function is called to dump the managed threadpool *
7453 \**********************************************************************/
7454 DECLARE_API(ThreadPool)
7457 MINIDUMP_NOT_SUPPORTED();
7459 DacpThreadpoolData threadpool;
7461 if ((Status = threadpool.Request(g_sos)) == S_OK)
7463 BOOL doHCDump = FALSE;
7465 CMDOption option[] =
7466 { // name, vptr, type, hasValue
7467 {"-ti", &doHCDump, COBOOL, FALSE}
7470 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
7475 ExtOut ("CPU utilization: %d%%\n", threadpool.cpuUtilization);
7476 ExtOut ("Worker Thread:");
7477 ExtOut (" Total: %d", threadpool.NumWorkingWorkerThreads + threadpool.NumIdleWorkerThreads + threadpool.NumRetiredWorkerThreads);
7478 ExtOut (" Running: %d", threadpool.NumWorkingWorkerThreads);
7479 ExtOut (" Idle: %d", threadpool.NumIdleWorkerThreads);
7480 ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalWorkerThreads);
7481 ExtOut (" MinLimit: %d", threadpool.MinLimitTotalWorkerThreads);
7484 int numWorkRequests = 0;
7485 CLRDATA_ADDRESS workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
7486 DacpWorkRequestData workRequestData;
7487 while (workRequestPtr)
7489 if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
7491 ExtOut(" Failed to examine a WorkRequest\n");
7495 workRequestPtr = workRequestData.NextWorkRequest;
7498 ExtOut ("Work Request in Queue: %d\n", numWorkRequests);
7499 workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
7500 while (workRequestPtr)
7502 if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
7504 ExtOut(" Failed to examine a WorkRequest\n");
7508 if (workRequestData.Function == threadpool.AsyncTimerCallbackCompletionFPtr)
7509 ExtOut (" AsyncTimerCallbackCompletion TimerInfo@%p\n", SOS_PTR(workRequestData.Context));
7511 ExtOut (" Unknown Function: %p Context: %p\n", SOS_PTR(workRequestData.Function),
7512 SOS_PTR(workRequestData.Context));
7514 workRequestPtr = workRequestData.NextWorkRequest;
7519 ExtOut ("--------------------------------------\n");
7520 ExtOut ("\nThread Injection History\n");
7521 if (threadpool.HillClimbingLogSize > 0)
7523 static char const * const TransitionNames[] =
7536 ExtOut("\n Time Transition New #Threads #Samples Throughput\n");
7537 DacpHillClimbingLogEntry entry;
7539 // get the most recent entry first, so we can calculate time offsets
7541 int index = (threadpool.HillClimbingLogFirstIndex + threadpool.HillClimbingLogSize-1) % HillClimbingLogCapacity;
7542 CLRDATA_ADDRESS entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
7543 if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
7545 ExtOut(" Failed to examine a HillClimbing log entry\n");
7548 DWORD endTime = entry.TickCount;
7550 for (int i = 0; i < threadpool.HillClimbingLogSize; i++)
7552 index = (i + threadpool.HillClimbingLogFirstIndex) % HillClimbingLogCapacity;
7553 entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
7555 if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
7557 ExtOut(" Failed to examine a HillClimbing log entry\n");
7561 ExtOut("%8.2lf %-14s %12d %12d %11.2lf\n",
7562 (double)(int)(entry.TickCount - endTime) / 1000.0,
7563 TransitionNames[entry.Transition],
7564 entry.NewControlSetting,
7565 entry.LastHistoryCount,
7566 entry.LastHistoryMean);
7571 ExtOut ("--------------------------------------\n");
7572 ExtOut ("Number of Timers: %d\n", threadpool.NumTimers);
7573 ExtOut ("--------------------------------------\n");
7575 ExtOut ("Completion Port Thread:");
7576 ExtOut ("Total: %d", threadpool.NumCPThreads);
7577 ExtOut (" Free: %d", threadpool.NumFreeCPThreads);
7578 ExtOut (" MaxFree: %d", threadpool.MaxFreeCPThreads);
7579 ExtOut (" CurrentLimit: %d", threadpool.CurrentLimitTotalCPThreads);
7580 ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalCPThreads);
7581 ExtOut (" MinLimit: %d", threadpool.MinLimitTotalCPThreads);
7586 ExtOut("Failed to request ThreadpoolMgr information\n");
7591 #endif // FEATURE_PAL
7593 DECLARE_API(FindAppDomain)
7596 MINIDUMP_NOT_SUPPORTED();
7598 DWORD_PTR p_Object = NULL;
7601 CMDOption option[] =
7602 { // name, vptr, type, hasValue
7604 {"/d", &dml, COBOOL, FALSE},
7613 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
7618 EnableDMLHolder dmlHolder(dml);
7620 if ((p_Object == 0) || !sos::IsObject(p_Object))
7622 ExtOut("%p is not a valid object\n", SOS_PTR(p_Object));
7626 DacpAppDomainStoreData adstore;
7627 if (adstore.Request(g_sos) != S_OK)
7629 ExtOut("Error getting AppDomain information\n");
7633 CLRDATA_ADDRESS appDomain = GetAppDomain (TO_CDADDR(p_Object));
7635 if (appDomain != NULL)
7637 DMLOut("AppDomain: %s\n", DMLDomain(appDomain));
7638 if (appDomain == adstore.sharedDomain)
7640 ExtOut("Name: Shared Domain\n");
7641 ExtOut("ID: (shared domain)\n");
7643 else if (appDomain == adstore.systemDomain)
7645 ExtOut("Name: System Domain\n");
7646 ExtOut("ID: (system domain)\n");
7650 DacpAppDomainData domain;
7651 if ((domain.Request(g_sos, appDomain) != S_OK) ||
7652 (g_sos->GetAppDomainName(appDomain,mdNameLen,g_mdName, NULL)!=S_OK))
7654 ExtOut("Error getting AppDomain %p.\n", SOS_PTR(appDomain));
7658 ExtOut("Name: %S\n", (g_mdName[0]!=L'\0') ? g_mdName : W("None"));
7659 ExtOut("ID: %d\n", domain.dwId);
7664 ExtOut("The type is declared in the shared domain and other\n");
7665 ExtOut("methods of finding the AppDomain failed. Try running\n");
7667 DMLOut("<exec cmd=\"!gcroot /d %p\">!gcroot %p</exec>, and if you find a root on a\n", p_Object, p_Object);
7669 ExtOut("!gcroot %p, and if you find a root on a\n", p_Object);
7670 ExtOut("stack, check the AppDomain of that stack with !threads.\n");
7671 ExtOut("Note that the Thread could have transitioned between\n");
7672 ExtOut("multiple AppDomains.\n");
7680 /**********************************************************************\
7681 * Routine Description: *
7683 * This function is called to get the COM state (e.g. APT,contexe *
7686 \**********************************************************************/
7687 #ifdef FEATURE_COMINTEROP
7688 DECLARE_API(COMState)
7691 MINIDUMP_NOT_SUPPORTED();
7696 g_ExtSystem->GetTotalNumberThreads(&numThread,&maxId);
7699 g_ExtSystem->GetCurrentThreadId(&curId);
7702 if (!ClrSafeInt<SIZE_T>::multiply(sizeof(ULONG), numThread, AllocSize))
7704 ExtOut(" Error! integer overflow on numThread 0x%08x\n", numThread);
7707 ULONG *ids = (ULONG*)alloca(AllocSize);
7708 ULONG *sysIds = (ULONG*)alloca(AllocSize);
7709 g_ExtSystem->GetThreadIdsByIndex(0,numThread,ids,sysIds);
7710 #if defined(_TARGET_WIN64_)
7711 ExtOut(" ID TEB APT APTId CallerTID Context\n");
7713 ExtOut(" ID TEB APT APTId CallerTID Context\n");
7715 for (ULONG i = 0; i < numThread; i ++) {
7716 g_ExtSystem->SetCurrentThreadId(ids[i]);
7717 CLRDATA_ADDRESS cdaTeb;
7718 g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
7719 ExtOut("%3d %4x %p", ids[i], sysIds[i], SOS_PTR(CDA_TO_UL64(cdaTeb)));
7721 TADDR OleTlsDataAddr;
7722 if (SafeReadMemory(TO_TADDR(cdaTeb) + offsetof(TEB,ReservedForOle),
7724 sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0) {
7726 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
7728 sizeof(AptState), NULL)) {
7729 if (AptState & OLETLS_APARTMENTTHREADED) {
7732 else if (AptState & OLETLS_MULTITHREADED) {
7735 else if (AptState & OLETLS_INNEUTRALAPT) {
7742 // Read these fields only if we were able to read anything of the SOleTlsData structure
7743 DWORD dwApartmentID;
7744 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwApartmentID),
7746 sizeof(dwApartmentID), NULL)) {
7747 ExtOut(" %8x", dwApartmentID);
7753 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwTIDCaller),
7755 sizeof(dwTIDCaller), NULL)) {
7756 ExtOut(" %8x", dwTIDCaller);
7762 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,pCurrentCtx),
7764 sizeof(Context), NULL)) {
7765 ExtOut(" %p", SOS_PTR(Context));
7768 ExtOut(" %p", SOS_PTR(0));
7779 g_ExtSystem->SetCurrentThreadId(curId);
7782 #endif // FEATURE_COMINTEROP
7784 #endif // FEATURE_PAL
7786 BOOL traverseEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
7788 size_t methodStart = (size_t) token;
7795 ExtOut("EHHandler %d: %s ", clauseIndex, EHTypeName(pEHInfo->clauseType));
7797 LPCWSTR typeName = EHTypedClauseTypeName(pEHInfo);
7798 if (typeName != NULL)
7800 ExtOut("catch(%S) ", typeName);
7803 if (IsClonedFinally(pEHInfo))
7804 ExtOut("(cloned finally)");
7805 else if (pEHInfo->isDuplicateClause)
7806 ExtOut("(duplicate)");
7811 ULONG64 addrStart = pEHInfo->tryStartOffset + methodStart;
7812 ULONG64 addrEnd = pEHInfo->tryEndOffset + methodStart;
7815 ExtOut("[%08x`%08x, %08x`%08x]",
7816 (ULONG)(addrStart >> 32), (ULONG)addrStart,
7817 (ULONG)(addrEnd >> 32), (ULONG)addrEnd);
7819 ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
7822 ExtOut(" [%x, %x]\n",
7823 (UINT32) pEHInfo->tryStartOffset,
7824 (UINT32) pEHInfo->tryEndOffset);
7826 ExtOut("Handler: ");
7828 addrStart = pEHInfo->handlerStartOffset + methodStart;
7829 addrEnd = pEHInfo->handlerEndOffset + methodStart;
7832 ExtOut("[%08x`%08x, %08x`%08x]",
7833 (ULONG)(addrStart >> 32), (ULONG)addrStart,
7834 (ULONG)(addrEnd >> 32), (ULONG)addrEnd);
7836 ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
7839 ExtOut(" [%x, %x]\n",
7840 (UINT32) pEHInfo->handlerStartOffset,
7841 (UINT32) pEHInfo->handlerEndOffset);
7843 if (pEHInfo->clauseType == EHFilter)
7847 addrStart = pEHInfo->filterOffset + methodStart;
7850 ExtOut("[%08x`%08x]", (ULONG)(addrStart >> 32), (ULONG)addrStart);
7852 ExtOut("[%08x]", (ULONG)addrStart);
7856 (UINT32) pEHInfo->filterOffset);
7866 MINIDUMP_NOT_SUPPORTED();
7868 DWORD_PTR dwStartAddr = NULL;
7871 CMDOption option[] =
7872 { // name, vptr, type, hasValue
7873 {"/d", &dml, COBOOL, FALSE},
7878 {&dwStartAddr, COHEX},
7882 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
7887 EnableDMLHolder dmlHolder(dml);
7888 DWORD_PTR tmpAddr = dwStartAddr;
7890 if (!IsMethodDesc(dwStartAddr))
7893 DWORD_PTR methodDesc;
7894 DWORD_PTR gcinfoAddr;
7895 IP2MethodDesc (dwStartAddr, methodDesc, jitType, gcinfoAddr);
7896 tmpAddr = methodDesc;
7899 DacpMethodDescData MD;
7900 if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
7902 ExtOut("%p is not a MethodDesc\n", SOS_PTR(tmpAddr));
7906 if (1 == nArg && !MD.bHasNativeCode)
7908 ExtOut("No EH info available\n");
7912 DacpCodeHeaderData codeHeaderData;
7913 if (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
7915 ExtOut("Unable to get codeHeader information\n");
7919 DMLOut("MethodDesc: %s\n", DMLMethodDesc(MD.MethodDescPtr));
7920 DumpMDInfo(TO_TADDR(MD.MethodDescPtr));
7923 Status = g_sos->TraverseEHInfo(TO_CDADDR(MD.NativeCodeAddr), traverseEh, (LPVOID)MD.NativeCodeAddr);
7925 if (Status == E_ABORT)
7927 ExtOut("<user aborted>\n");
7929 else if (Status != S_OK)
7931 ExtOut("Failed to perform EHInfo traverse\n");
7937 /**********************************************************************\
7938 * Routine Description: *
7940 * This function is called to dump the GC encoding of a managed *
7943 \**********************************************************************/
7947 MINIDUMP_NOT_SUPPORTED();
7950 TADDR taStartAddr = NULL;
7954 CMDOption option[] =
7955 { // name, vptr, type, hasValue
7956 {"/d", &dml, COBOOL, FALSE},
7960 {&taStartAddr, COHEX},
7963 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
7968 EnableDMLHolder dmlHolder(dml);
7969 TADDR tmpAddr = taStartAddr;
7971 if (!IsMethodDesc(taStartAddr))
7976 IP2MethodDesc(taStartAddr, methodDesc, jitType, gcinfoAddr);
7977 tmpAddr = methodDesc;
7980 DacpMethodDescData MD;
7981 if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
7983 ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(taStartAddr));
7987 if (1 == nArg && !MD.bHasNativeCode)
7989 ExtOut("No GC info available\n");
7993 DacpCodeHeaderData codeHeaderData;
7996 // Try to get code header data from taStartAddr. This will get the code
7997 // header corresponding to the IP address, even if the function was rejitted
7998 (codeHeaderData.Request(g_sos, TO_CDADDR(taStartAddr)) != S_OK) &&
8000 // If that didn't work, just try to use the code address that the MD
8001 // points to. If the function was rejitted, this will only give you the
8002 // original JITted code, but that's better than nothing
8003 (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
8006 // We always used to emit this (before rejit support), even if we couldn't get
8007 // the code header, so keep on doing so.
8008 ExtOut("entry point %p\n", SOS_PTR(MD.NativeCodeAddr));
8010 // And now the error....
8011 ExtOut("Unable to get codeHeader information\n");
8015 // We have the code header, so use it to determine the method start
8017 ExtOut("entry point %p\n", SOS_PTR(codeHeaderData.MethodStart));
8019 if (codeHeaderData.JITType == TYPE_UNKNOWN)
8021 ExtOut("unknown Jit\n");
8024 else if (codeHeaderData.JITType == TYPE_JIT)
8026 ExtOut("Normal JIT generated code\n");
8028 else if (codeHeaderData.JITType == TYPE_PJIT)
8030 ExtOut("preJIT generated code\n");
8033 taGCInfoAddr = TO_TADDR(codeHeaderData.GCInfo);
8035 ExtOut("GC info %p\n", SOS_PTR(taGCInfoAddr));
8037 // assume that GC encoding table is never more than
8038 // 40 + methodSize * 2
8040 if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
8041 !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
8043 ExtOut("<integer overflow>\n");
8046 ArrayHolder<BYTE> table = new NOTHROW BYTE[tableSize];
8049 ExtOut("Could not allocate memory to read the gc info.\n");
8050 return E_OUTOFMEMORY;
8053 memset(table, 0, tableSize);
8054 // We avoid using move here, because we do not want to return
8055 if (!SafeReadMemory(taGCInfoAddr, table, tableSize, NULL))
8057 ExtOut("Could not read memory %p\n", SOS_PTR(taGCInfoAddr));
8061 // Mutable table pointer since we need to pass the appropriate
8062 // offset into the table to DumpGCTable.
8063 GCInfoToken gcInfoToken = { table, GCINFO_VERSION };
8064 unsigned int methodSize = (unsigned int)codeHeaderData.MethodSize;
8066 g_targetMachine->DumpGCInfo(gcInfoToken, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/);
8071 #if !defined(FEATURE_PAL)
8073 void DecodeGCTableEntry (const char *fmt, ...)
8075 GCEncodingInfo *pInfo = (GCEncodingInfo*)GetFiberData();
8079 // Append the new data to the buffer
8084 int cch = _vsnprintf_s(&pInfo->buf[pInfo->cch], _countof(pInfo->buf) - pInfo->cch, _countof(pInfo->buf) - pInfo->cch - 1, fmt, va);
8090 pInfo->buf[pInfo->cch] = '\0';
8093 // If there are complete lines in the buffer, decode them.
8098 char *pNewLine = strchr(pInfo->buf, '\n');
8104 // The line should start with a 16-bit (x86) or 32-bit (non-x86) hex
8105 // offset. strtoul returns ULONG_MAX or 0 on failure. 0 is a valid
8106 // offset for the first encoding, or while the last offset was 0.
8109 if (isxdigit(pInfo->buf[0]))
8112 ULONG ofs = strtoul(pInfo->buf, &pEnd, 16);
8116 && ( -1 == pInfo->ofs
8123 SwitchToFiber(pInfo->pvMainFiber);
8126 else if (0 == strncmp(pInfo->buf, "Untracked:", 10))
8131 SwitchToFiber(pInfo->pvMainFiber);
8135 // Shift the remaining data to the start of the buffer
8138 strcpy_s(pInfo->buf, _countof(pInfo->buf), pNewLine+1);
8139 pInfo->cch = (int)strlen(pInfo->buf);
8144 VOID CALLBACK DumpGCTableFiberEntry (LPVOID pvGCEncodingInfo)
8146 GCEncodingInfo *pInfo = (GCEncodingInfo*)pvGCEncodingInfo;
8147 GCInfoToken gcInfoToken = { pInfo->table, GCINFO_VERSION };
8148 g_targetMachine->DumpGCInfo(gcInfoToken, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
8150 pInfo->fDoneDecoding = true;
8151 SwitchToFiber(pInfo->pvMainFiber);
8153 #endif // !FEATURE_PAL
8155 BOOL gatherEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
8157 SOSEHInfo *pInfo = (SOSEHInfo *) token;
8164 if (pInfo->m_pInfos == NULL)
8166 // First time, initialize structure
8167 pInfo->EHCount = totalClauses;
8168 pInfo->m_pInfos = new NOTHROW DACEHInfo[totalClauses];
8169 if (pInfo->m_pInfos == NULL)
8176 pInfo->m_pInfos[clauseIndex] = *((DACEHInfo*)pEHInfo);
8181 /**********************************************************************\
8182 * Routine Description: *
8184 * This function is called to unassembly a managed function. *
8185 * It tries to print symbolic info for function call, contants... *
8187 \**********************************************************************/
8191 MINIDUMP_NOT_SUPPORTED();
8194 DWORD_PTR dwStartAddr = NULL;
8195 BOOL fWithGCInfo = FALSE;
8196 BOOL fWithEHInfo = FALSE;
8197 BOOL bSuppressLines = FALSE;
8198 BOOL bDisplayOffsets = FALSE;
8202 CMDOption option[] =
8203 { // name, vptr, type, hasValue
8205 {"-gcinfo", &fWithGCInfo, COBOOL, FALSE},
8207 {"-ehinfo", &fWithEHInfo, COBOOL, FALSE},
8208 {"-n", &bSuppressLines, COBOOL, FALSE},
8209 {"-o", &bDisplayOffsets, COBOOL, FALSE},
8211 {"/d", &dml, COBOOL, FALSE},
8216 {&dwStartAddr, COHEX},
8218 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (nArg < 1))
8222 // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
8224 if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
8226 symlines &= SYMOPT_LOAD_LINES;
8228 bSuppressLines = bSuppressLines || (symlines == 0);
8230 EnableDMLHolder dmlHolder(dml);
8231 // dwStartAddr is either some IP address or a MethodDesc. Start off assuming it's a
8233 DWORD_PTR methodDesc = dwStartAddr;
8234 if (!IsMethodDesc(methodDesc))
8236 // Not a methodDesc, so gotta find it ourselves
8237 DWORD_PTR tmpAddr = dwStartAddr;
8239 DWORD_PTR gcinfoAddr;
8240 IP2MethodDesc (tmpAddr, methodDesc, jt,
8242 if (!methodDesc || jt == TYPE_UNKNOWN)
8244 // It is not managed code.
8245 ExtOut("Unmanaged code\n");
8246 UnassemblyUnmanaged(dwStartAddr, bSuppressLines);
8251 DacpMethodDescData MethodDescData;
8252 if ((Status=MethodDescData.Request(g_sos, TO_CDADDR(methodDesc))) != S_OK)
8254 ExtOut("Failed to get method desc for %p.\n", SOS_PTR(dwStartAddr));
8258 if (!MethodDescData.bHasNativeCode)
8260 ExtOut("Not jitted yet\n");
8264 // Get the appropriate code header. If we were passed an MD, then use
8265 // MethodDescData.NativeCodeAddr to find the code header; if we were passed an IP, use
8266 // that IP to find the code header. This ensures that, for rejitted functions, we
8267 // disassemble the rejit version that the user explicitly specified with their IP.
8268 DacpCodeHeaderData codeHeaderData;
8269 if (codeHeaderData.Request(
8272 (dwStartAddr == methodDesc) ? MethodDescData.NativeCodeAddr : dwStartAddr)
8276 ExtOut("Unable to get codeHeader information\n");
8280 if (codeHeaderData.MethodStart == 0)
8282 ExtOut("not a valid MethodDesc\n");
8286 if (codeHeaderData.JITType == TYPE_UNKNOWN)
8288 ExtOut("unknown Jit\n");
8291 else if (codeHeaderData.JITType == TYPE_JIT)
8293 ExtOut("Normal JIT generated code\n");
8295 else if (codeHeaderData.JITType == TYPE_PJIT)
8297 ExtOut("preJIT generated code\n");
8300 NameForMD_s(methodDesc, g_mdName, mdNameLen);
8301 ExtOut("%S\n", g_mdName);
8302 if (codeHeaderData.ColdRegionStart != NULL)
8304 ExtOut("Begin %p, size %x. Cold region begin %p, size %x\n",
8305 SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.HotRegionSize,
8306 SOS_PTR(codeHeaderData.ColdRegionStart), codeHeaderData.ColdRegionSize);
8310 ExtOut("Begin %p, size %x\n", SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.MethodSize);
8313 #if !defined(FEATURE_PAL)
8315 // Set up to mix gc info with the code if requested
8318 GCEncodingInfo gcEncodingInfo = {0};
8320 // The actual GC Encoding Table, this is updated during the course of the function.
8321 gcEncodingInfo.table = NULL;
8323 // The holder to make sure we clean up the memory for the table
8324 ArrayHolder<BYTE> table = NULL;
8328 // assume that GC encoding table is never more than 40 + methodSize * 2
8330 if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
8331 !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
8333 ExtOut("<integer overflow>\n");
8338 // Assign the new array to the mutable gcEncodingInfo table and to the
8339 // table ArrayHolder to clean this up when the function exits.
8340 table = gcEncodingInfo.table = new NOTHROW BYTE[tableSize];
8342 if (gcEncodingInfo.table == NULL)
8344 ExtOut("Could not allocate memory to read the gc info.\n");
8345 return E_OUTOFMEMORY;
8348 memset (gcEncodingInfo.table, 0, tableSize);
8349 // We avoid using move here, because we do not want to return
8350 if (!SafeReadMemory(TO_TADDR(codeHeaderData.GCInfo), gcEncodingInfo.table, tableSize, NULL))
8352 ExtOut("Could not read memory %p\n", SOS_PTR(codeHeaderData.GCInfo));
8357 // Skip the info header
8359 gcEncodingInfo.methodSize = (unsigned int)codeHeaderData.MethodSize;
8362 // DumpGCTable will call gcPrintf for each encoding. We'd like a "give
8363 // me the next encoding" interface, but we're stuck with the callback.
8364 // To reconcile this without messing up too much code, we'll create a
8365 // fiber to dump the gc table. When we need the next gc encoding,
8366 // we'll switch to this fiber. The callback will note the next offset,
8367 // and switch back to the main fiber.
8370 gcEncodingInfo.ofs = -1;
8371 gcEncodingInfo.hotSizeToAdd = 0;
8373 gcEncodingInfo.pvMainFiber = ConvertThreadToFiber(NULL);
8374 if (!gcEncodingInfo.pvMainFiber && ERROR_ALREADY_FIBER == GetLastError())
8375 gcEncodingInfo.pvMainFiber = GetCurrentFiber();
8377 if (!gcEncodingInfo.pvMainFiber)
8380 gcEncodingInfo.pvGCTableFiber = CreateFiber(0, DumpGCTableFiberEntry, &gcEncodingInfo);
8381 if (!gcEncodingInfo.pvGCTableFiber)
8384 SwitchToFiber(gcEncodingInfo.pvGCTableFiber);
8388 SOSEHInfo *pInfo = NULL;
8391 pInfo = new NOTHROW SOSEHInfo;
8396 else if (g_sos->TraverseEHInfo(MethodDescData.NativeCodeAddr, gatherEh, (LPVOID)pInfo) != S_OK)
8398 ExtOut("Failed to gather EHInfo data\n");
8404 if (codeHeaderData.ColdRegionStart == NULL)
8406 g_targetMachine->Unassembly (
8407 (DWORD_PTR) codeHeaderData.MethodStart,
8408 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.MethodSize,
8410 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
8411 #if !defined(FEATURE_PAL)
8412 fWithGCInfo ? &gcEncodingInfo :
8422 ExtOut("Hot region:\n");
8423 g_targetMachine->Unassembly (
8424 (DWORD_PTR) codeHeaderData.MethodStart,
8425 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.HotRegionSize,
8427 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
8428 #if !defined(FEATURE_PAL)
8429 fWithGCInfo ? &gcEncodingInfo :
8437 ExtOut("Cold region:\n");
8439 #if !defined(FEATURE_PAL)
8440 // Displaying gcinfo for a cold region requires knowing the size of
8441 // the hot region preceeding.
8442 gcEncodingInfo.hotSizeToAdd = codeHeaderData.HotRegionSize;
8444 g_targetMachine->Unassembly (
8445 (DWORD_PTR) codeHeaderData.ColdRegionStart,
8446 ((DWORD_PTR)codeHeaderData.ColdRegionStart) + codeHeaderData.ColdRegionSize,
8448 ((DWORD_PTR) MethodDescData.GCStressCodeCopy) + codeHeaderData.HotRegionSize,
8449 #if !defined(FEATURE_PAL)
8450 fWithGCInfo ? &gcEncodingInfo :
8466 #if !defined(FEATURE_PAL)
8468 DeleteFiber(gcEncodingInfo.pvGCTableFiber);
8474 /**********************************************************************\
8475 * Routine Description: *
8477 * This function is called to dump the in-memory stress log *
8478 * !DumpLog [filename] *
8479 * will dump the stress log corresponding to the clr.dll *
8480 * loaded in the debuggee's VAS *
8481 * !DumpLog -addr <addr_of_StressLog::theLog> [filename] *
8482 * will dump the stress log associated with any DLL linked *
8483 * against utilcode.lib, most commonly mscordbi.dll *
8484 * (e.g. !DumpLog -addr mscordbi!StressLog::theLog) *
8486 \**********************************************************************/
8487 DECLARE_API(DumpLog)
8489 INIT_API_NO_RET_ON_FAILURE();
8491 MINIDUMP_NOT_SUPPORTED();
8493 const char* fileName = "StressLog.txt";
8495 CLRDATA_ADDRESS StressLogAddress = NULL;
8497 StringHolder sFileName, sLogAddr;
8498 CMDOption option[] =
8499 { // name, vptr, type, hasValue
8500 {"-addr", &sLogAddr.data, COSTRING, TRUE}
8504 {&sFileName.data, COSTRING}
8507 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
8511 if (nArg > 0 && sFileName.data != NULL)
8513 fileName = sFileName.data;
8516 // allow users to specify -addr mscordbdi!StressLog::theLog, for example.
8517 if (sLogAddr.data != NULL)
8519 StressLogAddress = GetExpression(sLogAddr.data);
8522 if (StressLogAddress == NULL)
8527 ExtOut("No stress log address. DAC is broken; can't get it\n");
8530 // Try to find stress log symbols
8531 DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
8532 StressLogAddress = dwAddr;
8535 else if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
8537 ExtOut("Unable to find stress log via DAC\n");
8542 if (StressLogAddress == NULL)
8544 ExtOut("Please provide the -addr argument for the address of the stress log, since no recognized runtime is loaded.\n");
8548 ExtOut("Attempting to dump Stress log to file '%s'\n", fileName);
8552 Status = StressLog::Dump(StressLogAddress, fileName, g_ExtData);
8555 ExtOut("SUCCESS: Stress log dumped\n");
8556 else if (Status == S_FALSE)
8557 ExtOut("No Stress log in the image, no file written\n");
8559 ExtOut("FAILURE: Stress log not dumped\n");
8566 DECLARE_API (DumpGCLog)
8569 MINIDUMP_NOT_SUPPORTED();
8571 if (GetEEFlavor() == UNKNOWNEE)
8573 ExtOut("CLR not loaded\n");
8577 const char* fileName = "GCLog.txt";
8579 while (isspace (*args))
8585 DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_log_buffer");
8586 moveN (dwAddr, dwAddr);
8590 dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_log_buffer");
8591 moveN (dwAddr, dwAddr);
8594 ExtOut("Can't get either WKS or SVR GC's log file");
8599 ExtOut("Dumping GC log at %08x\n", dwAddr);
8601 g_bDacBroken = FALSE;
8603 ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
8607 HANDLE hGCLog = CreateFileA(
8613 FILE_ATTRIBUTE_NORMAL,
8616 if (hGCLog == INVALID_HANDLE_VALUE)
8618 ExtOut("failed to create file: %d\n", GetLastError());
8622 int iLogSize = 1024*1024;
8623 BYTE* bGCLog = new NOTHROW BYTE[iLogSize];
8630 memset (bGCLog, 0, iLogSize);
8631 if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
8633 ExtOut("failed to read memory from %08x\n", dwAddr);
8636 int iRealLogSize = iLogSize - 1;
8637 while (iRealLogSize >= 0)
8639 if (bGCLog[iRealLogSize] != '*')
8647 DWORD dwWritten = 0;
8648 WriteFile (hGCLog, bGCLog, iRealLogSize + 1, &dwWritten, NULL);
8654 if (hGCLog != INVALID_HANDLE_VALUE)
8656 CloseHandle (hGCLog);
8660 ExtOut("SUCCESS: Stress log dumped\n");
8661 else if (Status == S_FALSE)
8662 ExtOut("No Stress log in the image, no file written\n");
8664 ExtOut("FAILURE: Stress log not dumped\n");
8671 DECLARE_API (DumpGCConfigLog)
8674 #ifdef GC_CONFIG_DRIVEN
8675 MINIDUMP_NOT_SUPPORTED();
8677 if (GetEEFlavor() == UNKNOWNEE)
8679 ExtOut("CLR not loaded\n");
8683 const char* fileName = "GCConfigLog.txt";
8685 while (isspace (*args))
8691 if (!InitializeHeapData ())
8693 ExtOut("GC Heap not initialized yet.\n");
8697 BOOL fIsServerGC = IsServerBuild();
8699 DWORD_PTR dwAddr = 0;
8700 DWORD_PTR dwAddrOffset = 0;
8704 dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer");
8705 dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer_offset");
8709 dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer");
8710 dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer_offset");
8713 moveN (dwAddr, dwAddr);
8714 moveN (dwAddrOffset, dwAddrOffset);
8718 ExtOut("Can't get either WKS or SVR GC's config log buffer");
8722 ExtOut("Dumping GC log at %08x\n", dwAddr);
8724 g_bDacBroken = FALSE;
8726 ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
8730 HANDLE hGCLog = CreateFileA(
8736 FILE_ATTRIBUTE_NORMAL,
8739 if (hGCLog == INVALID_HANDLE_VALUE)
8741 ExtOut("failed to create file: %d\n", GetLastError());
8746 int iLogSize = (int)dwAddrOffset;
8748 ArrayHolder<BYTE> bGCLog = new NOTHROW BYTE[iLogSize];
8755 memset (bGCLog, 0, iLogSize);
8756 if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
8758 ExtOut("failed to read memory from %08x\n", dwAddr);
8761 SetFilePointer (hGCLog, 0, 0, FILE_END);
8763 WriteFile (hGCLog, bGCLog, iLogSize, &dwWritten, NULL);
8770 if (hGCLog != INVALID_HANDLE_VALUE)
8772 CloseHandle (hGCLog);
8776 ExtOut("SUCCESS: Stress log dumped\n");
8777 else if (Status == S_FALSE)
8778 ExtOut("No Stress log in the image, no file written\n");
8780 ExtOut("FAILURE: Stress log not dumped\n");
8784 ExtOut("Not implemented\n");
8786 #endif //GC_CONFIG_DRIVEN
8788 #endif // FEATURE_PAL
8790 #ifdef GC_CONFIG_DRIVEN
8791 static const char * const str_interesting_data_points[] =
8796 "converted pins", // 3
8799 "pre and post pin", // 6
8800 "pre short padded", // 7
8801 "post short padded", // 7
8804 static const char * const str_heap_compact_reasons[] =
8806 "low on ephemeral space",
8807 "high fragmetation",
8808 "couldn't allocate gaps",
8809 "user specfied compact LOH",
8810 "last GC before OOM",
8811 "induced compacting GC",
8812 "fragmented gen0 (ephemeral GC)",
8813 "high memory load (ephemeral GC)",
8814 "high memory load and frag",
8815 "very high memory load and frag",
8819 static BOOL gc_heap_compact_reason_mandatory_p[] =
8821 TRUE, //compact_low_ephemeral = 0,
8822 FALSE, //compact_high_frag = 1,
8823 TRUE, //compact_no_gaps = 2,
8824 TRUE, //compact_loh_forced = 3,
8825 TRUE, //compact_last_gc = 4
8826 TRUE, //compact_induced_compacting = 5,
8827 FALSE, //compact_fragmented_gen0 = 6,
8828 FALSE, //compact_high_mem_load = 7,
8829 TRUE, //compact_high_mem_frag = 8,
8830 TRUE, //compact_vhigh_mem_frag = 9,
8831 TRUE //compact_no_gc_mode = 10
8834 static const char * const str_heap_expand_mechanisms[] =
8836 "reused seg with normal fit",
8837 "reused seg with best fit",
8838 "expand promoting eph",
8839 "expand with a new seg",
8840 "no memory for a new seg",
8841 "expand in next full GC"
8844 static const char * const str_bit_mechanisms[] =
8850 static const char * const str_gc_global_mechanisms[] =
8855 "GCs that did demotion",
8860 void PrintInterestingGCInfo(DacpGCInterestingInfoData* dataPerHeap)
8862 ExtOut("Interesting data points\n");
8863 size_t* data = dataPerHeap->interestingDataPoints;
8864 for (int i = 0; i < DAC_NUM_GC_DATA_POINTS; i++)
8866 ExtOut("%20s: %d\n", str_interesting_data_points[i], data[i]);
8869 ExtOut("\nCompacting reasons\n");
8870 data = dataPerHeap->compactReasons;
8871 for (int i = 0; i < DAC_MAX_COMPACT_REASONS_COUNT; i++)
8873 ExtOut("[%s]%35s: %d\n", (gc_heap_compact_reason_mandatory_p[i] ? "M" : "W"), str_heap_compact_reasons[i], data[i]);
8876 ExtOut("\nExpansion mechanisms\n");
8877 data = dataPerHeap->expandMechanisms;
8878 for (int i = 0; i < DAC_MAX_EXPAND_MECHANISMS_COUNT; i++)
8880 ExtOut("%30s: %d\n", str_heap_expand_mechanisms[i], data[i]);
8883 ExtOut("\nOther mechanisms enabled\n");
8884 data = dataPerHeap->bitMechanisms;
8885 for (int i = 0; i < DAC_MAX_GC_MECHANISM_BITS_COUNT; i++)
8887 ExtOut("%20s: %d\n", str_bit_mechanisms[i], data[i]);
8890 #endif //GC_CONFIG_DRIVEN
8892 DECLARE_API(DumpGCData)
8896 #ifdef GC_CONFIG_DRIVEN
8897 MINIDUMP_NOT_SUPPORTED();
8899 if (!InitializeHeapData ())
8901 ExtOut("GC Heap not initialized yet.\n");
8905 DacpGCInterestingInfoData interestingInfo;
8906 interestingInfo.RequestGlobal(g_sos);
8907 for (int i = 0; i < DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT; i++)
8909 ExtOut("%-30s: %d\n", str_gc_global_mechanisms[i], interestingInfo.globalMechanisms[i]);
8912 ExtOut("\n[info per heap]\n");
8914 if (!IsServerBuild())
8916 if (interestingInfo.Request(g_sos) != S_OK)
8918 ExtOut("Error requesting interesting GC info\n");
8922 PrintInterestingGCInfo(&interestingInfo);
8926 DWORD dwNHeaps = GetGcHeapCount();
8928 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
8930 ExtOut("Failed to get GCHeaps: integer overflow\n");
8934 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
8935 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
8937 ExtOut("Failed to get GCHeaps\n");
8941 for (DWORD n = 0; n < dwNHeaps; n ++)
8943 if (interestingInfo.Request(g_sos, heapAddrs[n]) != S_OK)
8945 ExtOut("Heap %d: Error requesting interesting GC info\n", n);
8949 ExtOut("--------info for heap %d--------\n", n);
8950 PrintInterestingGCInfo(&interestingInfo);
8957 ExtOut("Not implemented\n");
8959 #endif //GC_CONFIG_DRIVEN
8963 /**********************************************************************\
8964 * Routine Description: *
8966 * This function is called to dump the build number and type of the *
8969 \**********************************************************************/
8970 DECLARE_API (EEVersion)
8974 EEFLAVOR eef = GetEEFlavor();
8975 if (eef == UNKNOWNEE) {
8976 ExtOut("CLR not loaded\n");
8980 if (g_ExtSymbols2) {
8981 VS_FIXEDFILEINFO version;
8983 BOOL ret = GetEEVersion(&version);
8987 if (version.dwFileVersionMS != (DWORD)-1)
8989 ExtOut("%u.%u.%u.%u",
8990 HIWORD(version.dwFileVersionMS),
8991 LOWORD(version.dwFileVersionMS),
8992 HIWORD(version.dwFileVersionLS),
8993 LOWORD(version.dwFileVersionLS));
8994 if (version.dwFileFlags & VS_FF_DEBUG)
8996 ExtOut(" Checked or debug build");
9000 BOOL fRet = IsRetailBuild ((size_t)moduleInfo[eef].baseAddr);
9013 if (!InitializeHeapData ())
9014 ExtOut("GC Heap not initialized, so GC mode is not determined yet.\n");
9015 else if (IsServerBuild())
9016 ExtOut("Server mode with %d gc heaps\n", GetGcHeapCount());
9018 ExtOut("Workstation mode\n");
9020 if (!GetGcStructuresValid())
9022 ExtOut("In plan phase of garbage collection\n");
9025 // Print SOS version
9026 VS_FIXEDFILEINFO sosVersion;
9027 if (GetSOSVersion(&sosVersion))
9029 if (sosVersion.dwFileVersionMS != (DWORD)-1)
9031 ExtOut("SOS Version: %u.%u.%u.%u",
9032 HIWORD(sosVersion.dwFileVersionMS),
9033 LOWORD(sosVersion.dwFileVersionMS),
9034 HIWORD(sosVersion.dwFileVersionLS),
9035 LOWORD(sosVersion.dwFileVersionLS));
9036 if (sosVersion.dwFileFlags & VS_FF_DEBUG)
9038 ExtOut(" Checked or debug build");
9042 ExtOut(" retail build");
9050 #endif // FEATURE_PAL
9053 /**********************************************************************\
9054 * Routine Description: *
9056 * This function is called to print the environment setting for *
9057 * the current process. *
9059 \**********************************************************************/
9060 DECLARE_API (ProcInfo)
9063 MINIDUMP_NOT_SUPPORTED();
9067 ExtOut("!ProcInfo is not supported on a dump file.\n");
9071 #define INFO_ENV 0x00000001
9072 #define INFO_TIME 0x00000002
9073 #define INFO_MEM 0x00000004
9074 #define INFO_ALL 0xFFFFFFFF
9076 DWORD fProcInfo = INFO_ALL;
9078 if (_stricmp (args, "-env") == 0) {
9079 fProcInfo = INFO_ENV;
9082 if (_stricmp (args, "-time") == 0) {
9083 fProcInfo = INFO_TIME;
9086 if (_stricmp (args, "-mem") == 0) {
9087 fProcInfo = INFO_MEM;
9090 if (fProcInfo & INFO_ENV) {
9091 ExtOut("---------------------------------------\n");
9092 ExtOut("Environment\n");
9094 g_ExtSystem->GetCurrentProcessPeb(&pPeb);
9096 static ULONG Offset_ProcessParam = -1;
9097 static ULONG Offset_Environment = -1;
9098 if (Offset_ProcessParam == -1)
9102 if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
9105 if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB", &TypeId)))
9107 if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
9108 "ProcessParameters", &Offset_ProcessParam)))
9109 Offset_ProcessParam = -1;
9111 if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "_RTL_USER_PROCESS_PARAMETERS", &TypeId)))
9113 if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
9114 "Environment", &Offset_Environment)))
9115 Offset_Environment = -1;
9119 // We can not get it from PDB. Use the fixed one.
9120 if (Offset_ProcessParam == -1)
9121 Offset_ProcessParam = offsetof (DT_PEB, ProcessParameters);
9123 if (Offset_Environment == -1)
9124 Offset_Environment = offsetof (DT_RTL_USER_PROCESS_PARAMETERS, Environment);
9127 ULONG64 addr = pPeb + Offset_ProcessParam;
9129 g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
9130 addr = value + Offset_Environment;
9131 g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
9133 static WCHAR buffer[DT_OS_PAGE_SIZE/2];
9134 ULONG readBytes = DT_OS_PAGE_SIZE;
9136 if ((g_ExtData->ReadDebuggerData( DEBUG_DATA_MmPageSize, &Page, sizeof(Page), NULL)) == S_OK
9139 ULONG uPageSize = (ULONG)(ULONG_PTR)Page;
9140 if (readBytes > uPageSize) {
9141 readBytes = uPageSize;
9148 if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &buffer, readBytes, NULL)))
9153 while (pt < &buffer[DT_OS_PAGE_SIZE/2]) {
9154 end = _wcschr (pt, L'\0');
9157 sprintf_s (format,_countof (format), "%dS", &buffer[DT_OS_PAGE_SIZE/2] - pt);
9161 else if (end == pt) {
9173 HANDLE hProcess = INVALID_HANDLE_VALUE;
9174 if (fProcInfo & (INFO_TIME | INFO_MEM)) {
9176 g_ExtSystem->GetCurrentProcessHandle(&handle);
9177 hProcess = (HANDLE)handle;
9180 if (!IsDumpFile() && fProcInfo & INFO_TIME) {
9181 FILETIME CreationTime;
9183 FILETIME KernelTime;
9186 typedef BOOL (WINAPI *FntGetProcessTimes)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
9187 static FntGetProcessTimes pFntGetProcessTimes = (FntGetProcessTimes)-1;
9188 if (pFntGetProcessTimes == (FntGetProcessTimes)-1) {
9189 HINSTANCE hstat = LoadLibrary ("Kernel32.dll");
9192 pFntGetProcessTimes = (FntGetProcessTimes)GetProcAddress (hstat, "GetProcessTimes");
9193 FreeLibrary (hstat);
9196 pFntGetProcessTimes = NULL;
9199 if (pFntGetProcessTimes && pFntGetProcessTimes (hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime)) {
9200 ExtOut("---------------------------------------\n");
9201 ExtOut("Process Times\n");
9202 static const char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
9203 "Oct", "Nov", "Dec"};
9204 SYSTEMTIME SystemTime;
9205 FILETIME LocalFileTime;
9206 if (FileTimeToLocalFileTime (&CreationTime,&LocalFileTime)
9207 && FileTimeToSystemTime (&LocalFileTime,&SystemTime)) {
9208 ExtOut("Process Started at: %4d %s %2d %d:%d:%d.%02d\n",
9209 SystemTime.wYear, Month[SystemTime.wMonth-1], SystemTime.wDay,
9210 SystemTime.wHour, SystemTime.wMinute,
9211 SystemTime.wSecond, SystemTime.wMilliseconds/10);
9222 totalTime = KernelTime.dwLowDateTime + (((ULONG64)KernelTime.dwHighDateTime) << 32);
9223 nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
9224 totalTime %= 24*3600*10000000ui64;
9225 nHour = (DWORD)(totalTime/(3600*10000000ui64));
9226 totalTime %= 3600*10000000ui64;
9227 nMin = (DWORD)(totalTime/(60*10000000));
9228 totalTime %= 60*10000000;
9229 nSec = (DWORD)(totalTime/10000000);
9230 totalTime %= 10000000;
9231 nHundred = (DWORD)(totalTime/100000);
9232 ExtOut("Kernel CPU time : %d days %02d:%02d:%02d.%02d\n",
9233 nDay, nHour, nMin, nSec, nHundred);
9236 DWORD sHour = nHour;
9239 DWORD sHundred = nHundred;
9241 totalTime = UserTime.dwLowDateTime + (((ULONG64)UserTime.dwHighDateTime) << 32);
9242 nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
9243 totalTime %= 24*3600*10000000ui64;
9244 nHour = (DWORD)(totalTime/(3600*10000000ui64));
9245 totalTime %= 3600*10000000ui64;
9246 nMin = (DWORD)(totalTime/(60*10000000));
9247 totalTime %= 60*10000000;
9248 nSec = (DWORD)(totalTime/10000000);
9249 totalTime %= 10000000;
9250 nHundred = (DWORD)(totalTime/100000);
9251 ExtOut("User CPU time : %d days %02d:%02d:%02d.%02d\n",
9252 nDay, nHour, nMin, nSec, nHundred);
9258 sHundred += nHundred;
9259 if (sHundred >= 100) {
9260 sSec += sHundred/100;
9275 ExtOut("Total CPU time : %d days %02d:%02d:%02d.%02d\n",
9276 sDay, sHour, sMin, sSec, sHundred);
9280 if (!IsDumpFile() && fProcInfo & INFO_MEM) {
9284 *FntNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
9286 static FntNtQueryInformationProcess pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)-1;
9287 if (pFntNtQueryInformationProcess == (FntNtQueryInformationProcess)-1) {
9288 HINSTANCE hstat = LoadLibrary ("ntdll.dll");
9291 pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)GetProcAddress (hstat, "NtQueryInformationProcess");
9292 FreeLibrary (hstat);
9295 pFntNtQueryInformationProcess = NULL;
9298 if (pFntNtQueryInformationProcess &&
9299 NT_SUCCESS (pFntNtQueryInformationProcess (hProcess,ProcessVmCounters,&memory,sizeof(memory),NULL))) {
9300 ExtOut("---------------------------------------\n");
9301 ExtOut("Process Memory\n");
9302 ExtOut("WorkingSetSize: %8d KB PeakWorkingSetSize: %8d KB\n",
9303 memory.WorkingSetSize/1024, memory.PeakWorkingSetSize/1024);
9304 ExtOut("VirtualSize: %8d KB PeakVirtualSize: %8d KB\n",
9305 memory.VirtualSize/1024, memory.PeakVirtualSize/1024);
9306 ExtOut("PagefileUsage: %8d KB PeakPagefileUsage: %8d KB\n",
9307 memory.PagefileUsage/1024, memory.PeakPagefileUsage/1024);
9310 MEMORYSTATUS memstat;
9311 GlobalMemoryStatus (&memstat);
9312 ExtOut("---------------------------------------\n");
9313 ExtOut("%ld percent of memory is in use.\n\n",
9314 memstat.dwMemoryLoad);
9315 ExtOut("Memory Availability (Numbers in MB)\n\n");
9316 ExtOut(" %8s %8s\n", "Total", "Avail");
9317 ExtOut("Physical Memory %8d %8d\n", memstat.dwTotalPhys/1024/1024, memstat.dwAvailPhys/1024/1024);
9318 ExtOut("Page File %8d %8d\n", memstat.dwTotalPageFile/1024/1024, memstat.dwAvailPageFile/1024/1024);
9319 ExtOut("Virtual Memory %8d %8d\n", memstat.dwTotalVirtual/1024/1024, memstat.dwAvailVirtual/1024/1024);
9324 #endif // FEATURE_PAL
9326 /**********************************************************************\
9327 * Routine Description: *
9329 * This function is called to find the address of EE data for a *
9332 \**********************************************************************/
9333 DECLARE_API(Token2EE)
9336 MINIDUMP_NOT_SUPPORTED();
9338 StringHolder DllName;
9342 CMDOption option[] =
9343 { // name, vptr, type, hasValue
9345 {"/d", &dml, COBOOL, FALSE},
9351 {&DllName.data, COSTRING},
9356 if (!GetCMDOption(args,option, _countof(option), arg, _countof(arg), &nArg))
9362 ExtOut("Usage: !Token2EE module_name mdToken\n");
9363 ExtOut(" You can pass * for module_name to search all modules.\n");
9367 EnableDMLHolder dmlHolder(dml);
9369 ArrayHolder<DWORD_PTR> moduleList = NULL;
9371 if (strcmp(DllName.data, "*") == 0)
9373 moduleList = ModuleFromName(NULL, &numModule);
9377 moduleList = ModuleFromName(DllName.data, &numModule);
9380 if (moduleList == NULL)
9382 ExtOut("Failed to request module list.\n");
9386 for (int i = 0; i < numModule; i ++)
9393 ExtOut("--------------------------------------\n");
9396 DWORD_PTR dwAddr = moduleList[i];
9397 WCHAR FileName[MAX_LONGPATH];
9398 FileNameForModule(dwAddr, FileName);
9400 // We'd like a short form for this output
9401 LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W);
9402 if (pszFilename == NULL)
9404 pszFilename = FileName;
9408 pszFilename++; // skip past the last "\" character
9411 DMLOut("Module: %s\n", DMLModule(dwAddr));
9412 ExtOut("Assembly: %S\n", pszFilename);
9414 GetInfoFromModule(dwAddr, (ULONG)token);
9421 /**********************************************************************\
9422 * Routine Description: *
9424 * This function is called to find the address of EE data for a *
9427 \**********************************************************************/
9428 DECLARE_API(Name2EE)
9431 MINIDUMP_NOT_SUPPORTED();
9433 StringHolder DllName, TypeName;
9436 CMDOption option[] =
9437 { // name, vptr, type, hasValue
9439 {"/d", &dml, COBOOL, FALSE},
9445 {&DllName.data, COSTRING},
9446 {&TypeName.data, COSTRING}
9450 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9455 EnableDMLHolder dmlHolder(dml);
9459 // The input may be in the form <modulename>!<type>
9460 // If so, do some surgery on the input params.
9462 // There should be only 1 ! character
9463 LPSTR pszSeperator = strchr (DllName.data, '!');
9464 if (pszSeperator != NULL)
9466 if (strchr (pszSeperator + 1, '!') == NULL)
9468 size_t capacity_TypeName_data = strlen(pszSeperator + 1) + 1;
9469 TypeName.data = new NOTHROW char[capacity_TypeName_data];
9472 // get the type name,
9473 strcpy_s (TypeName.data, capacity_TypeName_data, pszSeperator + 1);
9474 // and truncate DllName
9475 *pszSeperator = '\0';
9477 // Do some extra validation
9478 if (strlen (DllName.data) >= 1 &&
9479 strlen (TypeName.data) > 1)
9490 ExtOut("Usage: " SOSPrefix "name2ee module_name item_name\n");
9491 ExtOut(" or " SOSPrefix "name2ee module_name!item_name\n");
9492 ExtOut(" use * for module_name to search all loaded modules\n");
9493 ExtOut("Examples: " SOSPrefix "name2ee mscorlib.dll System.String.ToString\n");
9494 ExtOut(" " SOSPrefix "name2ee *!System.String\n");
9499 ArrayHolder<DWORD_PTR> moduleList = NULL;
9500 if (strcmp(DllName.data, "*") == 0)
9502 moduleList = ModuleFromName(NULL, &numModule);
9506 moduleList = ModuleFromName(DllName.data, &numModule);
9510 if (moduleList == NULL)
9512 ExtOut("Failed to request module list.\n", DllName.data);
9516 for (int i = 0; i < numModule; i ++)
9523 ExtOut("--------------------------------------\n");
9526 DWORD_PTR dwAddr = moduleList[i];
9527 WCHAR FileName[MAX_LONGPATH];
9528 FileNameForModule (dwAddr, FileName);
9530 // We'd like a short form for this output
9531 LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W);
9532 if (pszFilename == NULL)
9534 pszFilename = FileName;
9538 pszFilename++; // skip past the last "\" character
9541 DMLOut("Module: %s\n", DMLModule(dwAddr));
9542 ExtOut("Assembly: %S\n", pszFilename);
9543 GetInfoFromName(dwAddr, TypeName.data);
9555 MINIDUMP_NOT_SUPPORTED();
9557 DWORD_PTR root = NULL;
9558 DWORD_PTR target = NULL;
9562 CMDOption option[] =
9563 { // name, vptr, type, hasValue
9564 {"/d", &dml, COBOOL, FALSE},
9571 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9576 if (root == 0 || target == 0)
9578 ExtOut("Invalid argument %s\n", args);
9583 bool result = gcroot.PrintPathToObject(root, target);
9586 ExtOut("Did not find a path from %p to %p.\n", SOS_PTR(root), SOS_PTR(target));
9594 /**********************************************************************\
9595 * Routine Description: *
9597 * This function finds all roots (on stack or in handles) for a *
9600 \**********************************************************************/
9604 MINIDUMP_NOT_SUPPORTED();
9606 BOOL bNoStacks = FALSE;
9607 DWORD_PTR obj = NULL;
9612 CMDOption option[] =
9613 { // name, vptr, type, hasValue
9614 {"-nostacks", &bNoStacks, COBOOL, FALSE},
9615 {"-all", &all, COBOOL, FALSE},
9617 {"/d", &dml, COBOOL, FALSE},
9625 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9631 ExtOut("Invalid argument %s\n", args);
9635 EnableDMLHolder dmlHolder(dml);
9637 int i = gcroot.PrintRootsForObject(obj, all == TRUE, bNoStacks == TRUE);
9640 ExtOut("Interrupted, data may be incomplete.\n");
9643 ExtOut("Found %d roots.\n", i);
9645 ExtOut("Found %d unique roots (run '!GCRoot -all' to see all roots).\n", i);
9650 DECLARE_API(GCWhere)
9653 MINIDUMP_NOT_SUPPORTED();
9661 CMDOption option[] =
9662 { // name, vptr, type, hasValue
9663 {"-brick", &bGetBrick, COBOOL, FALSE},
9664 {"-card", &bGetCard, COBOOL, FALSE},
9665 {"/d", &dml, COBOOL, FALSE},
9671 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9676 EnableDMLHolder dmlHolder(dml);
9677 // Obtain allocation context for each managed thread.
9678 AllocInfo allocInfo;
9681 TADDR_SEGINFO trngSeg = { 0, 0, 0 };
9682 TADDR_RANGE allocCtx = { 0, 0 };
9684 BOOL bLarge = FALSE;
9685 BOOL bFound = FALSE;
9688 if (sos::IsObject(taddrObj))
9691 BOOL bContainsPointers;
9692 if(FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
9693 !GetSizeEfficient(taddrObj, taddrMT, FALSE, size, bContainsPointers))
9695 ExtWarn("Couldn't get size for object %#p: possible heap corruption.\n",
9700 if (!IsServerBuild())
9702 DacpGcHeapDetails heapDetails;
9703 if (heapDetails.Request(g_sos) != S_OK)
9705 ExtOut("Error requesting gc heap details\n");
9709 if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
9711 ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated " WIN64_8SPACES " size\n");
9712 ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n",
9713 SOS_PTR(taddrObj), gen, 0, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size);
9719 DacpGcHeapData gcheap;
9720 if (gcheap.Request(g_sos) != S_OK)
9722 ExtOut("Error requesting GC Heap data\n");
9727 DWORD dwNHeaps = gcheap.HeapCount;
9728 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
9730 ExtOut("Failed to get GCHeaps: integer overflow\n");
9734 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
9735 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
9737 ExtOut("Failed to get GCHeaps\n");
9741 for (DWORD n = 0; n < dwNHeaps; n ++)
9743 DacpGcHeapDetails heapDetails;
9744 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
9746 ExtOut("Error requesting details\n");
9750 if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
9752 ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated" WIN64_8SPACES " size\n");
9753 ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n",
9754 SOS_PTR(taddrObj), gen, n, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size);
9763 ExtOut("Address %#p not found in the managed heap.\n", SOS_PTR(taddrObj));
9771 DECLARE_API(FindRoots)
9775 MINIDUMP_NOT_SUPPORTED();
9779 ExtOut("!FindRoots is not supported on a dump file.\n");
9783 LONG_PTR gen = -100; // initialized outside the legal range: [-1, 2]
9789 CMDOption option[] =
9790 { // name, vptr, type, hasValue
9791 {"-gen", &sgen.data, COSTRING, TRUE},
9792 {"/d", &dml, COBOOL, FALSE},
9798 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9803 EnableDMLHolder dmlHolder(dml);
9804 if (sgen.data != NULL)
9806 if (_stricmp(sgen.data, "any") == 0)
9812 gen = GetExpression(sgen.data);
9815 if ((gen < -1 || gen > 2) && (taObj == 0))
9817 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");
9821 if (gen >= -1 && gen <= 2)
9823 IXCLRDataProcess2* idp2 = NULL;
9824 if (FAILED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
9826 ExtOut("Your version of the runtime/DAC do not support this command.\n");
9830 // Request GC_MARK_END notifications from debuggee
9831 GcEvtArgs gea = { GC_MARK_END, { ((gen == -1) ? 7 : (1 << gen)) } };
9832 idp2->SetGcNotification(gea);
9833 // ... and register the notification handler
9834 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
9835 // the above notification is removed in CNotification::OnGcEvent()
9839 // verify that the last event in the debugger was indeed a CLRN exception
9840 DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
9841 CNotification Notification;
9843 if (!CheckCLRNotificationEvent(&dle))
9845 ExtOut("The command !FindRoots can only be used after the debugger stopped on a CLRN GC notification.\n");
9846 ExtOut("At this time !GCRoot should be used instead.\n");
9849 // validate argument
9850 if (!g_snapshot.Build())
9852 ExtOut("Unable to build snapshot of the garbage collector state\n");
9856 if (g_snapshot.GetHeap(taObj) == NULL)
9858 ExtOut("Address %#p is not in the managed heap.\n", SOS_PTR(taObj));
9862 int ogen = g_snapshot.GetGeneration(taObj);
9863 if (ogen > CNotification::GetCondemnedGen())
9865 DMLOut("Object %s will survive this collection:\n\tgen(%#p) = %d > %d = condemned generation.\n",
9866 DMLObject(taObj), SOS_PTR(taObj), ogen, CNotification::GetCondemnedGen());
9871 int roots = gcroot.FindRoots(CNotification::GetCondemnedGen(), taObj);
9873 ExtOut("Found %d roots.\n", roots);
9882 class GCHandleStatsForDomains
9885 const static int SHARED_DOMAIN_INDEX = 0;
9886 const static int SYSTEM_DOMAIN_INDEX = 1;
9888 GCHandleStatsForDomains()
9889 : m_singleDomainMode(FALSE), m_numDomains(0), m_pStatistics(NULL), m_pDomainPointers(NULL)
9893 ~GCHandleStatsForDomains()
9897 if (m_singleDomainMode)
9898 delete m_pStatistics;
9900 delete [] m_pStatistics;
9903 if (m_pDomainPointers)
9904 delete [] m_pDomainPointers;
9907 BOOL Init(BOOL singleDomainMode)
9909 m_singleDomainMode = singleDomainMode;
9910 if (m_singleDomainMode)
9913 m_pStatistics = new NOTHROW GCHandleStatistics();
9914 if (m_pStatistics == NULL)
9919 DacpAppDomainStoreData adsData;
9920 if (adsData.Request(g_sos) != S_OK)
9923 m_numDomains = adsData.DomainCount + 2;
9924 ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount + 2];
9928 pArray[SHARED_DOMAIN_INDEX] = adsData.sharedDomain;
9929 pArray[SYSTEM_DOMAIN_INDEX] = adsData.systemDomain;
9931 if (g_sos->GetAppDomainList(adsData.DomainCount, pArray+2, NULL) != S_OK)
9934 m_pDomainPointers = pArray.Detach();
9935 m_pStatistics = new NOTHROW GCHandleStatistics[adsData.DomainCount + 2];
9936 if (m_pStatistics == NULL)
9943 GCHandleStatistics *LookupStatistics(CLRDATA_ADDRESS appDomainPtr) const
9945 if (m_singleDomainMode)
9947 // You can pass NULL appDomainPtr if you are in singleDomainMode
9948 return m_pStatistics;
9952 for (int i=0; i < m_numDomains; i++)
9953 if (m_pDomainPointers[i] == appDomainPtr)
9954 return m_pStatistics + i;
9961 GCHandleStatistics *GetStatistics(int appDomainIndex) const
9963 SOS_Assert(appDomainIndex >= 0);
9964 SOS_Assert(appDomainIndex < m_numDomains);
9966 return m_singleDomainMode ? m_pStatistics : m_pStatistics + appDomainIndex;
9969 int GetNumDomains() const
9971 return m_numDomains;
9974 CLRDATA_ADDRESS GetDomain(int index) const
9976 SOS_Assert(index >= 0);
9977 SOS_Assert(index < m_numDomains);
9978 return m_pDomainPointers[index];
9982 BOOL m_singleDomainMode;
9984 GCHandleStatistics *m_pStatistics;
9985 CLRDATA_ADDRESS *m_pDomainPointers;
9991 GCHandlesImpl(PCSTR args)
9992 : mPerDomain(FALSE), mStat(FALSE), mDML(FALSE), mType((int)~0)
9994 ArrayHolder<char> type = NULL;
9995 CMDOption option[] =
9997 {"-perdomain", &mPerDomain, COBOOL, FALSE},
9998 {"-stat", &mStat, COBOOL, FALSE},
9999 {"-type", &type, COSTRING, TRUE},
10000 {"/d", &mDML, COBOOL, FALSE},
10003 if (!GetCMDOption(args,option,_countof(option),NULL,0,NULL))
10004 sos::Throw<sos::Exception>("Failed to parse command line arguments.");
10007 if (_stricmp(type, "Pinned") == 0)
10008 mType = HNDTYPE_PINNED;
10009 else if (_stricmp(type, "RefCounted") == 0)
10010 mType = HNDTYPE_REFCOUNTED;
10011 else if (_stricmp(type, "WeakShort") == 0)
10012 mType = HNDTYPE_WEAK_SHORT;
10013 else if (_stricmp(type, "WeakLong") == 0)
10014 mType = HNDTYPE_WEAK_LONG;
10015 else if (_stricmp(type, "Strong") == 0)
10016 mType = HNDTYPE_STRONG;
10017 else if (_stricmp(type, "Variable") == 0)
10018 mType = HNDTYPE_VARIABLE;
10019 else if (_stricmp(type, "AsyncPinned") == 0)
10020 mType = HNDTYPE_ASYNCPINNED;
10021 else if (_stricmp(type, "SizedRef") == 0)
10022 mType = HNDTYPE_SIZEDREF;
10023 else if (_stricmp(type, "Dependent") == 0)
10024 mType = HNDTYPE_DEPENDENT;
10025 else if (_stricmp(type, "WeakWinRT") == 0)
10026 mType = HNDTYPE_WEAK_WINRT;
10028 sos::Throw<sos::Exception>("Unknown handle type '%s'.", type.GetPtr());
10033 EnableDMLHolder dmlHolder(mDML);
10035 mOut.ReInit(6, POINTERSIZE_HEX, AlignRight);
10036 mOut.SetWidths(5, POINTERSIZE_HEX, 11, POINTERSIZE_HEX, 8, POINTERSIZE_HEX);
10037 mOut.SetColAlignment(1, AlignLeft);
10039 if (mHandleStat.Init(!mPerDomain) == FALSE)
10040 sos::Throw<sos::Exception>("Error getting per-appdomain handle information");
10043 mOut.WriteRow("Handle", "Type", "Object", "Size", "Data", "Type");
10047 for (int i=0; (i < mHandleStat.GetNumDomains()) && !IsInterrupt(); i++)
10049 GCHandleStatistics *pStats = mHandleStat.GetStatistics(i);
10053 Print( "------------------------------------------------------------------------------\n");
10054 Print("GC Handle Statistics for AppDomain ", AppDomainPtr(mHandleStat.GetDomain(i)));
10056 if (i == GCHandleStatsForDomains::SHARED_DOMAIN_INDEX)
10057 Print(" (Shared Domain)\n");
10058 else if (i == GCHandleStatsForDomains::SYSTEM_DOMAIN_INDEX)
10059 Print(" (System Domain)\n");
10066 PrintGCStat(&pStats->hs);
10068 // Don't print handle stats if the user has filtered by type. All handles will be the same
10069 // type, and the total count will be displayed by PrintGCStat.
10070 if (mType == (unsigned int)~0)
10073 PrintGCHandleStats(pStats);
10081 ToRelease<ISOSHandleEnum> handles;
10082 if (FAILED(g_sos->GetHandleEnum(&handles)))
10084 if (IsMiniDumpFile())
10085 sos::Throw<sos::Exception>("Unable to display GC handles.\nA minidump without full memory may not have this information.");
10087 sos::Throw<sos::Exception>("Failed to walk the handle table.");
10090 // GCC can't handle stacks which are too large.
10091 #ifndef FEATURE_PAL
10092 SOSHandleData data[256];
10094 SOSHandleData data[4];
10097 unsigned int fetched = 0;
10101 if (FAILED(hr = handles->Next(_countof(data), data, &fetched)))
10103 ExtOut("Error %x while walking the handle table.\n", hr);
10107 WalkHandles(data, fetched);
10108 } while (_countof(data) == fetched);
10111 void WalkHandles(SOSHandleData data[], unsigned int count)
10113 for (unsigned int i = 0; i < count; ++i)
10115 sos::CheckInterrupt();
10117 if (mType != (unsigned int)~0 && mType != data[i].Type)
10120 GCHandleStatistics *pStats = mHandleStat.LookupStatistics(data[i].AppDomain);
10124 const WCHAR *mtName = 0;
10125 const char *type = 0;
10127 if (FAILED(MOVE(objAddr, data[i].Handle)))
10130 mtName = W("<error>");
10134 sos::Object obj(TO_TADDR(objAddr));
10135 mtAddr = obj.GetMT();
10136 if (sos::MethodTable::IsFreeMT(mtAddr))
10138 mtName = W("<free>");
10140 else if (!sos::MethodTable::IsValid(mtAddr))
10142 mtName = W("<error>");
10146 size = obj.GetSize();
10147 if (mType == (unsigned int)~0 || mType == data[i].Type)
10148 pStats->hs.Add(obj.GetMT(), (DWORD)size);
10152 switch(data[i].Type)
10154 case HNDTYPE_PINNED:
10156 if (pStats) pStats->pinnedHandleCount++;
10158 case HNDTYPE_REFCOUNTED:
10159 type = "RefCounted";
10160 if (pStats) pStats->refCntHandleCount++;
10162 case HNDTYPE_STRONG:
10164 if (pStats) pStats->strongHandleCount++;
10166 case HNDTYPE_WEAK_SHORT:
10167 type = "WeakShort";
10168 if (pStats) pStats->weakShortHandleCount++;
10170 case HNDTYPE_WEAK_LONG:
10172 if (pStats) pStats->weakLongHandleCount++;
10174 case HNDTYPE_ASYNCPINNED:
10175 type = "AsyncPinned";
10176 if (pStats) pStats->asyncPinnedHandleCount++;
10178 case HNDTYPE_VARIABLE:
10180 if (pStats) pStats->variableCount++;
10182 case HNDTYPE_SIZEDREF:
10184 if (pStats) pStats->sizedRefCount++;
10186 case HNDTYPE_DEPENDENT:
10187 type = "Dependent";
10188 if (pStats) pStats->dependentCount++;
10190 case HNDTYPE_WEAK_WINRT:
10191 type = "WeakWinRT";
10192 if (pStats) pStats->weakWinRTHandleCount++;
10197 pStats->unknownHandleCount++;
10201 if (type && !mStat)
10203 sos::MethodTable mt = mtAddr;
10205 mtName = mt.GetName();
10207 if (data[i].Type == HNDTYPE_REFCOUNTED)
10208 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Decimal(data[i].RefCount), mtName);
10209 else if (data[i].Type == HNDTYPE_DEPENDENT)
10210 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), ObjectPtr(data[i].Secondary), mtName);
10211 else if (data[i].Type == HNDTYPE_WEAK_WINRT)
10212 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Pointer(data[i].Secondary), mtName);
10214 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), "", mtName);
10219 inline void PrintHandleRow(const char *text, int count)
10222 mOut.WriteRow(text, Decimal(count));
10225 void PrintGCHandleStats(GCHandleStatistics *pStats)
10227 Print("Handles:\n");
10228 mOut.ReInit(2, 21, AlignLeft, 4);
10230 PrintHandleRow("Strong Handles:", pStats->strongHandleCount);
10231 PrintHandleRow("Pinned Handles:", pStats->pinnedHandleCount);
10232 PrintHandleRow("Async Pinned Handles:", pStats->asyncPinnedHandleCount);
10233 PrintHandleRow("Ref Count Handles:", pStats->refCntHandleCount);
10234 PrintHandleRow("Weak Long Handles:", pStats->weakLongHandleCount);
10235 PrintHandleRow("Weak Short Handles:", pStats->weakShortHandleCount);
10236 PrintHandleRow("Weak WinRT Handles:", pStats->weakWinRTHandleCount);
10237 PrintHandleRow("Variable Handles:", pStats->variableCount);
10238 PrintHandleRow("SizedRef Handles:", pStats->sizedRefCount);
10239 PrintHandleRow("Dependent Handles:", pStats->dependentCount);
10240 PrintHandleRow("Other Handles:", pStats->unknownHandleCount);
10244 BOOL mPerDomain, mStat, mDML;
10245 unsigned int mType;
10247 GCHandleStatsForDomains mHandleStat;
10250 /**********************************************************************\
10251 * Routine Description: *
10253 * This function dumps GC Handle statistics *
10255 \**********************************************************************/
10256 DECLARE_API(GCHandles)
10259 MINIDUMP_NOT_SUPPORTED();
10263 GCHandlesImpl gchandles(args);
10266 catch(const sos::Exception &e)
10274 BOOL derivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPWSTR baseString)
10276 // We want to follow back until we get the mt for System.Exception
10277 DacpMethodTableData dmtd;
10278 CLRDATA_ADDRESS walkMT = mtObj;
10279 while(walkMT != NULL)
10281 if (dmtd.Request(g_sos, walkMT) != S_OK)
10285 NameForMT_s (TO_TADDR(walkMT), g_mdName, mdNameLen);
10286 if (_wcscmp (baseString, g_mdName) == 0)
10290 walkMT = dmtd.ParentMethodTable;
10295 // This is an experimental and undocumented SOS API that attempts to step through code
10296 // stopping once jitted code is reached. It currently has some issues - it can take arbitrarily long
10297 // to reach jitted code and canceling it is terrible. At best it doesn't cancel, at worst it
10298 // kills the debugger. IsInterrupt() doesn't work nearly as nicely as one would hope :/
10299 #ifndef FEATURE_PAL
10300 DECLARE_API(TraceToCode)
10304 static ULONG64 g_clrBaseAddr = 0;
10311 ExtOut("Interrupted\n");
10316 g_ExtRegisters->GetInstructionOffset(&Offset);
10318 DWORD codeType = 0;
10320 CLRDATA_ADDRESS cdaStart = TO_CDADDR(Offset);
10321 DacpMethodDescData MethodDescData;
10322 if(g_ExtSymbols->GetModuleByOffset(Offset, 0, NULL, &base) == S_OK)
10324 if(g_clrBaseAddr == 0)
10326 g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL,
10329 if(g_clrBaseAddr == base)
10331 ExtOut("Compiled code in CLR\n");
10336 ExtOut("Compiled code in module @ 0x%I64x\n", base);
10340 else if (g_sos != NULL || LoadClrDebugDll()==S_OK)
10342 CLRDATA_ADDRESS addr;
10343 if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
10345 WCHAR wszNameBuffer[1024]; // should be large enough
10347 // get the MethodDesc name
10348 if ((g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK) &&
10349 _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0)
10351 ExtOut("ILStub\n");
10356 ExtOut("Jitted code\n");
10362 ExtOut("Not compiled or jitted, assuming stub\n");
10368 // not compiled but CLR isn't loaded... some other code generator?
10378 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "thr; .echo wait" ,0);
10379 if (FAILED(Status))
10381 ExtOut("Error tracing instruction\n");
10390 #endif // FEATURE_PAL
10392 // This is an experimental and undocumented API that sets a debugger pseudo-register based
10393 // on the type of code at the given IP. It can be used in scripts to keep stepping until certain
10394 // kinds of code have been reached. Presumbably its slower than !TraceToCode but at least it
10395 // cancels much better
10396 #ifndef FEATURE_PAL
10397 DECLARE_API(GetCodeTypeFlags)
10402 char buffer[100+mdNameLen];
10409 {&PReg.data, COSTRING}
10412 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
10417 size_t preg = 1; // by default
10420 preg = GetExpression(PReg.data);
10423 ExtOut("Pseudo-register number must be between 0 and 19\n");
10428 sprintf_s(buffer,_countof (buffer),
10431 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
10432 if (FAILED(Status))
10434 ExtOut("Error initialized register $t%d to zero\n", preg);
10439 CLRDATA_ADDRESS cdaStart = TO_CDADDR(ip);
10440 DWORD codeType = 0;
10441 CLRDATA_ADDRESS addr;
10442 if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
10444 WCHAR wszNameBuffer[1024]; // should be large enough
10446 // get the MethodDesc name
10447 if (g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK &&
10448 _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0)
10450 ExtOut("ILStub\n");
10455 ExtOut("Jitted code");
10459 else if(g_ExtSymbols->GetModuleByOffset (ip, 0, NULL, &base) == S_OK)
10461 ULONG64 clrBaseAddr = 0;
10462 if(SUCCEEDED(g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL, &clrBaseAddr)) && base==clrBaseAddr)
10464 ExtOut("Compiled code in CLR");
10469 ExtOut("Compiled code in module @ 0x%I64x\n", base);
10475 ExtOut("Not compiled or jitted, assuming stub\n");
10479 sprintf_s(buffer,_countof (buffer),
10482 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
10483 if (FAILED(Status))
10485 ExtOut("Error setting register $t%d\n", preg);
10491 #endif // FEATURE_PAL
10493 DECLARE_API(StopOnException)
10496 MINIDUMP_NOT_SUPPORTED();
10499 char buffer[100+mdNameLen];
10501 BOOL fDerived = FALSE;
10502 BOOL fCreate1 = FALSE;
10503 BOOL fCreate2 = FALSE;
10505 CMDOption option[] = {
10506 // name, vptr, type, hasValue
10507 {"-derived", &fDerived, COBOOL, FALSE}, // catch derived exceptions
10508 {"-create", &fCreate1, COBOOL, FALSE}, // create 1st chance handler
10509 {"-create2", &fCreate2, COBOOL, FALSE}, // create 2nd chance handler
10512 StringHolder TypeName,PReg;
10516 {&TypeName.data, COSTRING},
10517 {&PReg.data, COSTRING}
10520 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
10526 ExtOut("Live debugging session required\n");
10529 if (nArg < 1 || nArg > 2)
10531 ExtOut("usage: StopOnException [-derived] [-create | -create2] <type name>\n");
10532 ExtOut(" [<pseudo-register number for result>]\n");
10533 ExtOut("ex: StopOnException -create System.OutOfMemoryException 1\n");
10537 size_t preg = 1; // by default
10540 preg = GetExpression(PReg.data);
10543 ExtOut("Pseudo-register number must be between 0 and 19\n");
10548 sprintf_s(buffer,_countof (buffer),
10551 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
10552 if (FAILED(Status))
10554 ExtOut("Error initialized register $t%d to zero\n", preg);
10558 if (fCreate1 || fCreate2)
10560 sprintf_s(buffer,_countof (buffer),
10561 "sxe %s \"!soe %s %s %d;.if(@$t%d==0) {g} .else {.echo '%s hit'}\" %x",
10562 fCreate1 ? "-c" : "-c2",
10563 fDerived ? "-derived" : "",
10571 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
10572 if (FAILED(Status))
10574 ExtOut("Error setting breakpoint: %s\n", buffer);
10578 ExtOut("Breakpoint set\n");
10582 // Find the last thrown exception on this thread.
10583 // Does it match? If so, set the register.
10584 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
10585 DacpThreadData Thread;
10587 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
10589 ExtOut("The current thread is unmanaged\n");
10594 if (!SafeReadMemory(Thread.lastThrownObjectHandle,
10596 sizeof(taLTOH), NULL))
10598 ExtOut("There is no current managed exception on this thread\n");
10604 LPWSTR typeNameWide = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
10605 MultiByteToWideChar(CP_ACP,0,TypeName.data,-1,typeNameWide,mdNameLen);
10608 if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
10610 NameForMT_s (taMT, g_mdName, mdNameLen);
10611 if ((_wcscmp(g_mdName,typeNameWide) == 0) ||
10612 (fDerived && derivedFrom(taMT, typeNameWide)))
10614 sprintf_s(buffer,_countof (buffer),
10617 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
10618 if (FAILED(Status))
10620 ExtOut("Failed to execute the following command: %s\n", buffer);
10629 /**********************************************************************\
10630 * Routine Description: *
10632 * This function finds the size of an object or all roots. *
10634 \**********************************************************************/
10635 DECLARE_API(ObjSize)
10637 #ifndef FEATURE_PAL
10639 MINIDUMP_NOT_SUPPORTED();
10642 StringHolder str_Object;
10645 CMDOption option[] =
10646 { // name, vptr, type, hasValue
10647 {"/d", &dml, COBOOL, FALSE},
10651 {&str_Object.data, COSTRING}
10654 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
10659 EnableDMLHolder dmlHolder(dml);
10660 TADDR obj = GetExpression(str_Object.data);
10669 if(!sos::IsObject(obj))
10671 ExtOut("%p is not a valid object.\n", SOS_PTR(obj));
10675 size_t size = gcroot.ObjSize(obj);
10678 sos::MethodTable methodTable = mt;
10679 ExtOut("sizeof(%p) = %d (0x%x) bytes (%S)\n", SOS_PTR(obj), size, size, methodTable.GetName());
10688 #ifndef FEATURE_PAL
10689 // For FEATURE_PAL, MEMORY_BASIC_INFORMATION64 doesn't exist yet. TODO?
10690 DECLARE_API(GCHandleLeaks)
10693 MINIDUMP_NOT_SUPPORTED();
10695 ExtOut("-------------------------------------------------------------------------------\n");
10696 ExtOut("GCHandleLeaks will report any GCHandles that couldn't be found in memory. \n");
10697 ExtOut("Strong and Pinned GCHandles are reported at this time. You can safely abort the\n");
10698 ExtOut("memory scan with Control-C or Control-Break. \n");
10699 ExtOut("-------------------------------------------------------------------------------\n");
10701 static DWORD_PTR array[2000];
10705 CMDOption option[] =
10706 { // name, vptr, type, hasValue
10707 {"/d", &dml, COBOOL, FALSE},
10710 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
10715 EnableDMLHolder dmlHolder(dml);
10717 UINT iFinal = FindAllPinnedAndStrong(array,sizeof(array)/sizeof(DWORD_PTR));
10718 ExtOut("Found %d handles:\n",iFinal);
10719 for (i=1;i<=iFinal;i++)
10721 ExtOut("%p\t", SOS_PTR(array[i-1]));
10726 ExtOut("\nSearching memory\n");
10727 // Now search memory for this:
10728 DWORD_PTR buffer[1024];
10729 ULONG64 memCur = 0x0;
10730 BOOL bAbort = FALSE;
10732 //find out memory used by stress log
10733 StressLogMem stressLog;
10734 CLRDATA_ADDRESS StressLogAddress = NULL;
10735 if (LoadClrDebugDll() != S_OK)
10737 // Try to find stress log symbols
10738 DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
10739 StressLogAddress = dwAddr;
10740 g_bDacBroken = TRUE;
10744 if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
10746 ExtOut("Unable to find stress log via DAC\n");
10748 g_bDacBroken = FALSE;
10751 if (stressLog.Init (StressLogAddress, g_ExtData))
10753 ExtOut("Reference found in stress log will be ignored\n");
10757 ExtOut("Failed to read whole or part of stress log, some references may come from stress log\n");
10764 MEMORY_BASIC_INFORMATION64 memInfo;
10766 status = g_ExtData2->QueryVirtual(UL64_TO_CDA(memCur), &memInfo);
10768 if( !NT_SUCCESS(status) )
10773 if (memInfo.State == MEM_COMMIT)
10775 for (ULONG64 memIter = memCur; memIter < (memCur + memInfo.RegionSize); memIter+=sizeof(buffer))
10779 ExtOut("Quitting at %p due to user abort\n", SOS_PTR(memIter));
10784 if ((memIter % 0x10000000)==0x0)
10786 ExtOut("Searching %p...\n", SOS_PTR(memIter));
10791 ret = g_ExtData->ReadVirtual(UL64_TO_CDA(memIter), buffer, sizeof(buffer), &size);
10794 for (UINT x=0;x<1024;x++)
10796 DWORD_PTR value = buffer[x];
10797 // We don't care about the low bit. Also, the GCHandle class turns on the
10798 // low bit for pinned handles, so without the statement below, we wouldn't
10799 // notice pinned handles.
10800 value = value & ~1;
10801 for (i=0;i<iFinal;i++)
10803 ULONG64 addrInDebugee = (ULONG64)memIter+(x*sizeof(DWORD_PTR));
10804 if ((array[i] & ~1) == value)
10806 if (stressLog.IsInStressLog (addrInDebugee))
10808 ExtOut("Found %p in stress log at location %p, reference not counted\n", SOS_PTR(value), addrInDebugee);
10812 ExtOut("Found %p at location %p\n", SOS_PTR(value), addrInDebugee);
10823 ExtOut("only read %x bytes at %p\n", size, SOS_PTR(memIter));
10829 memCur += memInfo.RegionSize;
10832 int numNotFound = 0;
10833 for (i=0;i<iFinal;i++)
10835 if ((array[i] & 0x1) == 0)
10838 // ExtOut("WARNING: %p not found\n", SOS_PTR(array[i]));
10842 if (numNotFound > 0)
10844 ExtOut("------------------------------------------------------------------------------\n");
10845 ExtOut("Some handles were not found. If the number of not-found handles grows over the\n");
10846 ExtOut("lifetime of your application, you may have a GCHandle leak. This will cause \n");
10847 ExtOut("the GC Heap to grow larger as objects are being kept alive, referenced only \n");
10848 ExtOut("by the orphaned handle. If the number doesn't grow over time, note that there \n");
10849 ExtOut("may be some noise in this output, as an unmanaged application may be storing \n");
10850 ExtOut("the handle in a non-standard way, perhaps with some bits flipped. The memory \n");
10851 ExtOut("scan wouldn't be able to find those. \n");
10852 ExtOut("------------------------------------------------------------------------------\n");
10854 ExtOut("Didn't find %d handles:\n", numNotFound);
10856 for (i=0;i<iFinal;i++)
10858 if ((array[i] & 0x1) == 0)
10861 ExtOut("%p\t", SOS_PTR(array[i]));
10862 if ((numPrinted % 4) == 0)
10870 ExtOut("------------------------------------------------------------------------------\n");
10871 ExtOut("All handles found");
10873 ExtOut(" even though you aborted.\n");
10876 ExtOut("A leak may still exist because in a general scan of process memory SOS can't \n");
10877 ExtOut("differentiate between garbage and valid structures, so you may have false \n");
10878 ExtOut("positives. If you still suspect a leak, use this function over time to \n");
10879 ExtOut("identify a possible trend. \n");
10880 ExtOut("------------------------------------------------------------------------------\n");
10885 #endif // FEATURE_PAL
10887 #endif // FEATURE_PAL
10889 class ClrStackImplWithICorDebug
10892 static HRESULT DereferenceAndUnboxValue(ICorDebugValue * pValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = NULL)
10894 HRESULT Status = S_OK;
10895 *ppOutputValue = NULL;
10896 if(pIsNull != NULL) *pIsNull = FALSE;
10898 ToRelease<ICorDebugReferenceValue> pReferenceValue;
10899 Status = pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue);
10900 if (SUCCEEDED(Status))
10902 BOOL isNull = FALSE;
10903 IfFailRet(pReferenceValue->IsNull(&isNull));
10906 ToRelease<ICorDebugValue> pDereferencedValue;
10907 IfFailRet(pReferenceValue->Dereference(&pDereferencedValue));
10908 return DereferenceAndUnboxValue(pDereferencedValue, ppOutputValue);
10912 if(pIsNull != NULL) *pIsNull = TRUE;
10913 *ppOutputValue = pValue;
10914 (*ppOutputValue)->AddRef();
10919 ToRelease<ICorDebugBoxValue> pBoxedValue;
10920 Status = pValue->QueryInterface(IID_ICorDebugBoxValue, (LPVOID*) &pBoxedValue);
10921 if (SUCCEEDED(Status))
10923 ToRelease<ICorDebugObjectValue> pUnboxedValue;
10924 IfFailRet(pBoxedValue->GetObject(&pUnboxedValue));
10925 return DereferenceAndUnboxValue(pUnboxedValue, ppOutputValue);
10927 *ppOutputValue = pValue;
10928 (*ppOutputValue)->AddRef();
10932 static BOOL ShouldExpandVariable(__in_z WCHAR* varToExpand, __in_z WCHAR* currentExpansion)
10934 if(currentExpansion == NULL || varToExpand == NULL) return FALSE;
10936 size_t varToExpandLen = _wcslen(varToExpand);
10937 size_t currentExpansionLen = _wcslen(currentExpansion);
10938 if(currentExpansionLen > varToExpandLen) return FALSE;
10939 if(currentExpansionLen < varToExpandLen && varToExpand[currentExpansionLen] != L'.') return FALSE;
10940 if(_wcsncmp(currentExpansion, varToExpand, currentExpansionLen) != 0) return FALSE;
10945 static BOOL IsEnum(ICorDebugValue * pInputValue)
10947 ToRelease<ICorDebugValue> pValue;
10948 if(FAILED(DereferenceAndUnboxValue(pInputValue, &pValue, NULL))) return FALSE;
10950 WCHAR baseTypeName[mdNameLen];
10951 ToRelease<ICorDebugValue2> pValue2;
10952 ToRelease<ICorDebugType> pType;
10953 ToRelease<ICorDebugType> pBaseType;
10955 if(FAILED(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2))) return FALSE;
10956 if(FAILED(pValue2->GetExactType(&pType))) return FALSE;
10957 if(FAILED(pType->GetBase(&pBaseType)) || pBaseType == NULL) return FALSE;
10958 if(FAILED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen))) return FALSE;
10960 return (_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0);
10963 static HRESULT AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
10965 bool isFirst = true;
10966 ToRelease<ICorDebugTypeEnum> pTypeEnum;
10967 if(SUCCEEDED(pType->EnumerateTypeParameters(&pTypeEnum)))
10969 ULONG numTypes = 0;
10970 ToRelease<ICorDebugType> pCurrentTypeParam;
10972 while(SUCCEEDED(pTypeEnum->Next(1, &pCurrentTypeParam, &numTypes)))
10974 if(numTypes == 0) break;
10979 wcsncat_s(typeName, typeNameLen, W("<"), typeNameLen);
10981 else wcsncat_s(typeName, typeNameLen, W(","), typeNameLen);
10983 WCHAR typeParamName[mdNameLen];
10984 typeParamName[0] = L'\0';
10985 GetTypeOfValue(pCurrentTypeParam, typeParamName, mdNameLen);
10986 wcsncat_s(typeName, typeNameLen, typeParamName, typeNameLen);
10989 wcsncat_s(typeName, typeNameLen, W(">"), typeNameLen);
10995 static HRESULT GetTypeOfValue(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
10997 HRESULT Status = S_OK;
10999 CorElementType corElemType;
11000 IfFailRet(pType->GetType(&corElemType));
11002 switch (corElemType)
11004 //List of unsupported CorElementTypes:
11005 //ELEMENT_TYPE_END = 0x0,
11006 //ELEMENT_TYPE_VAR = 0x13, // a class type variable VAR <U1>
11007 //ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
11008 //ELEMENT_TYPE_TYPEDBYREF = 0x16, // TYPEDREF (it takes no args) a typed referece to some other type
11009 //ELEMENT_TYPE_MVAR = 0x1e, // a method type variable MVAR <U1>
11010 //ELEMENT_TYPE_CMOD_REQD = 0x1F, // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef>
11011 //ELEMENT_TYPE_CMOD_OPT = 0x20, // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
11012 //ELEMENT_TYPE_INTERNAL = 0x21, // INTERNAL <typehandle>
11013 //ELEMENT_TYPE_MAX = 0x22, // first invalid element type
11014 //ELEMENT_TYPE_MODIFIER = 0x40,
11015 //ELEMENT_TYPE_SENTINEL = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs
11016 //ELEMENT_TYPE_PINNED = 0x05 | ELEMENT_TYPE_MODIFIER,
11017 //ELEMENT_TYPE_R4_HFA = 0x06 | ELEMENT_TYPE_MODIFIER, // used only internally for R4 HFA types
11018 //ELEMENT_TYPE_R8_HFA = 0x07 | ELEMENT_TYPE_MODIFIER, // used only internally for R8 HFA types
11020 swprintf_s(typeName, typeNameLen, W("(Unhandled CorElementType: 0x%x)\0"), corElemType);
11023 case ELEMENT_TYPE_VALUETYPE:
11024 case ELEMENT_TYPE_CLASS:
11026 //Defaults in case we fail...
11027 if(corElemType == ELEMENT_TYPE_VALUETYPE) swprintf_s(typeName, typeNameLen, W("struct\0"));
11028 else swprintf_s(typeName, typeNameLen, W("class\0"));
11031 ToRelease<ICorDebugClass> pClass;
11032 if(SUCCEEDED(pType->GetClass(&pClass)) && SUCCEEDED(pClass->GetToken(&typeDef)))
11034 ToRelease<ICorDebugModule> pModule;
11035 IfFailRet(pClass->GetModule(&pModule));
11037 ToRelease<IUnknown> pMDUnknown;
11038 ToRelease<IMetaDataImport> pMD;
11039 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11040 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11042 if(SUCCEEDED(NameForToken_s(TokenFromRid(typeDef, mdtTypeDef), pMD, g_mdName, mdNameLen, false)))
11043 swprintf_s(typeName, typeNameLen, W("%s\0"), g_mdName);
11045 AddGenericArgs(pType, typeName, typeNameLen);
11048 case ELEMENT_TYPE_VOID:
11049 swprintf_s(typeName, typeNameLen, W("void\0"));
11051 case ELEMENT_TYPE_BOOLEAN:
11052 swprintf_s(typeName, typeNameLen, W("bool\0"));
11054 case ELEMENT_TYPE_CHAR:
11055 swprintf_s(typeName, typeNameLen, W("char\0"));
11057 case ELEMENT_TYPE_I1:
11058 swprintf_s(typeName, typeNameLen, W("signed byte\0"));
11060 case ELEMENT_TYPE_U1:
11061 swprintf_s(typeName, typeNameLen, W("byte\0"));
11063 case ELEMENT_TYPE_I2:
11064 swprintf_s(typeName, typeNameLen, W("short\0"));
11066 case ELEMENT_TYPE_U2:
11067 swprintf_s(typeName, typeNameLen, W("unsigned short\0"));
11069 case ELEMENT_TYPE_I4:
11070 swprintf_s(typeName, typeNameLen, W("int\0"));
11072 case ELEMENT_TYPE_U4:
11073 swprintf_s(typeName, typeNameLen, W("unsigned int\0"));
11075 case ELEMENT_TYPE_I8:
11076 swprintf_s(typeName, typeNameLen, W("long\0"));
11078 case ELEMENT_TYPE_U8:
11079 swprintf_s(typeName, typeNameLen, W("unsigned long\0"));
11081 case ELEMENT_TYPE_R4:
11082 swprintf_s(typeName, typeNameLen, W("float\0"));
11084 case ELEMENT_TYPE_R8:
11085 swprintf_s(typeName, typeNameLen, W("double\0"));
11087 case ELEMENT_TYPE_OBJECT:
11088 swprintf_s(typeName, typeNameLen, W("object\0"));
11090 case ELEMENT_TYPE_STRING:
11091 swprintf_s(typeName, typeNameLen, W("string\0"));
11093 case ELEMENT_TYPE_I:
11094 swprintf_s(typeName, typeNameLen, W("IntPtr\0"));
11096 case ELEMENT_TYPE_U:
11097 swprintf_s(typeName, typeNameLen, W("UIntPtr\0"));
11099 case ELEMENT_TYPE_SZARRAY:
11100 case ELEMENT_TYPE_ARRAY:
11101 case ELEMENT_TYPE_BYREF:
11102 case ELEMENT_TYPE_PTR:
11104 ToRelease<ICorDebugType> pFirstParameter;
11105 if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)))
11106 GetTypeOfValue(pFirstParameter, typeName, typeNameLen);
11108 swprintf_s(typeName, typeNameLen, W("<unknown>\0"));
11110 switch(corElemType)
11112 case ELEMENT_TYPE_SZARRAY:
11113 wcsncat_s(typeName, typeNameLen, W("[]\0"), typeNameLen);
11115 case ELEMENT_TYPE_ARRAY:
11118 pType->GetRank(&rank);
11119 wcsncat_s(typeName, typeNameLen, W("["), typeNameLen);
11120 for(ULONG32 i = 0; i < rank - 1; i++)
11123 wcsncat_s(typeName, typeNameLen, W(","), typeNameLen);
11125 wcsncat_s(typeName, typeNameLen, W("]\0"), typeNameLen);
11128 case ELEMENT_TYPE_BYREF:
11129 wcsncat_s(typeName, typeNameLen, W("&\0"), typeNameLen);
11131 case ELEMENT_TYPE_PTR:
11132 wcsncat_s(typeName, typeNameLen, W("*\0"), typeNameLen);
11135 // note we can never reach here as this is a nested switch
11136 // and corElemType can only be one of the values above
11141 case ELEMENT_TYPE_FNPTR:
11142 swprintf_s(typeName, typeNameLen, W("*(...)\0"));
11144 case ELEMENT_TYPE_TYPEDBYREF:
11145 swprintf_s(typeName, typeNameLen, W("typedbyref\0"));
11151 static HRESULT GetTypeOfValue(ICorDebugValue * pValue, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
11153 HRESULT Status = S_OK;
11155 CorElementType corElemType;
11156 IfFailRet(pValue->GetType(&corElemType));
11158 ToRelease<ICorDebugType> pType;
11159 ToRelease<ICorDebugValue2> pValue2;
11160 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugValue2, (void**) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
11161 return GetTypeOfValue(pType, typeName, typeNameLen);
11163 swprintf_s(typeName, typeNameLen, W("<unknown>\0"));
11168 static HRESULT PrintEnumValue(ICorDebugValue* pInputValue, BYTE* enumValue)
11170 HRESULT Status = S_OK;
11172 ToRelease<ICorDebugValue> pValue;
11173 IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, NULL));
11175 mdTypeDef currentTypeDef;
11176 ToRelease<ICorDebugClass> pClass;
11177 ToRelease<ICorDebugValue2> pValue2;
11178 ToRelease<ICorDebugType> pType;
11179 ToRelease<ICorDebugModule> pModule;
11180 IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
11181 IfFailRet(pValue2->GetExactType(&pType));
11182 IfFailRet(pType->GetClass(&pClass));
11183 IfFailRet(pClass->GetModule(&pModule));
11184 IfFailRet(pClass->GetToken(¤tTypeDef));
11186 ToRelease<IUnknown> pMDUnknown;
11187 ToRelease<IMetaDataImport> pMD;
11188 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11189 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11192 //First, we need to figure out the underlying enum type so that we can correctly type cast the raw values of each enum constant
11193 //We get that from the non-static field of the enum variable (I think the field is called __value or something similar)
11194 ULONG numFields = 0;
11195 HCORENUM fEnum = NULL;
11196 mdFieldDef fieldDef;
11197 CorElementType enumUnderlyingType = ELEMENT_TYPE_END;
11198 while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
11200 DWORD fieldAttr = 0;
11201 PCCOR_SIGNATURE pSignatureBlob = NULL;
11202 ULONG sigBlobLength = 0;
11203 if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, NULL, 0, NULL, &fieldAttr, &pSignatureBlob, &sigBlobLength, NULL, NULL, NULL)))
11205 if((fieldAttr & fdStatic) == 0)
11207 CorSigUncompressCallingConv(pSignatureBlob);
11208 enumUnderlyingType = CorSigUncompressElementType(pSignatureBlob);
11213 pMD->CloseEnum(fEnum);
11216 //Now that we know the underlying enum type, let's decode the enum variable into OR-ed, human readable enum contants
11218 bool isFirst = true;
11219 ULONG64 remainingValue = *((ULONG64*)enumValue);
11220 while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
11223 DWORD fieldAttr = 0;
11224 WCHAR mdName[mdNameLen];
11225 WCHAR typeName[mdNameLen];
11226 UVCP_CONSTANT pRawValue = NULL;
11227 ULONG rawValueLength = 0;
11228 if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, &pRawValue, &rawValueLength)))
11230 DWORD enumValueRequiredAttributes = fdPublic | fdStatic | fdLiteral | fdHasDefault;
11231 if((fieldAttr & enumValueRequiredAttributes) != enumValueRequiredAttributes)
11234 ULONG64 currentConstValue = 0;
11235 switch (enumUnderlyingType)
11237 case ELEMENT_TYPE_CHAR:
11238 case ELEMENT_TYPE_I1:
11239 currentConstValue = (ULONG64)(*((CHAR*)pRawValue));
11241 case ELEMENT_TYPE_U1:
11242 currentConstValue = (ULONG64)(*((BYTE*)pRawValue));
11244 case ELEMENT_TYPE_I2:
11245 currentConstValue = (ULONG64)(*((SHORT*)pRawValue));
11247 case ELEMENT_TYPE_U2:
11248 currentConstValue = (ULONG64)(*((USHORT*)pRawValue));
11250 case ELEMENT_TYPE_I4:
11251 currentConstValue = (ULONG64)(*((INT32*)pRawValue));
11253 case ELEMENT_TYPE_U4:
11254 currentConstValue = (ULONG64)(*((UINT32*)pRawValue));
11256 case ELEMENT_TYPE_I8:
11257 currentConstValue = (ULONG64)(*((LONG*)pRawValue));
11259 case ELEMENT_TYPE_U8:
11260 currentConstValue = (ULONG64)(*((ULONG*)pRawValue));
11262 case ELEMENT_TYPE_I:
11263 currentConstValue = (ULONG64)(*((int*)pRawValue));
11265 case ELEMENT_TYPE_U:
11266 case ELEMENT_TYPE_R4:
11267 case ELEMENT_TYPE_R8:
11268 // Technically U and the floating-point ones are options in the CLI, but not in the CLS or C#, so these are NYI
11270 currentConstValue = 0;
11273 if((currentConstValue == remainingValue) || ((currentConstValue != 0) && ((currentConstValue & remainingValue) == currentConstValue)))
11275 remainingValue &= ~currentConstValue;
11278 ExtOut(" = %S", mdName);
11281 else ExtOut(" | %S", mdName);
11285 pMD->CloseEnum(fEnum);
11290 static HRESULT PrintStringValue(ICorDebugValue * pValue)
11294 ToRelease<ICorDebugStringValue> pStringValue;
11295 IfFailRet(pValue->QueryInterface(IID_ICorDebugStringValue, (LPVOID*) &pStringValue));
11298 IfFailRet(pStringValue->GetLength(&cchValue));
11299 cchValue++; // Allocate one more for null terminator
11301 CQuickString quickString;
11302 quickString.Alloc(cchValue);
11304 ULONG32 cchValueReturned;
11305 IfFailRet(pStringValue->GetString(
11308 quickString.String()));
11310 ExtOut(" = \"%S\"\n", quickString.String());
11315 static HRESULT PrintSzArrayValue(ICorDebugValue * pValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
11317 HRESULT Status = S_OK;
11319 ToRelease<ICorDebugArrayValue> pArrayValue;
11320 IfFailRet(pValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue));
11323 IfFailRet(pArrayValue->GetRank(&nRank));
11326 return E_UNEXPECTED;
11330 IfFailRet(pArrayValue->GetCount(&cElements));
11332 if (cElements == 0) ExtOut(" (empty)\n");
11333 else if (cElements == 1) ExtOut(" (1 element)\n");
11334 else ExtOut(" (%d elements)\n", cElements);
11336 if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
11337 size_t currentExpansionLen = _wcslen(currentExpansion);
11339 for (ULONG32 i=0; i < cElements; i++)
11341 for(int j = 0; j <= indent; j++) ExtOut(" ");
11342 currentExpansion[currentExpansionLen] = L'\0';
11343 swprintf_s(currentExpansion, mdNameLen, W("%s.[%d]\0"), currentExpansion, i);
11345 bool printed = false;
11346 CorElementType corElemType;
11347 ToRelease<ICorDebugType> pFirstParameter;
11348 ToRelease<ICorDebugValue2> pValue2;
11349 ToRelease<ICorDebugType> pType;
11350 if(SUCCEEDED(pArrayValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
11352 if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)) && SUCCEEDED(pFirstParameter->GetType(&corElemType)))
11354 switch(corElemType)
11356 //If the array element is something that we can expand with !clrstack, show information about the type of this element
11357 case ELEMENT_TYPE_VALUETYPE:
11358 case ELEMENT_TYPE_CLASS:
11359 case ELEMENT_TYPE_SZARRAY:
11361 WCHAR typeOfElement[mdNameLen];
11362 GetTypeOfValue(pFirstParameter, typeOfElement, mdNameLen);
11363 DMLOut(" |- %s = %S", DMLManagedVar(currentExpansion, currentFrame, i), typeOfElement);
11372 if(!printed) DMLOut(" |- %s", DMLManagedVar(currentExpansion, currentFrame, i));
11374 ToRelease<ICorDebugValue> pElementValue;
11375 IfFailRet(pArrayValue->GetElementAtPosition(i, &pElementValue));
11376 IfFailRet(PrintValue(pElementValue, pILFrame, pMD, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame));
11382 static HRESULT PrintValue(ICorDebugValue * pInputValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
11384 HRESULT Status = S_OK;
11386 BOOL isNull = TRUE;
11387 ToRelease<ICorDebugValue> pValue;
11388 IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
11392 ExtOut(" = null\n");
11397 IfFailRet(pValue->GetSize(&cbSize));
11398 ArrayHolder<BYTE> rgbValue = new NOTHROW BYTE[cbSize];
11399 if (rgbValue == NULL)
11402 return E_OUTOFMEMORY;
11405 memset(rgbValue.GetPtr(), 0, cbSize * sizeof(BYTE));
11407 CorElementType corElemType;
11408 IfFailRet(pValue->GetType(&corElemType));
11409 if (corElemType == ELEMENT_TYPE_STRING)
11411 return PrintStringValue(pValue);
11414 if (corElemType == ELEMENT_TYPE_SZARRAY)
11416 return PrintSzArrayValue(pValue, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11419 ToRelease<ICorDebugGenericValue> pGenericValue;
11420 IfFailRet(pValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue));
11421 IfFailRet(pGenericValue->GetValue((LPVOID) &(rgbValue[0])));
11425 Status = PrintEnumValue(pValue, rgbValue);
11430 switch (corElemType)
11433 ExtOut(" (Unhandled CorElementType: 0x%x)\n", corElemType);
11436 case ELEMENT_TYPE_PTR:
11437 ExtOut(" = <pointer>\n");
11440 case ELEMENT_TYPE_FNPTR:
11442 CORDB_ADDRESS addr = 0;
11443 ToRelease<ICorDebugReferenceValue> pReferenceValue = NULL;
11444 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue)))
11445 pReferenceValue->GetValue(&addr);
11446 ExtOut(" = <function pointer 0x%x>\n", addr);
11450 case ELEMENT_TYPE_VALUETYPE:
11451 case ELEMENT_TYPE_CLASS:
11452 CORDB_ADDRESS addr;
11453 if(SUCCEEDED(pValue->GetAddress(&addr)))
11455 ExtOut(" @ 0x%I64x\n", addr);
11461 ProcessFields(pValue, NULL, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11464 case ELEMENT_TYPE_BOOLEAN:
11465 ExtOut(" = %s\n", rgbValue[0] == 0 ? "false" : "true");
11468 case ELEMENT_TYPE_CHAR:
11469 ExtOut(" = '%C'\n", *(WCHAR *) &(rgbValue[0]));
11472 case ELEMENT_TYPE_I1:
11473 ExtOut(" = %d\n", *(char*) &(rgbValue[0]));
11476 case ELEMENT_TYPE_U1:
11477 ExtOut(" = %d\n", *(unsigned char*) &(rgbValue[0]));
11480 case ELEMENT_TYPE_I2:
11481 ExtOut(" = %hd\n", *(short*) &(rgbValue[0]));
11484 case ELEMENT_TYPE_U2:
11485 ExtOut(" = %hu\n", *(unsigned short*) &(rgbValue[0]));
11488 case ELEMENT_TYPE_I:
11489 ExtOut(" = %d\n", *(int*) &(rgbValue[0]));
11492 case ELEMENT_TYPE_U:
11493 ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0]));
11496 case ELEMENT_TYPE_I4:
11497 ExtOut(" = %d\n", *(int*) &(rgbValue[0]));
11500 case ELEMENT_TYPE_U4:
11501 ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0]));
11504 case ELEMENT_TYPE_I8:
11505 ExtOut(" = %I64d\n", *(__int64*) &(rgbValue[0]));
11508 case ELEMENT_TYPE_U8:
11509 ExtOut(" = %I64u\n", *(unsigned __int64*) &(rgbValue[0]));
11512 case ELEMENT_TYPE_R4:
11513 ExtOut(" = %f\n", (double) *(float*) &(rgbValue[0]));
11516 case ELEMENT_TYPE_R8:
11517 ExtOut(" = %f\n", *(double*) &(rgbValue[0]));
11520 case ELEMENT_TYPE_OBJECT:
11521 ExtOut(" = object\n");
11524 // TODO: The following corElementTypes are not yet implemented here. Array
11525 // might be interesting to add, though the others may be of rather limited use:
11526 // ELEMENT_TYPE_ARRAY = 0x14, // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ...
11528 // ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
11534 static HRESULT PrintParameters(BOOL bParams, BOOL bLocals, IMetaDataImport * pMD, mdTypeDef typeDef, mdMethodDef methodDef, ICorDebugILFrame * pILFrame, ICorDebugModule * pModule, __in_z WCHAR* varToExpand, int currentFrame)
11536 HRESULT Status = S_OK;
11539 ToRelease<ICorDebugValueEnum> pParamEnum;
11540 IfFailRet(pILFrame->EnumerateArguments(&pParamEnum));
11541 IfFailRet(pParamEnum->GetCount(&cParams));
11542 if (cParams > 0 && bParams)
11544 DWORD methAttr = 0;
11545 IfFailRet(pMD->GetMethodProps(methodDef, NULL, NULL, 0, NULL, &methAttr, NULL, NULL, NULL, NULL));
11547 ExtOut("\nPARAMETERS:\n");
11548 for (ULONG i=0; i < cParams; i++)
11550 ULONG paramNameLen = 0;
11551 mdParamDef paramDef;
11552 WCHAR paramName[mdNameLen] = W("\0");
11554 if(i == 0 && (methAttr & mdStatic) == 0)
11555 swprintf_s(paramName, mdNameLen, W("this\0"));
11558 int idx = ((methAttr & mdStatic) == 0)? i : (i + 1);
11559 if(SUCCEEDED(pMD->GetParamForMethodIndex(methodDef, idx, ¶mDef)))
11560 pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, ¶mNameLen, NULL, NULL, NULL, NULL);
11562 if(_wcslen(paramName) == 0)
11563 swprintf_s(paramName, mdNameLen, W("param_%d\0"), i);
11565 ToRelease<ICorDebugValue> pValue;
11566 ULONG cArgsFetched;
11567 Status = pParamEnum->Next(1, &pValue, &cArgsFetched);
11569 if (FAILED(Status))
11571 ExtOut(" + (Error 0x%x retrieving parameter '%S')\n", Status, paramName);
11575 if (Status == S_FALSE)
11580 WCHAR typeName[mdNameLen] = W("\0");
11581 GetTypeOfValue(pValue, typeName, mdNameLen);
11582 DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
11584 ToRelease<ICorDebugReferenceValue> pRefValue;
11585 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
11587 BOOL bIsNull = TRUE;
11588 pRefValue->IsNull(&bIsNull);
11591 ExtOut(" = null\n");
11596 WCHAR currentExpansion[mdNameLen];
11597 swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName);
11598 if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
11599 ExtOut(" + (Error 0x%x printing parameter %d)\n", Status, i);
11602 else if (cParams == 0 && bParams)
11603 ExtOut("\nPARAMETERS: (none)\n");
11606 ToRelease<ICorDebugValueEnum> pLocalsEnum;
11607 IfFailRet(pILFrame->EnumerateLocalVariables(&pLocalsEnum));
11608 IfFailRet(pLocalsEnum->GetCount(&cLocals));
11609 if (cLocals > 0 && bLocals)
11611 bool symbolsAvailable = false;
11612 SymbolReader symReader;
11613 if(SUCCEEDED(symReader.LoadSymbols(pMD, pModule)))
11614 symbolsAvailable = true;
11615 ExtOut("\nLOCALS:\n");
11616 for (ULONG i=0; i < cLocals; i++)
11618 ULONG paramNameLen = 0;
11619 WCHAR paramName[mdNameLen] = W("\0");
11621 ToRelease<ICorDebugValue> pValue;
11622 if(symbolsAvailable)
11624 Status = symReader.GetNamedLocalVariable(pILFrame, i, paramName, mdNameLen, &pValue);
11628 ULONG cArgsFetched;
11629 Status = pLocalsEnum->Next(1, &pValue, &cArgsFetched);
11631 if(_wcslen(paramName) == 0)
11632 swprintf_s(paramName, mdNameLen, W("local_%d\0"), i);
11634 if (FAILED(Status))
11636 ExtOut(" + (Error 0x%x retrieving local variable '%S')\n", Status, paramName);
11640 if (Status == S_FALSE)
11645 WCHAR typeName[mdNameLen] = W("\0");
11646 GetTypeOfValue(pValue, typeName, mdNameLen);
11647 DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
11649 ToRelease<ICorDebugReferenceValue> pRefValue = NULL;
11650 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
11652 BOOL bIsNull = TRUE;
11653 pRefValue->IsNull(&bIsNull);
11656 ExtOut(" = null\n");
11661 WCHAR currentExpansion[mdNameLen];
11662 swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName);
11663 if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
11664 ExtOut(" + (Error 0x%x printing local variable %d)\n", Status, i);
11667 else if (cLocals == 0 && bLocals)
11668 ExtOut("\nLOCALS: (none)\n");
11670 if(bParams || bLocals)
11676 static HRESULT ProcessFields(ICorDebugValue* pInputValue, ICorDebugType* pTypeCast, ICorDebugILFrame * pILFrame, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
11678 if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
11679 size_t currentExpansionLen = _wcslen(currentExpansion);
11681 HRESULT Status = S_OK;
11683 BOOL isNull = FALSE;
11684 ToRelease<ICorDebugValue> pValue;
11685 IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
11687 if(isNull) return S_OK;
11689 mdTypeDef currentTypeDef;
11690 ToRelease<ICorDebugClass> pClass;
11691 ToRelease<ICorDebugValue2> pValue2;
11692 ToRelease<ICorDebugType> pType;
11693 ToRelease<ICorDebugModule> pModule;
11694 IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
11695 if(pTypeCast == NULL)
11696 IfFailRet(pValue2->GetExactType(&pType));
11702 IfFailRet(pType->GetClass(&pClass));
11703 IfFailRet(pClass->GetModule(&pModule));
11704 IfFailRet(pClass->GetToken(¤tTypeDef));
11706 ToRelease<IUnknown> pMDUnknown;
11707 ToRelease<IMetaDataImport> pMD;
11708 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11709 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11711 WCHAR baseTypeName[mdNameLen] = W("\0");
11712 ToRelease<ICorDebugType> pBaseType;
11713 if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen)))
11715 if(_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0)
11717 else if(_wcsncmp(baseTypeName, W("System.Object"), 13) != 0 && _wcsncmp(baseTypeName, W("System.ValueType"), 16) != 0)
11719 currentExpansion[currentExpansionLen] = W('\0');
11720 wcscat_s(currentExpansion, currentExpansionSize, W(".\0"));
11721 wcscat_s(currentExpansion, currentExpansionSize, W("[basetype]"));
11722 for(int i = 0; i < indent; i++) ExtOut(" ");
11723 DMLOut(" |- %S %s\n", baseTypeName, DMLManagedVar(currentExpansion, currentFrame, W("[basetype]")));
11725 if(ShouldExpandVariable(varToExpand, currentExpansion))
11726 ProcessFields(pInputValue, pBaseType, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11731 ULONG numFields = 0;
11732 HCORENUM fEnum = NULL;
11733 mdFieldDef fieldDef;
11734 while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
11737 DWORD fieldAttr = 0;
11738 WCHAR mdName[mdNameLen];
11739 WCHAR typeName[mdNameLen];
11740 if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, NULL, NULL)))
11742 currentExpansion[currentExpansionLen] = W('\0');
11743 wcscat_s(currentExpansion, currentExpansionSize, W(".\0"));
11744 wcscat_s(currentExpansion, currentExpansionSize, mdName);
11746 ToRelease<ICorDebugValue> pFieldVal;
11747 if(fieldAttr & fdLiteral)
11749 //TODO: Is it worth it??
11750 //ExtOut(" |- const %S", mdName);
11754 for(int i = 0; i < indent; i++) ExtOut(" ");
11756 if (fieldAttr & fdStatic)
11757 pType->GetStaticFieldValue(fieldDef, pILFrame, &pFieldVal);
11760 ToRelease<ICorDebugObjectValue> pObjValue;
11761 if (SUCCEEDED(pValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue)))
11762 pObjValue->GetFieldValue(pClass, fieldDef, &pFieldVal);
11765 if(pFieldVal != NULL)
11767 typeName[0] = L'\0';
11768 GetTypeOfValue(pFieldVal, typeName, mdNameLen);
11769 DMLOut(" |- %S %s", typeName, DMLManagedVar(currentExpansion, currentFrame, mdName));
11770 PrintValue(pFieldVal, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11772 else if(!(fieldAttr & fdLiteral))
11773 ExtOut(" |- < unknown type > %S\n", mdName);
11777 pMD->CloseEnum(fEnum);
11783 // This is the main worker function used if !clrstack is called with "-i" to indicate
11784 // that the public ICorDebug* should be used instead of the private DAC interface. NOTE:
11785 // Currently only bParams is supported. NOTE: This is a work in progress and the
11786 // following would be good to do:
11787 // * More thorough testing with interesting stacks, especially with transitions into
11788 // and out of managed code.
11789 // * Consider interleaving this code back into the main body of !clrstack if it turns
11790 // out that there's a lot of duplication of code between these two functions.
11791 // (Still unclear how things will look once locals is implemented.)
11792 static HRESULT ClrStackFromPublicInterface(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, __in_z WCHAR* varToExpand = NULL, int onlyShowFrame = -1)
11796 IfFailRet(InitCorDebugInterface());
11798 ExtOut("\n\n\nDumping managed stack and managed variables using ICorDebug.\n");
11799 ExtOut("=============================================================================\n");
11801 ToRelease<ICorDebugThread> pThread;
11802 ToRelease<ICorDebugThread3> pThread3;
11803 ToRelease<ICorDebugStackWalk> pStackWalk;
11804 ULONG ulThreadID = 0;
11805 g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID);
11807 IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread));
11808 IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3));
11809 IfFailRet(pThread3->CreateStackWalk(&pStackWalk));
11811 InternalFrameManager internalFrameManager;
11812 IfFailRet(internalFrameManager.Init(pThread3));
11814 #if defined(_AMD64_)
11815 ExtOut("%-16s %-16s %s\n", "Child SP", "IP", "Call Site");
11816 #elif defined(_X86_)
11817 ExtOut("%-8s %-8s %s\n", "Child SP", "IP", "Call Site");
11820 int currentFrame = -1;
11822 for (Status = S_OK; ; Status = pStackWalk->Next())
11826 if (Status == CORDBG_S_AT_END_OF_STACK)
11828 ExtOut("Stack walk complete.\n");
11835 ExtOut("<interrupted>\n");
11839 CROSS_PLATFORM_CONTEXT context;
11840 ULONG32 cbContextActual;
11841 if ((Status=pStackWalk->GetContext(
11845 (BYTE *)&context))!=S_OK)
11847 ExtOut("GetFrameContext failed: %lx\n",Status);
11851 // First find the info for the Frame object, if the current frame has an associated clr!Frame.
11852 CLRDATA_ADDRESS sp = GetSP(context);
11853 CLRDATA_ADDRESS ip = GetIP(context);
11855 ToRelease<ICorDebugFrame> pFrame;
11856 IfFailRet(pStackWalk->GetFrame(&pFrame));
11857 if (Status == S_FALSE)
11859 DMLOut("%p %s [NativeStackFrame]\n", SOS_PTR(sp), DMLIP(ip));
11863 // TODO: What about internal frames preceding the above native stack frame?
11864 // Should I just exclude the above native stack frame from the output?
11865 // TODO: Compare caller frame (instead of current frame) against internal frame,
11866 // to deal with issues of current frame's current SP being closer to leaf than
11867 // EE Frames it pushes. By "caller" I mean not just managed caller, but the
11868 // very next non-internal frame dbi would return (native or managed). OR...
11869 // perhaps I should use GetStackRange() instead, to see if the internal frame
11870 // appears leafier than the base-part of the range of the currently iterated
11871 // stack frame? I think I like that better.
11872 _ASSERTE(pFrame != NULL);
11873 IfFailRet(internalFrameManager.PrintPrecedingInternalFrames(pFrame));
11875 // Print the stack and instruction pointers.
11876 DMLOut("%p %s ", SOS_PTR(sp), DMLIP(ip));
11878 ToRelease<ICorDebugRuntimeUnwindableFrame> pRuntimeUnwindableFrame;
11879 Status = pFrame->QueryInterface(IID_ICorDebugRuntimeUnwindableFrame, (LPVOID *) &pRuntimeUnwindableFrame);
11880 if (SUCCEEDED(Status))
11882 ExtOut("[RuntimeUnwindableFrame]\n");
11886 // Print the method/Frame info
11888 // TODO: IS THE FOLLOWING NECESSARY, OR AM I GUARANTEED THAT ALL INTERNAL FRAMES
11889 // CAN BE FOUND VIA GetActiveInternalFrames?
11890 ToRelease<ICorDebugInternalFrame> pInternalFrame;
11891 Status = pFrame->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame);
11892 if (SUCCEEDED(Status))
11894 // This is a clr!Frame.
11895 LPCWSTR pwszFrameName = W("TODO: Implement GetFrameName");
11896 ExtOut("[%S: p] ", pwszFrameName);
11899 // Print the frame's associated function info, if it has any.
11900 ToRelease<ICorDebugILFrame> pILFrame;
11901 HRESULT hrILFrame = pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame);
11903 if (SUCCEEDED(hrILFrame))
11905 ToRelease<ICorDebugFunction> pFunction;
11906 Status = pFrame->GetFunction(&pFunction);
11907 if (FAILED(Status))
11909 // We're on a JITted frame, but there's no Function for it. So it must
11911 ExtOut("[IL Stub or LCG]\n");
11915 ToRelease<ICorDebugClass> pClass;
11916 ToRelease<ICorDebugModule> pModule;
11917 mdMethodDef methodDef;
11918 IfFailRet(pFunction->GetClass(&pClass));
11919 IfFailRet(pFunction->GetModule(&pModule));
11920 IfFailRet(pFunction->GetToken(&methodDef));
11922 WCHAR wszModuleName[100];
11923 ULONG32 cchModuleNameActual;
11924 IfFailRet(pModule->GetName(_countof(wszModuleName), &cchModuleNameActual, wszModuleName));
11926 ToRelease<IUnknown> pMDUnknown;
11927 ToRelease<IMetaDataImport> pMD;
11928 ToRelease<IMDInternalImport> pMDInternal;
11929 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11930 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11931 IfFailRet(GetMDInternalFromImport(pMD, &pMDInternal));
11934 IfFailRet(pClass->GetToken(&typeDef));
11936 // Note that we don't need to pretty print the class, as class name is
11937 // already printed from GetMethodName below
11939 CQuickBytes functionName;
11940 // TODO: WARNING: GetMethodName() appears to include lots of unexercised
11941 // code, as evidenced by some fundamental bugs I found. It should either be
11942 // thoroughly reviewed, or some other more exercised code path to grab the
11943 // name should be used.
11944 // TODO: If we do stay with GetMethodName, it should be updated to print
11945 // generics properly. Today, it does not show generic type parameters, and
11946 // if any arguments have a generic type, those arguments are just shown as
11947 // "__Canon", even when they're value types.
11948 GetMethodName(methodDef, pMD, &functionName);
11950 DMLOut(DMLManagedVar(W("-a"), currentFrame, (LPWSTR)functionName.Ptr()));
11951 ExtOut(" (%S)\n", wszModuleName);
11953 if (SUCCEEDED(hrILFrame) && (bParams || bLocals))
11955 if(onlyShowFrame == -1 || (onlyShowFrame >= 0 && currentFrame == onlyShowFrame))
11956 IfFailRet(PrintParameters(bParams, bLocals, pMD, typeDef, methodDef, pILFrame, pModule, varToExpand, currentFrame));
11960 ExtOut("=============================================================================\n");
11963 // Temporary until we get a process exit notification plumbed from lldb
11964 UninitCorDebugInterface();
11970 WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj)
11974 if (ref.HasRegisterInformation)
11977 HRESULT hr = g_sos->GetRegisterName(ref.Register, _countof(reg), reg, NULL);
11981 res = W("<unknown register>");
11985 int offset = ref.Offset;
11996 res += Hex(offset);
12003 res += WString(Pointer(ref.Address));
12010 res += WString(ObjectPtr(ref.Object));
12013 if (ref.Flags & SOSRefPinned)
12015 res += W(" (pinned)");
12018 if (ref.Flags & SOSRefInterior)
12020 res += W(" (interior)");
12026 void PrintRef(const SOSStackRefData &ref, TableOutput &out)
12028 WString res = BuildRegisterOutput(ref);
12030 if (ref.Object && (ref.Flags & SOSRefInterior) == 0)
12033 sos::BuildTypeWithExtraInfo(TO_TADDR(ref.Object), _countof(type), type);
12035 res += WString(W(" - ")) + type;
12038 out.WriteColumn(2, res);
12045 static void PrintThread(ULONG osID, BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bFull, BOOL bDisplayRegVals)
12047 // Symbols variables
12048 ULONG symlines = 0; // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
12049 if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
12051 symlines &= SYMOPT_LOAD_LINES;
12055 bSuppressLines = TRUE;
12057 ToRelease<IXCLRDataStackWalk> pStackWalk;
12059 HRESULT hr = CreateStackWalk(osID, &pStackWalk);
12060 if (FAILED(hr) || pStackWalk == NULL)
12062 ExtOut("Failed to start stack walk: %lx\n", hr);
12066 #ifdef _TARGET_WIN64_
12067 PDEBUG_STACK_FRAME currentNativeFrame = NULL;
12068 ULONG numNativeFrames = 0;
12071 hr = GetContextStackTrace(osID, &numNativeFrames);
12074 ExtOut("Failed to get native stack frames: %lx\n", hr);
12077 currentNativeFrame = &g_Frames[0];
12079 #endif // _TARGET_WIN64_
12081 unsigned int refCount = 0, errCount = 0;
12082 ArrayHolder<SOSStackRefData> pRefs = NULL;
12083 ArrayHolder<SOSStackRefError> pErrs = NULL;
12084 if (bGC && FAILED(GetGCRefs(osID, &pRefs, &refCount, &pErrs, &errCount)))
12087 TableOutput out(3, POINTERSIZE_HEX, AlignRight);
12088 out.WriteRow("Child SP", "IP", "Call Site");
12094 ExtOut("<interrupted>\n");
12097 CLRDATA_ADDRESS ip = 0, sp = 0;
12098 hr = GetFrameLocation(pStackWalk, &ip, &sp);
12100 DacpFrameData FrameData;
12101 HRESULT frameDataResult = FrameData.Request(pStackWalk);
12102 if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
12103 sp = FrameData.frameAddr;
12105 #ifdef _TARGET_WIN64_
12106 while ((numNativeFrames > 0) && (currentNativeFrame->StackOffset <= sp))
12108 if (currentNativeFrame->StackOffset != sp)
12110 PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines);
12112 currentNativeFrame++;
12115 #endif // _TARGET_WIN64_
12117 // Print the stack pointer.
12118 out.WriteColumn(0, sp);
12120 // Print the method/Frame info
12121 if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
12123 // Skip the instruction pointer because it doesn't really mean anything for method frames
12124 out.WriteColumn(1, bFull ? String("") : NativePtr(ip));
12126 // This is a clr!Frame.
12127 out.WriteColumn(2, GetFrameFromAddress(TO_TADDR(FrameData.frameAddr), pStackWalk, bFull));
12129 // Print out gc references for the Frame.
12130 for (unsigned int i = 0; i < refCount; ++i)
12131 if (pRefs[i].Source == sp)
12132 PrintRef(pRefs[i], out);
12134 // Print out an error message if we got one.
12135 for (unsigned int i = 0; i < errCount; ++i)
12136 if (pErrs[i].Source == sp)
12137 out.WriteColumn(2, "Failed to enumerate GC references.");
12141 out.WriteColumn(1, InstructionPtr(ip));
12142 out.WriteColumn(2, MethodNameFromIP(ip, bSuppressLines, bFull, bFull));
12144 // Print out gc references. refCount will be zero if bGC is false (or if we
12145 // failed to fetch gc reference information).
12146 for (unsigned int i = 0; i < refCount; ++i)
12147 if (pRefs[i].Source == ip && pRefs[i].StackPointer == sp)
12148 PrintRef(pRefs[i], out);
12150 // Print out an error message if we got one.
12151 for (unsigned int i = 0; i < errCount; ++i)
12152 if (pErrs[i].Source == sp)
12153 out.WriteColumn(2, "Failed to enumerate GC references.");
12155 if (bParams || bLocals)
12156 PrintArgsAndLocals(pStackWalk, bParams, bLocals);
12159 if (bDisplayRegVals)
12160 PrintManagedFrameContext(pStackWalk);
12162 } while (pStackWalk->Next() == S_OK);
12164 #ifdef _TARGET_WIN64_
12165 while (numNativeFrames > 0)
12167 PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines);
12168 currentNativeFrame++;
12171 #endif // _TARGET_WIN64_
12174 static HRESULT PrintManagedFrameContext(IXCLRDataStackWalk *pStackWalk)
12176 CROSS_PLATFORM_CONTEXT context;
12177 HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
12178 if (FAILED(hr) || hr == S_FALSE)
12180 // GetFrameContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us.
12181 ExtOut("GetFrameContext failed: %lx\n", hr);
12185 #if defined(SOS_TARGET_AMD64)
12186 String outputFormat3 = " %3s=%016x %3s=%016x %3s=%016x\n";
12187 String outputFormat2 = " %3s=%016x %3s=%016x\n";
12188 ExtOut(outputFormat3, "rsp", context.Amd64Context.Rsp, "rbp", context.Amd64Context.Rbp, "rip", context.Amd64Context.Rip);
12189 ExtOut(outputFormat3, "rax", context.Amd64Context.Rax, "rbx", context.Amd64Context.Rbx, "rcx", context.Amd64Context.Rcx);
12190 ExtOut(outputFormat3, "rdx", context.Amd64Context.Rdx, "rsi", context.Amd64Context.Rsi, "rdi", context.Amd64Context.Rdi);
12191 ExtOut(outputFormat3, "r8", context.Amd64Context.R8, "r9", context.Amd64Context.R9, "r10", context.Amd64Context.R10);
12192 ExtOut(outputFormat3, "r11", context.Amd64Context.R11, "r12", context.Amd64Context.R12, "r13", context.Amd64Context.R13);
12193 ExtOut(outputFormat2, "r14", context.Amd64Context.R14, "r15", context.Amd64Context.R15);
12194 #elif defined(SOS_TARGET_X86)
12195 String outputFormat3 = " %3s=%08x %3s=%08x %3s=%08x\n";
12196 String outputFormat2 = " %3s=%08x %3s=%08x\n";
12197 ExtOut(outputFormat3, "esp", context.X86Context.Esp, "ebp", context.X86Context.Ebp, "eip", context.X86Context.Eip);
12198 ExtOut(outputFormat3, "eax", context.X86Context.Eax, "ebx", context.X86Context.Ebx, "ecx", context.X86Context.Ecx);
12199 ExtOut(outputFormat3, "edx", context.X86Context.Edx, "esi", context.X86Context.Esi, "edi", context.X86Context.Edi);
12200 #elif defined(SOS_TARGET_ARM)
12201 String outputFormat3 = " %3s=%08x %3s=%08x %3s=%08x\n";
12202 String outputFormat2 = " %s=%08x %s=%08x\n";
12203 String outputFormat1 = " %s=%08x\n";
12204 ExtOut(outputFormat3, "r0", context.ArmContext.R0, "r1", context.ArmContext.R1, "r2", context.ArmContext.R2);
12205 ExtOut(outputFormat3, "r3", context.ArmContext.R3, "r4", context.ArmContext.R4, "r5", context.ArmContext.R5);
12206 ExtOut(outputFormat3, "r6", context.ArmContext.R6, "r7", context.ArmContext.R7, "r8", context.ArmContext.R8);
12207 ExtOut(outputFormat3, "r9", context.ArmContext.R9, "r10", context.ArmContext.R10, "r11", context.ArmContext.R11);
12208 ExtOut(outputFormat1, "r12", context.ArmContext.R12);
12209 ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc);
12210 ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr);
12211 #elif defined(SOS_TARGET_ARM64)
12212 String outputXRegFormat3 = " x%d=%016x x%d=%016x x%d=%016x\n";
12213 String outputXRegFormat1 = " x%d=%016x\n";
12214 String outputFormat3 = " %s=%016x %s=%016x %s=%016x\n";
12215 String outputFormat2 = " %s=%08x %s=%08x\n";
12216 DWORD64 *X = context.Arm64Context.X;
12217 for (int i = 0; i < 9; i++)
12219 ExtOut(outputXRegFormat3, i + 0, X[i + 0], i + 1, X[i + 1], i + 2, X[i + 2]);
12221 ExtOut(outputXRegFormat1, 28, X[28]);
12222 ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc);
12223 ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr);
12225 ExtOut("Can't display register values for this platform\n");
12231 static HRESULT GetFrameLocation(IXCLRDataStackWalk *pStackWalk, CLRDATA_ADDRESS *ip, CLRDATA_ADDRESS *sp)
12233 CROSS_PLATFORM_CONTEXT context;
12234 HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
12235 if (FAILED(hr) || hr == S_FALSE)
12237 // GetFrameContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us.
12238 ExtOut("GetFrameContext failed: %lx\n", hr);
12242 // First find the info for the Frame object, if the current frame has an associated clr!Frame.
12243 *ip = GetIP(context);
12244 *sp = GetSP(context);
12246 if (IsDbgTargetArm())
12247 *ip = *ip & ~THUMB_CODE;
12252 static void PrintNativeStackFrame(TableOutput out, PDEBUG_STACK_FRAME frame, BOOL bSuppressLines)
12254 char filename[MAX_LONGPATH + 1];
12256 ULONG64 displacement;
12258 ULONG64 ip = frame->InstructionOffset;
12260 out.WriteColumn(0, frame->StackOffset);
12261 out.WriteColumn(1, NativePtr(ip));
12263 HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, _countof(symbol), NULL, &displacement);
12264 if (SUCCEEDED(hr) && symbol[0] != '\0')
12266 String frameOutput;
12267 frameOutput += symbol;
12271 frameOutput += " + ";
12272 frameOutput += Decimal(displacement);
12275 if (!bSuppressLines)
12278 hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, _countof(filename), NULL, NULL);
12281 frameOutput += " at ";
12282 frameOutput += filename;
12283 frameOutput += ":";
12284 frameOutput += Decimal(line);
12288 out.WriteColumn(2, frameOutput);
12292 out.WriteColumn(2, "");
12296 static void PrintCurrentThread(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals)
12301 g_ExtSystem->GetCurrentThreadSystemId(&osid);
12302 ExtOut("OS Thread Id: 0x%x ", osid);
12303 g_ExtSystem->GetCurrentThreadId(&id);
12304 ExtOut("(%d)\n", id);
12306 PrintThread(osid, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
12309 static void PrintAllThreads(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals)
12313 DacpThreadStoreData ThreadStore;
12314 if ((Status = ThreadStore.Request(g_sos)) != S_OK)
12316 ExtErr("Failed to request ThreadStore\n");
12320 DacpThreadData Thread;
12321 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
12322 while (CurThread != 0)
12327 if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
12329 ExtErr("Failed to request thread at %p\n", CurThread);
12332 ExtOut("OS Thread Id: 0x%x\n", Thread.osThreadId);
12333 PrintThread(Thread.osThreadId, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
12334 CurThread = Thread.nextThread;
12339 static HRESULT CreateStackWalk(ULONG osID, IXCLRDataStackWalk **ppStackwalk)
12342 ToRelease<IXCLRDataTask> pTask;
12344 if ((hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK)
12346 ExtOut("Unable to walk the managed stack. The current thread is likely not a \n");
12347 ExtOut("managed thread. You can run !threads to get a list of managed threads in\n");
12348 ExtOut("the process\n");
12352 return pTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
12353 CLRDATA_SIMPFRAME_MANAGED_METHOD |
12354 CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
12355 CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
12359 /* Prints the args and locals of for a thread's stack.
12361 * pStackWalk - the stack we are printing
12362 * bArgs - whether to print args
12363 * bLocals - whether to print locals
12365 static void PrintArgsAndLocals(IXCLRDataStackWalk *pStackWalk, BOOL bArgs, BOOL bLocals)
12367 ToRelease<IXCLRDataFrame> pFrame;
12368 ToRelease<IXCLRDataValue> pVal;
12369 ULONG32 argCount = 0;
12370 ULONG32 localCount = 0;
12373 hr = pStackWalk->GetFrame(&pFrame);
12376 if (SUCCEEDED(hr) && bArgs)
12377 hr = pFrame->GetNumArguments(&argCount);
12379 if (SUCCEEDED(hr) && bArgs)
12380 hr = ShowArgs(argCount, pFrame, pVal);
12383 if (SUCCEEDED(hr) && bLocals)
12384 hr = pFrame->GetNumLocalVariables(&localCount);
12386 if (SUCCEEDED(hr) && bLocals)
12387 ShowLocals(localCount, pFrame, pVal);
12394 /* Displays the arguments to a function
12396 * argy - the number of arguments the function has
12397 * pFramey - the frame we are inspecting
12398 * pVal - a pointer to the CLRDataValue we use to query for info about the args
12400 static HRESULT ShowArgs(ULONG32 argy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
12402 CLRDATA_ADDRESS addr = 0;
12403 BOOL fPrintedLocation = FALSE;
12404 ULONG64 outVar = 0;
12408 ArrayHolder<WCHAR> argName = new NOTHROW WCHAR[mdNameLen];
12415 for (ULONG32 i=0; i < argy; i++)
12419 ExtOut(" PARAMETERS:\n");
12422 hr = pFramey->GetArgumentByIndex(i,
12433 if (argName[0] != L'\0')
12435 ExtOut("%S ", argName.GetPtr());
12438 // At times we cannot print the value of a parameter (most
12439 // common case being a non-primitive value type). In these
12440 // cases we need to print the location of the parameter,
12441 // so that we can later examine it (e.g. using !dumpvc)
12443 bool result = SUCCEEDED(pVal->GetNumLocations(&tmp)) && tmp == 1;
12445 result = SUCCEEDED(pVal->GetLocationByIndex(0, &tmp, &addr));
12449 if (tmp == CLRDATA_VLOC_REGISTER)
12451 ExtOut("(<CLR reg>) ");
12455 ExtOut("(0x%p) ", SOS_PTR(CDA_TO_UL64(addr)));
12457 fPrintedLocation = TRUE;
12461 if (argName[0] != L'\0' || fPrintedLocation)
12466 if (HRESULT_CODE(pVal->GetBytes(0,&tmp,NULL)) == ERROR_BUFFER_OVERFLOW)
12468 ArrayHolder<BYTE> pByte = new NOTHROW BYTE[tmp + 1];
12475 hr = pVal->GetBytes(tmp, &tmp, pByte);
12479 ExtOut("<unable to retrieve data>\n");
12485 case 1: outVar = *((BYTE *)pByte.GetPtr()); break;
12486 case 2: outVar = *((short *)pByte.GetPtr()); break;
12487 case 4: outVar = *((DWORD *)pByte.GetPtr()); break;
12488 case 8: outVar = *((ULONG64 *)pByte.GetPtr()); break;
12489 default: outVar = 0;
12493 DMLOut("0x%s\n", DMLObject(outVar));
12495 ExtOut("0x%p\n", SOS_PTR(outVar));
12501 ExtOut("<no data>\n");
12511 /* Prints the locals of a frame.
12513 * localy - the number of locals in the frame
12514 * pFramey - the frame we are inspecting
12515 * pVal - a pointer to the CLRDataValue we use to query for info about the args
12517 static HRESULT ShowLocals(ULONG32 localy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
12519 for (ULONG32 i=0; i < localy; i++)
12522 ExtOut(" LOCALS:\n");
12527 // local names don't work in Whidbey.
12528 hr = pFramey->GetLocalVariableByIndex(i, &pVal, mdNameLen, NULL, g_mdName);
12534 ULONG32 numLocations;
12535 if (SUCCEEDED(pVal->GetNumLocations(&numLocations)) &&
12539 CLRDATA_ADDRESS addr;
12540 if (SUCCEEDED(pVal->GetLocationByIndex(0, &flags, &addr)))
12542 if (flags == CLRDATA_VLOC_REGISTER)
12544 ExtOut("<CLR reg> ");
12548 ExtOut("0x%p ", SOS_PTR(CDA_TO_UL64(addr)));
12552 // Can I get a name for the item?
12556 ULONG32 dwSize = 0;
12557 hr = pVal->GetBytes(0, &dwSize, NULL);
12559 if (HRESULT_CODE(hr) == ERROR_BUFFER_OVERFLOW)
12561 ArrayHolder<BYTE> pByte = new NOTHROW BYTE[dwSize + 1];
12568 hr = pVal->GetBytes(dwSize,&dwSize,pByte);
12572 ExtOut("<unable to retrieve data>\n");
12576 ULONG64 outVar = 0;
12579 case 1: outVar = *((BYTE *) pByte.GetPtr()); break;
12580 case 2: outVar = *((short *) pByte.GetPtr()); break;
12581 case 4: outVar = *((DWORD *) pByte.GetPtr()); break;
12582 case 8: outVar = *((ULONG64 *) pByte.GetPtr()); break;
12583 default: outVar = 0;
12587 DMLOut("0x%s\n", DMLObject(outVar));
12589 ExtOut("0x%p\n", SOS_PTR(outVar));
12594 ExtOut("<no data>\n");
12605 #ifndef FEATURE_PAL
12607 WatchCmd g_watchCmd;
12609 // The grand new !Watch command, private to Apollo for now
12613 BOOL bExpression = FALSE;
12614 StringHolder addExpression;
12615 StringHolder aExpression;
12616 StringHolder saveName;
12617 StringHolder sName;
12618 StringHolder expression;
12619 StringHolder filterName;
12620 StringHolder renameOldName;
12621 size_t expandIndex = -1;
12622 size_t removeIndex = -1;
12623 BOOL clear = FALSE;
12626 CMDOption option[] =
12627 { // name, vptr, type, hasValue
12628 {"-add", &addExpression.data, COSTRING, TRUE},
12629 {"-a", &aExpression.data, COSTRING, TRUE},
12630 {"-save", &saveName.data, COSTRING, TRUE},
12631 {"-s", &sName.data, COSTRING, TRUE},
12632 {"-clear", &clear, COBOOL, FALSE},
12633 {"-c", &clear, COBOOL, FALSE},
12634 {"-expand", &expandIndex, COSIZE_T, TRUE},
12635 {"-filter", &filterName.data, COSTRING, TRUE},
12636 {"-r", &removeIndex, COSIZE_T, TRUE},
12637 {"-remove", &removeIndex, COSIZE_T, TRUE},
12638 {"-rename", &renameOldName.data, COSTRING, TRUE},
12643 {&expression.data, COSTRING}
12645 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
12650 if(addExpression.data != NULL || aExpression.data != NULL)
12652 WCHAR pAddExpression[MAX_EXPRESSION];
12653 swprintf_s(pAddExpression, MAX_EXPRESSION, W("%S"), addExpression.data != NULL ? addExpression.data : aExpression.data);
12654 Status = g_watchCmd.Add(pAddExpression);
12656 else if(removeIndex != -1)
12658 if(removeIndex <= 0)
12660 ExtOut("Index must be a postive decimal number\n");
12664 Status = g_watchCmd.Remove((int)removeIndex);
12666 ExtOut("Watch expression #%d has been removed\n", removeIndex);
12667 else if(Status == S_FALSE)
12668 ExtOut("There is no watch expression with index %d\n", removeIndex);
12670 ExtOut("Unknown failure 0x%x removing watch expression\n", Status);
12673 else if(saveName.data != NULL || sName.data != NULL)
12675 WCHAR pSaveName[MAX_EXPRESSION];
12676 swprintf_s(pSaveName, MAX_EXPRESSION, W("%S"), saveName.data != NULL ? saveName.data : sName.data);
12677 Status = g_watchCmd.SaveList(pSaveName);
12681 g_watchCmd.Clear();
12683 else if(renameOldName.data != NULL)
12687 ExtOut("Must provide an old and new name. Usage: !watch -rename <old_name> <new_name>.\n");
12690 WCHAR pOldName[MAX_EXPRESSION];
12691 swprintf_s(pOldName, MAX_EXPRESSION, W("%S"), renameOldName.data);
12692 WCHAR pNewName[MAX_EXPRESSION];
12693 swprintf_s(pNewName, MAX_EXPRESSION, W("%S"), expression.data);
12694 g_watchCmd.RenameList(pOldName, pNewName);
12696 // print the tree, possibly with filtering and/or expansion
12697 else if(expandIndex != -1 || expression.data == NULL)
12699 WCHAR pExpression[MAX_EXPRESSION];
12700 pExpression[0] = '\0';
12702 if(expandIndex != -1)
12704 if(expression.data != NULL)
12706 swprintf_s(pExpression, MAX_EXPRESSION, W("%S"), expression.data);
12710 ExtOut("No expression was provided. Usage !watch -expand <index> <expression>\n");
12714 WCHAR pFilterName[MAX_EXPRESSION];
12715 pFilterName[0] = '\0';
12717 if(filterName.data != NULL)
12719 swprintf_s(pFilterName, MAX_EXPRESSION, W("%S"), filterName.data);
12722 g_watchCmd.Print((int)expandIndex, pExpression, pFilterName);
12726 ExtOut("Unrecognized argument: %s\n", expression.data);
12732 #endif // FEATURE_PAL
12734 DECLARE_API(ClrStack)
12739 BOOL bParams = FALSE;
12740 BOOL bLocals = FALSE;
12741 BOOL bSuppressLines = FALSE;
12742 BOOL bICorDebug = FALSE;
12745 BOOL bFull = FALSE;
12746 BOOL bDisplayRegVals = FALSE;
12747 BOOL bAllThreads = FALSE;
12748 DWORD frameToDumpVariablesFor = -1;
12749 StringHolder cvariableName;
12750 ArrayHolder<WCHAR> wvariableName = new NOTHROW WCHAR[mdNameLen];
12751 if (wvariableName == NULL)
12754 return E_OUTOFMEMORY;
12757 memset(wvariableName, 0, sizeof(wvariableName));
12760 CMDOption option[] =
12761 { // name, vptr, type, hasValue
12762 {"-a", &bAll, COBOOL, FALSE},
12763 {"-all", &bAllThreads, COBOOL, FALSE},
12764 {"-p", &bParams, COBOOL, FALSE},
12765 {"-l", &bLocals, COBOOL, FALSE},
12766 {"-n", &bSuppressLines, COBOOL, FALSE},
12767 {"-i", &bICorDebug, COBOOL, FALSE},
12768 {"-gc", &bGC, COBOOL, FALSE},
12769 {"-f", &bFull, COBOOL, FALSE},
12770 {"-r", &bDisplayRegVals, COBOOL, FALSE },
12771 #ifndef FEATURE_PAL
12772 {"/d", &dml, COBOOL, FALSE},
12777 {&cvariableName.data, COSTRING},
12778 {&frameToDumpVariablesFor, COSIZE_T},
12780 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
12785 EnableDMLHolder dmlHolder(dml);
12786 if (bAll || bParams || bLocals)
12788 // No parameter or local supports for minidump case!
12789 MINIDUMP_NOT_SUPPORTED();
12794 bParams = bLocals = TRUE;
12801 bool firstParamIsNumber = true;
12802 for(DWORD i = 0; i < strlen(cvariableName.data); i++)
12803 firstParamIsNumber = firstParamIsNumber && isdigit(cvariableName.data[i]);
12805 if(firstParamIsNumber && nArg == 1)
12807 frameToDumpVariablesFor = (DWORD)GetExpression(cvariableName.data);
12808 cvariableName.data[0] = '\0';
12811 if(cvariableName.data != NULL && strlen(cvariableName.data) > 0)
12812 swprintf_s(wvariableName, mdNameLen, W("%S\0"), cvariableName.data);
12814 if(_wcslen(wvariableName) > 0)
12815 bParams = bLocals = TRUE;
12817 EnableDMLHolder dmlHolder(TRUE);
12818 return ClrStackImplWithICorDebug::ClrStackFromPublicInterface(bParams, bLocals, FALSE, wvariableName, frameToDumpVariablesFor);
12822 ClrStackImpl::PrintAllThreads(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
12825 ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
12831 #ifndef FEATURE_PAL
12833 BOOL IsMemoryInfoAvailable()
12837 g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
12838 if (Qualifier == DEBUG_DUMP_SMALL)
12840 g_ExtControl->GetDumpFormatFlags(&Qualifier);
12841 if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0)
12843 if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY_INFO) == 0)
12852 DECLARE_API( VMMap )
12856 if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
12858 ExtOut("!VMMap requires a full memory dump (.dump /ma) or a live process.\n");
12866 } // DECLARE_API( vmmap )
12868 DECLARE_API( SOSFlush )
12872 g_clrData->Flush();
12875 } // DECLARE_API( SOSFlush )
12877 DECLARE_API( VMStat )
12881 if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
12883 ExtOut("!VMStat requires a full memory dump (.dump /ma) or a live process.\n");
12891 } // DECLARE_API( vmmap )
12893 /**********************************************************************\
12894 * Routine Description: *
12896 * This function saves a dll to a file. *
12898 \**********************************************************************/
12899 DECLARE_API(SaveModule)
12902 MINIDUMP_NOT_SUPPORTED();
12904 StringHolder Location;
12905 DWORD_PTR moduleAddr = NULL;
12910 {&moduleAddr, COHEX},
12911 {&Location.data, COSTRING}
12914 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
12920 ExtOut("Usage: SaveModule <address> <file to save>\n");
12923 if (moduleAddr == 0) {
12924 ExtOut ("Invalid arg\n");
12928 char* ptr = Location.data;
12930 DWORD_PTR dllBase = 0;
12932 if (g_ExtSymbols->GetModuleByOffset(TO_CDADDR(moduleAddr),0,NULL,&base) == S_OK)
12934 dllBase = TO_TADDR(base);
12936 else if (IsModule(moduleAddr))
12938 DacpModuleData module;
12939 module.Request(g_sos, TO_CDADDR(moduleAddr));
12940 dllBase = TO_TADDR(module.ilBase);
12943 ExtOut ("Module does not have base address\n");
12949 ExtOut ("%p is not a Module or base address\n", SOS_PTR(moduleAddr));
12953 MEMORY_BASIC_INFORMATION64 mbi;
12954 if (FAILED(g_ExtData2->QueryVirtual(TO_CDADDR(dllBase), &mbi)))
12956 ExtOut("Failed to retrieve information about segment %p", SOS_PTR(dllBase));
12960 // module loaded as an image or mapped as a flat file?
12961 bIsImage = (mbi.Type == MEM_IMAGE);
12963 IMAGE_DOS_HEADER DosHeader;
12964 if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
12967 IMAGE_NT_HEADERS Header;
12968 if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK)
12971 DWORD_PTR sectionAddr = dllBase + DosHeader.e_lfanew + offsetof(IMAGE_NT_HEADERS,OptionalHeader)
12972 + Header.FileHeader.SizeOfOptionalHeader;
12974 IMAGE_SECTION_HEADER section;
12979 DWORD_PTR FileAddr;
12980 DWORD_PTR FileSize;
12983 int nSection = Header.FileHeader.NumberOfSections;
12984 ExtOut("%u sections in file\n",nSection);
12985 MemLocation *memLoc = (MemLocation*)_alloca(nSection*sizeof(MemLocation));
12988 for (int n = 0; n < nSection; n++)
12990 if (g_ExtData->ReadVirtual(TO_CDADDR(sectionAddr), §ion, sizeof(section), NULL) == S_OK)
12992 for (slot = 0; slot <= indxSec; slot ++)
12993 if (section.PointerToRawData < memLoc[slot].FileAddr)
12996 for (int k = indxSec; k >= slot; k --)
12997 memcpy(&memLoc[k+1], &memLoc[k], sizeof(MemLocation));
12999 memLoc[slot].VAAddr = section.VirtualAddress;
13000 memLoc[slot].VASize = section.Misc.VirtualSize;
13001 memLoc[slot].FileAddr = section.PointerToRawData;
13002 memLoc[slot].FileSize = section.SizeOfRawData;
13003 ExtOut("section %d - VA=%x, VASize=%x, FileAddr=%x, FileSize=%x\n",
13004 n, memLoc[slot].VAAddr,memLoc[slot]. VASize,memLoc[slot].FileAddr,
13005 memLoc[slot].FileSize);
13010 ExtOut("Fail to read PE section info\n");
13013 sectionAddr += sizeof(section);
13016 if (ptr[0] == '\0')
13018 ExtOut ("File not specified\n");
13023 ptr += strlen(ptr)-1;
13024 while (isspace(*ptr))
13030 HANDLE hFile = CreateFileA(file,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
13031 if (hFile == INVALID_HANDLE_VALUE)
13033 ExtOut ("Fail to create file %s\n", file);
13037 ULONG pageSize = OSPageSize();
13038 char *buffer = (char *)_alloca(pageSize);
13043 TADDR dwAddr = dllBase;
13044 TADDR dwEnd = dllBase + Header.OptionalHeader.SizeOfHeaders;
13045 while (dwAddr < dwEnd)
13048 if (dwEnd - dwAddr < nRead)
13049 nRead = (ULONG)(dwEnd - dwAddr);
13051 if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
13053 WriteFile(hFile,buffer,nRead,&nWrite,NULL);
13057 ExtOut ("Fail to read memory\n");
13063 for (slot = 0; slot <= indxSec; slot ++)
13065 dwAddr = dllBase + (bIsImage ? memLoc[slot].VAAddr : memLoc[slot].FileAddr);
13066 dwEnd = memLoc[slot].FileSize + dwAddr - 1;
13068 while (dwAddr <= dwEnd)
13071 if (dwEnd - dwAddr + 1 < pageSize)
13072 nRead = (ULONG)(dwEnd - dwAddr + 1);
13074 if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
13076 WriteFile(hFile,buffer,nRead,&nWrite,NULL);
13080 ExtOut ("Fail to read memory\n");
13083 dwAddr += pageSize;
13087 CloseHandle (hFile);
13092 DECLARE_API(dbgout)
13098 CMDOption option[] =
13099 { // name, vptr, type, hasValue
13100 {"-off", &bOff, COBOOL, FALSE},
13103 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
13108 Output::SetDebugOutputEnabled(!bOff);
13111 DECLARE_API(filthint)
13116 DWORD_PTR filter = 0;
13118 CMDOption option[] =
13119 { // name, vptr, type, hasValue
13120 {"-off", &bOff, COBOOL, FALSE},
13127 if (!GetCMDOption(args, option, _countof(option),
13128 arg, _countof(arg), &nArg))
13138 g_filterHint = filter;
13143 #endif // FEATURE_PAL
13145 static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp,
13146 ULONG64 IPAddr, StringOutput& so)
13148 #define DOAPPEND(str) \
13150 if (!so.Append((str))) { \
13151 return E_OUTOFMEMORY; \
13154 // Should we skip explicit frames? They are characterized by Esp = 0, && Eip = 0 or 1.
13155 // See comment in FormatGeneratedException() for explanation why on non_IA64 Eip is 1, and not 0
13156 if (!(Flags & SOS_STACKTRACE_SHOWEXPLICITFRAMES) && (Esp == 0) && (IPAddr == 1))
13161 DacpMethodDescData MethodDescData;
13162 if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
13167 ArrayHolder<WCHAR> wszNameBuffer = new WCHAR[MAX_LONGPATH+1];
13169 if (Flags & SOS_STACKTRACE_SHOWADDRESSES)
13171 _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("%p %p "), (void*)(size_t) Esp, (void*)(size_t) IPAddr); // _TRUNCATE
13172 DOAPPEND(wszNameBuffer);
13175 DacpModuleData dmd;
13176 BOOL bModuleNameWorked = FALSE;
13177 ULONG64 addrInModule = IPAddr;
13178 if (dmd.Request(g_sos, MethodDescData.ModulePtr) == S_OK)
13180 CLRDATA_ADDRESS base = 0;
13181 if (g_sos->GetPEFileBase(dmd.File, &base) == S_OK)
13185 addrInModule = base;
13191 if (g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index, &base) == S_OK)
13193 ArrayHolder<char> szModuleName = new char[MAX_LONGPATH+1];
13194 if (g_ExtSymbols->GetModuleNames(Index, base, NULL, 0, NULL, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL) == S_OK)
13196 MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, wszNameBuffer, MAX_LONGPATH);
13197 DOAPPEND (wszNameBuffer);
13198 bModuleNameWorked = TRUE;
13204 if (g_sos->GetPEFileName(dmd.File, MAX_LONGPATH, wszNameBuffer, NULL) == S_OK)
13206 if (wszNameBuffer[0] != W('\0'))
13208 WCHAR *pJustName = _wcsrchr(wszNameBuffer, DIRECTORY_SEPARATOR_CHAR_W);
13209 if (pJustName == NULL)
13210 pJustName = wszNameBuffer - 1;
13212 DOAPPEND(pJustName + 1);
13213 bModuleNameWorked = TRUE;
13217 #endif // FEATURE_PAL
13219 // Under certain circumstances DacpMethodDescData::GetMethodDescName()
13220 // returns a module qualified method name
13221 HRESULT hr = g_sos->GetMethodDescName(dwStartAddr, MAX_LONGPATH, wszNameBuffer, NULL);
13223 WCHAR* pwszMethNameBegin = (hr != S_OK ? NULL : _wcschr(wszNameBuffer, L'!'));
13224 if (!bModuleNameWorked && hr == S_OK && pwszMethNameBegin != NULL)
13226 // if we weren't able to get the module name, but GetMethodDescName returned
13227 // the module as part of the returned method name, use this data
13228 DOAPPEND(wszNameBuffer);
13232 if (!bModuleNameWorked)
13234 DOAPPEND (W("UNKNOWN"));
13239 // the module name we retrieved above from debugger will take
13240 // precedence over the name possibly returned by GetMethodDescName()
13241 DOAPPEND(pwszMethNameBegin != NULL ? (pwszMethNameBegin+1) : (WCHAR *)wszNameBuffer);
13245 DOAPPEND(W("UNKNOWN"));
13249 ULONG64 Displacement = (IPAddr - MethodDescData.NativeCodeAddr);
13252 _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("+%#x"), Displacement); // _TRUNCATE
13253 DOAPPEND (wszNameBuffer);
13260 BOOL AppendContext(LPVOID pTransitionContexts, size_t maxCount, size_t *pcurCount, size_t uiSizeOfContext,
13261 CROSS_PLATFORM_CONTEXT *context)
13263 if (pTransitionContexts == NULL || *pcurCount >= maxCount)
13268 if (uiSizeOfContext == sizeof(StackTrace_SimpleContext))
13270 StackTrace_SimpleContext *pSimple = (StackTrace_SimpleContext *) pTransitionContexts;
13271 g_targetMachine->FillSimpleContext(&pSimple[*pcurCount], context);
13273 else if (uiSizeOfContext == g_targetMachine->GetContextSize())
13275 // FillTargetContext ensures we only write uiSizeOfContext bytes in pTransitionContexts
13276 // and not sizeof(CROSS_PLATFORM_CONTEXT) bytes (which would overrun).
13277 g_targetMachine->FillTargetContext(pTransitionContexts, context, (int)(*pcurCount));
13287 HRESULT CALLBACK ImplementEFNStackTrace(
13288 PDEBUG_CLIENT client,
13289 __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
13290 size_t *puiTextLength,
13291 LPVOID pTransitionContexts,
13292 size_t *puiTransitionContextCount,
13293 size_t uiSizeOfContext,
13297 #define DOAPPEND(str) if (!so.Append((str))) { \
13298 Status = E_OUTOFMEMORY; \
13302 HRESULT Status = E_FAIL;
13304 size_t transitionContextCount = 0;
13306 if (puiTextLength == NULL)
13308 return E_INVALIDARG;
13311 if (pTransitionContexts)
13313 if (puiTransitionContextCount == NULL)
13315 return E_INVALIDARG;
13318 // Do error checking on context size
13319 if ((uiSizeOfContext != g_targetMachine->GetContextSize()) &&
13320 (uiSizeOfContext != sizeof(StackTrace_SimpleContext)))
13322 return E_INVALIDARG;
13326 IXCLRDataStackWalk *pStackWalk = NULL;
13327 IXCLRDataTask* Task;
13330 if ((Status = g_ExtSystem->GetCurrentThreadSystemId(&ThreadId)) != S_OK ||
13331 (Status = g_clrData->GetTaskByOSThreadID(ThreadId, &Task)) != S_OK)
13333 // Not a managed thread.
13334 return SOS_E_NOMANAGEDCODE;
13337 Status = Task->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
13338 CLRDATA_SIMPFRAME_MANAGED_METHOD |
13339 CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
13340 CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
13345 if (Status != S_OK)
13347 if (Status == E_FAIL)
13349 return SOS_E_NOMANAGEDCODE;
13354 #ifdef _TARGET_WIN64_
13355 ULONG numFrames = 0;
13356 BOOL bInNative = TRUE;
13358 Status = GetContextStackTrace(ThreadId, &numFrames);
13359 if (FAILED(Status))
13364 for (ULONG i = 0; i < numFrames; i++)
13366 PDEBUG_STACK_FRAME pCur = g_Frames + i;
13368 CLRDATA_ADDRESS pMD;
13369 if (g_sos->GetMethodDescPtrFromIP(pCur->InstructionOffset, &pMD) == S_OK)
13371 if (bInNative || transitionContextCount==0)
13373 // We only want to list one transition frame if there are multiple frames.
13376 DOAPPEND (W("(TransitionMU)\n"));
13377 // For each transition, we need to store the context information
13378 if (puiTransitionContextCount)
13380 // below we cast the i-th AMD64_CONTEXT to CROSS_PLATFORM_CONTEXT
13381 AppendContext (pTransitionContexts, *puiTransitionContextCount,
13382 &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
13386 transitionContextCount++;
13390 Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
13391 pCur->StackOffset, pCur->InstructionOffset, so);
13392 if (FAILED(Status))
13396 else if (Status == S_OK)
13398 DOAPPEND (W("\n"));
13400 // for S_FALSE do not append anything
13407 // We only want to list one transition frame if there are multiple frames.
13410 DOAPPEND (W("(TransitionUM)\n"));
13411 // For each transition, we need to store the context information
13412 if (puiTransitionContextCount)
13414 AppendContext (pTransitionContexts, *puiTransitionContextCount,
13415 &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
13419 transitionContextCount++;
13426 #else // _TARGET_WIN64_
13429 size_t prevLength = 0;
13430 static WCHAR wszNameBuffer[1024]; // should be large enough
13431 wcscpy_s(wszNameBuffer, 1024, W("Frame")); // default value
13434 BOOL bInNative = TRUE;
13436 UINT frameCount = 0;
13439 DacpFrameData FrameData;
13440 if ((Status = FrameData.Request(pStackWalk)) != S_OK)
13445 CROSS_PLATFORM_CONTEXT context;
13446 if ((Status=pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(),
13447 NULL, (BYTE *)&context))!=S_OK)
13452 ExtDbgOut ( " * Ctx[BSI]: %08x %08x %08x ", GetBP(context), GetSP(context), GetIP(context) );
13454 CLRDATA_ADDRESS pMD;
13455 if (!FrameData.frameAddr)
13457 if (bInNative || transitionContextCount==0)
13459 // We only want to list one transition frame if there are multiple frames.
13462 DOAPPEND (W("(TransitionMU)\n"));
13463 // For each transition, we need to store the context information
13464 if (puiTransitionContextCount)
13466 AppendContext (pTransitionContexts, *puiTransitionContextCount,
13467 &transitionContextCount, uiSizeOfContext, &context);
13471 transitionContextCount++;
13475 // we may have a method, try to get the methoddesc
13476 if (g_sos->GetMethodDescPtrFromIP(GetIP(context), &pMD)==S_OK)
13478 Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
13479 GetSP(context), GetIP(context), so);
13480 if (FAILED(Status))
13484 else if (Status == S_OK)
13486 DOAPPEND (W("\n"));
13488 // for S_FALSE do not append anything
13494 if (Output::IsDebugOutputEnabled())
13497 MOVE(vtAddr, TO_TADDR(FrameData.frameAddr));
13498 if (g_sos->GetFrameName(TO_CDADDR(vtAddr), 1024, wszNameBuffer, NULL) == S_OK)
13499 ExtDbgOut("[%ls: %08x] ", wszNameBuffer, FrameData.frameAddr);
13501 ExtDbgOut("[Frame: %08x] ", FrameData.frameAddr);
13506 // We only want to list one transition frame if there are multiple frames.
13509 DOAPPEND (W("(TransitionUM)\n"));
13510 // For each transition, we need to store the context information
13511 if (puiTransitionContextCount)
13513 AppendContext (pTransitionContexts, *puiTransitionContextCount,
13514 &transitionContextCount, uiSizeOfContext, &context);
13518 transitionContextCount++;
13524 if (so.Length() > prevLength)
13526 ExtDbgOut ( "%ls", so.String()+prevLength );
13527 prevLength = so.Length();
13530 ExtDbgOut ( "\n" );
13534 while ((frameCount++) < MAX_STACK_FRAMES && pStackWalk->Next()==S_OK);
13539 #endif // _TARGET_WIN64_
13543 pStackWalk->Release();
13547 // We have finished. Does the user want to copy this data to a buffer?
13548 if (Status == S_OK)
13552 // They want at least partial output
13553 wcsncpy_s (wszTextOut, *puiTextLength, so.String(), *puiTextLength-1); // _TRUNCATE
13557 *puiTextLength = _wcslen (so.String()) + 1;
13560 if (puiTransitionContextCount)
13562 *puiTransitionContextCount = transitionContextCount;
13570 #define PAL_TRY_NAKED PAL_CPP_TRY
13571 #define PAL_EXCEPT_NAKED(disp) PAL_CPP_CATCH_ALL
13572 #define PAL_ENDTRY_NAKED PAL_CPP_ENDTRY
13575 // TODO: Convert PAL_TRY_NAKED to something that works on the Mac.
13576 HRESULT CALLBACK ImplementEFNStackTraceTry(
13577 PDEBUG_CLIENT client,
13578 __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
13579 size_t *puiTextLength,
13580 LPVOID pTransitionContexts,
13581 size_t *puiTransitionContextCount,
13582 size_t uiSizeOfContext,
13585 HRESULT Status = E_FAIL;
13589 Status = ImplementEFNStackTrace(client, wszTextOut, puiTextLength,
13590 pTransitionContexts, puiTransitionContextCount,
13591 uiSizeOfContext, Flags);
13593 PAL_EXCEPT_NAKED (EXCEPTION_EXECUTE_HANDLER)
13601 // See sos_stacktrace.h for the contract with the callers regarding the LPVOID arguments.
13602 HRESULT CALLBACK _EFN_StackTrace(
13603 PDEBUG_CLIENT client,
13604 __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
13605 size_t *puiTextLength,
13606 __out_bcount_opt(uiSizeOfContext*(*puiTransitionContextCount)) LPVOID pTransitionContexts,
13607 size_t *puiTransitionContextCount,
13608 size_t uiSizeOfContext,
13613 Status = ImplementEFNStackTraceTry(client, wszTextOut, puiTextLength,
13614 pTransitionContexts, puiTransitionContextCount,
13615 uiSizeOfContext, Flags);
13621 BOOL FormatFromRemoteString(DWORD_PTR strObjPointer, __out_ecount(cchString) PWSTR wszBuffer, ULONG cchString)
13625 wszBuffer[0] = L'\0';
13627 DacpObjectData objData;
13628 if (objData.Request(g_sos, TO_CDADDR(strObjPointer))!=S_OK)
13635 if (MOVE(stInfo, strObjPointer) != S_OK)
13640 DWORD dwBufLength = 0;
13641 if (!ClrSafeInt<DWORD>::addition(stInfo.m_StringLength, 1, dwBufLength))
13643 ExtOut("<integer overflow>\n");
13647 LPWSTR pwszBuf = new NOTHROW WCHAR[dwBufLength];
13648 if (pwszBuf == NULL)
13653 if (g_sos->GetObjectStringData(TO_CDADDR(strObjPointer), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK)
13659 // String is in format
13660 // <SP><SP><SP>at <function name>(args,...)\n
13662 // Parse and copy just <function name>(args,...)
13664 LPWSTR pwszPointer = pwszBuf;
13666 WCHAR PSZSEP[] = W(" at ");
13671 if (_wcsncmp(pwszPointer, PSZSEP, _countof(PSZSEP)-1) != 0)
13677 pwszPointer += _wcslen(PSZSEP);
13678 LPWSTR nextPos = _wcsstr(pwszPointer, PSZSEP);
13679 if (nextPos == NULL)
13681 // Done! Note that we are leaving the function before we add the last
13682 // line of stack trace to the output string. This is on purpose because
13683 // this string needs to be merged with a real trace, and the last line
13684 // of the trace will be common to the real trace.
13687 WCHAR c = *nextPos;
13690 // Buffer is calculated for sprintf below (" %p %p %S\n");
13691 WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2];
13693 // Note that we don't add a newline because we have this embedded in wszLineBuffer
13694 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %p %p %s"), (void*)(size_t)-1, (void*)(size_t)-1, pwszPointer);
13695 Length += (UINT)_wcslen(wszLineBuffer);
13699 wcsncat_s(wszBuffer, cchString, wszLineBuffer, _TRUNCATE);
13703 // Move to the next line.
13704 pwszPointer = nextPos;
13709 // Return TRUE only if the stack string had any information that was successfully parsed.
13710 // (Length > 0) is a good indicator of that.
13711 bRet = (Length > 0);
13715 HRESULT AppendExceptionInfo(CLRDATA_ADDRESS cdaObj,
13716 __out_ecount(cchString) PWSTR wszStackString,
13718 BOOL bNestedCase) // If bNestedCase is TRUE, the last frame of the computed stack is left off
13720 DacpObjectData objData;
13721 if (objData.Request(g_sos, cdaObj) != S_OK)
13726 // Make sure it is an exception object, and get the MT of Exception
13727 CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
13728 if (exceptionMT == NULL)
13730 return E_INVALIDARG;
13733 // First try to get exception object data using ISOSDacInterface2
13734 DacpExceptionObjectData excData;
13735 BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, cdaObj));
13738 // Is there a _remoteStackTraceString? We'll want to prepend that data.
13739 // We only have string data, so IP/SP info has to be set to -1.
13740 DWORD_PTR strPointer;
13743 strPointer = TO_TADDR(excData.RemoteStackTraceString);
13747 iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_remoteStackTraceString"));
13748 MOVE (strPointer, TO_TADDR(cdaObj) + iOffset);
13752 WCHAR *pwszBuffer = new NOTHROW WCHAR[cchString];
13753 if (pwszBuffer == NULL)
13755 return E_OUTOFMEMORY;
13758 if (FormatFromRemoteString(strPointer, pwszBuffer, cchString))
13760 // Prepend this stuff to the string for the user
13761 wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
13763 delete[] pwszBuffer;
13766 BOOL bAsync = bGotExcData ? IsAsyncException(excData)
13767 : IsAsyncException(TO_TADDR(cdaObj), TO_TADDR(objData.MethodTable));
13769 DWORD_PTR arrayPtr;
13772 arrayPtr = TO_TADDR(excData.StackTrace);
13776 iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_stackTrace"));
13777 MOVE (arrayPtr, TO_TADDR(cdaObj) + iOffset);
13783 MOVE (arrayLen, arrayPtr + sizeof(DWORD_PTR));
13787 #ifdef _TARGET_WIN64_
13788 DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
13790 DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD);
13791 #endif // _TARGET_WIN64_
13792 size_t stackTraceSize = 0;
13793 MOVE (stackTraceSize, dataPtr); // data length is stored at the beginning of the array in this case
13795 DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
13796 dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
13798 if (stackTraceSize != 0)
13800 size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, bNestedCase);
13801 WCHAR *pwszBuffer = new NOTHROW WCHAR[iLength + 1];
13804 FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer, iLength + 1, bAsync, bNestedCase);
13805 wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
13806 delete[] pwszBuffer;
13810 return E_OUTOFMEMORY;
13818 HRESULT ImplementEFNGetManagedExcepStack(
13819 CLRDATA_ADDRESS cdaStackObj,
13820 __out_ecount(cchString) PWSTR wszStackString,
13823 HRESULT Status = E_FAIL;
13825 if (wszStackString == NULL || cchString == 0)
13827 return E_INVALIDARG;
13830 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
13831 DacpThreadData Thread;
13832 BOOL bCanUseThreadContext = TRUE;
13834 ZeroMemory(&Thread, sizeof(DacpThreadData));
13836 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
13838 // The current thread is unmanaged
13839 bCanUseThreadContext = FALSE;
13842 if (cdaStackObj == NULL)
13844 if (!bCanUseThreadContext)
13846 return E_INVALIDARG;
13849 TADDR taLTOH = NULL;
13850 if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
13852 sizeof(taLTOH), NULL)) || (taLTOH==NULL))
13858 cdaStackObj = TO_CDADDR(taLTOH);
13862 // Put the stack trace header on
13863 AddExceptionHeader(wszStackString, cchString);
13865 // First is there a nested exception?
13866 if (bCanUseThreadContext && Thread.firstNestedException)
13868 CLRDATA_ADDRESS obj = 0, next = 0;
13869 CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
13872 Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
13874 // deal with the inability to read a nested exception gracefully
13875 if (Status != S_OK)
13880 Status = AppendExceptionInfo(obj, wszStackString, cchString, TRUE);
13881 currentNested = next;
13883 while(currentNested != NULL);
13886 Status = AppendExceptionInfo(cdaStackObj, wszStackString, cchString, FALSE);
13891 // TODO: Enable this when ImplementEFNStackTraceTry is fixed.
13892 // This function, like VerifyDAC, exists for the purpose of testing
13893 // hard-to-get-to SOS APIs.
13894 DECLARE_API(VerifyStackTrace)
13898 BOOL bVerifyManagedExcepStack = FALSE;
13899 CMDOption option[] =
13900 { // name, vptr, type, hasValue
13901 {"-ManagedExcepStack", &bVerifyManagedExcepStack, COBOOL, FALSE},
13904 if (!GetCMDOption(args, option, _countof(option), NULL,0,NULL))
13909 if (bVerifyManagedExcepStack)
13911 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
13912 DacpThreadData Thread;
13914 TADDR taExc = NULL;
13915 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
13917 ExtOut("The current thread is unmanaged\n");
13921 TADDR taLTOH = NULL;
13922 if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
13924 sizeof(taLTOH), NULL)) || (taLTOH == NULL))
13926 ExtOut("There is no current managed exception on this thread\n");
13934 const SIZE_T cchStr = 4096;
13935 WCHAR *wszStr = (WCHAR *)alloca(cchStr * sizeof(WCHAR));
13936 if (ImplementEFNGetManagedExcepStack(TO_CDADDR(taExc), wszStr, cchStr) != S_OK)
13938 ExtOut("Error!\n");
13942 ExtOut("_EFN_GetManagedExcepStack(%P, wszStr, sizeof(wszStr)) returned:\n", SOS_PTR(taExc));
13943 ExtOut("%S\n", wszStr);
13945 if (ImplementEFNGetManagedExcepStack((ULONG64)NULL, wszStr, cchStr) != S_OK)
13947 ExtOut("Error!\n");
13951 ExtOut("_EFN_GetManagedExcepStack(NULL, wszStr, sizeof(wszStr)) returned:\n");
13952 ExtOut("%S\n", wszStr);
13956 size_t textLength = 0;
13957 size_t contextLength = 0;
13958 Status = ImplementEFNStackTraceTry(client,
13966 if (Status != S_OK)
13968 ExtOut("Error: %lx\n", Status);
13972 ExtOut("Number of characters requested: %d\n", textLength);
13973 WCHAR *wszBuffer = new NOTHROW WCHAR[textLength + 1];
13974 if (wszBuffer == NULL)
13980 // For the transition contexts buffer the callers are expected to allocate
13981 // contextLength * sizeof(TARGET_CONTEXT), and not
13982 // contextLength * sizeof(CROSS_PLATFORM_CONTEXT). See sos_stacktrace.h for
13984 LPBYTE pContexts = new NOTHROW BYTE[contextLength * g_targetMachine->GetContextSize()];
13986 if (pContexts == NULL)
13989 delete[] wszBuffer;
13993 Status = ImplementEFNStackTrace(client,
13998 g_targetMachine->GetContextSize(),
14001 if (Status != S_OK)
14003 ExtOut("Error: %lx\n", Status);
14004 delete[] wszBuffer;
14005 delete [] pContexts;
14009 ExtOut("%S\n", wszBuffer);
14011 ExtOut("Context information:\n");
14012 if (IsDbgTargetX86())
14014 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14015 "Ebp", "Esp", "Eip");
14017 else if (IsDbgTargetAmd64())
14019 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14020 "Rbp", "Rsp", "Rip");
14022 else if (IsDbgTargetArm())
14024 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14029 ExtOut("Unsupported platform");
14030 delete [] pContexts;
14031 delete[] wszBuffer;
14035 for (size_t j=0; j < contextLength; j++)
14037 CROSS_PLATFORM_CONTEXT *pCtx = (CROSS_PLATFORM_CONTEXT*)(pContexts + j*g_targetMachine->GetContextSize());
14038 ExtOut("%p %p %p\n", GetBP(*pCtx), GetSP(*pCtx), GetIP(*pCtx));
14041 delete [] pContexts;
14043 StackTrace_SimpleContext *pSimple = new NOTHROW StackTrace_SimpleContext[contextLength];
14044 if (pSimple == NULL)
14047 delete[] wszBuffer;
14051 Status = ImplementEFNStackTrace(client,
14056 sizeof(StackTrace_SimpleContext),
14059 if (Status != S_OK)
14061 ExtOut("Error: %lx\n", Status);
14062 delete[] wszBuffer;
14067 ExtOut("Simple Context information:\n");
14068 if (IsDbgTargetX86())
14069 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14070 "Ebp", "Esp", "Eip");
14071 else if (IsDbgTargetAmd64())
14072 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14073 "Rbp", "Rsp", "Rip");
14074 else if (IsDbgTargetArm())
14075 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14079 ExtOut("Unsupported platform");
14080 delete[] wszBuffer;
14084 for (size_t j=0; j < contextLength; j++)
14086 ExtOut("%p %p %p\n", SOS_PTR(pSimple[j].FrameOffset),
14087 SOS_PTR(pSimple[j].StackOffset),
14088 SOS_PTR(pSimple[j].InstructionOffset));
14091 delete[] wszBuffer;
14097 #ifndef FEATURE_PAL
14099 // This is an internal-only Apollo extension to de-optimize the code
14100 DECLARE_API(SuppressJitOptimization)
14103 MINIDUMP_NOT_SUPPORTED();
14105 StringHolder onOff;
14108 {&onOff.data, COSTRING},
14111 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
14116 if(nArg == 1 && (_stricmp(onOff.data, "On") == 0))
14118 // if CLR is already loaded, try to change the flags now
14119 if(CheckEEDll() == S_OK)
14121 SetNGENCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION);
14124 if(!g_fAllowJitOptimization)
14125 ExtOut("JIT optimization is already suppressed\n");
14128 g_fAllowJitOptimization = FALSE;
14129 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
14130 ExtOut("JIT optimization will be suppressed\n");
14135 else if(nArg == 1 && (_stricmp(onOff.data, "Off") == 0))
14137 // if CLR is already loaded, try to change the flags now
14138 if(CheckEEDll() == S_OK)
14140 SetNGENCompilerFlags(CORDEBUG_JIT_DEFAULT);
14143 if(g_fAllowJitOptimization)
14144 ExtOut("JIT optimization is already permitted\n");
14147 g_fAllowJitOptimization = TRUE;
14148 ExtOut("JIT optimization will be permitted\n");
14153 ExtOut("Usage: !SuppressJitOptimization <on|off>\n");
14159 // Uses ICorDebug to set the state of desired NGEN compiler flags. This can suppress pre-jitted optimized
14161 HRESULT SetNGENCompilerFlags(DWORD flags)
14165 ToRelease<ICorDebugProcess2> proc2;
14166 if(FAILED(hr = InitCorDebugInterface()))
14168 ExtOut("SOS: warning, prejitted code optimizations could not be changed. Failed to load ICorDebug HR = 0x%x\n", hr);
14170 else if(FAILED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess2), (void**) &proc2)))
14172 if(flags != CORDEBUG_JIT_DEFAULT)
14174 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
14181 else if(FAILED(hr = proc2->SetDesiredNGENCompilerFlags(flags)))
14183 // Versions of CLR that don't have SetDesiredNGENCompilerFlags DAC-ized will return E_FAIL.
14184 // This was first supported in the clr_triton branch around 4/1/12, Apollo release
14185 // It will likely be supported in desktop CLR during Dev12
14188 if(flags != CORDEBUG_JIT_DEFAULT)
14190 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
14197 else if(hr == CORDBG_E_NGEN_NOT_SUPPORTED)
14199 if(flags != CORDEBUG_JIT_DEFAULT)
14201 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support NGEN\n");
14208 else if(hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS)
14210 DWORD currentFlags = 0;
14211 if(FAILED(hr = proc2->GetDesiredNGENCompilerFlags(¤tFlags)))
14213 ExtOut("SOS: warning, prejitted code optimizations could not be changed. GetDesiredNGENCompilerFlags failed hr=0x%x\n", hr);
14215 else if(currentFlags != flags)
14217 ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. This setting is fixed once CLR starts\n");
14226 ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. SetDesiredNGENCompilerFlags hr = 0x%x\n", hr);
14234 // This is an internal-only Apollo extension to save breakpoint/watch state
14235 DECLARE_API(SaveState)
14238 MINIDUMP_NOT_SUPPORTED();
14240 StringHolder filePath;
14243 {&filePath.data, COSTRING},
14246 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
14253 ExtOut("Usage: !SaveState <file_path>\n");
14257 errno_t error = fopen_s(&pFile, filePath.data, "w");
14260 ExtOut("Failed to open file %s, error=0x%x\n", filePath.data, error);
14264 g_bpoints.SaveBreakpoints(pFile);
14265 g_watchCmd.SaveListToFile(pFile);
14268 ExtOut("Session breakpoints and watch expressions saved to %s\n", filePath.data);
14272 #endif // FEATURE_PAL
14274 DECLARE_API(StopOnCatch)
14277 MINIDUMP_NOT_SUPPORTED();
14279 g_stopOnNextCatch = TRUE;
14281 g_clrData->GetOtherNotificationFlags(&flags);
14282 flags |= CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER;
14283 g_clrData->SetOtherNotificationFlags(flags);
14284 ExtOut("Debuggee will break the next time a managed exception is caught during execution\n");
14288 // This is an undocumented SOS extension command intended to help test SOS
14289 // It causes the Dml output to be printed to the console uninterpretted so
14290 // that a test script can read the commands which are hidden in the markup
14291 DECLARE_API(ExposeDML)
14293 Output::SetDMLExposed(true);
14297 // According to kksharma the Windows debuggers always sign-extend
14298 // arguments when calling externally, therefore StackObjAddr
14299 // conforms to CLRDATA_ADDRESS contract.
14301 _EFN_GetManagedExcepStack(
14302 PDEBUG_CLIENT client,
14303 ULONG64 StackObjAddr,
14304 __out_ecount (cbString) PSTR szStackString,
14310 ArrayHolder<WCHAR> tmpStr = new NOTHROW WCHAR[cbString];
14311 if (tmpStr == NULL)
14314 return E_OUTOFMEMORY;
14317 if (FAILED(Status = ImplementEFNGetManagedExcepStack(StackObjAddr, tmpStr, cbString)))
14322 if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmpStr, -1, szStackString, cbString, NULL, NULL) == 0)
14330 // same as _EFN_GetManagedExcepStack, but returns the stack as a wide string.
14332 _EFN_GetManagedExcepStackW(
14333 PDEBUG_CLIENT client,
14334 ULONG64 StackObjAddr,
14335 __out_ecount(cchString) PWSTR wszStackString,
14341 return ImplementEFNGetManagedExcepStack(StackObjAddr, wszStackString, cchString);
14344 // According to kksharma the Windows debuggers always sign-extend
14345 // arguments when calling externally, therefore objAddr
14346 // conforms to CLRDATA_ADDRESS contract.
14348 _EFN_GetManagedObjectName(
14349 PDEBUG_CLIENT client,
14351 __out_ecount (cbName) PSTR szName,
14357 if (!sos::IsObject(objAddr, false))
14359 return E_INVALIDARG;
14362 sos::Object obj = TO_TADDR(objAddr);
14364 if (WideCharToMultiByte(CP_ACP, 0, obj.GetTypeName(), (int) (_wcslen(obj.GetTypeName()) + 1),
14365 szName, cbName, NULL, NULL) == 0)
14372 // According to kksharma the Windows debuggers always sign-extend
14373 // arguments when calling externally, therefore objAddr
14374 // conforms to CLRDATA_ADDRESS contract.
14376 _EFN_GetManagedObjectFieldInfo(
14377 PDEBUG_CLIENT client,
14379 __out_ecount (mdNameLen) PSTR szFieldName,
14385 DacpObjectData objData;
14386 LPWSTR fieldName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
14388 if (szFieldName == NULL || *szFieldName == '\0' ||
14394 if (pOffset == NULL && pValue == NULL)
14396 // One of these needs to be valid
14400 if (FAILED(objData.Request(g_sos, objAddr)))
14405 MultiByteToWideChar(CP_ACP,0,szFieldName,-1,fieldName,mdNameLen);
14407 int iOffset = GetObjFieldOffset (objAddr, objData.MethodTable, fieldName);
14415 *pOffset = (ULONG) iOffset;
14420 if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(objAddr + iOffset), pValue, sizeof(ULONG64), NULL)))
14431 #ifdef CREATE_DUMP_SUPPORTED
14432 #include <dumpcommon.h>
14433 #include "datatarget.h"
14434 extern bool CreateDumpForSOS(const char* programPath, const char* dumpPathTemplate, pid_t pid, MINIDUMP_TYPE minidumpType, ICLRDataTarget* dataTarget);
14435 extern bool g_diagnostics;
14436 #endif // CREATE_DUMP_SUPPORTED
14438 DECLARE_API(CreateDump)
14441 #ifdef CREATE_DUMP_SUPPORTED
14442 StringHolder sFileName;
14443 BOOL normal = FALSE;
14444 BOOL withHeap = FALSE;
14445 BOOL triage = FALSE;
14450 CMDOption option[] =
14451 { // name, vptr, type, hasValue
14452 {"-n", &normal, COBOOL, FALSE},
14453 {"-h", &withHeap, COBOOL, FALSE},
14454 {"-t", &triage, COBOOL, FALSE},
14455 {"-f", &full, COBOOL, FALSE},
14456 {"-d", &diag, COBOOL, FALSE},
14460 {&sFileName.data, COSTRING}
14462 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
14466 MINIDUMP_TYPE minidumpType = MiniDumpWithPrivateReadWriteMemory;
14468 g_ExtSystem->GetCurrentProcessId(&pid);
14472 minidumpType = MiniDumpWithFullMemory;
14476 minidumpType = MiniDumpWithPrivateReadWriteMemory;
14480 minidumpType = MiniDumpFilterTriage;
14484 minidumpType = MiniDumpNormal;
14486 g_diagnostics = diag;
14488 const char* programPath = g_ExtServices->GetCoreClrDirectory();
14489 const char* dumpPathTemplate = "/tmp/coredump.%d";
14490 ToRelease<ICLRDataTarget> dataTarget = new DataTarget();
14491 dataTarget->AddRef();
14493 if (sFileName.data != nullptr)
14495 dumpPathTemplate = sFileName.data;
14497 if (!CreateDumpForSOS(programPath, dumpPathTemplate, pid, minidumpType, dataTarget))
14501 #else // CREATE_DUMP_SUPPORTED
14502 ExtErr("CreateDump not supported on this platform\n");
14503 #endif // CREATE_DUMP_SUPPORTED
14507 #endif // FEATURE_PAL
14509 void PrintHelp (__in_z LPCSTR pszCmdName)
14511 static LPSTR pText = NULL;
14513 if (pText == NULL) {
14514 #ifndef FEATURE_PAL
14515 HGLOBAL hResource = NULL;
14516 HRSRC hResInfo = FindResource (g_hInstance, TEXT ("DOCUMENTATION"), TEXT ("TEXT"));
14517 if (hResInfo) hResource = LoadResource (g_hInstance, hResInfo);
14518 if (hResource) pText = (LPSTR) LockResource (hResource);
14521 ExtOut("Error loading documentation resource\n");
14525 int err = PAL_InitializeDLL();
14528 ExtOut("Error initializing PAL\n");
14531 char lpFilename[MAX_LONGPATH + 12]; // + 12 to make enough room for strcat function.
14532 strcpy_s(lpFilename, _countof(lpFilename), g_ExtServices->GetCoreClrDirectory());
14533 strcat_s(lpFilename, _countof(lpFilename), "sosdocsunix.txt");
14535 HANDLE hSosDocFile = CreateFileA(lpFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
14536 if (hSosDocFile == INVALID_HANDLE_VALUE) {
14537 ExtOut("Error finding documentation file\n");
14541 HANDLE hMappedSosDocFile = CreateFileMappingA(hSosDocFile, NULL, PAGE_READONLY, 0, 0, NULL);
14542 CloseHandle(hSosDocFile);
14543 if (hMappedSosDocFile == NULL) {
14544 ExtOut("Error mapping documentation file\n");
14548 pText = (LPSTR)MapViewOfFile(hMappedSosDocFile, FILE_MAP_READ, 0, 0, 0);
14549 CloseHandle(hMappedSosDocFile);
14552 ExtOut("Error loading documentation file\n");
14558 // Find our line in the text file
14559 char searchString[MAX_LONGPATH];
14560 sprintf_s(searchString, _countof(searchString), "COMMAND: %s.", pszCmdName);
14562 LPSTR pStart = strstr(pText, searchString);
14566 ExtOut("Documentation for %s not found.\n", pszCmdName);
14570 // Go to the end of this line:
14571 pStart = strchr(pStart, '\n');
14574 ExtOut("Expected newline in documentation resource.\n");
14578 // Bypass the newline that pStart points to and setup pEnd for the loop below. We set
14579 // pEnd to be the old pStart since we add one to it when we call strstr.
14582 // Find the first occurrence of \\ followed by an \r or an \n on a line by itself.
14585 pEnd = strstr(pEnd+1, "\\\\");
14586 } while (pEnd && ((pEnd[-1] != '\r' && pEnd[-1] != '\n') || (pEnd[3] != '\r' && pEnd[3] != '\n')));
14590 // We have found a \\ followed by a \r or \n. Do not print out the character pEnd points
14591 // to, as this will be the first \ (this is why we don't add one to the second parameter).
14592 ExtOut("%.*s", pEnd - pStart, pStart);
14596 // If pEnd is false then we have run to the end of the document. However, we did find
14597 // the command to print, so we should simply print to the end of the file. We'll add
14598 // an extra newline here in case the file does not contain one.
14599 ExtOut("%s\n", pStart);
14603 /**********************************************************************\
14604 * Routine Description: *
14606 * This function displays the commands available in strike and the *
14607 * arguments passed into each.
14609 \**********************************************************************/
14612 // Call extension initialization functions directly, because we don't need the DAC dll to be initialized to get help.
14614 __ExtensionCleanUp __extensionCleanUp;
14615 if ((Status = ExtQuery(client)) != S_OK) return Status;
14618 StringHolder commandName;
14621 {&commandName.data, COSTRING}
14624 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
14629 ExtOut("-------------------------------------------------------------------------------\n");
14633 // Convert commandName to lower-case
14634 LPSTR curChar = commandName.data;
14635 while (*curChar != '\0')
14637 if ( ((unsigned) *curChar <= 0x7F) && isupper(*curChar))
14639 *curChar = (CHAR) tolower(*curChar);
14644 // Strip off leading "!" if the user put that.
14645 curChar = commandName.data;
14646 if (*curChar == '!')
14649 PrintHelp (curChar);
14653 PrintHelp ("contents");
14659 #if defined(FEATURE_PAL) && defined(_TARGET_WIN64_)
14662 ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size)
14665 HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(address), buffer, size, &fetched);
14666 return SUCCEEDED(hr);
14670 GetStackFrame(CONTEXT* context, ULONG numNativeFrames)
14672 KNONVOLATILE_CONTEXT_POINTERS contextPointers;
14673 memset(&contextPointers, 0, sizeof(contextPointers));
14675 ULONG64 baseAddress;
14676 HRESULT hr = g_ExtSymbols->GetModuleByOffset(context->Rip, 0, NULL, &baseAddress);
14679 PDEBUG_STACK_FRAME frame = &g_Frames[0];
14680 for (int i = 0; i < numNativeFrames; i++, frame++) {
14681 if (frame->InstructionOffset == context->Rip)
14683 if ((i + 1) >= numNativeFrames) {
14686 memcpy(context, &(g_X64FrameContexts[i + 1]), sizeof(*context));
14692 if (!PAL_VirtualUnwindOutOfProc(context, &contextPointers, baseAddress, ReadMemoryAdapter))
14700 UnwindStackFrames(ULONG32 osThreadId)
14702 ULONG numNativeFrames = 0;
14703 HRESULT hr = GetContextStackTrace(osThreadId, &numNativeFrames);
14709 memset(&context, 0, sizeof(context));
14710 context.ContextFlags = CONTEXT_FULL;
14712 hr = g_ExtSystem->GetThreadContextById(osThreadId, CONTEXT_FULL, sizeof(context), (PBYTE)&context);
14717 TableOutput out(3, POINTERSIZE_HEX, AlignRight);
14718 out.WriteRow("RSP", "RIP", "Call Site");
14720 DEBUG_STACK_FRAME nativeFrame;
14721 memset(&nativeFrame, 0, sizeof(nativeFrame));
14725 if (context.Rip == 0)
14729 nativeFrame.InstructionOffset = context.Rip;
14730 nativeFrame.ReturnOffset = context.Rip;
14731 nativeFrame.FrameOffset = context.Rbp;
14732 nativeFrame.StackOffset = context.Rsp;
14733 ClrStackImpl::PrintNativeStackFrame(out, &nativeFrame, FALSE);
14735 } while (GetStackFrame(&context, numNativeFrames));
14740 #endif // FEATURE_PAL && _TARGET_WIN64_