2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 // ===========================================================================
14 // ===========================================================================
17 // 09/07/99 Microsoft Created
19 //************************************************************************************************
20 // SOS is the native debugging extension designed to support investigations into CLR (mis-)
21 // behavior by both users of the runtime as well as the code owners. It allows inspection of
22 // internal structures, of user visible entities, as well as execution control.
24 // This is the main SOS file hosting the implementation of all the exposed commands. A good
25 // starting point for understanding the semantics of these commands is the sosdocs.txt file.
28 // SOS currently supports cross platform debugging from x86 to ARM. It takes a different approach
29 // from the DAC: whereas for the DAC we produce one binary for each supported host-target
30 // architecture pair, for SOS we produce only one binary for each host architecture; this one
31 // binary contains code for all supported target architectures. In doing this SOS depends on two
33 // . that the debugger will load the appropriate DAC, and
34 // . that the host and target word size is identical.
35 // The second assumption is identical to the DAC assumption, and there will be considerable effort
36 // required (in the EE, the DAC, and SOS) if we ever need to remove it.
38 // In an ideal world SOS would be able to retrieve all platform specific information it needs
39 // either from the debugger or from DAC. However, SOS has taken some subtle and not so subtle
40 // dependencies on the CLR and the target platform.
41 // To resolve this problem, SOS now abstracts the target behind the IMachine interface, and uses
42 // calls on IMachine to take target-specific actions. It implements X86Machine, ARMMachine, and
43 // AMD64Machine. An instance of these exists in each appropriate host (e.g. the X86 version of SOS
44 // contains instaces of X86Machine and ARMMachine, the ARM version contains an instance of
45 // ARMMachine, and the AMD64 version contains an instance of AMD64Machine). The code included in
46 // each version if determined by the SosTarget*** MSBuild symbols, and SOS_TARGET_*** conditional
47 // compilation symbols (as specified in sos.targets).
49 // Most of the target specific code is hosted in disasm.h/.cpp, and disasmX86.cpp, disasmARM.cpp.
50 // Some code currently under _TARGET_*** ifdefs may need to be reviewed/revisited.
53 // The one-binary-per-host decision does have some drawbacks:
54 // . Currently including system headers or even CLR headers will only account for the host
55 // target, IOW, when building the X86 version of SOS, CONTEXT will refer to the X86 CONTEXT
56 // structure, so we need to be careful when debugging ARM targets. The CONTEXT issue is
57 // partially resolved by CROSS_PLATFORM_CONTEXT (there is still a need to be very careful
58 // when handling arrays of CONTEXTs - see _EFN_StackTrace for details on this).
59 // . For larger includes (e.g. GC info), we will need to include files in specific namespaces,
60 // with specific _TARGET_*** macros defined in order to avoid name clashes and ensure correct
61 // system types are used.
62 // -----------------------------------------------------------------------------------------------
64 #define DO_NOT_DISABLE_RAND //this is a standalone tool, and can use rand()
72 #endif // !FEATURE_PAL
75 #include "platformspecific.h"
98 #define STRESS_LOG_READONLY
99 #include "stresslog.h"
105 #include "cordebug.h"
106 #include "dacprivate.h"
107 #include "corexcep.h"
109 #define CORHANDLE_MASK 0x1
110 #define SWITCHED_OUT_FIBER_OSID 0xbaadf00d;
112 #define DEFINE_EXT_GLOBALS
117 #endif // !FEATURE_PAL
119 #include "predeftlsslot.h"
121 #include "hillclimbing.h"
127 #include "ExpressionNode.h"
128 #include "WatchCmd.h"
136 typedef struct _VM_COUNTERS {
137 SIZE_T PeakVirtualSize;
139 ULONG PageFaultCount;
140 SIZE_T PeakWorkingSetSize;
141 SIZE_T WorkingSetSize;
142 SIZE_T QuotaPeakPagedPoolUsage;
143 SIZE_T QuotaPagedPoolUsage;
144 SIZE_T QuotaPeakNonPagedPoolUsage;
145 SIZE_T QuotaNonPagedPoolUsage;
146 SIZE_T PagefileUsage;
147 SIZE_T PeakPagefileUsage;
149 typedef VM_COUNTERS *PVM_COUNTERS;
151 const PROCESSINFOCLASS ProcessVmCounters = static_cast<PROCESSINFOCLASS>(3);
153 #endif // !FEATURE_PAL
156 BOOL ControlC = FALSE;
158 IMetaDataDispenserEx *pDisp = NULL;
159 WCHAR g_mdName[mdNameLen];
162 HMODULE g_hInstance = NULL;
165 #endif // !FEATURE_PAL
168 #pragma warning(disable:4244) // conversion from 'unsigned int' to 'unsigned short', possible loss of data
169 #pragma warning(disable:4189) // local variable is initialized but not referenced
174 #if defined _X86_ && !defined FEATURE_PAL
175 // disable FPO for X86 builds
176 #pragma optimize("y", off)
184 #pragma warning(default:4244)
185 #pragma warning(default:4189)
190 #endif // FEATURE_PAL
192 // Size of a fixed array:
194 #define ARRAYSIZE(a) (sizeof(a)/sizeof((a)[0]))
199 #define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
202 #define NOTHROW (std::nothrow)
204 #define MINIDUMP_NOT_SUPPORTED() \
205 if (IsMiniDumpFile()) \
207 ExtOut("This command is not supported in a minidump without full memory\n"); \
208 ExtOut("To try the command anyway, run !MinidumpMode 0\n"); \
213 #include "safemath.h"
215 DECLARE_API (MinidumpMode)
226 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
232 // Print status of current mode
233 ExtOut("Current mode: %s - unsafe minidump commands are %s.\n",
234 g_InMinidumpSafeMode ? "1" : "0",
235 g_InMinidumpSafeMode ? "disabled" : "enabled");
239 if (Value != 0 && Value != 1)
241 ExtOut("Mode must be 0 or 1\n");
245 g_InMinidumpSafeMode = (BOOL) Value;
246 ExtOut("Unsafe minidump commands are %s.\n",
247 g_InMinidumpSafeMode ? "disabled" : "enabled");
253 #endif // FEATURE_PAL
255 /**********************************************************************\
256 * Routine Description: *
258 * This function is called to get the MethodDesc for a given eip *
260 \**********************************************************************/
264 MINIDUMP_NOT_SUPPORTED();
269 { // name, vptr, type, hasValue
270 {"/d", &dml, COBOOL, FALSE},
278 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
282 EnableDMLHolder dmlHolder(dml);
286 ExtOut("%s is not IP\n", args);
290 CLRDATA_ADDRESS cdaStart = TO_CDADDR(IP);
294 if ((Status = g_sos->GetMethodDescPtrFromIP(cdaStart, &pMD)) != S_OK)
296 ExtOut("Failed to request MethodData, not in JIT code range\n");
300 DMLOut("MethodDesc: %s\n", DMLMethodDesc(pMD));
301 DumpMDInfo(TO_TADDR(pMD), cdaStart, FALSE /* fStackTraceFormat */);
304 char filename[MAX_PATH+1];
306 // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
308 if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
310 symlines &= SYMOPT_LOAD_LINES;
314 && SUCCEEDED(GetLineByOffset(TO_CDADDR(IP),
319 ExtOut("Source file: %s @ %d\n", filename, linenum);
328 // (MAX_STACK_FRAMES is also used by x86 to prevent infinite loops in _EFN_StackTrace)
329 #define MAX_STACK_FRAMES 1000
332 #ifdef _TARGET_WIN64_
333 // I use a global set of frames for stack walking on win64 because the debugger's
334 // GetStackTrace function doesn't provide a way to find out the total size of a stackwalk,
335 // and I'd like to have a reasonably big maximum without overflowing the stack by declaring
336 // the buffer locally and I also want to get a managed trace in a low memory environment
337 // (so no dynamic allocation if possible).
338 DEBUG_STACK_FRAME g_Frames[MAX_STACK_FRAMES];
339 AMD64_CONTEXT g_X64FrameContexts[MAX_STACK_FRAMES];
343 /**********************************************************************\
344 * Routine Description: *
346 * This function displays the stack trace. It looks at each DWORD *
347 * on stack. If the DWORD is a return address, the symbol name or
348 * managed function name is displayed. *
350 \**********************************************************************/
351 void DumpStackInternal(DumpStackFlag *pDSFlag)
353 ReloadSymbolWithLineInfo();
356 g_ExtRegisters->GetStackOffset (&StackOffset);
357 if (pDSFlag->top == 0) {
358 pDSFlag->top = TO_TADDR(StackOffset);
361 while (g_ExtData->ReadVirtual(TO_CDADDR(pDSFlag->top), &value, sizeof(size_t), NULL) != S_OK) {
364 pDSFlag->top = NextOSPageAddress(pDSFlag->top);
368 if (pDSFlag->end == 0) {
369 // Find the current stack range
373 g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
374 if (SafeReadMemory(TO_TADDR(dwTebAddr), &teb, sizeof(NT_TIB), NULL))
376 if (pDSFlag->top > TO_TADDR(teb.StackLimit)
377 && pDSFlag->top <= TO_TADDR(teb.StackBase))
379 if (pDSFlag->end == 0 || pDSFlag->end > TO_TADDR(teb.StackBase))
380 pDSFlag->end = TO_TADDR(teb.StackBase);
384 #endif // FEATURE_PAL
386 if (pDSFlag->end == 0)
388 ExtOut("TEB information is not available so a stack size of 0xFFFF is assumed\n");
389 pDSFlag->end = pDSFlag->top + 0xFFFF;
392 if (pDSFlag->end < pDSFlag->top)
394 ExtOut("Wrong option: stack selection wrong\n");
398 DumpStackWorker(*pDSFlag);
402 DECLARE_API(DumpStack)
404 INIT_API_NO_RET_ON_FAILURE();
406 MINIDUMP_NOT_SUPPORTED();
408 DumpStackFlag DSFlag;
409 DSFlag.fEEonly = FALSE;
414 CMDOption option[] = {
415 // name, vptr, type, hasValue
416 {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
417 {"-n", &DSFlag.fSuppressSrcInfo, COBOOL, FALSE},
418 {"/d", &dml, COBOOL, FALSE}
422 {&DSFlag.top, COHEX},
426 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
429 // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
431 if (!DSFlag.fSuppressSrcInfo && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
433 symlines &= SYMOPT_LOAD_LINES;
435 DSFlag.fSuppressSrcInfo = DSFlag.fSuppressSrcInfo || (symlines == 0);
437 EnableDMLHolder enabledml(dml);
440 g_ExtSystem->GetCurrentThreadSystemId(&id);
441 ExtOut("OS Thread Id: 0x%x ", id);
442 g_ExtSystem->GetCurrentThreadId(&id);
443 ExtOut("(%d)\n", id);
445 DumpStackInternal(&DSFlag);
450 /**********************************************************************\
451 * Routine Description: *
453 * This function displays the stack trace for threads that EE knows *
454 * from ThreadStore. *
456 \**********************************************************************/
457 DECLARE_API (EEStack)
461 MINIDUMP_NOT_SUPPORTED();
463 DumpStackFlag DSFlag;
464 DSFlag.fEEonly = FALSE;
468 BOOL bShortList = FALSE;
471 { // name, vptr, type, hasValue
472 {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
473 {"-short", &bShortList, COBOOL, FALSE},
474 {"/d", &dml, COBOOL, FALSE}
477 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
482 EnableDMLHolder enableDML(dml);
485 g_ExtSystem->GetCurrentThreadId(&Tid);
487 DacpThreadStoreData ThreadStore;
488 if ((Status = ThreadStore.Request(g_sos)) != S_OK)
490 ExtOut("Failed to request ThreadStore\n");
494 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
500 DacpThreadData Thread;
501 if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
503 ExtOut("Failed to request Thread at %p\n", CurThread);
508 if (g_ExtSystem->GetThreadIdBySystemId (Thread.osThreadId, &id) != S_OK)
510 CurThread = Thread.nextThread;
514 ExtOut("---------------------------------------------\n");
515 ExtOut("Thread %3d\n", id);
519 #define TS_Hijacked 0x00000080
525 else if ((Thread.lockCount > 0) || (Thread.state & TS_Hijacked))
527 // TODO: bring back || (int)vThread.m_pFrame != -1 {
533 g_ExtRegisters->GetInstructionOffset (&IP);
537 IP2MethodDesc (TO_TADDR(IP), methodDesc, jitType, gcinfoAddr);
546 g_ExtSystem->SetCurrentThreadId(id);
549 DumpStackInternal(&DSFlag);
552 CurThread = Thread.nextThread;
555 g_ExtSystem->SetCurrentThreadId(Tid);
559 HRESULT DumpStackObjectsRaw(size_t nArg, __in_z LPSTR exprBottom, __in_z LPSTR exprTop, BOOL bVerify)
562 size_t StackBottom = 0;
566 g_ExtRegisters->GetStackOffset(&StackOffset);
568 StackTop = TO_TADDR(StackOffset);
572 StackTop = GetExpression(exprTop);
575 ExtOut("wrong option: %s\n", exprTop);
581 StackBottom = GetExpression(exprBottom);
582 if (StackBottom == 0)
584 ExtOut("wrong option: %s\n", exprBottom);
593 HRESULT hr = g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
594 if (SUCCEEDED(hr) && SafeReadMemory (TO_TADDR(dwTebAddr), &teb, sizeof (NT_TIB), NULL))
596 if (StackTop > TO_TADDR(teb.StackLimit) && StackTop <= TO_TADDR(teb.StackBase))
598 if (StackBottom == 0 || StackBottom > TO_TADDR(teb.StackBase))
599 StackBottom = TO_TADDR(teb.StackBase);
604 if (StackBottom == 0)
605 StackBottom = StackTop + 0xFFFF;
607 if (StackBottom < StackTop)
609 ExtOut("Wrong option: stack selection wrong\n");
614 // We can use the gc snapshot to eliminate object addresses that are
615 // not on the gc heap.
616 if (!g_snapshot.Build())
618 ExtOut("Unable to determine bounds of gc heap\n");
621 #endif // !FEATURE_PAL
625 g_ExtSystem->GetCurrentThreadSystemId (&id);
626 ExtOut("OS Thread Id: 0x%x ", id);
627 g_ExtSystem->GetCurrentThreadId (&id);
628 ExtOut("(%d)\n", id);
630 DumpStackObjectsHelper(StackTop, StackBottom, bVerify);
634 /**********************************************************************\
635 * Routine Description: *
637 * This function is called to dump the address and name of all *
638 * Managed Objects on the stack. *
640 \**********************************************************************/
641 DECLARE_API(DumpStackObjects)
644 MINIDUMP_NOT_SUPPORTED();
645 StringHolder exprTop, exprBottom;
647 BOOL bVerify = FALSE;
650 { // name, vptr, type, hasValue
651 {"-verify", &bVerify, COBOOL, FALSE},
652 {"/d", &dml, COBOOL, FALSE}
656 {&exprTop.data, COSTRING},
657 {&exprBottom.data, COSTRING}
661 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
666 EnableDMLHolder enableDML(dml);
668 return DumpStackObjectsRaw(nArg, exprBottom.data, exprTop.data, bVerify);
671 /**********************************************************************\
672 * Routine Description: *
674 * This function is called to dump the contents of a MethodDesc *
675 * for a given address *
677 \**********************************************************************/
681 MINIDUMP_NOT_SUPPORTED();
683 DWORD_PTR dwStartAddr = NULL;
687 { // name, vptr, type, hasValue
688 {"/d", &dml, COBOOL, FALSE},
692 {&dwStartAddr, COHEX},
696 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
701 EnableDMLHolder dmlHolder(dml);
703 DumpMDInfo(dwStartAddr);
708 BOOL GatherDynamicInfo(TADDR DynamicMethodObj, DacpObjectData *codeArray,
709 DacpObjectData *tokenArray, TADDR *ptokenArrayAddr)
713 DacpObjectData objData; // temp object
715 if (codeArray == NULL || tokenArray == NULL)
718 if (objData.Request(g_sos, TO_CDADDR(DynamicMethodObj)) != S_OK)
721 iOffset = GetObjFieldOffset(DynamicMethodObj, objData.MethodTable, L"m_resolver");
726 if (FAILED(MOVE(resolverPtr, DynamicMethodObj + iOffset)))
729 if (objData.Request(g_sos, TO_CDADDR(resolverPtr)) != S_OK)
732 iOffset = GetObjFieldOffset(resolverPtr, objData.MethodTable, L"m_code");
737 if (FAILED(MOVE(codePtr, resolverPtr + iOffset)))
740 if (codeArray->Request(g_sos, TO_CDADDR(codePtr)) != S_OK)
743 if (codeArray->dwComponentSize != 1)
746 // We also need the resolution table
747 iOffset = GetObjFieldOffset (resolverPtr, objData.MethodTable, L"m_scope");
752 if (FAILED(MOVE(scopePtr, resolverPtr + iOffset)))
755 if (objData.Request(g_sos, TO_CDADDR(scopePtr)) != S_OK)
758 iOffset = GetObjFieldOffset (scopePtr, objData.MethodTable, L"m_tokens");
763 if (FAILED(MOVE(tokensPtr, scopePtr + iOffset)))
766 if (objData.Request(g_sos, TO_CDADDR(tokensPtr)) != S_OK)
769 iOffset = GetObjFieldOffset(tokensPtr, objData.MethodTable, L"_items");
774 MOVE (itemsPtr, tokensPtr + iOffset);
776 *ptokenArrayAddr = itemsPtr;
778 if (tokenArray->Request(g_sos, TO_CDADDR(itemsPtr)) != S_OK)
781 bRet = TRUE; // whew.
788 MINIDUMP_NOT_SUPPORTED();
789 DWORD_PTR dwStartAddr = NULL;
790 DWORD_PTR dwDynamicMethodObj = NULL;
792 BOOL fILPointerDirectlySpecified = FALSE;
795 { // name, vptr, type, hasValue
796 {"/d", &dml, COBOOL, FALSE},
797 {"/i", &fILPointerDirectlySpecified, COBOOL, FALSE},
801 {&dwStartAddr, COHEX},
805 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
810 EnableDMLHolder dmlHolder(dml);
811 if (dwStartAddr == NULL)
813 ExtOut("Must pass a valid expression\n");
817 if (fILPointerDirectlySpecified)
819 return DecodeILFromAddress(NULL, dwStartAddr);
822 if (!g_snapshot.Build())
824 ExtOut("Unable to build snapshot of the garbage collector state\n");
828 if (g_snapshot.GetHeap(dwStartAddr) != NULL)
830 dwDynamicMethodObj = dwStartAddr;
833 if (dwDynamicMethodObj == NULL)
835 // We have been given a MethodDesc
836 DacpMethodDescData MethodDescData;
837 if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
839 ExtOut("%p is not a MethodDesc\n", (ULONG64)dwStartAddr);
843 if (MethodDescData.bIsDynamic && MethodDescData.managedDynamicMethodObject)
845 dwDynamicMethodObj = TO_TADDR(MethodDescData.managedDynamicMethodObject);
846 if (dwDynamicMethodObj == NULL)
848 ExtOut("Unable to print IL for DynamicMethodDesc %p\n", (ULONG64) dwDynamicMethodObj);
854 // This is not a dynamic method, print the IL for it.
857 if (dmd.Request(g_sos, MethodDescData.ModulePtr) != S_OK)
859 ExtOut("Unable to get module\n");
863 ToRelease<IMetaDataImport> pImport = MDImportForModule(&dmd);
866 ExtOut("bad import\n");
872 if (pImport->GetRVA(MethodDescData.MDToken, &pRva, &dwFlags) != S_OK)
874 ExtOut("error in import\n");
878 CLRDATA_ADDRESS ilAddrClr;
879 if (g_sos->GetILForModule(MethodDescData.ModulePtr, pRva, &ilAddrClr) != S_OK)
881 ExtOut("FindIL failed\n");
885 TADDR ilAddr = TO_TADDR(ilAddrClr);
886 IfFailRet(DecodeILFromAddress(pImport, ilAddr));
890 if (dwDynamicMethodObj != NULL)
892 // We have a DynamicMethod managed object, let us visit the town and paint.
893 DacpObjectData codeArray;
894 DacpObjectData tokenArray;
895 DWORD_PTR tokenArrayAddr;
896 if (!GatherDynamicInfo (dwDynamicMethodObj, &codeArray, &tokenArray, &tokenArrayAddr))
898 DMLOut("Error gathering dynamic info from object at %s.\n", DMLObject(dwDynamicMethodObj));
902 // Read the memory into a local buffer
903 BYTE *pArray = new NOTHROW BYTE[(SIZE_T)codeArray.dwNumComponents];
906 ExtOut("Not enough memory to read IL\n");
910 Status = g_ExtData->ReadVirtual(UL64_TO_CDA(codeArray.ArrayDataPtr), pArray, (ULONG)codeArray.dwNumComponents, NULL);
913 ExtOut("Failed to read memory\n");
918 // Now we have a local copy of the IL, and a managed array for token resolution.
919 // Visit our IL parser with this info.
920 ExtOut("This is dynamic IL. Exception info is not reported at this time.\n");
921 ExtOut("If a token is unresolved, run \"!do <addr>\" on the addr given\n");
922 ExtOut("in parenthesis. You can also look at the token table yourself, by\n");
923 ExtOut("running \"!DumpArray %p\".\n\n", SOS_PTR(tokenArrayAddr));
924 DecodeDynamicIL(pArray, (ULONG)codeArray.dwNumComponents, tokenArray);
933 DWORD_PTR dwModuleAddr,
937 // Find the length of the signature and copy it into the debugger process.
941 const ULONG cbSigInc = 256;
942 ArrayHolder<COR_SIGNATURE> pSig = new NOTHROW COR_SIGNATURE[cbSigInc];
949 CQuickBytes sigString;
956 if (!SafeReadMemory(TO_TADDR(dwSigAddr + cbSig), pSig + cbSig, cbSigInc, &cbCopied))
961 GetSignatureStringResults result;
963 result = GetMethodSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
965 result = GetSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
967 if (GSS_ERROR == result)
970 if (GSS_SUCCESS == result)
973 // If we didn't get the full amount back, and we failed to parse the
974 // signature, it's not valid because of insufficient data
977 ExtOut("Invalid signature\n");
982 #pragma warning(push)
983 #pragma warning(disable:6280) // "Suppress PREFast warning about mismatch alloc/free"
986 PCOR_SIGNATURE pSigNew = (PCOR_SIGNATURE)realloc(pSig, cbSig+cbSigInc);
994 ExtOut("Out of memory\n");
1001 ExtOut("%S\n", (PCWSTR)sigString.Ptr());
1005 /**********************************************************************\
1006 * Routine Description: *
1008 * This function is called to dump a signature object. *
1010 \**********************************************************************/
1011 DECLARE_API(DumpSig)
1015 MINIDUMP_NOT_SUPPORTED();
1021 StringHolder sigExpr;
1022 StringHolder moduleExpr;
1025 {&sigExpr.data, COSTRING},
1026 {&moduleExpr.data, COSTRING}
1029 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
1035 ExtOut("!DumpSig <sigaddr> <moduleaddr>\n");
1039 DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);
1040 DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
1042 if (dwSigAddr == 0 || dwModuleAddr == 0)
1044 ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
1048 DumpSigWorker(dwSigAddr, dwModuleAddr, TRUE);
1054 /**********************************************************************\
1055 * Routine Description: *
1057 * This function is called to dump a portion of a signature object. *
1059 \**********************************************************************/
1060 DECLARE_API(DumpSigElem)
1064 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))
1086 ExtOut("!DumpSigElem <sigaddr> <moduleaddr>\n");
1090 DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);
1091 DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
1093 if (dwSigAddr == 0 || dwModuleAddr == 0)
1095 ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
1099 DumpSigWorker(dwSigAddr, dwModuleAddr, FALSE);
1104 /**********************************************************************\
1105 * Routine Description: *
1107 * This function is called to dump the contents of an EEClass from *
1110 \**********************************************************************/
1111 DECLARE_API(DumpClass)
1114 MINIDUMP_NOT_SUPPORTED();
1116 DWORD_PTR dwStartAddr = 0;
1119 CMDOption option[] =
1120 { // name, vptr, type, hasValue
1121 {"/d", &dml, COBOOL, FALSE},
1125 {&dwStartAddr, COHEX}
1129 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
1136 ExtOut("Missing EEClass address\n");
1140 EnableDMLHolder dmlHolder(dml);
1142 CLRDATA_ADDRESS methodTable;
1143 if ((Status=g_sos->GetMethodTableForEEClass(TO_CDADDR(dwStartAddr), &methodTable)) != S_OK)
1145 ExtOut("Invalid EEClass address\n");
1149 DacpMethodTableData mtdata;
1150 if ((Status=mtdata.Request(g_sos, TO_CDADDR(methodTable)))!=S_OK)
1152 ExtOut("EEClass has an invalid MethodTable address\n");
1156 sos::MethodTable mt = TO_TADDR(methodTable);
1157 ExtOut("Class Name: %S\n", mt.GetName());
1159 WCHAR fileName[MAX_PATH];
1160 FileNameForModule(TO_TADDR(mtdata.Module), fileName);
1161 ExtOut("mdToken: %p\n", mtdata.cl);
1162 ExtOut("File: %S\n", fileName);
1164 CLRDATA_ADDRESS ParentEEClass = NULL;
1165 if (mtdata.ParentMethodTable)
1167 DacpMethodTableData mtdataparent;
1168 if ((Status=mtdataparent.Request(g_sos, TO_CDADDR(mtdata.ParentMethodTable)))!=S_OK)
1170 ExtOut("EEClass has an invalid MethodTable address\n");
1173 ParentEEClass = mtdataparent.Class;
1176 DMLOut("Parent Class: %s\n", DMLClass(ParentEEClass));
1177 DMLOut("Module: %s\n", DMLModule(mtdata.Module));
1178 DMLOut("Method Table: %s\n", DMLMethodTable(methodTable));
1179 ExtOut("Vtable Slots: %x\n", mtdata.wNumVirtuals);
1180 ExtOut("Total Method Slots: %x\n", mtdata.wNumVtableSlots);
1181 ExtOut("Class Attributes: %x ", mtdata.dwAttrClass);
1183 if (IsTdInterface(mtdata.dwAttrClass))
1184 ExtOut("Interface, ");
1185 if (IsTdAbstract(mtdata.dwAttrClass))
1186 ExtOut("Abstract, ");
1187 if (IsTdImport(mtdata.dwAttrClass))
1188 ExtOut("ComImport, ");
1192 DacpMethodTableTransparencyData transparency;
1193 if (SUCCEEDED(transparency.Request(g_sos, methodTable)))
1195 ExtOut("Transparency: %s\n", GetTransparency(transparency));
1198 DacpMethodTableFieldData vMethodTableFields;
1199 if (SUCCEEDED(vMethodTableFields.Request(g_sos, methodTable)))
1201 ExtOut("NumInstanceFields: %x\n", vMethodTableFields.wNumInstanceFields);
1202 ExtOut("NumStaticFields: %x\n", vMethodTableFields.wNumStaticFields);
1204 if (vMethodTableFields.wNumThreadStaticFields != 0)
1206 ExtOut("NumThreadStaticFields: %x\n", vMethodTableFields.wNumThreadStaticFields);
1210 if (vMethodTableFields.wContextStaticsSize)
1212 ExtOut("ContextStaticOffset: %x\n", vMethodTableFields.wContextStaticOffset);
1213 ExtOut("ContextStaticsSize: %x\n", vMethodTableFields.wContextStaticsSize);
1217 if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1219 DisplayFields(methodTable, &mtdata, &vMethodTableFields, NULL, TRUE, FALSE);
1227 /**********************************************************************\
1228 * Routine Description: *
1230 * This function is called to dump the contents of a MethodTable *
1231 * from a given address *
1233 \**********************************************************************/
1236 DWORD_PTR dwStartAddr=0;
1237 DWORD_PTR dwOriginalAddr;
1241 MINIDUMP_NOT_SUPPORTED();
1243 BOOL bDumpMDTable = FALSE;
1246 CMDOption option[] =
1247 { // name, vptr, type, hasValue
1248 {"-MD", &bDumpMDTable, COBOOL, FALSE},
1249 {"/d", &dml, COBOOL, FALSE}
1253 {&dwStartAddr, COHEX}
1256 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
1261 EnableDMLHolder dmlHolder(dml);
1262 TableOutput table(2, 16, AlignLeft, false);
1266 Print("Missing MethodTable address\n");
1270 dwOriginalAddr = dwStartAddr;
1271 dwStartAddr = dwStartAddr&~3;
1273 if (!IsMethodTable(dwStartAddr))
1275 Print(dwOriginalAddr, " is not a MethodTable\n");
1279 DacpMethodTableData vMethTable;
1280 vMethTable.Request(g_sos, TO_CDADDR(dwStartAddr));
1282 if (vMethTable.bIsFree)
1284 Print("Free MethodTable\n");
1288 table.WriteRow("EEClass:", EEClassPtr(vMethTable.Class));
1290 table.WriteRow("Module:", ModulePtr(vMethTable.Module));
1292 sos::MethodTable mt = (TADDR)dwStartAddr;
1293 table.WriteRow("Name:", mt.GetName());
1295 WCHAR fileName[MAX_PATH];
1296 FileNameForModule(TO_TADDR(vMethTable.Module), fileName);
1297 table.WriteRow("mdToken:", Pointer(vMethTable.cl));
1298 table.WriteRow("File:", fileName[0] ? fileName : L"Unknown Module");
1300 table.WriteRow("BaseSize:", PrefixHex(vMethTable.BaseSize));
1301 table.WriteRow("ComponentSize:", PrefixHex(vMethTable.ComponentSize));
1302 table.WriteRow("Slots in VTable:", Decimal(vMethTable.wNumMethods));
1304 table.SetColWidth(0, 29);
1305 table.WriteRow("Number of IFaces in IFaceMap:", Decimal(vMethTable.wNumInterfaces));
1309 table.ReInit(4, POINTERSIZE_HEX, AlignRight);
1310 table.SetColAlignment(3, AlignLeft);
1311 table.SetColWidth(2, 6);
1313 Print("--------------------------------------\n");
1314 Print("MethodDesc Table\n");
1316 table.WriteRow("Entry", "MethodDesc", "JIT", "Name");
1318 for (DWORD n = 0; n < vMethTable.wNumMethods; n++)
1321 DWORD_PTR methodDesc=0;
1322 DWORD_PTR gcinfoAddr;
1324 CLRDATA_ADDRESS entry;
1325 if (g_sos->GetMethodTableSlot(dwStartAddr, n, &entry) != S_OK)
1327 PrintLn("<error getting slot ", Decimal(n), ">");
1331 IP2MethodDesc((DWORD_PTR)entry, methodDesc, jitType, gcinfoAddr);
1332 table.WriteColumn(0, entry);
1333 table.WriteColumn(1, MethodDescPtr(methodDesc));
1335 if (jitType == TYPE_UNKNOWN && methodDesc != NULL)
1337 // We can get a more accurate jitType from NativeCodeAddr of the methoddesc,
1338 // because the methodtable entry hasn't always been patched.
1339 DacpMethodDescData tmpMethodDescData;
1340 if (tmpMethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
1342 DacpCodeHeaderData codeHeaderData;
1343 if (codeHeaderData.Request(g_sos,tmpMethodDescData.NativeCodeAddr) == S_OK)
1345 jitType = (JITTypes) codeHeaderData.JITType;
1350 const char *pszJitType = "NONE";
1351 if (jitType == TYPE_JIT)
1353 else if (jitType == TYPE_PJIT)
1354 pszJitType = "PreJIT";
1357 DacpMethodDescData MethodDescData;
1358 if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
1361 if ((TO_TADDR(MethodDescData.NativeCodeAddr) >= TO_TADDR(moduleInfo[MSCORWKS].baseAddr)) &&
1362 ((TO_TADDR(MethodDescData.NativeCodeAddr) < TO_TADDR(moduleInfo[MSCORWKS].baseAddr + moduleInfo[MSCORWKS].size))))
1364 pszJitType = "FCALL";
1369 table.WriteColumn(2, pszJitType);
1371 NameForMD_s(methodDesc,g_mdName,mdNameLen);
1372 table.WriteColumn(3, g_mdName);
1378 extern size_t Align (size_t nbytes);
1380 HRESULT PrintVC(TADDR taMT, TADDR taObject, BOOL bPrintFields = TRUE)
1383 DacpMethodTableData mtabledata;
1384 if ((Status = mtabledata.Request(g_sos, TO_CDADDR(taMT)))!=S_OK)
1387 size_t size = mtabledata.BaseSize;
1388 if ((Status=g_sos->GetMethodTableName(TO_CDADDR(taMT), mdNameLen, g_mdName, NULL))!=S_OK)
1391 ExtOut("Name: %S\n", g_mdName);
1392 DMLOut("MethodTable: %s\n", DMLMethodTable(taMT));
1393 DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
1394 ExtOut("Size: %d(0x%x) bytes\n", size, size);
1396 FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
1397 ExtOut("File: %S\n", g_mdName[0] ? g_mdName : L"Unknown Module");
1401 DacpMethodTableFieldData vMethodTableFields;
1402 if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(taMT)))!=S_OK)
1405 ExtOut("Fields:\n");
1407 if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1408 DisplayFields(TO_CDADDR(taMT), &mtabledata, &vMethodTableFields, taObject, TRUE, TRUE);
1414 void PrintRuntimeTypeInfo(TADDR p_rtObject, const DacpObjectData & rtObjectData)
1416 // Get the method table
1417 int iOffset = GetObjFieldOffset(p_rtObject, rtObjectData.MethodTable, L"m_handle");
1421 if (SUCCEEDED(GetMTOfObject(p_rtObject + iOffset, &mtPtr)))
1423 sos::MethodTable mt = mtPtr;
1424 ExtOut("Type Name: %S\n", mt.GetName());
1425 DMLOut("Type MT: %s\n", DMLMethodTable(mtPtr));
1430 HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE)
1432 if (!sos::IsObject(taObj, true))
1434 ExtOut("<Note: this object has an invalid CLASS field>\n");
1437 DacpObjectData objData;
1439 if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
1441 ExtOut("Invalid object\n");
1445 if (objData.ObjectType==OBJ_FREE)
1447 ExtOut("Free Object\n");
1448 DWORD_PTR size = (DWORD_PTR)objData.Size;
1449 ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1453 sos::Object obj = taObj;
1454 ExtOut("Name: %S\n", obj.GetTypeName());
1455 DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
1458 DacpMethodTableData mtabledata;
1459 if ((Status=mtabledata.Request(g_sos,objData.MethodTable)) == S_OK)
1461 DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
1465 ExtOut("Invalid EEClass address\n");
1469 if (objData.RCW != NULL)
1471 DMLOut("RCW: %s\n", DMLRCWrapper(objData.RCW));
1473 if (objData.CCW != NULL)
1475 DMLOut("CCW: %s\n", DMLCCWrapper(objData.CCW));
1478 DWORD_PTR size = (DWORD_PTR)objData.Size;
1479 ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1481 if (wcscmp(obj.GetTypeName(), L"System.RuntimeType") == 0)
1483 PrintRuntimeTypeInfo(taObj, objData);
1486 if (wcscmp(obj.GetTypeName(), L"System.RuntimeType+RuntimeTypeCache") == 0)
1488 // Get the method table
1489 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, L"m_runtimeType");
1493 if (MOVE(rtPtr, taObj + iOffset) == S_OK)
1495 DacpObjectData rtObjectData;
1496 if ((Status=rtObjectData.Request(g_sos, TO_CDADDR(rtPtr))) != S_OK)
1498 ExtOut("Error when reading RuntimeType field\n");
1502 PrintRuntimeTypeInfo(rtPtr, rtObjectData);
1507 if (objData.ObjectType==OBJ_ARRAY)
1509 ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s",
1510 objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
1512 IfDMLOut(" (<exec cmd=\"!DumpArray /d %p\">Print Array</exec>)", SOS_PTR(taObj));
1515 if (objData.ElementType == ELEMENT_TYPE_I1 ||
1516 objData.ElementType == ELEMENT_TYPE_U1 ||
1517 objData.ElementType == ELEMENT_TYPE_CHAR)
1519 bool wide = objData.ElementType == ELEMENT_TYPE_CHAR;
1521 // Get the size of the character array, but clamp it to a reasonable length.
1522 TADDR pos = taObj + (2 * sizeof(DWORD_PTR));
1524 moveN(num, taObj + sizeof(DWORD_PTR));
1527 DMLOut("<exec cmd=\"%s %x L%x\">Content</exec>: ", (wide) ? "dw" : "db", pos, num);
1529 ExtOut("Content: ");
1530 CharArrayContent(pos, (ULONG)(num <= 128 ? num : 128), wide);
1536 FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
1537 ExtOut("File: %S\n", g_mdName[0] ? g_mdName : L"Unknown Module");
1540 if (objData.ObjectType == OBJ_STRING)
1543 StringObjectContent(taObj);
1546 else if (objData.ObjectType == OBJ_OBJECT)
1553 DacpMethodTableFieldData vMethodTableFields;
1554 if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(objData.MethodTable)))!=S_OK)
1557 ExtOut("Fields:\n");
1558 if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1560 DisplayFields(objData.MethodTable, &mtabledata, &vMethodTableFields, taObj, TRUE, FALSE);
1568 sos::ThinLockInfo lockInfo;
1569 if (obj.GetThinLock(lockInfo))
1571 ExtOut("ThinLock owner %x (%p), Recursive %x\n", lockInfo.ThreadId,
1572 SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
1578 BOOL IndicesInRange (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
1581 if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
1583 ExtOut("<integer underflow>\n");
1589 if (indices[i] >= bounds[i] + lowerBounds[i])
1596 indices[i] = lowerBounds[i];
1604 void ExtOutIndices (DWORD * indices, DWORD rank)
1606 for (DWORD i = 0; i < rank; i++)
1608 ExtOut("[%d]", indices[i]);
1612 size_t OffsetFromIndices (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
1614 _ASSERTE(rank >= 0);
1615 size_t multiplier = 1;
1618 if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
1620 ExtOut("<integer underflow>\n");
1626 DWORD curIndex = indices[i] - lowerBounds[i];
1627 offset += curIndex * multiplier;
1628 multiplier *= bounds[i];
1633 HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint);
1635 HRESULT PrintPermissionSet (TADDR p_PermSet)
1637 HRESULT Status = S_OK;
1639 DacpObjectData PermSetData;
1640 if ((Status=PermSetData.Request(g_sos, TO_CDADDR(p_PermSet))) != S_OK)
1642 ExtOut("Invalid object\n");
1647 sos::MethodTable mt = TO_TADDR(PermSetData.MethodTable);
1648 if (wcscmp (L"System.Security.PermissionSet", mt.GetName()) != 0 && wcscmp(L"System.Security.NamedPermissionSet", mt.GetName()) != 0)
1650 ExtOut("Invalid PermissionSet object\n");
1654 ExtOut("PermissionSet object: %p\n", (ULONG64)p_PermSet);
1658 // Walk the fields, printing some fields in a special way.
1660 int iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, L"m_Unrestricted");
1665 MOVE(unrestricted, p_PermSet + iOffset);
1667 ExtOut("Unrestricted: TRUE\n");
1669 ExtOut("Unrestricted: FALSE\n");
1672 iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, L"m_permSet");
1676 MOVE(tbSetPtr, p_PermSet + iOffset);
1677 if (tbSetPtr != NULL)
1679 DacpObjectData tbSetData;
1680 if ((Status=tbSetData.Request(g_sos, TO_CDADDR(tbSetPtr))) != S_OK)
1682 ExtOut("Invalid object\n");
1686 iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, L"m_Set");
1689 DWORD_PTR PermsArrayPtr;
1690 MOVE(PermsArrayPtr, tbSetPtr + iOffset);
1691 if (PermsArrayPtr != NULL)
1693 // Print all the permissions in the array
1694 DacpObjectData objData;
1695 if ((Status=objData.Request(g_sos, TO_CDADDR(PermsArrayPtr))) != S_OK)
1697 ExtOut("Invalid object\n");
1700 DumpArrayFlags flags;
1701 flags.bDetail = TRUE;
1702 return PrintArray(objData, flags, TRUE);
1706 iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, L"m_Obj");
1709 DWORD_PTR PermObjPtr;
1710 MOVE(PermObjPtr, tbSetPtr + iOffset);
1711 if (PermObjPtr != NULL)
1713 // Print the permission object
1714 return PrintObj(PermObjPtr);
1726 /**********************************************************************\
1727 * Routine Description: *
1729 * This function is called to dump the contents of an object from a *
1732 \**********************************************************************/
1733 DECLARE_API(DumpArray)
1737 DumpArrayFlags flags;
1739 MINIDUMP_NOT_SUPPORTED();
1743 CMDOption option[] =
1744 { // name, vptr, type, hasValue
1745 {"-start", &flags.startIndex, COSIZE_T, TRUE},
1746 {"-length", &flags.Length, COSIZE_T, TRUE},
1747 {"-details", &flags.bDetail, COBOOL, FALSE},
1748 {"-nofields", &flags.bNoFieldsForElement, COBOOL, FALSE},
1749 {"/d", &dml, COBOOL, FALSE},
1753 {&flags.strObject, COSTRING}
1756 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
1761 EnableDMLHolder dmlHolder(dml);
1762 DWORD_PTR p_Object = GetExpression (flags.strObject);
1765 ExtOut("Invalid parameter %s\n", flags.strObject);
1769 if (!sos::IsObject(p_Object, true))
1771 ExtOut("<Note: this object has an invalid CLASS field>\n");
1774 DacpObjectData objData;
1775 if ((Status=objData.Request(g_sos, TO_CDADDR(p_Object))) != S_OK)
1777 ExtOut("Invalid object\n");
1781 if (objData.ObjectType != OBJ_ARRAY)
1783 ExtOut("Not an array, please use !DumpObj instead\n");
1786 return PrintArray(objData, flags, FALSE);
1790 HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint)
1792 HRESULT Status = S_OK;
1794 if (objData.dwRank != 1 && (flags.Length != (DWORD_PTR)-1 ||flags.startIndex != 0))
1796 ExtOut("For multi-dimension array, length and start index are supported\n");
1800 if (flags.startIndex > objData.dwNumComponents)
1802 ExtOut("Start index out of range\n");
1806 if (!flags.bDetail && flags.bNoFieldsForElement)
1808 ExtOut("-nofields has no effect unless -details is specified\n");
1812 if (!isPermSetPrint)
1814 // TODO: don't depend on this being a MethodTable
1815 NameForMT_s(TO_TADDR(objData.ElementTypeHandle), g_mdName, mdNameLen);
1817 ExtOut("Name: %S[", g_mdName);
1818 for (i = 1; i < objData.dwRank; i++)
1822 DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
1825 DacpMethodTableData mtdata;
1826 if (SUCCEEDED(mtdata.Request(g_sos, objData.MethodTable)))
1828 DMLOut("EEClass: %s\n", DMLClass(mtdata.Class));
1832 DWORD_PTR size = (DWORD_PTR)objData.Size;
1833 ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1835 ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s\n",
1836 objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
1837 DMLOut("Element Methodtable: %s\n", DMLMethodTable(objData.ElementTypeHandle));
1840 BOOL isElementValueType = IsElementValueType(objData.ElementType);
1842 DWORD dwRankAllocSize;
1843 if (!ClrSafeInt<DWORD>::multiply(sizeof(DWORD), objData.dwRank, dwRankAllocSize))
1845 ExtOut("Integer overflow on array rank\n");
1849 DWORD *lowerBounds = (DWORD *)alloca(dwRankAllocSize);
1850 if (!SafeReadMemory(objData.ArrayLowerBoundsPtr, lowerBounds, dwRankAllocSize, NULL))
1852 ExtOut("Failed to read lower bounds info from the array\n");
1856 DWORD *bounds = (DWORD *)alloca(dwRankAllocSize);
1857 if (!SafeReadMemory (objData.ArrayBoundsPtr, bounds, dwRankAllocSize, NULL))
1859 ExtOut("Failed to read bounds info from the array\n");
1863 //length is only supported for single-dimension array
1864 if (objData.dwRank == 1 && flags.Length != (DWORD_PTR)-1)
1866 bounds[0] = min(bounds[0], (DWORD)(flags.Length + flags.startIndex) - lowerBounds[0]);
1869 DWORD *indices = (DWORD *)alloca(dwRankAllocSize);
1870 for (i = 0; i < objData.dwRank; i++)
1872 indices[i] = lowerBounds[i];
1875 //start index is only supported for single-dimension array
1876 if (objData.dwRank == 1)
1878 indices[0] = (DWORD)flags.startIndex;
1881 //Offset should be calculated by OffsetFromIndices. However because of the way
1882 //how we grow indices, incrementing offset by one happens to match indices in every iteration
1883 for (size_t offset = OffsetFromIndices (indices, lowerBounds, bounds, objData.dwRank);
1884 IndicesInRange (indices, lowerBounds, bounds, objData.dwRank);
1885 indices[objData.dwRank - 1]++, offset++)
1889 ExtOut("interrupted by user\n");
1893 TADDR elementAddress = TO_TADDR(objData.ArrayDataPtr + offset * objData.dwComponentSize);
1894 TADDR p_Element = NULL;
1895 if (isElementValueType)
1897 p_Element = elementAddress;
1899 else if (!SafeReadMemory (elementAddress, &p_Element, sizeof (p_Element), NULL))
1901 ExtOut("Failed to read element at ");
1902 ExtOutIndices(indices, objData.dwRank);
1909 ExtOutIndices(indices, objData.dwRank);
1911 if (isElementValueType)
1913 DMLOut( " %s\n", DMLValueClass(objData.MethodTable, p_Element));
1917 DMLOut(" %s\n", DMLObject(p_Element));
1920 else if (!isPermSetPrint)
1922 ExtOutIndices(indices, objData.dwRank);
1929 if (isElementValueType)
1931 PrintVC(TO_TADDR(objData.ElementTypeHandle), elementAddress, !flags.bNoFieldsForElement);
1933 else if (p_Element != NULL)
1935 PrintObj(p_Element, !flags.bNoFieldsForElement);
1944 /**********************************************************************\
1945 * Routine Description: *
1947 * This function is called to dump the contents of an object from a *
1950 \**********************************************************************/
1951 DECLARE_API(DumpObj)
1955 MINIDUMP_NOT_SUPPORTED();
1958 BOOL bNoFields = FALSE;
1960 StringHolder str_Object;
1961 CMDOption option[] =
1962 { // name, vptr, type, hasValue
1963 {"-nofields", &bNoFields, COBOOL, FALSE},
1964 {"-refs", &bRefs, COBOOL, FALSE},
1965 {"/d", &dml, COBOOL, FALSE},
1969 {&str_Object.data, COSTRING}
1972 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
1977 DWORD_PTR p_Object = GetExpression(str_Object.data);
1978 EnableDMLHolder dmlHolder(dml);
1981 ExtOut("Invalid parameter %s\n", args);
1985 Status = PrintObj(p_Object, !bNoFields);
1987 if (SUCCEEDED(Status) && bRefs)
1989 ExtOut("GC Refs:\n");
1990 TableOutput out(2, POINTERSIZE_HEX, AlignRight, 4);
1991 out.WriteRow("offset", "object");
1992 for (sos::RefIterator itr(TO_TADDR(p_Object)); itr; ++itr)
1993 out.WriteRow(Hex(itr.GetOffset()), ObjectPtr(*itr));
1999 CLRDATA_ADDRESS isExceptionObj(CLRDATA_ADDRESS mtObj)
2001 // We want to follow back until we get the mt for System.Exception
2002 DacpMethodTableData dmtd;
2003 CLRDATA_ADDRESS walkMT = mtObj;
2004 while(walkMT != NULL)
2006 if (dmtd.Request(g_sos, walkMT) != S_OK)
2010 if (walkMT == g_special_usefulGlobals.ExceptionMethodTable)
2014 walkMT = dmtd.ParentMethodTable;
2019 CLRDATA_ADDRESS isSecurityExceptionObj(CLRDATA_ADDRESS mtObj)
2021 // We want to follow back until we get the mt for System.Exception
2022 DacpMethodTableData dmtd;
2023 CLRDATA_ADDRESS walkMT = mtObj;
2024 while(walkMT != NULL)
2026 if (dmtd.Request(g_sos, walkMT) != S_OK)
2030 NameForMT_s(TO_TADDR(walkMT), g_mdName, mdNameLen);
2031 if (wcscmp(L"System.Security.SecurityException", g_mdName) == 0)
2035 walkMT = dmtd.ParentMethodTable;
2040 // Fill the passed in buffer with a text header for generated exception information.
2041 // Returns the number of characters in the wszBuffer array on exit.
2042 // If NULL is passed for wszBuffer, just returns the number of characters needed.
2043 size_t AddExceptionHeader (__out_ecount (bufferLength) __out_opt WCHAR *wszBuffer, size_t bufferLength)
2045 #ifdef _TARGET_WIN64_
2046 const WCHAR *wszHeader = L" SP IP Function\n";
2048 const WCHAR *wszHeader = L" SP IP Function\n";
2049 #endif // _TARGET_WIN64_
2052 swprintf_s(wszBuffer, bufferLength, wszHeader);
2054 return wcslen(wszHeader);
2057 struct StackTraceElement
2061 DWORD_PTR pFunc; // MethodDesc
2062 #if defined(FEATURE_EXCEPTIONDISPATCHINFO)
2063 // TRUE if this element represents the last frame of the foreign
2064 // exception stack trace.
2065 BOOL fIsLastFrameFromForeignStackTrace;
2066 #endif // defined(FEATURE_EXCEPTIONDISPATCHINFO)
2070 #include "sos_stacktrace.h"
2079 cs.String()[0] = L'\0';
2082 BOOL Append(__in_z LPWSTR pszStr)
2084 size_t iInputLen = wcslen (pszStr);
2085 size_t iCurLen = wcslen (cs.String());
2086 if ((iCurLen + iInputLen + 1) > cs.Size())
2088 if (cs.ReSize(iCurLen + iInputLen + 1) != S_OK)
2094 wcsncat_s (cs.String(), cs.Size(), pszStr, _TRUNCATE);
2100 return wcslen(cs.String());
2109 static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp, ULONG64 IPAddr, StringOutput& so);
2111 // Using heuristics to determine if an exception object represented an async (hardware) or a
2112 // managed exception
2113 // We need to use these heuristics when the System.Exception object is not the active exception
2114 // on some thread, but it's something found somewhere on the managed heap.
2116 // uses the MapWin32FaultToCOMPlusException to figure out how we map async exceptions
2117 // to managed exceptions and their HRESULTs
2118 static const HRESULT AsyncHResultValues[] =
2120 COR_E_ARITHMETIC, // kArithmeticException
2121 COR_E_OVERFLOW, // kOverflowException
2122 COR_E_DIVIDEBYZERO, // kDivideByZeroException
2123 COR_E_FORMAT, // kFormatException
2124 COR_E_NULLREFERENCE, // kNullReferenceException
2125 E_POINTER, // kAccessViolationException
2126 // the EE is raising the next exceptions more often than the OS will raise an async
2127 // exception for these conditions, so in general treat these as Synchronous
2128 // COR_E_INDEXOUTOFRANGE, // kIndexOutOfRangeException
2129 // COR_E_OUTOFMEMORY, // kOutOfMemoryException
2130 // COR_E_STACKOVERFLOW, // kStackOverflowException
2131 COR_E_DATAMISALIGNED, // kDataMisalignedException
2134 BOOL IsAsyncException(TADDR taObj, TADDR mtObj)
2136 // by default we'll treat exceptions as synchronous
2137 UINT32 xcode = EXCEPTION_COMPLUS;
2138 int iOffset = GetObjFieldOffset (taObj, mtObj, L"_xcode");
2141 HRESULT hr = MOVE(xcode, taObj + iOffset);
2144 xcode = EXCEPTION_COMPLUS;
2149 if (xcode == EXCEPTION_COMPLUS)
2152 iOffset = GetObjFieldOffset (taObj, mtObj, L"_HResult");
2155 HRESULT hr = MOVE(ehr, taObj + iOffset);
2158 xcode = EXCEPTION_COMPLUS;
2161 for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
2163 if (ehr == AsyncHResultValues[idx])
2172 return xcode != EXCEPTION_COMPLUS;
2175 // Overload that mirrors the code above when the ExceptionObjectData was already retrieved from LS
2176 BOOL IsAsyncException(const DacpExceptionObjectData & excData)
2178 if (excData.XCode != EXCEPTION_COMPLUS)
2181 HRESULT ehr = excData.HResult;
2182 for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
2184 if (ehr == AsyncHResultValues[idx])
2194 #define SOS_STACKTRACE_SHOWEXPLICITFRAMES 0x00000002
2195 size_t FormatGeneratedException (DWORD_PTR dataPtr,
2197 __out_ecount (bufferLength) __out_opt WCHAR *wszBuffer,
2198 size_t bufferLength,
2200 BOOL bNestedCase=FALSE,
2201 BOOL bLineNumbers=FALSE)
2203 UINT count = bytes / sizeof(StackTraceElement);
2206 if (wszBuffer && bufferLength>0)
2208 wszBuffer[0] = L'\0';
2211 // Buffer is calculated for sprintf below (" %p %p %S\n");
2212 WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2 + MAX_PATH + 8];
2221 // If we are computing the call stack for a nested exception, we
2222 // don't want to print the last frame, because the outer exception
2223 // will have that frame.
2227 for (UINT i = 0; i < count; i++)
2229 StackTraceElement ste;
2230 MOVE (ste, dataPtr + i*sizeof(StackTraceElement));
2232 // ste.ip must be adjusted because of an ancient workaround in the exception
2233 // infrastructure. The workaround is that the exception needs to have
2234 // an ip address that will map to the line number where the exception was thrown.
2235 // (It doesn't matter that it's not a valid instruction). (see /vm/excep.cpp)
2237 // This "counterhack" is not 100% accurate
2238 // The biggest issue is that !PrintException must work with exception objects
2239 // that may not be currently active; as a consequence we cannot rely on the
2240 // state of some "current thread" to infer whether the IP values stored in
2241 // the exception object have been adjusted or not. If we could, we may examine
2242 // the topmost "Frame" and make the decision based on whether it's a
2243 // FaultingExceptionFrame or not.
2244 // 1. On IA64 the IP values are never adjusted by the EE so there's nothing
2247 // (a) if the exception was an async (hardware) exception add 1 to all
2248 // IP values in the exception object
2249 // (b) if the exception was a managed exception (either raised by the
2250 // EE or thrown by managed code) do not adjust any IP values
2252 // (a) if the exception was an async (hardware) exception add 1 to
2253 // all but the topmost IP value in the exception object
2254 // (b) if the exception was a managed exception (either raised by
2255 // the EE or thrown by managed code) add 1 to all IP values in
2256 // the exception object
2257 #if defined(_TARGET_AMD64_)
2262 #elif defined(_TARGET_X86_)
2263 if (IsDbgTargetX86() && (!bAsync || i != 0))
2267 #endif // defined(_TARGET_AMD64_) || defined(_TARGET__X86_)
2270 HRESULT Status = DumpMDInfoBuffer(ste.pFunc, SOS_STACKTRACE_SHOWADDRESSES|SOS_STACKTRACE_SHOWEXPLICITFRAMES, ste.sp, ste.ip, so);
2272 // If DumpMDInfoBuffer failed (due to out of memory or missing metadata),
2273 // or did not update so (when ste is an explicit frames), do not update wszBuffer
2276 char filename[MAX_PATH+1] = "";
2279 && FAILED(GetLineByOffset(TO_CDADDR(ste.ip),
2282 _countof(filename))))
2284 bLineNumbers = FALSE;
2289 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), L" %s\n", so.String());
2293 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), L" %s [%S @ %d]\n", so.String(), filename, linenum);
2296 Length += wcslen(wszLineBuffer);
2300 wcsncat_s(wszBuffer, bufferLength, wszLineBuffer, _TRUNCATE);
2308 // ExtOut has an internal limit for the string size
2309 void SosExtOutLargeString(__inout_z __inout_ecount_opt(len) WCHAR * pwszLargeString, size_t len)
2311 const size_t chunkLen = 2048;
2313 WCHAR *pwsz = pwszLargeString; // beginning of a chunk
2314 size_t count = len/chunkLen;
2315 // write full chunks
2316 for (size_t idx = 0; idx < count; ++idx)
2318 WCHAR *pch = pwsz + chunkLen; // after the chunk
2319 // zero terminate the chunk
2325 // restore whacked char
2328 // advance to next chunk
2336 HRESULT FormatException(TADDR taObj, BOOL bLineNumbers = FALSE)
2338 HRESULT Status = S_OK;
2340 DacpObjectData objData;
2341 if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
2343 ExtOut("Invalid object\n");
2347 // Make sure it is an exception object, and get the MT of Exception
2348 CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
2349 if (exceptionMT == NULL)
2351 ExtOut("Not a valid exception object\n");
2355 DMLOut("Exception object: %s\n", DMLObject(taObj));
2357 if (NameForMT_s(TO_TADDR(objData.MethodTable), g_mdName, mdNameLen))
2359 ExtOut("Exception type: %S\n", g_mdName);
2363 ExtOut("Exception type: <Unknown>\n");
2368 // First try to get exception object data using ISOSDacInterface2
2369 DacpExceptionObjectData excData;
2370 BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, TO_CDADDR(taObj)));
2372 // Walk the fields, printing some fields in a special way.
2373 // HR, InnerException, Message, StackTrace, StackTraceString
2379 taMsg = TO_TADDR(excData.Message);
2383 int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, L"_message");
2386 MOVE (taMsg, taObj + iOffset);
2390 ExtOut("Message: ");
2393 StringObjectContent(taMsg);
2404 taInnerExc = TO_TADDR(excData.InnerException);
2408 int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, L"_innerException");
2411 MOVE (taInnerExc, taObj + iOffset);
2415 ExtOut("InnerException: ");
2419 if (SUCCEEDED(GetMTOfObject(taInnerExc, &taMT)))
2421 NameForMT_s(taMT, g_mdName, mdNameLen);
2422 ExtOut("%S, ", g_mdName);
2424 DMLOut("Use <exec cmd=\"!PrintException /d %p\">!PrintException %p</exec> to see more.\n", taInnerExc, taInnerExc);
2426 ExtOut("Use !PrintException %p to see more.\n", SOS_PTR(taInnerExc));
2430 ExtOut("<invalid MethodTable of inner exception>");
2439 BOOL bAsync = bGotExcData ? IsAsyncException(excData)
2440 : IsAsyncException(taObj, TO_TADDR(objData.MethodTable));
2446 taStackTrace = TO_TADDR(excData.StackTrace);
2450 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, L"_stackTrace");
2453 MOVE(taStackTrace, taObj + iOffset);
2457 ExtOut("StackTrace (generated):\n");
2461 HRESULT hr = MOVE(arrayLen, taStackTrace + sizeof(DWORD_PTR));
2463 if (arrayLen != 0 && hr == S_OK)
2465 #ifdef _TARGET_WIN64_
2466 DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
2468 DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD);
2469 #endif // _TARGET_WIN64_
2470 size_t stackTraceSize = 0;
2471 MOVE (stackTraceSize, dataPtr);
2473 DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
2474 dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
2476 if (stackTraceSize == 0)
2478 ExtOut("Unable to decipher generated stack trace\n");
2482 size_t iHeaderLength = AddExceptionHeader (NULL, 0);
2483 size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, FALSE, bLineNumbers);
2484 WCHAR *pwszBuffer = new NOTHROW WCHAR[iHeaderLength + iLength + 1];
2487 AddExceptionHeader(pwszBuffer, iHeaderLength + 1);
2488 FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer + iHeaderLength, iLength + 1, bAsync, FALSE, bLineNumbers);
2489 SosExtOutLargeString(pwszBuffer, iHeaderLength + iLength + 1);
2490 delete[] pwszBuffer;
2497 ExtOut("<Not Available>\n");
2507 TADDR taStackString;
2510 taStackString = TO_TADDR(excData.StackTraceString);
2514 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, L"_stackTraceString");
2515 MOVE (taStackString, taObj + iOffset);
2518 ExtOut("StackTraceString: ");
2521 StringObjectContent(taStackString);
2522 ExtOut("\n\n"); // extra newline looks better
2534 hResult = excData.HResult;
2538 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, L"_HResult");
2539 MOVE (hResult, taObj + iOffset);
2542 ExtOut("HResult: %lx\n", hResult);
2545 if (isSecurityExceptionObj(objData.MethodTable) != NULL)
2547 // We have a SecurityException Object: print out the debugString if present
2548 int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, L"m_debugString");
2551 TADDR taDebugString;
2552 MOVE (taDebugString, taObj + iOffset);
2556 ExtOut("SecurityException Message: ");
2557 StringObjectContent(taDebugString);
2558 ExtOut("\n\n"); // extra newline looks better
2566 DECLARE_API(PrintException)
2571 BOOL bShowNested = FALSE;
2572 BOOL bLineNumbers = FALSE;
2574 StringHolder strObject;
2575 CMDOption option[] =
2576 { // name, vptr, type, hasValue
2577 {"-nested", &bShowNested, COBOOL, FALSE},
2578 {"-lines", &bLineNumbers, COBOOL, FALSE},
2579 {"-l", &bLineNumbers, COBOOL, FALSE},
2580 {"-ccw", &bCCW, COBOOL, FALSE},
2581 {"/d", &dml, COBOOL, FALSE}
2585 {&strObject, COSTRING}
2588 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2595 if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
2597 symlines &= SYMOPT_LOAD_LINES;
2601 ExtOut("In order for the option -lines to enable display of source information\n"
2602 "the debugger must be configured to load the line number information from\n"
2603 "the symbol files. Use the \".lines; .reload\" command to achieve this.\n");
2605 bLineNumbers = FALSE;
2609 EnableDMLHolder dmlHolder(dml);
2610 DWORD_PTR p_Object = NULL;
2615 ExtOut("No CCW pointer specified\n");
2619 // Look at the last exception object on this thread
2621 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
2622 DacpThreadData Thread;
2624 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
2626 ExtOut("The current thread is unmanaged\n");
2630 DWORD_PTR dwAddr = NULL;
2631 if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
2633 sizeof(dwAddr), NULL)) || (dwAddr==NULL))
2635 ExtOut("There is no current managed exception on this thread\n");
2644 p_Object = GetExpression(strObject.data);
2649 ExtOut("Invalid CCW pointer %s\n", args);
2653 ExtOut("Invalid exception object %s\n", args);
2660 // check if the address is a CCW pointer and then
2661 // get the exception object from it
2662 DacpCCWData ccwData;
2663 if (ccwData.Request(g_sos, p_Object) == S_OK)
2665 p_Object = TO_TADDR(ccwData.managedObject);
2672 FormatException(p_Object, bLineNumbers);
2675 // Are there nested exceptions?
2676 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
2677 DacpThreadData Thread;
2679 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
2681 ExtOut("The current thread is unmanaged\n");
2685 if (Thread.firstNestedException)
2689 ExtOut("There are nested exceptions on this thread. Run with -nested for details\n");
2693 CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
2696 CLRDATA_ADDRESS obj = 0, next = 0;
2697 Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
2701 ExtOut("Error retrieving nested exception info %p\n", (ULONG64)currentNested);
2707 ExtOut("<aborted>\n");
2711 ExtOut("\nNested exception -------------------------------------------------------------\n");
2712 Status = FormatException((DWORD_PTR) obj, bLineNumbers);
2718 currentNested = next;
2720 while(currentNested != NULL);
2726 /**********************************************************************\
2727 * Routine Description: *
2729 * This function is called to dump the contents of an object from a *
2732 \**********************************************************************/
2736 MINIDUMP_NOT_SUPPORTED();
2738 DWORD_PTR p_MT = NULL;
2739 DWORD_PTR p_Object = NULL;
2742 CMDOption option[] =
2743 { // name, vptr, type, hasValue
2744 {"/d", &dml, COBOOL, FALSE}
2752 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2757 EnableDMLHolder dmlHolder(dml);
2760 ExtOut("Usage: !DumpVC <Method Table> <Value object start addr>\n");
2764 if (!IsMethodTable(p_MT))
2766 ExtOut("Not a managed object\n");
2770 return PrintVC(p_MT, p_Object);
2773 #ifdef FEATURE_COMINTEROP
2775 DECLARE_API(DumpRCW)
2780 StringHolder strObject;
2782 CMDOption option[] =
2783 { // name, vptr, type, hasValue
2784 {"/d", &dml, COBOOL, FALSE}
2788 {&strObject, COSTRING}
2791 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2796 EnableDMLHolder dmlHolder(dml);
2799 ExtOut("Missing RCW address\n");
2804 DWORD_PTR p_RCW = GetExpression(strObject.data);
2807 ExtOut("Invalid RCW %s\n", args);
2811 DacpRCWData rcwData;
2812 if ((Status = rcwData.Request(g_sos, p_RCW)) != S_OK)
2814 ExtOut("Error requesting RCW data\n");
2818 if (FAILED(rcwData.IsDCOMProxy(g_sos, p_RCW, &isDCOMProxy)))
2820 isDCOMProxy = FALSE;
2823 DMLOut("Managed object: %s\n", DMLObject(rcwData.managedObject));
2824 DMLOut("Creating thread: %p\n", SOS_PTR(rcwData.creatorThread));
2825 ExtOut("IUnknown pointer: %p\n", SOS_PTR(rcwData.unknownPointer));
2826 ExtOut("COM Context: %p\n", SOS_PTR(rcwData.ctxCookie));
2827 ExtOut("Managed ref count: %d\n", rcwData.refCount);
2828 ExtOut("IUnknown V-table pointer : %p (captured at RCW creation time)\n", SOS_PTR(rcwData.vtablePtr));
2830 ExtOut("Flags: %s%s%s%s%s%s%s%s\n",
2831 (rcwData.isDisconnected? "IsDisconnected " : ""),
2832 (rcwData.supportsIInspectable? "SupportsIInspectable " : ""),
2833 (rcwData.isAggregated? "IsAggregated " : ""),
2834 (rcwData.isContained? "IsContained " : ""),
2835 (rcwData.isJupiterObject? "IsJupiterObject " : ""),
2836 (rcwData.isFreeThreaded? "IsFreeThreaded " : ""),
2837 (rcwData.identityPointer == TO_CDADDR(p_RCW)? "IsUnique " : ""),
2838 (isDCOMProxy ? "IsDCOMProxy " : "")
2841 // Jupiter data hidden by default
2842 if (rcwData.isJupiterObject)
2844 ExtOut("IJupiterObject: %p\n", SOS_PTR(rcwData.jupiterObject));
2847 ExtOut("COM interface pointers:\n");
2849 ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[rcwData.interfaceCount];
2856 if ((Status = g_sos->GetRCWInterfaces(p_RCW, rcwData.interfaceCount, pArray, NULL)) != S_OK)
2858 ExtOut("Error requesting COM interface pointers\n");
2862 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "Context", "MT");
2863 for (int i = 0; i < rcwData.interfaceCount; i++)
2865 // Ignore any NULL MethodTable interface cache. At this point only IJupiterObject
2866 // is saved as NULL MethodTable at first slot, and we've already printed outs its
2868 if (pArray[i].methodTable == NULL)
2871 NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
2873 DMLOut("%p %p %s %S\n", SOS_PTR(pArray[i].interfacePtr), SOS_PTR(pArray[i].comContext), DMLMethodTable(pArray[i].methodTable), g_mdName);
2881 DECLARE_API(DumpCCW)
2886 StringHolder strObject;
2888 CMDOption option[] =
2889 { // name, vptr, type, hasValue
2890 {"/d", &dml, COBOOL, FALSE}
2894 {&strObject, COSTRING}
2897 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
2902 EnableDMLHolder dmlHolder(dml);
2905 ExtOut("Missing CCW address\n");
2910 DWORD_PTR p_CCW = GetExpression(strObject.data);
2913 ExtOut("Invalid CCW %s\n", args);
2917 DacpCCWData ccwData;
2918 if ((Status = ccwData.Request(g_sos, p_CCW)) != S_OK)
2920 ExtOut("Error requesting CCW data\n");
2924 if (ccwData.ccwAddress != p_CCW)
2925 ExtOut("CCW: %p\n", SOS_PTR(ccwData.ccwAddress));
2927 DMLOut("Managed object: %s\n", DMLObject(ccwData.managedObject));
2928 ExtOut("Outer IUnknown: %p\n", SOS_PTR(ccwData.outerIUnknown));
2929 ExtOut("Ref count: %d%s\n", ccwData.refCount, ccwData.isNeutered ? " (NEUTERED)" : "");
2930 ExtOut("Flags: %s%s\n",
2931 (ccwData.isExtendsCOMObject? "IsExtendsCOMObject " : ""),
2932 (ccwData.isAggregated? "IsAggregated " : "")
2935 // Jupiter information hidden by default
2936 if (ccwData.jupiterRefCount > 0)
2938 ExtOut("Jupiter ref count: %d%s%s%s%s\n",
2939 ccwData.jupiterRefCount,
2940 (ccwData.isPegged || ccwData.isGlobalPegged) ? ", Pegged by" : "",
2941 ccwData.isPegged ? " Jupiter " : "",
2942 (ccwData.isPegged && ccwData.isGlobalPegged) ? "&" : "",
2943 ccwData.isGlobalPegged ? " CLR " : ""
2947 ExtOut("RefCounted Handle: %p%s\n",
2948 SOS_PTR(ccwData.handle),
2949 (ccwData.hasStrongRef ? " (STRONG)" : " (WEAK)"));
2951 ExtOut("COM interface pointers:\n");
2953 ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[ccwData.interfaceCount];
2960 if ((Status = g_sos->GetCCWInterfaces(p_CCW, ccwData.interfaceCount, pArray, NULL)) != S_OK)
2962 ExtOut("Error requesting COM interface pointers\n");
2966 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "MT", "Type");
2967 for (int i = 0; i < ccwData.interfaceCount; i++)
2969 if (pArray[i].methodTable == NULL)
2971 wcscpy_s(g_mdName, mdNameLen, L"IDispatch/IUnknown");
2975 NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
2978 DMLOut("%p %s %S\n", pArray[i].interfacePtr, DMLMethodTable(pArray[i].methodTable), g_mdName);
2986 #endif // FEATURE_COMINTEROP
2989 /**********************************************************************\
2990 * Routine Description: *
2992 * This function is called to dump the contents of a PermissionSet *
2993 * from a given address. *
2995 \**********************************************************************/
2997 COMMAND: dumppermissionset.
2998 !DumpPermissionSet <PermissionSet object address>
3000 This command allows you to examine a PermissionSet object. Note that you can
3001 also use DumpObj such an object in greater detail. DumpPermissionSet attempts
3002 to extract all the relevant information from a PermissionSet that you might be
3003 interested in when performing Code Access Security (CAS) related debugging.
3005 Here is a simple PermissionSet object:
3007 0:000> !DumpPermissionSet 014615f4
3008 PermissionSet object: 014615f4
3011 Note that this is an unrestricted PermissionSet object that does not contain
3012 any individual permissions.
3014 Here is another example of a PermissionSet object, one that is not unrestricted
3015 and contains a single permission:
3017 0:003> !DumpPermissionSet 01469fa8
3018 PermissionSet object: 01469fa8
3020 Name: System.Security.Permissions.ReflectionPermission
3021 MethodTable: 5b731308
3024 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.
3025 0.0__b77a5c561934e089\mscorlib.dll)
3028 MT Field Offset Type VT Attr Value Name
3029 5b73125c 4001d66 4 System.Int32 0 instance 2 m_flags
3031 Here is another example of an unrestricted PermissionSet, one that contains
3032 several permissions. The numbers in parentheses before each Permission object
3033 represents the index of that Permission in the PermissionSet.
3035 0:003> !DumpPermissionSet 01467bd8
3036 PermissionSet object: 01467bd8
3039 Name: System.Security.Permissions.FileDialogPermission
3040 MethodTable: 5b73023c
3043 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
3045 MT Field Offset Type VT Attr Value Name
3046 5b730190 4001cc2 4 System.Int32 0 instance 1 access
3048 Name: System.Security.Permissions.ReflectionPermission
3049 MethodTable: 5b731308
3052 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
3054 MT Field Offset Type VT Attr Value Name
3055 5b73125c 4001d66 4 System.Int32 0 instance 0 m_flags
3057 Name: System.Diagnostics.EventLogPermission
3058 MethodTable: 569841c4
3060 Size: 28(0x1c) bytes
3061 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3063 MT Field Offset Type VT Attr Value Name
3064 5b6d65d4 4003078 4 System.Object[] 0 instance 0146c190 tagNames
3065 5b6c9ed8 4003079 8 System.Type 0 instance 0146c17c permissionAccessType
3066 5b6cd928 400307a 10 System.Boolean 0 instance 0 isUnrestricted
3067 5b6c45f8 400307b c ...ections.Hashtable 0 instance 0146c1a4 rootTable
3068 5b6c090c 4003077 bfc System.String 0 static 00000000 computerName
3069 56984434 40030e7 14 ...onEntryCollection 0 instance 00000000 innerCollection
3071 Name: System.Net.WebPermission
3072 MethodTable: 5696dfc4
3074 Size: 20(0x14) bytes
3075 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3077 MT Field Offset Type VT Attr Value Name
3078 5b6cd928 400238e c System.Boolean 0 instance 0 m_Unrestricted
3079 5b6cd928 400238f d System.Boolean 0 instance 0 m_UnrestrictedConnect
3080 5b6cd928 4002390 e System.Boolean 0 instance 0 m_UnrestrictedAccept
3081 5b6c639c 4002391 4 ...ections.ArrayList 0 instance 0146cf3c m_connectList
3082 5b6c639c 4002392 8 ...ections.ArrayList 0 instance 0146cf54 m_acceptList
3083 569476f8 4002393 8a4 ...Expressions.Regex 0 static 00000000 s_MatchAllRegex
3085 Name: System.Net.DnsPermission
3086 MethodTable: 56966408
3089 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3091 MT Field Offset Type VT Attr Value Name
3092 5b6cd928 4001d2c 4 System.Boolean 0 instance 1 m_noRestriction
3094 Name: System.Web.AspNetHostingPermission
3095 MethodTable: 569831bc
3098 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3100 MT Field Offset Type VT Attr Value Name
3101 56983090 4003074 4 System.Int32 0 instance 600 _level
3103 Name: System.Net.NetworkInformation.NetworkInformationPermission
3104 MethodTable: 5697ac70
3106 Size: 16(0x10) bytes
3107 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3109 MT Field Offset Type VT Attr Value Name
3110 5697ab38 4002c34 4 System.Int32 0 instance 0 access
3111 5b6cd928 4002c35 8 System.Boolean 0 instance 0 unrestricted
3114 The abbreviation !dps can be used for brevity.
3118 DECLARE_API(DumpPermissionSet)
3121 MINIDUMP_NOT_SUPPORTED();
3123 DWORD_PTR p_Object = NULL;
3130 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
3136 ExtOut("Usage: !DumpPermissionSet <PermissionSet object addr>\n");
3141 return PrintPermissionSet(p_Object);
3146 void GCPrintGenerationInfo(DacpGcHeapDetails &heap);
3147 void GCPrintSegmentInfo(DacpGcHeapDetails &heap, DWORD_PTR &total_size);
3149 void DisplayInvalidStructuresMessage()
3151 ExtOut("The garbage collector data structures are not in a valid state for traversal.\n");
3152 ExtOut("It is either in the \"plan phase,\" where objects are being moved around, or\n");
3153 ExtOut("we are at the initialization or shutdown of the gc heap. Commands related to \n");
3154 ExtOut("displaying, finding or traversing objects as well as gc heap segments may not \n");
3155 ExtOut("work properly. !dumpheap and !verifyheap may incorrectly complain of heap \n");
3156 ExtOut("consistency errors.\n");
3159 /**********************************************************************\
3160 * Routine Description: *
3162 * This function dumps GC heap size. *
3164 \**********************************************************************/
3168 MINIDUMP_NOT_SUPPORTED();
3171 BOOL showgc = FALSE;
3172 BOOL showloader = FALSE;
3174 CMDOption option[] =
3175 { // name, vptr, type, hasValue
3176 {"-gc", &showgc, COBOOL, FALSE},
3177 {"-loader", &showloader, COBOOL, FALSE},
3178 {"/d", &dml, COBOOL, FALSE},
3181 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
3186 EnableDMLHolder dmlHolder(dml);
3187 if (showloader || !showgc)
3190 DWORD_PTR allHeapSize = 0;
3191 DWORD_PTR wasted = 0;
3192 DacpAppDomainStoreData adsData;
3193 if ((Status=adsData.Request(g_sos))!=S_OK)
3195 ExtOut("Unable to get AppDomain information\n");
3199 // The first one is the system domain.
3200 ExtOut("Loader Heap:\n");
3201 IfFailRet(PrintDomainHeapInfo("System Domain", adsData.systemDomain, &allHeapSize, &wasted));
3202 IfFailRet(PrintDomainHeapInfo("Shared Domain", adsData.sharedDomain, &allHeapSize, &wasted));
3204 ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
3212 if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
3214 ExtOut("Unable to get the array of all AppDomains.\n");
3218 for (int n=0;n<adsData.DomainCount;n++)
3224 sprintf_s(domain, _countof(domain), "Domain %d", n+1);
3226 IfFailRet(PrintDomainHeapInfo(domain, pArray[n], &allHeapSize, &wasted));
3231 ExtOut("--------------------------------------\n");
3232 ExtOut("Jit code heap:\n");
3234 if (IsMiniDumpFile())
3236 ExtOut("<no information>\n");
3240 allHeapSize += JitHeapInfo();
3247 ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(NULL, &numModule);
3248 if (moduleList == NULL)
3250 ExtOut("Failed to request module list.\n");
3254 // Module Thunk Heaps
3255 ExtOut("--------------------------------------\n");
3256 ExtOut("Module Thunk heaps:\n");
3257 allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_ThunkHeap, &wasted);
3259 // Module Lookup Table Heaps
3260 ExtOut("--------------------------------------\n");
3261 ExtOut("Module Lookup Table heaps:\n");
3262 allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_LookupTableHeap, &wasted);
3266 ExtOut("--------------------------------------\n");
3267 ExtOut("Total LoaderHeap size: ");
3268 PrintHeapSize(allHeapSize, wasted);
3269 ExtOut("=======================================\n");
3272 if (showgc || !showloader)
3277 if (!GetGcStructuresValid())
3279 DisplayInvalidStructuresMessage();
3282 DacpGcHeapData gcheap;
3283 if (gcheap.Request(g_sos) != S_OK)
3285 ExtOut("Error requesting GC Heap data\n");
3289 if (gcheap.bServerMode)
3291 dwNHeaps = gcheap.HeapCount;
3294 ExtOut("Number of GC Heaps: %d\n", dwNHeaps);
3295 DWORD_PTR totalSize = 0;
3296 if (!gcheap.bServerMode)
3298 DacpGcHeapDetails heapDetails;
3299 if (heapDetails.Request(g_sos) != S_OK)
3301 ExtOut("Error requesting details\n");
3305 GCHeapInfo (heapDetails, totalSize);
3306 ExtOut("Total Size: ");
3307 PrintHeapSize(totalSize, 0);
3312 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
3314 ExtOut("Failed to get GCHeaps: integer overflow\n");
3318 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
3319 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
3321 ExtOut("Failed to get GCHeaps\n");
3326 for (n = 0; n < dwNHeaps; n ++)
3328 DacpGcHeapDetails heapDetails;
3329 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
3331 ExtOut("Error requesting details\n");
3334 ExtOut("------------------------------\n");
3335 ExtOut("Heap %d (%p)\n", n, (ULONG64)heapAddrs[n]);
3336 DWORD_PTR heapSize = 0;
3337 GCHeapInfo (heapDetails, heapSize);
3338 totalSize += heapSize;
3339 ExtOut("Heap Size: " WIN86_8SPACES);
3340 PrintHeapSize(heapSize, 0);
3343 ExtOut("------------------------------\n");
3344 ExtOut("GC Heap Size: " WIN86_8SPACES);
3345 PrintHeapSize(totalSize, 0);
3350 void PrintGCStat(HeapStat *inStat, const char* label=NULL)
3354 bool sorted = false;
3359 inStat->Print(label);
3363 ExtOut("Exception occurred while trying to %s the GC stats.\n", sorted ? "print" : "sort");
3370 DECLARE_API(TraverseHeap)
3373 MINIDUMP_NOT_SUPPORTED();
3375 BOOL bXmlFormat = FALSE;
3376 BOOL bVerify = FALSE;
3377 StringHolder Filename;
3379 CMDOption option[] =
3380 { // name, vptr, type, hasValue
3381 {"-xml", &bXmlFormat, COBOOL, FALSE},
3382 {"-verify", &bVerify, COBOOL, FALSE},
3386 {&Filename.data, COSTRING},
3389 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
3396 ExtOut("usage: HeapTraverse [-xml] filename\n");
3400 if (!g_snapshot.Build())
3402 ExtOut("Unable to build snapshot of the garbage collector state\n");
3407 if (fopen_s(&file, Filename.data, "w") != 0) {
3408 ExtOut("Unable to open file\n");
3413 ExtOut("Assuming a uncorrupted GC heap. If this is a crash dump consider -verify option\n");
3415 HeapTraverser traverser(bVerify != FALSE);
3417 ExtOut("Writing %s format to file %s\n", bXmlFormat ? "Xml" : "CLRProfiler", Filename.data);
3418 ExtOut("Gathering types...\n");
3420 // TODO: there may be a canonical list of methodtables in the runtime that we can
3421 // traverse instead of exploring the gc heap for that list. We could then simplify the
3422 // tree structure to a sorted list of methodtables, and the index is the ID.
3424 // TODO: "Traversing object members" code should be generalized and shared between
3425 // !gcroot and !traverseheap. Also !dumpheap can begin using GCHeapsTraverse.
3427 if (!traverser.Initialize())
3429 ExtOut("Error initializing heap traversal\n");
3434 if (!traverser.CreateReport (file, bXmlFormat ? FORMAT_XML : FORMAT_CLRPROFILER))
3436 ExtOut("Unable to write heap report\n");
3442 ExtOut("\nfile %s saved\n", Filename.data);
3447 struct PrintRuntimeTypeArgs
3449 DWORD_PTR mtOfRuntimeType;
3450 int handleFieldOffset;
3451 DacpAppDomainStoreData adstore;
3454 void PrintRuntimeTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token)
3456 PrintRuntimeTypeArgs *pArgs = (PrintRuntimeTypeArgs *)token;
3458 if (pArgs->mtOfRuntimeType == NULL)
3460 NameForMT_s(methodTable, g_mdName, mdNameLen);
3462 if (wcscmp(g_mdName, L"System.RuntimeType") == 0)
3464 pArgs->mtOfRuntimeType = methodTable;
3465 pArgs->handleFieldOffset = GetObjFieldOffset(objAddr, methodTable, L"m_handle");
3466 if (pArgs->handleFieldOffset <= 0)
3467 ExtOut("Error getting System.RuntimeType.m_handle offset\n");
3469 pArgs->adstore.Request(g_sos);
3473 if ((methodTable == pArgs->mtOfRuntimeType) && (pArgs->handleFieldOffset > 0))
3475 // Get the method table and display the information.
3477 if (MOVE(mtPtr, objAddr + pArgs->handleFieldOffset) == S_OK)
3479 DMLOut(DMLObject(objAddr));
3481 CLRDATA_ADDRESS appDomain = GetAppDomainForMT(mtPtr);
3482 if (appDomain != NULL)
3484 if (appDomain == pArgs->adstore.sharedDomain)
3485 ExtOut(" %" POINTERSIZE "s", "Shared");
3487 else if (appDomain == pArgs->adstore.systemDomain)
3488 ExtOut(" %" POINTERSIZE "s", "System");
3490 DMLOut(" %s", DMLDomain(appDomain));
3494 ExtOut(" %" POINTERSIZE "s", "?");
3497 NameForMT_s(mtPtr, g_mdName, mdNameLen);
3498 DMLOut(" %s %S\n", DMLMethodTable(mtPtr), g_mdName);
3504 DECLARE_API(DumpRuntimeTypes)
3507 MINIDUMP_NOT_SUPPORTED();
3511 CMDOption option[] =
3512 { // name, vptr, type, hasValue
3513 {"/d", &dml, COBOOL, FALSE},
3516 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
3519 EnableDMLHolder dmlHolder(dml);
3521 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type Name \n",
3522 "Address", "Domain", "MT");
3523 ExtOut("------------------------------------------------------------------------------\n");
3525 PrintRuntimeTypeArgs pargs;
3526 ZeroMemory(&pargs, sizeof(PrintRuntimeTypeArgs));
3528 GCHeapsTraverse(PrintRuntimeTypes, (LPVOID)&pargs);
3532 #define MIN_FRAGMENTATIONBLOCK_BYTES (1024*512)
3535 class FragmentationBlock
3538 FragmentationBlock(TADDR addr, size_t size, TADDR next, TADDR mt)
3539 : mAddress(addr), mSize(size), mNext(next), mNextMT(mt)
3543 inline TADDR GetAddress() const
3547 inline size_t GetSize() const
3552 inline TADDR GetNextObject() const
3557 inline TADDR GetNextMT() const
3573 DumpHeapImpl(PCSTR args)
3574 : mStart(0), mStop(0), mMT(0), mMinSize(0), mMaxSize(~0),
3575 mStat(FALSE), mStrings(FALSE), mVerify(FALSE),
3576 mThinlock(FALSE), mShort(FALSE), mDML(FALSE),
3577 mLive(FALSE), mDead(FALSE), mType(NULL)
3579 ArrayHolder<char> type = NULL;
3582 CMDOption option[] =
3583 { // name, vptr, type, hasValue
3584 {"-mt", &mMT, COHEX, TRUE}, // dump objects with a given MethodTable
3585 {"-type", &type, COSTRING, TRUE}, // list objects of specified type
3586 {"-stat", &mStat, COBOOL, FALSE}, // dump a summary of types and the number of instances of each
3587 {"-strings", &mStrings, COBOOL, FALSE}, // dump a summary of string objects
3588 {"-verify", &mVerify, COBOOL, FALSE}, // verify heap objects (!heapverify)
3589 {"-thinlock", &mThinlock, COBOOL, FALSE},// list only thinlocks
3590 {"-short", &mShort, COBOOL, FALSE}, // list only addresses
3591 {"-min", &mMinSize, COHEX, TRUE}, // min size of objects to display
3592 {"-max", &mMaxSize, COHEX, TRUE}, // max size of objects to display
3593 {"-live", &mLive, COHEX, FALSE}, // only print live objects
3594 {"-dead", &mDead, COHEX, FALSE}, // only print dead objects
3595 {"/d", &mDML, COBOOL, FALSE}, // Debugger Markup Language
3605 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArgs))
3606 sos::Throw<sos::Exception>("Failed to parse command line arguments.");
3612 mStop = sos::GCHeap::HeapEnd;
3616 sos::Throw<sos::Exception>("Cannot specify both -mt and -type");
3621 sos::Throw<sos::Exception>("Cannot specify both -live and -dead.");
3624 if (mMinSize > mMaxSize)
3626 sos::Throw<sos::Exception>("wrong argument");
3629 // If the user gave us a type, convert it to unicode and clean up "type".
3630 if (type && !mStrings)
3632 size_t iLen = strlen(type) + 1;
3633 mType = new WCHAR[iLen];
3634 MultiByteToWideChar(CP_ACP, 0, type, -1, mType, (int)iLen);
3646 // enable Debugger Markup Language
3647 EnableDMLHolder dmlholder(mDML);
3650 if (!gcheap.AreGCStructuresValid())
3651 DisplayInvalidStructuresMessage();
3653 if (IsMiniDumpFile())
3655 ExtOut("In a minidump without full memory, most gc heap structures will not be valid.\n");
3656 ExtOut("If you need this functionality, get a full memory dump with \".dump /ma mydump.dmp\"\n");
3663 mLiveness = gcroot.GetLiveObjects();
3667 // Some of the "specialty" versions of DumpHeap have slightly
3668 // different implementations than the standard version of DumpHeap.
3669 // We seperate them out to not clutter the standard DumpHeap function.
3671 DumpHeapShort(gcheap);
3673 DumpHeapThinlock(gcheap);
3675 DumpHeapStrings(gcheap);
3680 ValidateSyncTable(gcheap);
3683 static bool ValidateSyncTable(sos::GCHeap &gcheap)
3685 bool succeeded = true;
3686 for (sos::SyncBlkIterator itr; itr; ++itr)
3688 sos::CheckInterrupt();
3692 if (!sos::IsObject(itr->GetObject(), true))
3694 ExtOut("SyncBlock %d corrupted, points to invalid object %p\n",
3695 itr->GetIndex(), SOS_PTR(itr->GetObject()));
3700 // Does the object header point to this syncblock index?
3701 sos::Object obj = itr->GetObject();
3702 unsigned long header = 0;
3704 if (!obj.TryGetHeader(header))
3706 ExtOut("Failed to get object header for object %p while inspecting syncblock at index %d.\n",
3707 SOS_PTR(itr->GetObject()), itr->GetIndex());
3713 if ((header & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0 && (header & BIT_SBLK_IS_HASHCODE) == 0)
3715 unsigned long index = header & MASK_SYNCBLOCKINDEX;
3716 valid = (unsigned long)itr->GetIndex() == index;
3721 ExtOut("Object header for %p should have a SyncBlock index of %d.\n",
3722 SOS_PTR(itr->GetObject()), itr->GetIndex());
3733 DumpHeapImpl(const DumpHeapImpl &);
3735 bool Verify(const sos::ObjectIterator &itr)
3740 if (!itr.Verify(buffer, _countof(buffer)))
3750 bool IsCorrectType(const sos::Object &obj)
3753 return mMT == obj.GetMT();
3757 WString name = obj.GetTypeName();
3758 return wcsstr(name.c_str(), mType) != NULL;
3764 bool IsCorrectSize(const sos::Object &obj)
3766 size_t size = obj.GetSize();
3767 return size >= mMinSize && size <= mMaxSize;
3770 bool IsCorrectLiveness(const sos::Object &obj)
3773 if (mLive && mLiveness.find(obj.GetAddress()) == mLiveness.end())
3776 if (mDead && (mLiveness.find(obj.GetAddress()) != mLiveness.end() || obj.IsFree()))
3784 inline void PrintHeader()
3786 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s\n", "Address", "MT", "Size");
3789 void DumpHeap(sos::GCHeap &gcheap)
3793 // For heap fragmentation tracking.
3794 TADDR lastFreeObj = NULL;
3795 size_t lastFreeSize = 0;
3800 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3805 bool onLOH = itr.IsCurrObjectOnLOH();
3807 // Check for free objects to report fragmentation
3808 if (lastFreeObj != NULL)
3809 ReportFreeObject(lastFreeObj, lastFreeSize, itr->GetAddress(), itr->GetMT());
3811 if (!onLOH && itr->IsFree())
3814 lastFreeSize = itr->GetSize();
3821 if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
3823 stats.Add((DWORD_PTR)itr->GetMT(), (DWORD)itr->GetSize());
3825 DMLOut("%s %s %8d%s\n", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize(),
3826 itr->IsFree() ? " Free":" ");
3836 PrintFragmentationReport();
3839 struct StringSetEntry
3841 StringSetEntry() : count(0), size(0)
3846 StringSetEntry(__in_ecount(64) wchar_t tmp[64], size_t _size)
3847 : count(1), size(_size)
3849 memcpy(str, tmp, sizeof(str));
3852 void Add(size_t _size) const
3858 mutable size_t count;
3859 mutable size_t size;
3862 bool operator<(const StringSetEntry &rhs) const
3864 return wcscmp(str, rhs.str) == -1;
3869 static bool StringSetCompare(const StringSetEntry &a1, const StringSetEntry &a2)
3871 return a1.size < a2.size;
3874 void DumpHeapStrings(sos::GCHeap &gcheap)
3877 ExtOut("Not implemented.\n");
3879 const int offset = sos::Object::GetStringDataOffset();
3880 typedef std::set<StringSetEntry> Set;
3881 Set set; // A set keyed off of the string's text
3883 StringSetEntry tmp; // Temp string used to keep track of the set
3886 TableOutput out(3, POINTERSIZE_HEX, AlignRight);
3887 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3892 if (itr->IsString() && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
3894 CLRDATA_ADDRESS addr = itr->GetAddress();
3895 size_t size = itr->GetSize();
3898 out.WriteRow(ObjectPtr(addr), Pointer(itr->GetMT()), Decimal(size));
3900 // Don't bother calculating the size of the string, just read the full 64 characters of the buffer. The null
3901 // terminator we read will terminate the string.
3902 HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(addr+offset), tmp.str, sizeof(wchar_t)*(_countof(tmp.str)-1), &fetched);
3905 // Ensure we null terminate the string. Note that this will not overrun the buffer as we only
3906 // wrote a max of 63 characters into the 64 character buffer.
3907 tmp.str[fetched/sizeof(wchar_t)] = 0;
3908 Set::iterator sitr = set.find(tmp);
3909 if (sitr == set.end())
3925 // Now flatten the set into a vector. This is much faster than keeping two sets, or using a multimap.
3926 typedef std::vector<StringSetEntry> Vect;
3927 Vect v(set.begin(), set.end());
3928 std::sort(v.begin(), v.end(), &DumpHeapImpl::StringSetCompare);
3930 // Now print out the data. The call to Flatten ensures that we don't print newlines to break up the
3931 // output in strange ways.
3932 for (Vect::iterator vitr = v.begin(); vitr != v.end(); ++vitr)
3937 Flatten(vitr->str, (unsigned int)wcslen(vitr->str));
3938 out.WriteRow(Decimal(vitr->size), Decimal(vitr->count), vitr->str);
3943 void DumpHeapShort(sos::GCHeap &gcheap)
3945 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3950 if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
3951 DMLOut("%s\n", DMLObject(itr->GetAddress()));
3955 void DumpHeapThinlock(sos::GCHeap &gcheap)
3960 for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3965 sos::ThinLockInfo lockInfo;
3966 if (IsCorrectType(*itr) && itr->GetThinLock(lockInfo))
3968 DMLOut("%s %s %8d", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize());
3969 ExtOut(" ThinLock owner %x (%p) Recursive %x\n", lockInfo.ThreadId,
3970 (ULONG64)lockInfo.ThreadPtr, lockInfo.Recursion);
3976 ExtOut("Found %d objects.\n", count);
3999 #if !defined(FEATURE_PAL)
4001 std::hash_set<TADDR> mLiveness;
4002 typedef std::list<sos::FragmentationBlock> FragmentationList;
4003 FragmentationList mFrag;
4005 void InitFragmentationList()
4010 void ReportFreeObject(TADDR addr, size_t size, TADDR next, TADDR mt)
4012 if (size >= MIN_FRAGMENTATIONBLOCK_BYTES)
4013 mFrag.push_back(sos::FragmentationBlock(addr, size, next, mt));
4016 void PrintFragmentationReport()
4018 if (mFrag.size() > 0)
4020 ExtOut("Fragmented blocks larger than 0.5 MB:\n");
4021 ExtOut("%" POINTERSIZE "s %8s %16s\n", "Addr", "Size", "Followed by");
4023 for (FragmentationList::const_iterator itr = mFrag.begin(); itr != mFrag.end(); ++itr)
4025 sos::MethodTable mt = itr->GetNextMT();
4026 ExtOut("%p %6.1fMB " WIN64_8SPACES "%p %S\n",
4027 SOS_PTR(itr->GetAddress()),
4028 ((double)itr->GetSize()) / 1024.0 / 1024.0,
4029 SOS_PTR(itr->GetNextObject()),
4035 void InitFragmentationList() {}
4036 void ReportFreeObject(TADDR, TADDR, size_t, TADDR) {}
4037 void PrintFragmentationReport() {}
4042 /**********************************************************************\
4043 * Routine Description: *
4045 * This function dumps all objects on GC heap. It also displays *
4046 * statistics of objects. If GC heap is corrupted, it will stop at
4047 * the bad place. (May not work if GC is in progress.) *
4049 \**********************************************************************/
4050 DECLARE_API(DumpHeap)
4053 MINIDUMP_NOT_SUPPORTED();
4055 if (!g_snapshot.Build())
4057 ExtOut("Unable to build snapshot of the garbage collector state\n");
4063 DumpHeapImpl dumpHeap(args);
4068 catch(const sos::Exception &e)
4070 ExtOut("%s\n", e.what());
4075 DECLARE_API(VerifyHeap)
4078 MINIDUMP_NOT_SUPPORTED();
4080 if (!g_snapshot.Build())
4082 ExtOut("Unable to build snapshot of the garbage collector state\n");
4088 bool succeeded = true;
4091 sos::ObjectIterator itr = gcheap.WalkHeap();
4095 if (itr.Verify(buffer, _countof(buffer)))
4103 itr.MoveToNextObjectCarefully();
4107 if (!DumpHeapImpl::ValidateSyncTable(gcheap))
4111 ExtOut("No heap corruption detected.\n");
4115 catch(const sos::Exception &e)
4117 ExtOut("%s\n", e.what());
4122 enum failure_get_memory
4125 fgm_reserve_segment = 1,
4126 fgm_commit_segment_beg = 2,
4127 fgm_commit_eph_segment = 3,
4129 fgm_commit_table = 5
4136 oom_cant_commit = 2,
4137 oom_cant_reserve = 3,
4140 oom_unproductive_full_gc = 6
4143 static const char *const str_oom[] =
4145 "There was no managed OOM due to allocations on the GC heap", // oom_no_failure
4146 "This is likely to be a bug in GC", // oom_budget
4147 "Didn't have enough memory to commit", // oom_cant_commit
4148 "This is likely to be a bug in GC", // oom_cant_reserve
4149 "Didn't have enough memory to allocate an LOH segment", // oom_loh
4150 "Low on memory during GC", // oom_low_mem
4151 "Could not do a full GC" // oom_unproductive_full_gc
4154 static const char *const str_fgm[] =
4156 "There was no failure to allocate memory", // fgm_no_failure
4157 "Failed to reserve memory", // fgm_reserve_segment
4158 "Didn't have enough memory to commit beginning of the segment", // fgm_commit_segment_beg
4159 "Didn't have enough memory to commit the new ephemeral segment", // fgm_commit_eph_segment
4160 "Didn't have enough memory to grow the internal GC datastructures", // fgm_grow_table
4161 "Didn't have enough memory to commit the internal GC datastructures", // fgm_commit_table
4164 void PrintOOMInfo(DacpOomData* oomData)
4166 ExtOut("Managed OOM occured after GC #%d (Requested to allocate %d bytes)\n",
4167 oomData->gc_index, oomData->alloc_size);
4169 if ((oomData->reason == oom_budget) ||
4170 (oomData->reason == oom_cant_reserve))
4172 // TODO: This message needs to be updated with more precious info.
4173 ExtOut("%s, please contact PSS\n", str_oom[oomData->reason]);
4177 ExtOut("Reason: %s\n", str_oom[oomData->reason]);
4180 // Now print out the more detailed memory info if any.
4181 if (oomData->fgm != fgm_no_failure)
4183 ExtOut("Detail: %s: %s (%d bytes)",
4184 (oomData->loh_p ? "LOH" : "SOH"),
4185 str_fgm[oomData->fgm],
4188 if ((oomData->fgm == fgm_commit_segment_beg) ||
4189 (oomData->fgm == fgm_commit_eph_segment) ||
4190 (oomData->fgm == fgm_grow_table) ||
4191 (oomData->fgm == fgm_commit_table))
4193 // If it's a commit error (fgm_grow_table can indicate a reserve
4194 // or a commit error since we make one VirtualAlloc call to
4195 // reserve and commit), we indicate the available commit
4196 // space if we recorded it.
4197 if (oomData->available_pagefile_mb)
4199 ExtOut(" - on GC entry available commit space was %d MB",
4200 oomData->available_pagefile_mb);
4208 DECLARE_API(AnalyzeOOM)
4211 MINIDUMP_NOT_SUPPORTED();
4215 if (!InitializeHeapData ())
4217 ExtOut("GC Heap not initialized yet.\n");
4221 BOOL bHasManagedOOM = FALSE;
4222 DacpOomData oomData;
4223 memset (&oomData, 0, sizeof(oomData));
4224 if (!IsServerBuild())
4226 if (oomData.Request(g_sos) != S_OK)
4228 ExtOut("Error requesting OOM data\n");
4231 if (oomData.reason != oom_no_failure)
4233 bHasManagedOOM = TRUE;
4234 PrintOOMInfo(&oomData);
4239 DWORD dwNHeaps = GetGcHeapCount();
4241 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
4243 ExtOut("Failed to get GCHeaps: integer overflow\n");
4247 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
4248 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
4250 ExtOut("Failed to get GCHeaps\n");
4254 for (DWORD n = 0; n < dwNHeaps; n ++)
4256 if (oomData.Request(g_sos, heapAddrs[n]) != S_OK)
4258 ExtOut("Heap %d: Error requesting OOM data\n", n);
4261 if (oomData.reason != oom_no_failure)
4263 if (!bHasManagedOOM)
4265 bHasManagedOOM = TRUE;
4267 ExtOut("---------Heap %#-2d---------\n", n);
4268 PrintOOMInfo(&oomData);
4273 if (!bHasManagedOOM)
4275 ExtOut("%s\n", str_oom[oomData.reason]);
4282 #endif // FEATURE_PAL
4285 DECLARE_API(VerifyObj)
4288 MINIDUMP_NOT_SUPPORTED();
4296 BOOL bValid = FALSE;
4299 CMDOption option[] =
4300 { // name, vptr, type, hasValue
4301 {"/d", &dml, COBOOL, FALSE},
4308 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
4313 EnableDMLHolder dmlHolder(dml);
4314 BOOL bContainsPointers;
4316 if (FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
4317 !GetSizeEfficient(taddrObj, taddrMT, FALSE, objSize, bContainsPointers))
4319 ExtOut("object %#p does not have valid method table\n", (ULONG64)taddrObj);
4323 // we need to build g_snapshot as it is later used in GetGeneration
4324 if (!g_snapshot.Build())
4326 ExtOut("Unable to build snapshot of the garbage collector state\n");
4329 DacpGcHeapDetails *pheapDetails = g_snapshot.GetHeap(taddrObj);
4330 bValid = VerifyObject(*pheapDetails, taddrObj, taddrMT, objSize, TRUE);
4335 ExtOut("object %#p is a valid object\n", (ULONG64) taddrObj);
4345 #endif // FEATURE_PAL
4348 void LNODisplayOutput(LPCWSTR tag, TADDR pMT, TADDR currentObj, size_t size)
4350 sos::Object obj(currentObj, pMT);
4351 DMLOut("%S %s %12d (0x%x)\t%S\n", tag, DMLObject(currentObj), size, size, obj.GetTypeName());
4354 DECLARE_API(ListNearObj)
4357 MINIDUMP_NOT_SUPPORTED();
4359 #if !defined(FEATURE_PAL)
4363 // we may want to provide a more exact version of searching for the
4364 // previous object in the heap, using the brick table, instead of
4365 // looking for what may be valid method tables...
4367 //CMDOption option[] =
4369 // // name, vptr, type, hasValue
4370 // {"-exact", &bExact, COBOOL, FALSE}
4374 CMDOption option[] =
4375 { // name, vptr, type, hasValue
4376 {"/d", &dml, COBOOL, FALSE},
4384 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || nArg != 1)
4386 ExtOut("Usage: !ListNearObj <obj_address>\n");
4390 EnableDMLHolder dmlHolder(dml);
4392 if (!g_snapshot.Build())
4394 ExtOut("Unable to build snapshot of the garbage collector state\n");
4398 taddrObj = Align(taddrArg);
4400 DacpGcHeapDetails *heap = g_snapshot.GetHeap(taddrArg);
4403 ExtOut("Address %p does not lie in the managed heap\n", (ULONG64) taddrObj);
4407 TADDR_SEGINFO trngSeg = {0, 0, 0};
4408 TADDR_RANGE allocCtx = {0, 0};
4411 if (!GCObjInHeap(taddrObj, *heap, trngSeg, gen, allocCtx, bLarge))
4413 ExtOut("Failed to find the segment of the managed heap where the object %p resides\n",
4414 (ULONG64) taddrObj);
4429 BOOL bContainsPointers;
4431 std::vector<TADDR> candidate;
4432 candidate.reserve(10);
4434 // since we'll be reading back I'll prime the read cache to a buffer before the current address
4435 MOVE(taddrCur, max(trngSeg.start, taddrObj-DT_OS_PAGE_SIZE));
4437 // ===== Look for a good candidate preceeding taddrObj
4439 for (taddrCur = taddrObj - sizeof(TADDR); taddrCur >= trngSeg.start; taddrCur -= sizeof(TADDR))
4441 // currently we don't pay attention to allocation contexts. if this
4442 // proves to be an issue we need to reconsider the code below
4443 if (SUCCEEDED(GetMTOfObject(taddrCur, &curMT)) &&
4444 GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers))
4446 // remember this as one of the possible "good" objects preceeding taddrObj
4447 candidate.push_back(taddrCur);
4449 std::vector<TADDR>::iterator it =
4450 std::find(candidate.begin(), candidate.end(), taddrCur+curSize);
4451 if (it != candidate.end())
4453 // We found a chain of two objects preceeding taddrObj. We'll
4454 // trust this is a good indication that the two objects are valid.
4455 // What is not valid is possibly the object following the second
4458 GetMTOfObject(taddrCur, &curMT);
4459 GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
4466 if (!bCur && !candidate.empty())
4468 // pick the closest object to taddrObj
4469 taddrCur = *(candidate.begin());
4470 GetMTOfObject(taddrCur, &curMT);
4471 GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
4472 // we have a candidate, even if not confirmed
4476 taddrNxt = taddrObj;
4477 if (taddrArg == taddrObj)
4479 taddrNxt += sizeof(TADDR);
4482 // ===== Now look at taddrObj
4483 if (taddrObj == taddrArg)
4485 // only look at taddrObj if it's the same as what user passed in, meaning it's aligned.
4486 if (SUCCEEDED(GetMTOfObject(taddrObj, &objMT)) &&
4487 GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
4490 taddrNxt = taddrObj+objSize;
4494 if ((taddrCur + curSize > taddrArg) && taddrCur + curSize < trngSeg.end)
4496 if (SUCCEEDED(GetMTOfObject(taddrCur + curSize, &nxtMT)) &&
4497 GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
4499 taddrNxt = taddrCur+curSize;
4503 // ===== And finally move on to elements following taddrObj
4505 for (; taddrNxt < trngSeg.end; taddrNxt += sizeof(TADDR))
4507 if (SUCCEEDED(GetMTOfObject(taddrNxt, &nxtMT)) &&
4508 GetSizeEfficient(taddrNxt, nxtMT, bLarge, nxtSize, bContainsPointers))
4516 LNODisplayOutput(L"Before: ", curMT, taddrCur, curSize);
4518 ExtOut("Before: couldn't find any object between %#p and %#p\n",
4519 (ULONG64)trngSeg.start, (ULONG64)taddrArg);
4522 LNODisplayOutput(L"Current:", objMT, taddrObj, objSize);
4525 LNODisplayOutput(L"After: ", nxtMT, taddrNxt, nxtSize);
4527 ExtOut("After: couldn't find any object between %#p and %#p\n",
4528 (ULONG64)taddrArg, (ULONG64)trngSeg.end);
4531 (((taddrCur+curSize == taddrObj) && (taddrObj+objSize == taddrNxt)) || (taddrCur+curSize == taddrNxt)))
4533 ExtOut("Heap local consistency confirmed.\n");
4537 ExtOut("Heap local consistency not confirmed.\n");
4547 #endif // FEATURE_PAL
4551 DECLARE_API(GCHeapStat)
4554 MINIDUMP_NOT_SUPPORTED();
4559 BOOL bIncUnreachable = FALSE;
4562 CMDOption option[] = {
4563 // name, vptr, type, hasValue
4564 {"-inclUnrooted", &bIncUnreachable, COBOOL, FALSE},
4565 {"-iu", &bIncUnreachable, COBOOL, FALSE},
4566 {"/d", &dml, COBOOL, FALSE}
4569 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
4574 EnableDMLHolder dmlHolder(dml);
4575 ExtOut("%-8s %12s %12s %12s %12s\n", "Heap", "Gen0", "Gen1", "Gen2", "LOH");
4577 if (!IsServerBuild())
4580 DacpGcHeapDetails heapDetails;
4581 if (heapDetails.Request(g_sos) != S_OK)
4583 ExtErr("Error requesting gc heap details\n");
4587 HeapUsageStat hpUsage;
4588 if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage))
4590 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", 0,
4591 hpUsage.genUsage[0].allocd, hpUsage.genUsage[1].allocd,
4592 hpUsage.genUsage[2].allocd, hpUsage.genUsage[3].allocd);
4593 ExtOut("\nFree space: Percentage\n");
4594 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0,
4595 hpUsage.genUsage[0].freed, hpUsage.genUsage[1].freed,
4596 hpUsage.genUsage[2].freed, hpUsage.genUsage[3].freed);
4597 tempf = ((float)(hpUsage.genUsage[0].freed+hpUsage.genUsage[1].freed+hpUsage.genUsage[2].freed)) /
4598 (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
4599 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4600 (int)(100*((float)hpUsage.genUsage[3].freed) / (hpUsage.genUsage[3].allocd)));
4601 if (bIncUnreachable)
4603 ExtOut("\nUnrooted objects: Percentage\n");
4604 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0,
4605 hpUsage.genUsage[0].unrooted, hpUsage.genUsage[1].unrooted,
4606 hpUsage.genUsage[2].unrooted, hpUsage.genUsage[3].unrooted);
4607 tempf = ((float)(hpUsage.genUsage[0].unrooted+hpUsage.genUsage[1].unrooted+hpUsage.genUsage[2].unrooted)) /
4608 (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
4609 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4610 (int)(100*((float)hpUsage.genUsage[3].unrooted) / (hpUsage.genUsage[3].allocd)));
4617 DacpGcHeapData gcheap;
4618 if (gcheap.Request(g_sos) != S_OK)
4620 ExtErr("Error requesting GC Heap data\n");
4625 DWORD dwNHeaps = gcheap.HeapCount;
4626 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
4628 ExtErr("Failed to get GCHeaps: integer overflow\n");
4632 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
4633 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
4635 ExtErr("Failed to get GCHeaps\n");
4639 ArrayHolder<HeapUsageStat> hpUsage = new NOTHROW HeapUsageStat[dwNHeaps];
4640 if (hpUsage == NULL)
4646 // aggregate stats accross heaps / generation
4647 GenUsageStat genUsageStat[4] = {0, 0, 0, 0};
4649 for (DWORD n = 0; n < dwNHeaps; n ++)
4651 DacpGcHeapDetails heapDetails;
4652 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
4654 ExtErr("Error requesting gc heap details\n");
4658 if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage[n]))
4660 for (int i = 0; i < 4; ++i)
4662 genUsageStat[i].allocd += hpUsage[n].genUsage[i].allocd;
4663 genUsageStat[i].freed += hpUsage[n].genUsage[i].freed;
4664 if (bIncUnreachable)
4666 genUsageStat[i].unrooted += hpUsage[n].genUsage[i].unrooted;
4672 for (DWORD n = 0; n < dwNHeaps; n ++)
4674 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", n,
4675 hpUsage[n].genUsage[0].allocd, hpUsage[n].genUsage[1].allocd,
4676 hpUsage[n].genUsage[2].allocd, hpUsage[n].genUsage[3].allocd);
4678 ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4679 genUsageStat[0].allocd, genUsageStat[1].allocd,
4680 genUsageStat[2].allocd, genUsageStat[3].allocd);
4682 ExtOut("\nFree space: Percentage\n");
4683 for (DWORD n = 0; n < dwNHeaps; n ++)
4685 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n,
4686 hpUsage[n].genUsage[0].freed, hpUsage[n].genUsage[1].freed,
4687 hpUsage[n].genUsage[2].freed, hpUsage[n].genUsage[3].freed);
4689 tempf = ((float)(hpUsage[n].genUsage[0].freed+hpUsage[n].genUsage[1].freed+hpUsage[n].genUsage[2].freed)) /
4690 (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
4691 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4692 (int)(100*((float)hpUsage[n].genUsage[3].freed) / (hpUsage[n].genUsage[3].allocd))
4695 ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4696 genUsageStat[0].freed, genUsageStat[1].freed,
4697 genUsageStat[2].freed, genUsageStat[3].freed);
4699 if (bIncUnreachable)
4701 ExtOut("\nUnrooted objects: Percentage\n");
4702 for (DWORD n = 0; n < dwNHeaps; n ++)
4704 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n,
4705 hpUsage[n].genUsage[0].unrooted, hpUsage[n].genUsage[1].unrooted,
4706 hpUsage[n].genUsage[2].unrooted, hpUsage[n].genUsage[3].unrooted);
4708 tempf = ((float)(hpUsage[n].genUsage[0].unrooted+hpUsage[n].genUsage[1].unrooted+hpUsage[n].genUsage[2].unrooted)) /
4709 (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
4710 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4711 (int)(100*((float)hpUsage[n].genUsage[3].unrooted) / (hpUsage[n].genUsage[3].allocd)));
4713 ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4714 genUsageStat[0].unrooted, genUsageStat[1].unrooted,
4715 genUsageStat[2].unrooted, genUsageStat[3].unrooted);
4727 #endif // FEATURE_PAL
4730 /**********************************************************************\
4731 * Routine Description: *
4733 * This function dumps what is in the syncblock cache. By default *
4734 * it dumps all active syncblocks. Using -all to dump all syncblocks
4736 \**********************************************************************/
4737 DECLARE_API(SyncBlk)
4740 MINIDUMP_NOT_SUPPORTED();
4742 BOOL bDumpAll = FALSE;
4746 CMDOption option[] =
4747 { // name, vptr, type, hasValue
4748 {"-all", &bDumpAll, COBOOL, FALSE},
4749 {"/d", &dml, COBOOL, FALSE}
4753 {&nbAsked, COSIZE_T}
4756 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
4761 EnableDMLHolder dmlHolder(dml);
4762 DacpSyncBlockData syncBlockData;
4763 if (syncBlockData.Request(g_sos,1) != S_OK)
4765 ExtOut("Error requesting SyncBlk data\n");
4769 DWORD dwCount = syncBlockData.SyncBlockCount;
4771 ExtOut("Index" WIN64_8SPACES " SyncBlock MonitorHeld Recursion Owning Thread Info" WIN64_8SPACES " SyncBlock Owner\n");
4772 ULONG freeCount = 0;
4776 for (DWORD nb = 1; nb <= dwCount; nb++)
4781 if (nbAsked && nb != nbAsked)
4786 if (syncBlockData.Request(g_sos,nb) != S_OK)
4788 ExtOut("SyncBlock %d is invalid%s\n", nb,
4789 (nb != nbAsked) ? ", continuing..." : "");
4793 BOOL bPrint = (bDumpAll || nb == nbAsked || (syncBlockData.MonitorHeld > 0 && !syncBlockData.bFree));
4798 if (!syncBlockData.bFree || nb != nbAsked)
4800 ExtOut("%p ", syncBlockData.SyncBlockPointer);
4801 ExtOut("%11d ", syncBlockData.MonitorHeld);
4802 ExtOut("%9d ", syncBlockData.Recursion);
4803 ExtOut("%p ", syncBlockData.HoldingThread);
4805 if (syncBlockData.HoldingThread == ~0ul)
4807 ExtOut(" orphaned ");
4809 else if (syncBlockData.HoldingThread != NULL)
4811 DacpThreadData Thread;
4812 if ((Status = Thread.Request(g_sos, syncBlockData.HoldingThread)) != S_OK)
4814 ExtOut("Failed to request Thread at %p\n", syncBlockData.HoldingThread);
4818 DMLOut(DMLThreadID(Thread.osThreadId));
4820 if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
4834 if (syncBlockData.bFree)
4836 ExtOut(" %8d", 0); // TODO: do we need to print the free synctable list?
4840 sos::Object obj = TO_TADDR(syncBlockData.Object);
4841 DMLOut(" %s %S", DMLObject(syncBlockData.Object), obj.GetTypeName());
4846 if (syncBlockData.bFree)
4855 #ifdef FEATURE_COMINTEROP
4856 if (syncBlockData.COMFlags) {
4857 switch (syncBlockData.COMFlags) {
4858 case SYNCBLOCKDATA_COMFLAGS_CCW:
4861 case SYNCBLOCKDATA_COMFLAGS_RCW:
4864 case SYNCBLOCKDATA_COMFLAGS_CF:
4869 #endif // FEATURE_COMINTEROP
4872 if (syncBlockData.MonitorHeld > 1)
4874 // TODO: implement this
4877 DWORD_PTR pHead = (DWORD_PTR)vSyncBlock.m_Link.m_pNext;
4878 DWORD_PTR pNext = pHead;
4885 DWORD_PTR pWaitEventLink = pNext - offsetLinkSB;
4886 WaitEventLink vWaitEventLink;
4887 vWaitEventLink.Fill(pWaitEventLink);
4891 DWORD_PTR dwAddr = (DWORD_PTR)vWaitEventLink.m_Thread;
4892 ExtOut("%x ", dwAddr);
4893 vThread.Fill (dwAddr);
4898 DMLOut("%s,", DMLThreadID(vThread.m_OSThreadId));
4899 pNext = (DWORD_PTR)vWaitEventLink.m_LinkSB.m_pNext;
4910 ExtOut("-----------------------------\n");
4911 ExtOut("Total %d\n", dwCount);
4912 ExtOut("CCW %d\n", CCWCount);
4913 ExtOut("RCW %d\n", RCWCount);
4914 ExtOut("ComClassFactory %d\n", CFCount);
4915 ExtOut("Free %d\n", freeCount);
4920 #ifdef FEATURE_COMINTEROP
4929 void VisitRcw(CLRDATA_ADDRESS RCW,CLRDATA_ADDRESS Context,CLRDATA_ADDRESS Thread, BOOL bIsFreeThreaded, LPVOID token)
4931 VisitRcwArgs *pArgs = (VisitRcwArgs *) token;
4935 if (pArgs->MTACount == 0 && pArgs->STACount == 0 && pArgs->FTMCount == 0)
4937 // First time, print a header
4938 ExtOut("RuntimeCallableWrappers (RCW) to be cleaned:\n");
4939 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Apartment\n",
4940 "RCW", "CONTEXT", "THREAD");
4942 LPCSTR szThreadApartment;
4943 if (bIsFreeThreaded)
4945 szThreadApartment = "(FreeThreaded)";
4948 else if (Thread == NULL)
4950 szThreadApartment = "(MTA)";
4955 szThreadApartment = "(STA)";
4959 ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %9s\n",
4967 DECLARE_API(RCWCleanupList)
4970 MINIDUMP_NOT_SUPPORTED();
4972 DWORD_PTR p_CleanupList = GetExpression(args);
4974 VisitRcwArgs travArgs;
4975 ZeroMemory(&travArgs,sizeof(VisitRcwArgs));
4976 travArgs.bDetail = TRUE;
4978 // We need to detect when !RCWCleanupList is called with an expression which evaluates to 0
4979 // (to print out an Invalid parameter message), but at the same time we need to allow an
4980 // empty argument list which would result in p_CleanupList equaling 0.
4981 if (p_CleanupList || strlen(args) == 0)
4983 HRESULT hr = g_sos->TraverseRCWCleanupList(p_CleanupList, (VISITRCWFORCLEANUP)VisitRcw, &travArgs);
4987 ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
4988 ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
4989 ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
4993 ExtOut("An error occurred while traversing the cleanup list.\n");
4998 ExtOut("Invalid parameter %s\n", args);
5003 #endif // FEATURE_COMINTEROP
5006 /**********************************************************************\
5007 * Routine Description: *
5009 * This function is called to dump the contents of the finalizer *
5012 \**********************************************************************/
5013 DECLARE_API(FinalizeQueue)
5016 MINIDUMP_NOT_SUPPORTED();
5018 BOOL bDetail = FALSE;
5019 BOOL bAllReady = FALSE;
5020 BOOL bShort = FALSE;
5024 CMDOption option[] =
5025 { // name, vptr, type, hasValue
5026 {"-detail", &bDetail, COBOOL, FALSE},
5027 {"-allReady", &bAllReady, COBOOL, FALSE},
5028 {"-short", &bShort, COBOOL, FALSE},
5029 {"/d", &dml, COBOOL, FALSE},
5030 {"-mt", &taddrMT, COHEX, TRUE},
5033 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
5038 EnableDMLHolder dmlHolder(dml);
5041 DacpSyncBlockCleanupData dsbcd;
5042 CLRDATA_ADDRESS sbCurrent = NULL;
5043 ULONG cleanCount = 0;
5044 while ((dsbcd.Request(g_sos,sbCurrent) == S_OK) && dsbcd.SyncBlockPointer)
5048 if (cleanCount == 0) // print first time only
5050 ExtOut("SyncBlocks to be cleaned by the finalizer thread:\n");
5051 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
5052 "SyncBlock", "RCW", "CCW", "ComClassFactory");
5055 ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p\n",
5056 (ULONG64) dsbcd.SyncBlockPointer,
5057 (ULONG64) dsbcd.blockRCW,
5058 (ULONG64) dsbcd.blockCCW,
5059 (ULONG64) dsbcd.blockClassFactory);
5063 sbCurrent = dsbcd.nextSyncBlock;
5064 if (sbCurrent == NULL)
5070 ExtOut("SyncBlocks to be cleaned up: %d\n", cleanCount);
5072 #ifdef FEATURE_COMINTEROP
5073 VisitRcwArgs travArgs;
5074 ZeroMemory(&travArgs,sizeof(VisitRcwArgs));
5075 travArgs.bDetail = bDetail;
5076 g_sos->TraverseRCWCleanupList(0, (VISITRCWFORCLEANUP) VisitRcw, &travArgs);
5077 ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
5078 ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
5079 ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
5080 #endif // FEATURE_COMINTEROP
5083 ExtOut("----------------------------------\n");
5087 DWORD dwNHeaps = GetGcHeapCount();
5091 if (!IsServerBuild())
5093 DacpGcHeapDetails heapDetails;
5094 if (heapDetails.Request(g_sos) != S_OK)
5096 ExtOut("Error requesting details\n");
5100 GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
5105 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
5107 ExtOut("Failed to get GCHeaps: integer overflow\n");
5111 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
5112 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
5114 ExtOut("Failed to get GCHeaps\n");
5118 for (DWORD n = 0; n < dwNHeaps; n ++)
5120 DacpGcHeapDetails heapDetails;
5121 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
5123 ExtOut("Error requesting details\n");
5127 ExtOut("------------------------------\n");
5128 ExtOut("Heap %d\n", n);
5129 GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
5137 PrintGCStat(&hpStat, "Statistics for all finalizable objects that are no longer rooted:\n");
5141 PrintGCStat(&hpStat, "Statistics for all finalizable objects (including all objects ready for finalization):\n");
5149 // These are the values set in m_dwTransientFlags.
5150 // Note that none of these flags survive a prejit save/restore.
5152 M_CRST_NOTINITIALIZED = 0x00000001, // Used to prevent destruction of garbage m_crst
5153 M_LOOKUPCRST_NOTINITIALIZED = 0x00000002,
5155 SUPPORTS_UPDATEABLE_METHODS = 0x00000020,
5156 CLASSES_FREED = 0x00000040,
5157 HAS_PHONY_IL_RVAS = 0x00000080,
5158 IS_EDIT_AND_CONTINUE = 0x00000200,
5161 void ModuleMapTraverse(UINT index, CLRDATA_ADDRESS methodTable, LPVOID token)
5163 ULONG32 rid = (ULONG32)(size_t)token;
5164 NameForMT_s(TO_TADDR(methodTable), g_mdName, mdNameLen);
5166 DMLOut("%s 0x%08x %S\n", DMLMethodTable(methodTable), (ULONG32)TokenFromRid(rid, index), g_mdName);
5170 /**********************************************************************\
5171 * Routine Description: *
5173 * This function is called to dump the contents of a Module *
5174 * for a given address *
5176 \**********************************************************************/
5177 DECLARE_API(DumpModule)
5180 MINIDUMP_NOT_SUPPORTED();
5183 DWORD_PTR p_ModuleAddr = NULL;
5184 BOOL bMethodTables = FALSE;
5187 CMDOption option[] =
5188 { // name, vptr, type, hasValue
5189 {"-mt", &bMethodTables, COBOOL, FALSE},
5190 {"/d", &dml, COBOOL, FALSE}
5194 {&p_ModuleAddr, COHEX}
5198 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
5204 ExtOut("Usage: DumpModule [-mt] <Module Address>\n");
5208 EnableDMLHolder dmlHolder(dml);
5209 DacpModuleData module;
5210 if ((Status=module.Request(g_sos, TO_CDADDR(p_ModuleAddr)))!=S_OK)
5212 ExtOut("Fail to fill Module %p\n", (ULONG64) p_ModuleAddr);
5216 WCHAR FileName[MAX_PATH];
5217 FileNameForModule (&module, FileName);
5218 ExtOut("Name: %S\n", FileName[0] ? FileName : L"Unknown Module");
5220 ExtOut("Attributes: ");
5221 if (module.bIsPEFile)
5223 if (module.bIsReflection)
5224 ExtOut("Reflection ");
5225 if (module.dwTransientFlags & SUPPORTS_UPDATEABLE_METHODS)
5226 ExtOut("SupportsUpdateableMethods");
5229 DMLOut("Assembly: %s\n", DMLAssembly(module.Assembly));
5231 ExtOut("LoaderHeap: %p\n", (ULONG64)module.pLookupTableHeap);
5232 ExtOut("TypeDefToMethodTableMap: %p\n", (ULONG64)module.TypeDefToMethodTableMap);
5233 ExtOut("TypeRefToMethodTableMap: %p\n", (ULONG64)module.TypeRefToMethodTableMap);
5234 ExtOut("MethodDefToDescMap: %p\n", (ULONG64)module.MethodDefToDescMap);
5235 ExtOut("FieldDefToDescMap: %p\n", (ULONG64)module.FieldDefToDescMap);
5236 ExtOut("MemberRefToDescMap: %p\n", (ULONG64)module.MemberRefToDescMap);
5237 ExtOut("FileReferencesMap: %p\n", (ULONG64)module.FileReferencesMap);
5238 ExtOut("AssemblyReferencesMap: %p\n", (ULONG64)module.ManifestModuleReferencesMap);
5240 if (module.ilBase && module.metadataStart)
5241 ExtOut("MetaData start address: %p (%d bytes)\n", (ULONG64)module.metadataStart, module.metadataSize);
5245 ExtOut("\nTypes defined in this module\n\n");
5246 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeDef", "Name");
5248 ExtOut("------------------------------------------------------------------------------\n");
5249 g_sos->TraverseModuleMap(TYPEDEFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);
5251 ExtOut("\nTypes referenced in this module\n\n");
5252 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeRef", "Name");
5254 ExtOut("------------------------------------------------------------------------------\n");
5255 g_sos->TraverseModuleMap(TYPEREFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);
5261 /**********************************************************************\
5262 * Routine Description: *
5264 * This function is called to dump the contents of a Domain *
5265 * for a given address *
5267 \**********************************************************************/
5268 DECLARE_API(DumpDomain)
5271 MINIDUMP_NOT_SUPPORTED();
5273 DWORD_PTR p_DomainAddr = 0;
5276 CMDOption option[] =
5277 { // name, vptr, type, hasValue
5278 {"/d", &dml, COBOOL, FALSE},
5282 {&p_DomainAddr, COHEX},
5286 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
5291 EnableDMLHolder dmlHolder(dml);
5293 DacpAppDomainStoreData adsData;
5294 if ((Status=adsData.Request(g_sos))!=S_OK)
5296 ExtOut("Unable to get AppDomain information\n");
5302 DacpAppDomainData appDomain1;
5303 if ((Status=appDomain1.Request(g_sos, TO_CDADDR(p_DomainAddr)))!=S_OK)
5305 ExtOut("Fail to fill AppDomain\n");
5309 ExtOut("--------------------------------------\n");
5311 if (p_DomainAddr == adsData.sharedDomain)
5313 DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain));
5315 else if (p_DomainAddr == adsData.systemDomain)
5317 DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain));
5321 DMLOut("Domain %d:%s %s\n", appDomain1.dwId, (appDomain1.dwId >= 10) ? "" : " ", DMLDomain(p_DomainAddr));
5324 DomainInfo(&appDomain1);
5328 ExtOut("--------------------------------------\n");
5329 DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain));
5330 DacpAppDomainData appDomain;
5331 if ((Status=appDomain.Request(g_sos,adsData.systemDomain))!=S_OK)
5333 ExtOut("Unable to get system domain info.\n");
5336 DomainInfo(&appDomain);
5338 ExtOut("--------------------------------------\n");
5339 DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain));
5340 if ((Status=appDomain.Request(g_sos, adsData.sharedDomain))!=S_OK)
5342 ExtOut("Unable to get shared domain info\n");
5345 DomainInfo(&appDomain);
5347 ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
5354 if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
5356 ExtOut("Unable to get array of AppDomains\n");
5360 for (int n=0;n<adsData.DomainCount;n++)
5365 if ((Status=appDomain.Request(g_sos, pArray[n])) != S_OK)
5367 ExtOut("Failed to get appdomain %p, error %lx\n", (ULONG64)pArray[n], Status);
5371 ExtOut("--------------------------------------\n");
5372 DMLOut("Domain %d:%s %s\n", appDomain.dwId, (appDomain.dwId >= 10) ? "" : " ", DMLDomain(pArray[n]));
5373 DomainInfo(&appDomain);
5379 /**********************************************************************\
5380 * Routine Description: *
5382 * This function is called to dump the contents of a Assembly *
5383 * for a given address *
5385 \**********************************************************************/
5386 DECLARE_API(DumpAssembly)
5389 MINIDUMP_NOT_SUPPORTED();
5391 DWORD_PTR p_AssemblyAddr = 0;
5394 CMDOption option[] =
5395 { // name, vptr, type, hasValue
5396 {"/d", &dml, COBOOL, FALSE},
5400 {&p_AssemblyAddr, COHEX},
5404 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
5409 EnableDMLHolder dmlHolder(dml);
5411 if (p_AssemblyAddr == 0)
5413 ExtOut("Invalid Assembly %s\n", args);
5417 DacpAssemblyData Assembly;
5418 if ((Status=Assembly.Request(g_sos, TO_CDADDR(p_AssemblyAddr)))!=S_OK)
5420 ExtOut("Fail to fill Assembly\n");
5423 DMLOut("Parent Domain: %s\n", DMLDomain(Assembly.ParentDomain));
5424 if (g_sos->GetAssemblyName(TO_CDADDR(p_AssemblyAddr), mdNameLen, g_mdName, NULL)==S_OK)
5425 ExtOut("Name: %S\n", g_mdName);
5427 ExtOut("Name: Unknown\n");
5429 AssemblyInfo(&Assembly);
5433 String GetHostingCapabilities(DWORD hostConfig)
5437 bool bAnythingPrinted = false;
5439 #define CHK_AND_PRINT(hType,hStr) \
5440 if (hostConfig & (hType)) { \
5441 if (bAnythingPrinted) result += ", "; \
5443 bAnythingPrinted = true; \
5446 CHK_AND_PRINT(CLRMEMORYHOSTED, "Memory");
5447 CHK_AND_PRINT(CLRTASKHOSTED, "Task");
5448 CHK_AND_PRINT(CLRSYNCHOSTED, "Sync");
5449 CHK_AND_PRINT(CLRTHREADPOOLHOSTED, "Threadpool");
5450 CHK_AND_PRINT(CLRIOCOMPLETIONHOSTED, "IOCompletion");
5451 CHK_AND_PRINT(CLRASSEMBLYHOSTED, "Assembly");
5452 CHK_AND_PRINT(CLRGCHOSTED, "GC");
5453 CHK_AND_PRINT(CLRSECURITYHOSTED, "Security");
5455 #undef CHK_AND_PRINT
5460 /**********************************************************************\
5461 * Routine Description: *
5463 * This function is called to dump the managed threads *
5465 \**********************************************************************/
5466 HRESULT PrintThreadsFromThreadStore(BOOL bMiniDump, BOOL bPrintLiveThreadsOnly)
5470 DacpThreadStoreData ThreadStore;
5471 if ((Status = ThreadStore.Request(g_sos)) != S_OK)
5473 Print("Failed to request ThreadStore\n");
5477 TableOutput table(2, 17);
5479 table.WriteRow("ThreadCount:", Decimal(ThreadStore.threadCount));
5480 table.WriteRow("UnstartedThread:", Decimal(ThreadStore.unstartedThreadCount));
5481 table.WriteRow("BackgroundThread:", Decimal(ThreadStore.backgroundThreadCount));
5482 table.WriteRow("PendingThread:", Decimal(ThreadStore.pendingThreadCount));
5483 table.WriteRow("DeadThread:", Decimal(ThreadStore.deadThreadCount));
5485 if (ThreadStore.fHostConfig & ~CLRHOSTED)
5487 String hosting = "yes";
5490 hosting += GetHostingCapabilities(ThreadStore.fHostConfig);
5493 table.WriteRow("Hosted Runtime:", hosting);
5497 table.WriteRow("Hosted Runtime:", "no");
5500 const bool hosted = (ThreadStore.fHostConfig & CLRTASKHOSTED) != 0;
5501 table.ReInit(hosted ? 12 : 11, POINTERSIZE_HEX);
5502 table.SetWidths(10, 4, 4, 4, max(9, POINTERSIZE_HEX),
5503 8, 11, 1+POINTERSIZE_HEX*2, POINTERSIZE_HEX,
5504 5, 3, POINTERSIZE_HEX);
5506 table.SetColAlignment(0, AlignRight);
5507 table.SetColAlignment(1, AlignRight);
5508 table.SetColAlignment(2, AlignRight);
5509 table.SetColAlignment(4, AlignRight);
5511 table.WriteColumn(8, "Lock");
5512 table.WriteRow("", "ID", "OSID", "ThreadOBJ", "State", "GC Mode", "GC Alloc Context", "Domain", "Count", "Apt");
5515 table.WriteColumn("Fiber");
5517 table.WriteColumn("Exception");
5519 DacpThreadData Thread;
5520 CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
5526 if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
5528 PrintLn("Failed to request Thread at ", Pointer(CurThread));
5532 BOOL bSwitchedOutFiber = Thread.osThreadId == SWITCHED_OUT_FIBER_OSID;
5533 if (!IsKernelDebugger())
5537 if (bSwitchedOutFiber)
5539 table.WriteColumn(0, "<<<< ");
5541 else if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
5543 table.WriteColumn(0, Decimal(id));
5545 else if (bPrintLiveThreadsOnly)
5547 CurThread = Thread.nextThread;
5552 table.WriteColumn(0, "XXXX ");
5556 table.WriteColumn(1, Decimal(Thread.corThreadId));
5557 table.WriteColumn(2, ThreadID(bSwitchedOutFiber ? 0 : Thread.osThreadId));
5558 table.WriteColumn(3, Pointer(CurThread));
5559 table.WriteColumn(4, ThreadState(Thread.state));
5560 table.WriteColumn(5, Thread.preemptiveGCDisabled == 1 ? "Cooperative" : "Preemptive");
5561 table.WriteColumnFormat(6, "%p:%p", Thread.allocContextPtr, Thread.allocContextLimit);
5565 table.WriteColumn(7, AppDomainPtr(Thread.domain));
5569 CLRDATA_ADDRESS domain = 0;
5570 if (FAILED(g_sos->GetDomainFromContext(Thread.context, &domain)))
5571 table.WriteColumn(7, "<error>");
5573 table.WriteColumn(7, AppDomainPtr(domain));
5576 table.WriteColumn(8, Decimal(Thread.lockCount));
5580 DWORD_PTR OleTlsDataAddr;
5581 if (!bSwitchedOutFiber
5582 && SafeReadMemory(Thread.teb + offsetof(TEB, ReservedForOle),
5584 sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0)
5587 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
5589 sizeof(AptState), NULL))
5591 if (AptState & OLETLS_APARTMENTTHREADED)
5592 table.WriteColumn(9, "STA");
5593 else if (AptState & OLETLS_MULTITHREADED)
5594 table.WriteColumn(9, "MTA");
5595 else if (AptState & OLETLS_INNEUTRALAPT)
5596 table.WriteColumn(9, "NTA");
5598 table.WriteColumn(9, "Ukn");
5602 table.WriteColumn(9, "Ukn");
5606 #endif // FEATURE_PAL
5607 table.WriteColumn(9, "Ukn");
5610 table.WriteColumn(10, Thread.fiberData);
5613 if (CurThread == ThreadStore.finalizerThread)
5614 lastCol += L"(Finalizer) ";
5615 if (CurThread == ThreadStore.gcThread)
5616 lastCol += L"(GC) ";
5618 const int TS_TPWorkerThread = 0x01000000; // is this a threadpool worker thread?
5619 const int TS_CompletionPortThread = 0x08000000; // is this is a completion port thread?
5621 if (Thread.state & TS_TPWorkerThread)
5622 lastCol += L"(Threadpool Worker) ";
5623 else if (Thread.state & TS_CompletionPortThread)
5624 lastCol += L"(Threadpool Completion Port) ";
5628 if (Thread.lastThrownObjectHandle && SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
5629 &taLTOH, sizeof(taLTOH), NULL) && taLTOH)
5632 if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
5634 if (NameForMT_s(taMT, g_mdName, mdNameLen))
5635 lastCol += WString(g_mdName) + L" " + ExceptionPtr(taLTOH);
5637 lastCol += WString(L"<Invalid Object> (") + Pointer(taLTOH) + L")";
5639 // Print something if there are nested exceptions on the thread
5640 if (Thread.firstNestedException)
5641 lastCol += L" (nested exceptions)";
5645 table.WriteColumn(lastCol);
5646 CurThread = Thread.nextThread;
5653 HRESULT PrintSpecialThreads()
5657 DWORD dwCLRTLSDataIndex = 0;
5658 HRESULT Status = g_sos->GetTLSIndex(&dwCLRTLSDataIndex);
5660 if (!SUCCEEDED (Status))
5662 Print("Failed to retrieve Tls Data index\n");
5667 ULONG ulOriginalThreadID = 0;
5668 Status = g_ExtSystem->GetCurrentThreadId (&ulOriginalThreadID);
5669 if (!SUCCEEDED (Status))
5671 Print("Failed to require current Thread ID\n");
5675 ULONG ulTotalThreads = 0;
5676 Status = g_ExtSystem->GetNumberThreads (&ulTotalThreads);
5677 if (!SUCCEEDED (Status))
5679 Print("Failed to require total thread number\n");
5683 TableOutput table(3, 4, AlignRight, 5);
5684 table.WriteRow("", "OSID", "Special thread type");
5686 for (ULONG ulThread = 0; ulThread < ulTotalThreads; ulThread++)
5690 HRESULT threadStatus = g_ExtSystem->GetThreadIdsByIndex(ulThread, 1, &Id, &SysId);
5691 if (!SUCCEEDED (threadStatus))
5693 PrintLn("Failed to get thread ID for thread ", Decimal(ulThread));
5697 threadStatus = g_ExtSystem->SetCurrentThreadId(Id);
5698 if (!SUCCEEDED (threadStatus))
5700 PrintLn("Failed to switch to thread ", ThreadID(SysId));
5704 CLRDATA_ADDRESS cdaTeb = 0;
5705 threadStatus = g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
5706 if (!SUCCEEDED (threadStatus))
5708 PrintLn("Failed to get Teb for Thread ", ThreadID(SysId));
5712 TADDR CLRTLSDataAddr = 0;
5714 #ifdef FEATURE_IMPLICIT_TLS
5715 TADDR tlsArrayAddr = NULL;
5716 if (!SafeReadMemory (TO_TADDR(cdaTeb) + WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer , &tlsArrayAddr, sizeof (void**), NULL))
5718 PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
5722 TADDR moduleTlsDataAddr = 0;
5724 if (!SafeReadMemory (tlsArrayAddr + sizeof (void*) * dwCLRTLSDataIndex, &moduleTlsDataAddr, sizeof (void**), NULL))
5726 PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
5730 CLRTLSDataAddr = moduleTlsDataAddr + OFFSETOF__TLS__tls_EETlsData;
5732 if (dwCLRTLSDataIndex < TLS_MINIMUM_AVAILABLE)
5734 CLRTLSDataAddr = TO_TADDR(cdaTeb) + offsetof(TEB, TlsSlots) + sizeof (void*) * dwCLRTLSDataIndex;
5738 //if TLS index is bigger than TLS_MINIMUM_AVAILABLE, the TLS slot lives in ExpansionSlots
5739 TADDR TebExpsionAddr = NULL;
5740 if (!SafeReadMemory (TO_TADDR(cdaTeb) + offsetof(TEB, TlsExpansionSlots) , &TebExpsionAddr, sizeof (void**), NULL))
5742 PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));
5746 if (TebExpsionAddr == NULL)
5751 CLRTLSDataAddr = TebExpsionAddr + sizeof (void*) * (dwCLRTLSDataIndex - TLS_MINIMUM_AVAILABLE);
5753 #endif // FEATURE_IMPLICIT_TLS
5755 TADDR CLRTLSData = NULL;
5756 if (!SafeReadMemory (CLRTLSDataAddr, &CLRTLSData, sizeof (TADDR), NULL))
5758 PrintLn("Failed to get CLR Tls data for thread ", ThreadID(SysId));
5762 if (CLRTLSData == NULL)
5767 size_t ThreadType = 0;
5768 if (!SafeReadMemory (CLRTLSData + sizeof (TADDR) * TlsIdx_ThreadType, &ThreadType, sizeof (size_t), NULL))
5770 PrintLn("Failed to get thread type info not found for thread ", ThreadID(SysId));
5774 if (ThreadType == 0)
5779 table.WriteColumn(0, Decimal(Id));
5780 table.WriteColumn(1, ThreadID(SysId));
5783 if (ThreadType & ThreadType_GC)
5787 if (ThreadType & ThreadType_Timer)
5791 if (ThreadType & ThreadType_Gate)
5795 if (ThreadType & ThreadType_DbgHelper)
5797 type += "DbgHelper ";
5799 if (ThreadType & ThreadType_Shutdown)
5801 type += "Shutdown ";
5803 if (ThreadType & ThreadType_DynamicSuspendEE)
5805 type += "SuspendEE ";
5807 if (ThreadType & ThreadType_Finalizer)
5809 type += "Finalizer ";
5811 if (ThreadType & ThreadType_ADUnloadHelper)
5813 type += "ADUnloadHelper ";
5815 if (ThreadType & ThreadType_ShutdownHelper)
5817 type += "ShutdownHelper ";
5819 if (ThreadType & ThreadType_Threadpool_IOCompletion)
5821 type += "IOCompletion ";
5823 if (ThreadType & ThreadType_Threadpool_Worker)
5825 type += "ThreadpoolWorker ";
5827 if (ThreadType & ThreadType_Wait)
5831 if (ThreadType & ThreadType_ProfAPI_Attach)
5833 type += "ProfilingAPIAttach ";
5835 if (ThreadType & ThreadType_ProfAPI_Detach)
5837 type += "ProfilingAPIDetach ";
5840 table.WriteColumn(2, type);
5843 Status = g_ExtSystem->SetCurrentThreadId (ulOriginalThreadID);
5844 if (!SUCCEEDED (Status))
5846 ExtOut("Failed to switch to original thread\n");
5852 #endif //FEATURE_PAL
5854 struct ThreadStateTable
5859 static const struct ThreadStateTable ThreadStates[] =
5861 {0x1, "Thread Abort Requested"},
5862 {0x2, "GC Suspend Pending"},
5863 {0x4, "User Suspend Pending"},
5864 {0x8, "Debug Suspend Pending"},
5865 {0x10, "GC On Transitions"},
5866 {0x20, "Legal to Join"},
5867 {0x40, "Yield Requested"},
5868 {0x80, "Hijacked by the GC"},
5869 {0x100, "Blocking GC for Stack Overflow"},
5870 {0x200, "Background"},
5871 {0x400, "Unstarted"},
5873 {0x1000, "CLR Owns"},
5874 {0x2000, "CoInitialized"},
5875 {0x4000, "In Single Threaded Apartment"},
5876 {0x8000, "In Multi Threaded Apartment"},
5877 {0x10000, "Reported Dead"},
5878 {0x20000, "Fully initialized"},
5879 {0x40000, "Task Reset"},
5880 {0x80000, "Sync Suspended"},
5881 {0x100000, "Debug Will Sync"},
5882 {0x200000, "Stack Crawl Needed"},
5883 {0x400000, "Suspend Unstarted"},
5884 {0x800000, "Aborted"},
5885 {0x1000000, "Thread Pool Worker Thread"},
5886 {0x2000000, "Interruptible"},
5887 {0x4000000, "Interrupted"},
5888 {0x8000000, "Completion Port Thread"},
5889 {0x10000000, "Abort Initiated"},
5890 {0x20000000, "Finalized"},
5891 {0x40000000, "Failed to Start"},
5892 {0x80000000, "Detached"},
5895 DECLARE_API(ThreadState)
5899 size_t state = GetExpression(args);
5905 for (unsigned int i = 0; i < _countof(ThreadStates); ++i)
5906 if (state & ThreadStates[i].State)
5908 ExtOut(" %s\n", ThreadStates[i].Name);
5913 // If we did not find any thread states, print out a message to let the user
5914 // know that the function is working correctly.
5916 ExtOut(" No thread states for '%s'.", args);
5921 DECLARE_API(Threads)
5925 BOOL bPrintSpecialThreads = FALSE;
5926 BOOL bPrintLiveThreadsOnly = FALSE;
5929 CMDOption option[] =
5930 { // name, vptr, type, hasValue
5931 {"-special", &bPrintSpecialThreads, COBOOL, FALSE},
5932 {"-live", &bPrintLiveThreadsOnly, COBOOL, FALSE},
5933 {"/d", &dml, COBOOL, FALSE},
5935 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
5940 // We need to support minidumps for this command.
5941 BOOL bMiniDump = IsMiniDumpFile();
5943 if (bMiniDump && bPrintSpecialThreads)
5945 Print("Special thread information is not available in mini dumps.\n");
5948 EnableDMLHolder dmlHolder(dml);
5952 Status = PrintThreadsFromThreadStore(bMiniDump, bPrintLiveThreadsOnly);
5953 if (!bMiniDump && bPrintSpecialThreads)
5956 Print("\n-special not supported.\n");
5958 HRESULT Status2 = PrintSpecialThreads();
5959 if (!SUCCEEDED(Status2))
5961 #endif //FEATURE_PAL
5964 catch (sos::Exception &e)
5966 ExtOut("%s\n", e.what());
5973 /**********************************************************************\
5974 * Routine Description: *
5976 * This function is called to dump the Watson Buckets. *
5978 \**********************************************************************/
5979 DECLARE_API(WatsonBuckets)
5983 // We don't need to support minidumps for this command.
5984 if (IsMiniDumpFile())
5986 ExtOut("Not supported on mini dumps.\n");
5989 // Get the current managed thread.
5990 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
5991 DacpThreadData Thread;
5993 if ((threadAddr == NULL) || ((Status = Thread.Request(g_sos, threadAddr)) != S_OK))
5995 ExtOut("The current thread is unmanaged\n");
5999 // Get the definition of GenericModeBlock.
6001 GenericModeBlock gmb;
6003 if ((Status = g_sos->GetClrWatsonBuckets(threadAddr, &gmb)) != S_OK)
6005 ExtOut("Can't get Watson Buckets\n");
6009 ExtOut("Watson Bucket parameters:\n");
6010 ExtOut("b1: %S\n", gmb.wzP1);
6011 ExtOut("b2: %S\n", gmb.wzP2);
6012 ExtOut("b3: %S\n", gmb.wzP3);
6013 ExtOut("b4: %S\n", gmb.wzP4);
6014 ExtOut("b5: %S\n", gmb.wzP5);
6015 ExtOut("b6: %S\n", gmb.wzP6);
6016 ExtOut("b7: %S\n", gmb.wzP7);
6017 ExtOut("b8: %S\n", gmb.wzP8);
6018 ExtOut("b9: %S\n", gmb.wzP9);
6021 } // WatsonBuckets()
6023 struct PendingBreakpoint
6025 WCHAR szModuleName[MAX_PATH];
6026 WCHAR szFunctionName[mdNameLen];
6027 WCHAR szFilename[MAX_PATH];
6031 mdMethodDef methodToken;
6032 void SetModule(TADDR module)
6037 bool ModuleMatches(TADDR compare)
6039 return (compare == pModule);
6042 PendingBreakpoint *pNext;
6043 PendingBreakpoint() : pNext(NULL), ilOffset(0), lineNumber(0), methodToken(0)
6045 szModuleName[0] = L'\0';
6046 szFunctionName[0] = L'\0';
6047 szFilename[0] = L'\0';
6051 void IssueDebuggerBPCommand ( CLRDATA_ADDRESS addr )
6053 const int MaxBPsCached = 1024;
6054 static CLRDATA_ADDRESS alreadyPlacedBPs[MaxBPsCached];
6055 static int curLimit = 0;
6057 // on ARM the debugger requires breakpoint addresses to be sanitized
6058 if (IsDbgTargetArm())
6059 addr &= ~THUMB_CODE;
6062 // if we overflowed our cache consider all new BPs unique...
6063 BOOL bUnique = curLimit >= MaxBPsCached;
6067 for ( int i = 0; i < curLimit; ++i )
6068 if (alreadyPlacedBPs[i] == addr)
6076 char buffer[64]; // sufficient for "bp <pointersize>"
6077 static WCHAR wszNameBuffer[1024]; // should be large enough
6079 // get the MethodDesc name
6080 CLRDATA_ADDRESS pMD;
6081 if (g_sos->GetMethodDescPtrFromIP(addr, &pMD) != S_OK
6082 || g_sos->GetMethodDescName(pMD, 1024, wszNameBuffer, NULL) != S_OK)
6084 wcscpy_s(wszNameBuffer, _countof(wszNameBuffer),L"UNKNOWN");
6087 sprintf_s(buffer, _countof(buffer), "bp %p", (void*) (size_t) addr);
6088 ExtOut("Setting breakpoint: %s [%S]\n", buffer, wszNameBuffer);
6089 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
6091 if ( curLimit < MaxBPsCached )
6092 alreadyPlacedBPs[curLimit++] = addr;
6098 PendingBreakpoint* m_breakpoints;
6102 m_breakpoints = NULL;
6106 PendingBreakpoint *pCur = m_breakpoints;
6109 PendingBreakpoint *pNext = pCur->pNext;
6113 m_breakpoints = NULL;
6116 void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod, DWORD ilOffset)
6118 if (!IsIn(szModule, szName, mod))
6120 PendingBreakpoint *pNew = new PendingBreakpoint();
6121 wcscpy_s(pNew->szModuleName, MAX_PATH, szModule);
6122 wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
6123 pNew->SetModule(mod);
6124 pNew->ilOffset = ilOffset;
6125 pNew->pNext = m_breakpoints;
6126 m_breakpoints = pNew;
6130 void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
6132 if (!IsIn(methodToken, mod, ilOffset))
6134 PendingBreakpoint *pNew = new PendingBreakpoint();
6135 wcscpy_s(pNew->szModuleName, MAX_PATH, szModule);
6136 wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
6137 pNew->methodToken = methodToken;
6138 pNew->SetModule(mod);
6139 pNew->ilOffset = ilOffset;
6140 pNew->pNext = m_breakpoints;
6141 m_breakpoints = pNew;
6145 void Add(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
6147 if (!IsIn(szFilename, lineNumber, mod))
6149 PendingBreakpoint *pNew = new PendingBreakpoint();
6150 wcscpy_s(pNew->szFilename, MAX_PATH, szFilename);
6151 pNew->lineNumber = lineNumber;
6152 pNew->SetModule(mod);
6153 pNew->pNext = m_breakpoints;
6154 m_breakpoints = pNew;
6158 void Add(__in_z LPWSTR szFilename, DWORD lineNumber, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
6160 if (!IsIn(methodToken, mod, ilOffset))
6162 PendingBreakpoint *pNew = new PendingBreakpoint();
6163 wcscpy_s(pNew->szFilename, MAX_PATH, szFilename);
6164 pNew->lineNumber = lineNumber;
6165 pNew->methodToken = methodToken;
6166 pNew->SetModule(mod);
6167 pNew->ilOffset = ilOffset;
6168 pNew->pNext = m_breakpoints;
6169 m_breakpoints = pNew;
6173 //returns true if updates are still needed for this module, FALSE if all BPs are now bound
6174 BOOL Update(TADDR mod, BOOL isNewModule)
6176 BOOL bNeedUpdates = FALSE;
6177 PendingBreakpoint *pCur = NULL;
6181 SymbolReader symbolReader;
6182 SymbolReader* pSymReader = &symbolReader;
6183 if(LoadSymbolsForModule(mod, &symbolReader) != S_OK)
6186 // Get tokens for any modules that match. If there was a change,
6187 // update notifications.
6188 pCur = m_breakpoints;
6191 PendingBreakpoint *pNext = pCur->pNext;
6192 ResolvePendingNonModuleBoundBreakpoint(mod, pCur, pSymReader);
6197 pCur = m_breakpoints;
6200 PendingBreakpoint *pNext = pCur->pNext;
6201 if (ResolvePendingBreakpoint(mod, pCur))
6203 bNeedUpdates = TRUE;
6207 return bNeedUpdates;
6210 void RemovePendingForModule(TADDR mod)
6212 PendingBreakpoint *pCur = m_breakpoints;
6215 PendingBreakpoint *pNext = pCur->pNext;
6216 if (pCur->ModuleMatches(mod))
6218 // Delete the current node, and keep going
6226 void ListBreakpoints()
6228 PendingBreakpoint *pCur = m_breakpoints;
6229 size_t iBreakpointIndex = 1;
6230 ExtOut("!bpmd pending breakpoint list\n Breakpoint index - Location, ModuleID, Method Token\n");
6233 //windbg likes to format %p as always being 64 bits
6234 ULONG64 modulePtr = (ULONG64)pCur->pModule;
6236 if(pCur->szModuleName[0] != L'\0')
6237 ExtOut("%d - %ws!%ws+%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset, modulePtr, pCur->methodToken);
6239 ExtOut("%d - %ws:%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szFilename, pCur->lineNumber, modulePtr, pCur->methodToken);
6245 void SaveBreakpoints(FILE* pFile)
6247 PendingBreakpoint *pCur = m_breakpoints;
6250 if(pCur->szModuleName[0] != L'\0')
6251 fprintf_s(pFile, "!bpmd %ws %ws %d\n", pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset);
6253 fprintf_s(pFile, "!bpmd %ws:%d\n", pCur->szFilename, pCur->lineNumber);
6258 void ClearBreakpoint(size_t breakPointToClear)
6260 PendingBreakpoint *pCur = m_breakpoints;
6261 size_t iBreakpointIndex = 1;
6264 if (breakPointToClear == iBreakpointIndex)
6266 ExtOut("%d - %ws, %ws, %p\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->pModule);
6267 ExtOut("Cleared\n");
6277 ExtOut("Invalid pending breakpoint index.\n");
6281 void ClearAllBreakpoints()
6283 size_t iBreakpointIndex = 1;
6284 for (PendingBreakpoint *pCur = m_breakpoints; pCur != NULL; )
6286 PendingBreakpoint* pNext = pCur->pNext;
6291 ExtOut("All pending breakpoints cleared.\n");
6294 HRESULT LoadSymbolsForModule(TADDR mod, SymbolReader* pSymbolReader)
6296 HRESULT Status = S_OK;
6297 ToRelease<IXCLRDataModule> module;
6298 IfFailRet(g_sos->GetModule(mod, &module));
6300 ToRelease<IMetaDataImport> pMDImport = NULL;
6301 IfFailRet(module->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
6303 WCHAR wszNameBuffer[MAX_PATH];
6304 ULONG32 nameLen = 0;
6305 if(FAILED(Status = module->GetFileName(MAX_PATH, &nameLen, wszNameBuffer)))
6307 ExtOut("SOS error: IXCLRDataModule->GetFileName failed hr=0x%x\n", wszNameBuffer);
6311 //get a pointer to just the filename (the portion after the last backslash)
6312 WCHAR* pModuleFilename = wszNameBuffer;
6313 WCHAR* pSlash = wcschr(pModuleFilename, L'\\');
6314 while(pSlash != NULL)
6316 pModuleFilename = pSlash+1;
6317 pSlash = wcschr(pModuleFilename, L'\\');
6321 if(FAILED(Status = GetClrModuleImages(module, CLRDATA_MODULE_PE_FILE, &ii)))
6323 ExtOut("SOS error: GetClrModuleImages failed hr=0x%x\n", Status);
6327 if(FAILED(Status = pSymbolReader->LoadSymbols(pMDImport, ii.modBase, pModuleFilename, FALSE)) &&
6328 FAILED(pSymbolReader->LoadSymbols(pMDImport, ii.modBase, pModuleFilename, TRUE)))
6330 ExtOut("SOS warning: No symbols for module %S, source line breakpoints in this module will not bind hr=0x%x\n", wszNameBuffer, Status);
6331 return S_FALSE; // not finding symbols is a typical case
6337 HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pFilename, DWORD lineNumber, TADDR mod, SymbolReader* pSymbolReader)
6339 HRESULT Status = S_OK;
6340 if(pSymbolReader == NULL)
6341 return S_FALSE; // no symbols, can't bind here
6343 mdMethodDef methodDef;
6345 if(FAILED(Status = pSymbolReader->ResolveSequencePoint(pFilename, lineNumber, &methodDef, &ilOffset)))
6347 return S_FALSE; // not binding in a module is typical
6350 Add(pFilename, lineNumber, methodDef, mod, ilOffset);
6354 HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pModuleName, __in_z WCHAR* pMethodName, TADDR mod, DWORD ilOffset)
6357 char szName[mdNameLen];
6359 ToRelease<IXCLRDataModule> module;
6360 HRESULT Status = S_OK;
6361 IfFailRet(g_sos->GetModule(mod, &module));
6363 WideCharToMultiByte(CP_ACP, 0, pModuleName, (int) (wcslen(pModuleName) + 1),
6364 szName, mdNameLen, NULL, NULL);
6366 ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(szName, &numModule);
6367 if (moduleList == NULL)
6369 ExtOut("Failed to request module list.\n");
6373 for(int i=0;i<numModule;i++)
6375 // If any one entry in moduleList matches, then the current PendingBreakpoint
6376 // is the right one.
6377 if(moduleList[i] != TO_TADDR(mod))
6381 if (module->StartEnumMethodDefinitionsByName(pMethodName, 0, &h) == S_OK)
6383 IXCLRDataMethodDefinition *pMeth = NULL;
6384 while (module->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
6386 mdMethodDef methodToken;
6387 ToRelease<IXCLRDataModule> pUnusedModule;
6388 IfFailRet(pMeth->GetTokenAndScope(&methodToken, &pUnusedModule));
6390 Add(pModuleName, pMethodName, methodToken, mod, ilOffset);
6393 module->EndEnumMethodDefinitionsByName(h);
6399 // Return TRUE if there might be more instances that will be JITTED later
6400 static BOOL ResolveMethodInstances(IXCLRDataMethodDefinition *pMeth, DWORD ilOffset)
6402 BOOL bFoundCode = FALSE;
6403 BOOL bNeedDefer = FALSE;
6406 if (pMeth->StartEnumInstances (NULL, &h1) == S_OK)
6408 IXCLRDataMethodInstance *inst = NULL;
6409 while (pMeth->EnumInstance (&h1, &inst) == S_OK)
6411 BOOL foundByIlOffset = FALSE;
6412 ULONG32 rangesNeeded = 0;
6413 if(inst->GetAddressRangesByILOffset(ilOffset, 0, &rangesNeeded, NULL) == S_OK)
6415 ArrayHolder<CLRDATA_ADDRESS_RANGE> ranges = new NOTHROW CLRDATA_ADDRESS_RANGE[rangesNeeded];
6418 if (inst->GetAddressRangesByILOffset(ilOffset, rangesNeeded, NULL, ranges) == S_OK)
6420 for (DWORD i = 0; i < rangesNeeded; i++)
6422 IssueDebuggerBPCommand(ranges[i].startAddress);
6424 foundByIlOffset = TRUE;
6430 if (!foundByIlOffset && ilOffset == 0)
6432 CLRDATA_ADDRESS addr = 0;
6433 if (inst->GetRepresentativeEntryAddress(&addr) == S_OK)
6435 IssueDebuggerBPCommand(addr);
6440 pMeth->EndEnumInstances (h1);
6443 // if this is a generic method we need to add a defered bp
6444 BOOL bGeneric = FALSE;
6445 pMeth->HasClassOrMethodInstantiation(&bGeneric);
6447 bNeedDefer = !bFoundCode || bGeneric;
6448 // This is down here because we only need to call SetCodeNofiication once.
6451 if (pMeth->SetCodeNotification (CLRDATA_METHNOTIFY_GENERATED) != S_OK)
6454 ExtOut("Failed to set code notification\n");
6461 BOOL IsIn(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod)
6463 PendingBreakpoint *pCur = m_breakpoints;
6466 if (pCur->ModuleMatches(mod) &&
6467 _wcsicmp(pCur->szModuleName, szModule) == 0 &&
6468 wcscmp(pCur->szFunctionName, szName) == 0)
6477 BOOL IsIn(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
6479 PendingBreakpoint *pCur = m_breakpoints;
6482 if (pCur->ModuleMatches(mod) &&
6483 _wcsicmp(pCur->szFilename, szFilename) == 0 &&
6484 pCur->lineNumber == lineNumber)
6493 BOOL IsIn(mdMethodDef token, TADDR mod, DWORD ilOffset)
6495 PendingBreakpoint *pCur = m_breakpoints;
6498 if (pCur->ModuleMatches(mod) &&
6499 pCur->methodToken == token &&
6500 pCur->ilOffset == ilOffset)
6509 void Delete(PendingBreakpoint *pDelete)
6511 PendingBreakpoint *pCur = m_breakpoints;
6512 PendingBreakpoint *pPrev = NULL;
6515 if (pCur == pDelete)
6519 m_breakpoints = pCur->pNext;
6523 pPrev->pNext = pCur->pNext;
6535 HRESULT ResolvePendingNonModuleBoundBreakpoint(TADDR mod, PendingBreakpoint *pCur, SymbolReader* pSymbolReader)
6537 // This function only works with pending breakpoints that are not module bound.
6538 if (pCur->pModule == NULL)
6540 if(pCur->szModuleName[0] != L'\0')
6542 return ResolvePendingNonModuleBoundBreakpoint(pCur->szModuleName, pCur->szFunctionName, mod, pCur->ilOffset);
6546 return ResolvePendingNonModuleBoundBreakpoint(pCur->szFilename, pCur->lineNumber, mod, pSymbolReader);
6555 // Returns TRUE if further instances may be jitted, FALSE if all instances are now resolved
6556 BOOL ResolvePendingBreakpoint(TADDR addr, PendingBreakpoint *pCur)
6558 // Only go forward if the module matches the current PendingBreakpoint
6559 if (!pCur->ModuleMatches(addr))
6564 ToRelease<IXCLRDataModule> mod;
6565 if (FAILED(g_sos->GetModule(addr, &mod)))
6570 if(pCur->methodToken == 0)
6575 ToRelease<IXCLRDataMethodDefinition> pMeth = NULL;
6576 mod->GetMethodDefinitionByToken(pCur->methodToken, &pMeth);
6577 // We may not need the code notification. Maybe it was ngen'd and we
6578 // already have the method?
6579 // We can delete the current entry if ResolveMethodInstances() set all BPs
6580 return ResolveMethodInstances(pMeth, pCur->ilOffset);
6584 Breakpoints g_bpoints;
6587 // Controls whether optimizations are disabled on module load and whether NGEN can be used
6588 BOOL g_fAllowJitOptimization = TRUE;
6590 // Controls whether a one-shot breakpoint should be inserted the next time
6591 // execution is about to enter a catch clause
6592 BOOL g_stopOnNextCatch = FALSE;
6594 // According to the latest debuggers these callbacks will not get called
6595 // unless the user (or an extension, like SOS :-)) had previously enabled
6596 // clrn with "sxe clrn".
6597 class CNotification : public IXCLRDataExceptionNotification4
6599 static int s_condemnedGen;
6606 , m_dbgStatus(DEBUG_STATUS_NO_CHANGE)
6609 int GetDebugStatus()
6614 STDMETHODIMP QueryInterface (REFIID iid, void **ppvObject)
6616 if (ppvObject == NULL)
6617 return E_INVALIDARG;
6619 if (IsEqualIID(iid, IID_IUnknown)
6620 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification)
6621 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification2)
6622 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification3)
6623 || IsEqualIID(iid, IID_IXCLRDataExceptionNotification4))
6625 *ppvObject = static_cast<IXCLRDataExceptionNotification4*>(this);
6630 return E_NOINTERFACE;
6634 STDMETHODIMP_(ULONG) AddRef(void) { return ++m_count; }
6635 STDMETHODIMP_(ULONG) Release(void)
6647 * New code was generated or discarded for a method.:
6649 STDMETHODIMP OnCodeGenerated(IXCLRDataMethodInstance* method)
6651 // Some method has been generated, make a breakpoint and remove it.
6652 ULONG32 len = mdNameLen;
6653 LPWSTR szModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
6654 if (method->GetName(0, mdNameLen, &len, g_mdName) == S_OK)
6656 ToRelease<IXCLRDataModule> pMod;
6657 HRESULT hr = method->GetTokenAndScope(NULL, &pMod);
6661 if (pMod->GetName(mdNameLen, &len, szModuleName) == S_OK)
6663 ExtOut("JITTED %S!%S\n", szModuleName, g_mdName);
6665 // Add breakpoint, perhaps delete pending breakpoint
6666 DacpGetModuleAddress dgma;
6667 if (SUCCEEDED(dgma.Request(pMod)))
6670 g_bpoints.Update(TO_TADDR(dgma.ModulePtr), FALSE);
6675 ExtOut("Failed to request module address.\n");
6681 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6685 STDMETHODIMP OnCodeDiscarded(IXCLRDataMethodInstance* method)
6691 * The process or task reached the desired execution state.
6693 STDMETHODIMP OnProcessExecution(ULONG32 state) { return E_NOTIMPL; }
6694 STDMETHODIMP OnTaskExecution(IXCLRDataTask* task,
6695 ULONG32 state) { return E_NOTIMPL; }
6698 * The given module was loaded or unloaded.
6700 STDMETHODIMP OnModuleLoaded(IXCLRDataModule* mod)
6702 DacpGetModuleAddress dgma;
6703 if (SUCCEEDED(dgma.Request(mod)))
6706 g_bpoints.Update(TO_TADDR(dgma.ModulePtr), TRUE);
6710 if(!g_fAllowJitOptimization)
6713 ToRelease<IXCLRDataModule2> mod2;
6714 if(FAILED(mod->QueryInterface(__uuidof(IXCLRDataModule2), (void**) &mod2)))
6716 ExtOut("SOS: warning, optimizations for this module could not be suppressed because this CLR version doesn't support the functionality\n");
6718 else if(FAILED(hr = mod2->SetJITCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION)))
6720 if(hr == CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE)
6721 ExtOut("SOS: warning, optimizations for this module could not be surpressed because an optimized prejitted image was loaded\n");
6723 ExtOut("SOS: warning, optimizations for this module could not be surpressed hr=0x%x\n", hr);
6727 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6731 STDMETHODIMP OnModuleUnloaded(IXCLRDataModule* mod)
6733 DacpGetModuleAddress dgma;
6734 if (SUCCEEDED(dgma.Request(mod)))
6737 g_bpoints.RemovePendingForModule(TO_TADDR(dgma.ModulePtr));
6741 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6746 * The given type was loaded or unloaded.
6748 STDMETHODIMP OnTypeLoaded(IXCLRDataTypeInstance* typeInst)
6749 { return E_NOTIMPL; }
6750 STDMETHODIMP OnTypeUnloaded(IXCLRDataTypeInstance* typeInst)
6751 { return E_NOTIMPL; }
6753 STDMETHODIMP OnAppDomainLoaded(IXCLRDataAppDomain* domain)
6754 { return E_NOTIMPL; }
6755 STDMETHODIMP OnAppDomainUnloaded(IXCLRDataAppDomain* domain)
6756 { return E_NOTIMPL; }
6757 STDMETHODIMP OnException(IXCLRDataExceptionState* exception)
6758 { return E_NOTIMPL; }
6760 STDMETHODIMP OnGcEvent(GcEvtArgs gcEvtArgs)
6762 // by default don't stop on these notifications...
6763 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6765 IXCLRDataProcess2* idp2 = NULL;
6766 if (SUCCEEDED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
6768 if (gcEvtArgs.typ == GC_MARK_END)
6770 // erase notification request
6771 GcEvtArgs gea = { GC_MARK_END, { 0 } };
6772 idp2->SetGcNotification(gea);
6774 s_condemnedGen = bitidx(gcEvtArgs.condemnedGeneration);
6776 ExtOut("CLR notification: GC - Performing a gen %d collection. Determined surviving objects...\n", s_condemnedGen);
6778 // GC_MARK_END notification means: give the user a chance to examine the debuggee
6779 m_dbgStatus = DEBUG_STATUS_BREAK;
6787 * Catch is about to be entered
6789 STDMETHODIMP ExceptionCatcherEnter(IXCLRDataMethodInstance* method, DWORD catcherNativeOffset)
6791 if(g_stopOnNextCatch)
6793 CLRDATA_ADDRESS startAddr;
6794 if(method->GetRepresentativeEntryAddress(&startAddr) == S_OK)
6797 sprintf_s(buffer, _countof(buffer), "bp /1 %p", (void*) (size_t) (startAddr+catcherNativeOffset));
6798 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
6800 g_stopOnNextCatch = FALSE;
6803 m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6807 static int GetCondemnedGen()
6809 return s_condemnedGen;
6814 int CNotification::s_condemnedGen = -1;
6816 BOOL CheckCLRNotificationEvent(DEBUG_LAST_EVENT_INFO_EXCEPTION* pdle)
6818 ULONG Type, ProcessId, ThreadId;
6819 ULONG ExtraInformationUsed;
6820 HRESULT Status = g_ExtControl->GetLastEventInformation( &Type,
6824 sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION),
6825 &ExtraInformationUsed,
6830 if (Status != S_OK || Type != DEBUG_EVENT_EXCEPTION)
6835 if (!pdle->FirstChance || pdle->ExceptionRecord.ExceptionCode != CLRDATA_NOTIFY_EXCEPTION)
6843 HRESULT HandleCLRNotificationEvent()
6845 HRESULT Status = E_FAIL;
6847 * Did we get module load notification? If so, check if any in our pending list
6848 * need to be registered for jit notification.
6850 * Did we get a jit notification? If so, check if any can be removed and
6851 * real breakpoints be set.
6853 DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
6854 CNotification Notification;
6856 if (!CheckCLRNotificationEvent(&dle))
6858 ExtOut("Expecting first chance CLRN exception\n");
6862 // Notification only needs to live for the lifetime of the call below, so it's a non-static
6864 if (g_clrData->TranslateExceptionRecordToNotification(&dle.ExceptionRecord, &Notification) != S_OK)
6866 ExtOut("Error processing exception notification\n");
6871 switch (Notification.GetDebugStatus())
6873 case DEBUG_STATUS_GO:
6874 case DEBUG_STATUS_GO_HANDLED:
6875 case DEBUG_STATUS_GO_NOT_HANDLED:
6876 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "g", 0);
6887 DECLARE_API(HandleCLRN)
6890 MINIDUMP_NOT_SUPPORTED();
6892 return HandleCLRNotificationEvent();
6899 MINIDUMP_NOT_SUPPORTED();
6905 ExtOut("!bpmd is not supported on a dump file.\n");
6909 // We keep a list of managed breakpoints the user wants to set, and display pending bps
6910 // bpmd. If you call bpmd <module name> <method> we will set or update an existing bp.
6911 // bpmd acts as a feeder of breakpoints to bp when the time is right.
6914 StringHolder DllName,TypeName;
6918 DWORD_PTR pMD = NULL;
6919 BOOL fNoFutureModule = FALSE;
6921 size_t clearItem = 0;
6922 BOOL fClearAll = FALSE;
6923 CMDOption option[] =
6924 { // name, vptr, type, hasValue
6925 {"-md", &pMD, COHEX, TRUE},
6926 {"-nofuturemodule", &fNoFutureModule, COBOOL, FALSE},
6927 {"-list", &fList, COBOOL, FALSE},
6928 {"-clear", &clearItem, COSIZE_T, TRUE},
6929 {"-clearall", &fClearAll, COBOOL, FALSE},
6933 {&DllName.data, COSTRING},
6934 {&TypeName.data, COSTRING},
6935 {&Offset, COSIZE_T},
6938 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
6943 bool fBadParam = false;
6944 bool fIsFilename = false;
6945 int commandsParsed = 0;
6979 if (1 <= nArg && nArg <= 3)
6982 // did we get dll and type name or file:line#? Search for a colon in the first arg
6983 // to see if it is in fact a file:line#
6984 CHAR* pColon = strchr(DllName.data, ':');
6990 if(1 != sscanf_s(pColon, "%d", &lineNumber))
6992 ExtOut("Unable to parse line number\n");
6995 else if(lineNumber < 0)
6997 ExtOut("Line number must be positive\n");
7000 if(nArg != 1) fBadParam = 1;
7004 if (fBadParam || (commandsParsed != 1))
7006 ExtOut("Usage: !bpmd -md <MethodDesc pointer>\n");
7007 ExtOut("Usage: !bpmd [-nofuturemodule] <module name> <managed function name> [<il offset>]\n");
7008 ExtOut("Usage: !bpmd <filename>:<line number>\n");
7009 ExtOut("Usage: !bpmd -list\n");
7010 ExtOut("Usage: !bpmd -clear <pending breakpoint number>\n");
7011 ExtOut("Usage: !bpmd -clearall\n");
7012 ExtOut("See \"!help bpmd\" for more details.\n");
7018 g_bpoints.ListBreakpoints();
7023 g_bpoints.ClearBreakpoint(clearItem);
7028 g_bpoints.ClearAllBreakpoints();
7032 // Do we already have this breakpoint?
7033 // Or, before setting it, is the module perhaps already loaded and code
7034 // is available? If so, don't add to our pending list, just go ahead and
7035 // set the real breakpoint.
7037 LPWSTR ModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
7038 LPWSTR FunctionName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
7039 LPWSTR Filename = (LPWSTR)alloca(MAX_PATH * sizeof(WCHAR));
7041 BOOL bNeedNotificationExceptions=FALSE;
7048 ArrayHolder<DWORD_PTR> moduleList = NULL;
7052 MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, ModuleName, mdNameLen);
7053 MultiByteToWideChar(CP_ACP, 0, TypeName.data, -1, FunctionName, mdNameLen);
7057 MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, Filename, MAX_PATH);
7060 // get modules that may need a breakpoint bound
7061 if ((Status = CheckEEDll()) == S_OK)
7063 if ((Status = LoadClrDebugDll()) != S_OK)
7065 // if the EE is loaded but DAC isn't we should stop.
7071 // get the module list
7072 moduleList =ModuleFromName(fIsFilename ? NULL : DllName.data, &numModule);
7074 // Its OK if moduleList is NULL
7075 // There is a very normal case when checking for modules after clr is loaded
7076 // but before any AppDomains or assemblies are created
7082 // >!bpmd Foo.dll Foo.Bar
7087 // we can get here with EE not loaded => 0 modules
7088 // EE is loaded => 0 or more modules
7089 ArrayHolder<DWORD_PTR> pMDs = NULL;
7090 for (int iModule = 0; iModule < numModule; iModule++)
7092 ToRelease<IXCLRDataModule> ModDef;
7093 if (g_sos->GetModule(moduleList[iModule], &ModDef) != S_OK)
7096 HRESULT symbolsLoaded = S_FALSE;
7099 g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, moduleList[iModule], (DWORD)Offset);
7103 SymbolReader symbolReader;
7104 symbolsLoaded = g_bpoints.LoadSymbolsForModule(moduleList[iModule], &symbolReader);
7105 if(symbolsLoaded == S_OK &&
7106 g_bpoints.ResolvePendingNonModuleBoundBreakpoint(Filename, lineNumber, moduleList[iModule], &symbolReader) == S_OK)
7108 // if we have symbols then get the function name so we can lookup the MethodDescs
7109 mdMethodDef methodDefToken;
7111 if(SUCCEEDED(symbolReader.ResolveSequencePoint(Filename, lineNumber, &methodDefToken, &ilOffset)))
7113 ToRelease<IXCLRDataMethodDefinition> pMethodDef = NULL;
7114 if (SUCCEEDED(ModDef->GetMethodDefinitionByToken(methodDefToken, &pMethodDef)))
7116 ULONG32 nameLen = 0;
7117 pMethodDef->GetName(0, mdNameLen, &nameLen, FunctionName);
7119 // get the size of the required buffer
7120 int buffSize = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, 0, NULL, NULL);
7122 TypeName.data = new NOTHROW char[buffSize];
7123 if (TypeName.data != NULL)
7125 int bytesWritten = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, buffSize, NULL, NULL);
7126 _ASSERTE(bytesWritten == buffSize);
7133 HRESULT gotMethodDescs = GetMethodDescsFromName(moduleList[iModule], ModDef, TypeName.data, &pMDs, &numMethods);
7134 if (FAILED(gotMethodDescs) && (!fIsFilename))
7136 // BPs via file name will enumerate through modules so there will be legitimate failures.
7137 // for module/type name we already found a match so this shouldn't fail (this is the original behavior).
7138 ExtOut("Error getting MethodDescs for module %p\n", moduleList[iModule]);
7142 // for filename+line number only print extra info if symbols for this module are loaded (it can get quite noisy otherwise).
7143 if ((!fIsFilename) || (fIsFilename && symbolsLoaded == S_OK))
7145 ExtOut("Found %d methods in module %p...\n", numMethods, moduleList[iModule]);
7147 for (int i = 0; i < numMethods; i++)
7149 if (pMDs[i] == MD_NOT_YET_LOADED)
7153 ExtOut("MethodDesc = %p\n", (ULONG64) pMDs[i]);
7157 if(g_bpoints.Update(moduleList[iModule], FALSE))
7158 bNeedNotificationExceptions = TRUE;
7161 if (!fNoFutureModule)
7163 // add a pending breakpoint that will find future loaded modules, and
7164 // wait for the module load notification.
7167 g_bpoints.Add(ModuleName, FunctionName, NULL, (DWORD)Offset);
7171 g_bpoints.Add(Filename, lineNumber, NULL);
7173 bNeedNotificationExceptions = TRUE;
7176 else /* We were given a MethodDesc already */
7178 // if we've got an explicit MD, then we better have CLR and mscordacwks loaded
7179 if ((Status = CheckEEDll()) != S_OK)
7181 EENotLoadedMessage(Status);
7184 if ((Status = LoadClrDebugDll()) != S_OK)
7190 DacpMethodDescData MethodDescData;
7191 ExtOut("MethodDesc = %p\n", (ULONG64) pMD);
7192 if (MethodDescData.Request(g_sos, TO_CDADDR(pMD)) != S_OK)
7194 ExtOut("%p is not a valid MethodDesc\n", (ULONG64)pMD);
7198 if (MethodDescData.bHasNativeCode)
7200 IssueDebuggerBPCommand((size_t) MethodDescData.NativeCodeAddr);
7202 else if (MethodDescData.bIsDynamic)
7204 // Dynamic methods don't have JIT notifications. This is something we must
7205 // fix in the next release. Until then, you have a cumbersome user experience.
7206 ExtOut("This DynamicMethodDesc is not yet JITTED. Placing memory breakpoint at %p\n",
7207 MethodDescData.AddressOfNativeCodeSlot);
7209 sprintf_s(buffer, _countof(buffer),
7210 #ifdef _TARGET_WIN64_
7214 #endif // _TARGET_WIN64_
7216 " /1 %p \"bp poi(%p); g\"",
7217 (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot,
7218 (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot);
7220 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
7223 ExtOut("Unable to set breakpoint with IDebugControl::Execute: %x\n",Status);
7224 ExtOut("Attempted to run: %s\n", buffer);
7229 // Must issue a pending breakpoint.
7231 if (g_sos->GetMethodDescName(pMD, mdNameLen, FunctionName, NULL) != S_OK)
7233 ExtOut("Unable to get method name for MethodDesc %p\n", (ULONG64)pMD);
7237 FileNameForModule ((DWORD_PTR) MethodDescData.ModulePtr, ModuleName);
7239 // We didn't find code, add a breakpoint.
7240 g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, TO_TADDR(MethodDescData.ModulePtr), 0);
7241 g_bpoints.Update(TO_TADDR(MethodDescData.ModulePtr), FALSE);
7242 bNeedNotificationExceptions = TRUE;
7246 if (bNeedNotificationExceptions)
7248 ExtOut("Adding pending breakpoints...\n");
7249 sprintf_s(buffer, _countof(buffer), "sxe -c \"!HandleCLRN\" clrn");
7250 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
7257 /**********************************************************************\
7258 * Routine Description: *
7260 * This function is called to dump the managed threadpool *
7262 \**********************************************************************/
7263 DECLARE_API(ThreadPool)
7266 MINIDUMP_NOT_SUPPORTED();
7268 DacpThreadpoolData threadpool;
7270 if ((Status = threadpool.Request(g_sos)) == S_OK)
7272 BOOL doHCDump = FALSE;
7274 CMDOption option[] =
7275 { // name, vptr, type, hasValue
7276 {"-ti", &doHCDump, COBOOL, FALSE}
7279 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
7284 ExtOut ("CPU utilization: %d%%\n", threadpool.cpuUtilization);
7285 ExtOut ("Worker Thread:");
7286 ExtOut (" Total: %d", threadpool.NumWorkingWorkerThreads + threadpool.NumIdleWorkerThreads + threadpool.NumRetiredWorkerThreads);
7287 ExtOut (" Running: %d", threadpool.NumWorkingWorkerThreads);
7288 ExtOut (" Idle: %d", threadpool.NumIdleWorkerThreads);
7289 ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalWorkerThreads);
7290 ExtOut (" MinLimit: %d", threadpool.MinLimitTotalWorkerThreads);
7293 int numWorkRequests = 0;
7294 CLRDATA_ADDRESS workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
7295 DacpWorkRequestData workRequestData;
7296 while (workRequestPtr)
7298 if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
7300 ExtOut(" Failed to examine a WorkRequest\n");
7304 workRequestPtr = workRequestData.NextWorkRequest;
7307 ExtOut ("Work Request in Queue: %d\n", numWorkRequests);
7308 workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
7309 while (workRequestPtr)
7311 if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
7313 ExtOut(" Failed to examine a WorkRequest\n");
7317 if (workRequestData.Function == threadpool.AsyncTimerCallbackCompletionFPtr)
7318 ExtOut (" AsyncTimerCallbackCompletion TimerInfo@%p\n", (ULONG64)workRequestData.Context);
7320 ExtOut (" Unknown Function: %p Context: %p\n", (ULONG64)workRequestData.Function,
7321 (ULONG64)workRequestData.Context);
7323 workRequestPtr = workRequestData.NextWorkRequest;
7328 ExtOut ("--------------------------------------\n");
7329 ExtOut ("\nThread Injection History\n");
7330 if (threadpool.HillClimbingLogSize > 0)
7332 static char const * const TransitionNames[] =
7345 ExtOut("\n Time Transition New #Threads #Samples Throughput\n");
7346 DacpHillClimbingLogEntry entry;
7348 // get the most recent entry first, so we can calculate time offsets
7350 int index = (threadpool.HillClimbingLogFirstIndex + threadpool.HillClimbingLogSize-1) % HillClimbingLogCapacity;
7351 CLRDATA_ADDRESS entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
7352 if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
7354 ExtOut(" Failed to examine a HillClimbing log entry\n");
7357 DWORD endTime = entry.TickCount;
7359 for (int i = 0; i < threadpool.HillClimbingLogSize; i++)
7361 index = (i + threadpool.HillClimbingLogFirstIndex) % HillClimbingLogCapacity;
7362 entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
7364 if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
7366 ExtOut(" Failed to examine a HillClimbing log entry\n");
7370 ExtOut("%8.2lf %-14s %12d %12d %11.2lf\n",
7371 (double)(int)(entry.TickCount - endTime) / 1000.0,
7372 TransitionNames[entry.Transition],
7373 entry.NewControlSetting,
7374 entry.LastHistoryCount,
7375 entry.LastHistoryMean);
7380 ExtOut ("--------------------------------------\n");
7381 ExtOut ("Number of Timers: %d\n", threadpool.NumTimers);
7382 ExtOut ("--------------------------------------\n");
7384 ExtOut ("Completion Port Thread:");
7385 ExtOut ("Total: %d", threadpool.NumCPThreads);
7386 ExtOut (" Free: %d", threadpool.NumFreeCPThreads);
7387 ExtOut (" MaxFree: %d", threadpool.MaxFreeCPThreads);
7388 ExtOut (" CurrentLimit: %d", threadpool.CurrentLimitTotalCPThreads);
7389 ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalCPThreads);
7390 ExtOut (" MinLimit: %d", threadpool.MinLimitTotalCPThreads);
7395 ExtOut("Failed to request ThreadpoolMgr information\n");
7400 DECLARE_API(FindAppDomain)
7403 MINIDUMP_NOT_SUPPORTED();
7405 DWORD_PTR p_Object = NULL;
7408 CMDOption option[] =
7409 { // name, vptr, type, hasValue
7410 {"/d", &dml, COBOOL, FALSE},
7418 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
7423 EnableDMLHolder dmlHolder(dml);
7425 if ((p_Object == 0) || !sos::IsObject(p_Object))
7427 ExtOut("%p is not a valid object\n", (ULONG64) p_Object);
7431 DacpAppDomainStoreData adstore;
7432 if (adstore.Request(g_sos) != S_OK)
7434 ExtOut("Error getting AppDomain information\n");
7438 CLRDATA_ADDRESS appDomain = GetAppDomain (TO_CDADDR(p_Object));
7440 if (appDomain != NULL)
7442 DMLOut("AppDomain: %s\n", DMLDomain(appDomain));
7443 if (appDomain == adstore.sharedDomain)
7445 ExtOut("Name: Shared Domain\n");
7446 ExtOut("ID: (shared domain)\n");
7448 else if (appDomain == adstore.systemDomain)
7450 ExtOut("Name: System Domain\n");
7451 ExtOut("ID: (system domain)\n");
7455 DacpAppDomainData domain;
7456 if ((domain.Request(g_sos, appDomain) != S_OK) ||
7457 (g_sos->GetAppDomainName(appDomain,mdNameLen,g_mdName, NULL)!=S_OK))
7459 ExtOut("Error getting AppDomain %p.\n", (ULONG64) appDomain);
7463 ExtOut("Name: %S\n", (g_mdName[0]!=L'\0') ? g_mdName : L"None");
7464 ExtOut("ID: %d\n", domain.dwId);
7469 ExtOut("The type is declared in the shared domain and other\n");
7470 ExtOut("methods of finding the AppDomain failed. Try running\n");
7472 DMLOut("<exec cmd=\"!gcroot /d %p\">!gcroot %p</exec>, and if you find a root on a\n", p_Object, p_Object);
7474 ExtOut("!gcroot %p, and if you find a root on a\n", p_Object);
7475 ExtOut("stack, check the AppDomain of that stack with !threads.\n");
7476 ExtOut("Note that the Thread could have transitioned between\n");
7477 ExtOut("multiple AppDomains.\n");
7483 /**********************************************************************\
7484 * Routine Description: *
7486 * This function is called to get the COM state (e.g. APT,contexe *
7489 \**********************************************************************/
7490 #ifdef FEATURE_COMINTEROP
7491 DECLARE_API(COMState)
7494 MINIDUMP_NOT_SUPPORTED();
7499 g_ExtSystem->GetTotalNumberThreads(&numThread,&maxId);
7502 g_ExtSystem->GetCurrentThreadId(&curId);
7505 if (!ClrSafeInt<SIZE_T>::multiply(sizeof(ULONG), numThread, AllocSize))
7507 ExtOut(" Error! integer overflow on numThread 0x%08x\n", numThread);
7510 ULONG *ids = (ULONG*)alloca(AllocSize);
7511 ULONG *sysIds = (ULONG*)alloca(AllocSize);
7512 g_ExtSystem->GetThreadIdsByIndex(0,numThread,ids,sysIds);
7513 #if defined(_TARGET_WIN64_)
7514 ExtOut(" ID TEB APT APTId CallerTID Context\n");
7516 ExtOut(" ID TEB APT APTId CallerTID Context\n");
7518 for (ULONG i = 0; i < numThread; i ++) {
7519 g_ExtSystem->SetCurrentThreadId(ids[i]);
7520 CLRDATA_ADDRESS cdaTeb;
7521 g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
7522 ExtOut("%3d %4x %p", ids[i], sysIds[i], CDA_TO_UL64(cdaTeb));
7524 TADDR OleTlsDataAddr;
7525 if (SafeReadMemory(TO_TADDR(cdaTeb) + offsetof(TEB,ReservedForOle),
7527 sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0) {
7529 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
7531 sizeof(AptState), NULL)) {
7532 if (AptState & OLETLS_APARTMENTTHREADED) {
7535 else if (AptState & OLETLS_MULTITHREADED) {
7538 else if (AptState & OLETLS_INNEUTRALAPT) {
7545 // Read these fields only if we were able to read anything of the SOleTlsData structure
7546 DWORD dwApartmentID;
7547 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwApartmentID),
7549 sizeof(dwApartmentID), NULL)) {
7550 ExtOut(" %8x", dwApartmentID);
7556 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwTIDCaller),
7558 sizeof(dwTIDCaller), NULL)) {
7559 ExtOut(" %8x", dwTIDCaller);
7565 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,pCurrentCtx),
7567 sizeof(Context), NULL)) {
7568 ExtOut(" %p", (ULONG64)Context);
7571 ExtOut(" %p", (ULONG64)0);
7582 g_ExtSystem->SetCurrentThreadId(curId);
7585 #endif // FEATURE_COMINTEROP
7587 BOOL traverseEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
7589 size_t methodStart = (size_t) token;
7596 ExtOut("EHHandler %d: %s ", clauseIndex, EHTypeName(pEHInfo->clauseType));
7598 LPCWSTR typeName = EHTypedClauseTypeName(pEHInfo);
7599 if (typeName != NULL)
7601 ExtOut("catch(%S) ", typeName);
7604 if (IsClonedFinally(pEHInfo))
7605 ExtOut("(cloned finally)");
7606 else if (pEHInfo->isDuplicateClause)
7607 ExtOut("(duplicate)");
7612 ULONG64 addrStart = pEHInfo->tryStartOffset + methodStart;
7613 ULONG64 addrEnd = pEHInfo->tryEndOffset + methodStart;
7616 ExtOut("[%08x`%08x, %08x`%08x]",
7617 (ULONG)(addrStart >> 32), (ULONG)addrStart,
7618 (ULONG)(addrEnd >> 32), (ULONG)addrEnd);
7620 ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
7623 ExtOut(" [%x, %x]\n",
7624 (UINT32) pEHInfo->tryStartOffset,
7625 (UINT32) pEHInfo->tryEndOffset);
7627 ExtOut("Handler: ");
7629 addrStart = pEHInfo->handlerStartOffset + methodStart;
7630 addrEnd = pEHInfo->handlerEndOffset + methodStart;
7633 ExtOut("[%08x`%08x, %08x`%08x]",
7634 (ULONG)(addrStart >> 32), (ULONG)addrStart,
7635 (ULONG)(addrEnd >> 32), (ULONG)addrEnd);
7637 ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
7640 ExtOut(" [%x, %x]\n",
7641 (UINT32) pEHInfo->handlerStartOffset,
7642 (UINT32) pEHInfo->handlerEndOffset);
7644 if (pEHInfo->clauseType == EHFilter)
7648 addrStart = pEHInfo->filterOffset + methodStart;
7651 ExtOut("[%08x`%08x]", (ULONG)(addrStart >> 32), (ULONG)addrStart);
7653 ExtOut("[%08x]", (ULONG)addrStart);
7657 (UINT32) pEHInfo->filterOffset);
7668 MINIDUMP_NOT_SUPPORTED();
7670 DWORD_PTR dwStartAddr = NULL;
7673 CMDOption option[] =
7674 { // name, vptr, type, hasValue
7675 {"/d", &dml, COBOOL, FALSE},
7680 {&dwStartAddr, COHEX},
7684 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
7689 EnableDMLHolder dmlHolder(dml);
7690 DWORD_PTR tmpAddr = dwStartAddr;
7692 if (!IsMethodDesc(dwStartAddr))
7695 DWORD_PTR methodDesc;
7696 DWORD_PTR gcinfoAddr;
7697 IP2MethodDesc (dwStartAddr, methodDesc, jitType, gcinfoAddr);
7698 tmpAddr = methodDesc;
7701 DacpMethodDescData MD;
7702 if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
7704 ExtOut("%p is not a MethodDesc\n", (ULONG64)tmpAddr);
7708 if (1 == nArg && !MD.bHasNativeCode)
7710 ExtOut("No EH info available\n");
7714 DacpCodeHeaderData codeHeaderData;
7715 if (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
7717 ExtOut("Unable to get codeHeader information\n");
7721 DMLOut("MethodDesc: %s\n", DMLMethodDesc(MD.MethodDescPtr));
7722 DumpMDInfo(TO_TADDR(MD.MethodDescPtr));
7725 Status = g_sos->TraverseEHInfo(TO_CDADDR(MD.NativeCodeAddr), traverseEh, (LPVOID)MD.NativeCodeAddr);
7727 if (Status == E_ABORT)
7729 ExtOut("<user aborted>\n");
7731 else if (Status != S_OK)
7733 ExtOut("Failed to perform EHInfo traverse\n");
7740 /**********************************************************************\
7741 * Routine Description: *
7743 * This function is called to dump the GC encoding of a managed *
7746 \**********************************************************************/
7750 MINIDUMP_NOT_SUPPORTED();
7753 TADDR taStartAddr = NULL;
7757 CMDOption option[] =
7758 { // name, vptr, type, hasValue
7759 {"/d", &dml, COBOOL, FALSE},
7763 {&taStartAddr, COHEX},
7766 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
7771 EnableDMLHolder dmlHolder(dml);
7772 TADDR tmpAddr = taStartAddr;
7774 if (!IsMethodDesc(taStartAddr))
7779 IP2MethodDesc(taStartAddr, methodDesc, jitType, gcinfoAddr);
7780 tmpAddr = methodDesc;
7783 DacpMethodDescData MD;
7784 if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
7786 ExtOut("%p is not a valid MethodDesc\n", (ULONG64)taStartAddr);
7790 if (1 == nArg && !MD.bHasNativeCode)
7792 ExtOut("No GC info available\n");
7796 DacpCodeHeaderData codeHeaderData;
7799 // Try to get code header data from taStartAddr. This will get the code
7800 // header corresponding to the IP address, even if the function was rejitted
7801 (codeHeaderData.Request(g_sos, TO_CDADDR(taStartAddr)) != S_OK) &&
7803 // If that didn't work, just try to use the code address that the MD
7804 // points to. If the function was rejitted, this will only give you the
7805 // original JITted code, but that's better than nothing
7806 (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
7809 // We always used to emit this (before rejit support), even if we couldn't get
7810 // the code header, so keep on doing so.
7811 ExtOut("entry point %p\n", SOS_PTR(MD.NativeCodeAddr));
7813 // And now the error....
7814 ExtOut("Unable to get codeHeader information\n");
7818 // We have the code header, so use it to determine the method start
7820 ExtOut("entry point %p\n", SOS_PTR(codeHeaderData.MethodStart));
7822 if (codeHeaderData.JITType == TYPE_UNKNOWN)
7824 ExtOut("unknown Jit\n");
7827 else if (codeHeaderData.JITType == TYPE_JIT)
7829 ExtOut("Normal JIT generated code\n");
7831 else if (codeHeaderData.JITType == TYPE_PJIT)
7833 ExtOut("preJIT generated code\n");
7836 taGCInfoAddr = TO_TADDR(codeHeaderData.GCInfo);
7838 ExtOut("GC info %p\n", (ULONG64)taGCInfoAddr);
7840 // assume that GC encoding table is never more than
7841 // 40 + methodSize * 2
7843 if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
7844 !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
7846 ExtOut("<integer overflow>\n");
7849 ArrayHolder<BYTE> table = new NOTHROW BYTE[tableSize];
7852 ExtOut("Could not allocate memory to read the gc info.\n");
7853 return E_OUTOFMEMORY;
7856 memset(table, 0, tableSize);
7857 // We avoid using move here, because we do not want to return
7858 if (!SafeReadMemory(taGCInfoAddr, table, tableSize, NULL))
7860 ExtOut("Could not read memory %p\n", (ULONG64)taGCInfoAddr);
7864 // Mutable table pointer since we need to pass the appropriate
7865 // offset into the table to DumpGCTable.
7866 BYTE *pTable = table;
7867 unsigned int methodSize = (unsigned int)codeHeaderData.MethodSize;
7869 g_targetMachine->DumpGCInfo(pTable, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/);
7874 #if !defined(FEATURE_PAL)
7876 void DecodeGCTableEntry (const char *fmt, ...)
7878 GCEncodingInfo *pInfo = (GCEncodingInfo*)GetFiberData();
7882 // Append the new data to the buffer
7887 int cch = _vsnprintf_s(&pInfo->buf[pInfo->cch], _countof(pInfo->buf) - pInfo->cch, _countof(pInfo->buf) - pInfo->cch - 1, fmt, va);
7893 pInfo->buf[pInfo->cch] = '\0';
7896 // If there are complete lines in the buffer, decode them.
7901 char *pNewLine = strchr(pInfo->buf, '\n');
7907 // The line should start with a 16-bit (x86) or 32-bit (non-x86) hex
7908 // offset. strtoul returns ULONG_MAX or 0 on failure. 0 is a valid
7909 // offset for the first encoding, or while the last offset was 0.
7912 if (isxdigit(pInfo->buf[0]))
7915 ULONG ofs = strtoul(pInfo->buf, &pEnd, 16);
7919 && ( -1 == pInfo->ofs
7926 SwitchToFiber(pInfo->pvMainFiber);
7929 else if (0 == strncmp(pInfo->buf, "Untracked:", 10))
7934 SwitchToFiber(pInfo->pvMainFiber);
7938 // Shift the remaining data to the start of the buffer
7941 strcpy_s(pInfo->buf, _countof(pInfo->buf), pNewLine+1);
7942 pInfo->cch = (int)strlen(pInfo->buf);
7947 VOID CALLBACK DumpGCTableFiberEntry (LPVOID pvGCEncodingInfo)
7949 GCEncodingInfo *pInfo = (GCEncodingInfo*)pvGCEncodingInfo;
7951 g_targetMachine->DumpGCInfo(pInfo->table, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
7953 pInfo->fDoneDecoding = true;
7954 SwitchToFiber(pInfo->pvMainFiber);
7956 #endif // !FEATURE_PAL
7958 BOOL gatherEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
7960 SOSEHInfo *pInfo = (SOSEHInfo *) token;
7967 if (pInfo->m_pInfos == NULL)
7969 // First time, initialize structure
7970 pInfo->EHCount = totalClauses;
7971 pInfo->m_pInfos = new NOTHROW DACEHInfo[totalClauses];
7972 if (pInfo->m_pInfos == NULL)
7979 pInfo->m_pInfos[clauseIndex] = *((DACEHInfo*)pEHInfo);
7984 /**********************************************************************\
7985 * Routine Description: *
7987 * This function is called to unassembly a managed function. *
7988 * It tries to print symbolic info for function call, contants... *
7990 \**********************************************************************/
7994 MINIDUMP_NOT_SUPPORTED();
7997 DWORD_PTR dwStartAddr = NULL;
7998 BOOL fWithGCInfo = FALSE;
7999 BOOL fWithEHInfo = FALSE;
8000 BOOL bSuppressLines = FALSE;
8001 BOOL bDisplayOffsets = FALSE;
8005 CMDOption option[] =
8006 { // name, vptr, type, hasValue
8007 {"-gcinfo", &fWithGCInfo, COBOOL, FALSE},
8008 {"-ehinfo", &fWithEHInfo, COBOOL, FALSE},
8009 {"-n", &bSuppressLines, COBOOL, FALSE},
8010 {"-o", &bDisplayOffsets, COBOOL, FALSE},
8011 {"/d", &dml, COBOOL, FALSE},
8015 {&dwStartAddr, COHEX},
8017 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (nArg < 1))
8021 // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
8023 if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
8025 symlines &= SYMOPT_LOAD_LINES;
8027 bSuppressLines = bSuppressLines || (symlines == 0);
8029 EnableDMLHolder dmlHolder(dml);
8030 // dwStartAddr is either some IP address or a MethodDesc. Start off assuming it's a
8032 DWORD_PTR methodDesc = dwStartAddr;
8033 if (!IsMethodDesc(methodDesc))
8035 // Not a methodDesc, so gotta find it ourselves
8036 DWORD_PTR tmpAddr = dwStartAddr;
8038 DWORD_PTR gcinfoAddr;
8039 IP2MethodDesc (tmpAddr, methodDesc, jt,
8041 if (!methodDesc || jt == TYPE_UNKNOWN)
8043 // It is not managed code.
8044 ExtOut("Unmanaged code\n");
8045 UnassemblyUnmanaged(dwStartAddr, bSuppressLines);
8050 DacpMethodDescData MethodDescData;
8051 if ((Status=MethodDescData.Request(g_sos, TO_CDADDR(methodDesc))) != S_OK)
8053 ExtOut("Failed to get method desc for %p.\n", SOS_PTR(dwStartAddr));
8057 if (!MethodDescData.bHasNativeCode)
8059 ExtOut("Not jitted yet\n");
8063 // Get the appropriate code header. If we were passed an MD, then use
8064 // MethodDescData.NativeCodeAddr to find the code header; if we were passed an IP, use
8065 // that IP to find the code header. This ensures that, for rejitted functions, we
8066 // disassemble the rejit version that the user explicitly specified with their IP.
8067 DacpCodeHeaderData codeHeaderData;
8068 if (codeHeaderData.Request(
8071 (dwStartAddr == methodDesc) ? MethodDescData.NativeCodeAddr : dwStartAddr)
8075 ExtOut("Unable to get codeHeader information\n");
8079 if (codeHeaderData.MethodStart == 0)
8081 ExtOut("not a valid MethodDesc\n");
8085 if (codeHeaderData.JITType == TYPE_UNKNOWN)
8087 ExtOut("unknown Jit\n");
8090 else if (codeHeaderData.JITType == TYPE_JIT)
8092 ExtOut("Normal JIT generated code\n");
8094 else if (codeHeaderData.JITType == TYPE_PJIT)
8096 ExtOut("preJIT generated code\n");
8099 NameForMD_s(methodDesc, g_mdName, mdNameLen);
8100 ExtOut("%S\n", g_mdName);
8101 if (codeHeaderData.ColdRegionStart != NULL)
8103 ExtOut("Begin %p, size %x. Cold region begin %p, size %x\n",
8104 (ULONG64)codeHeaderData.MethodStart, codeHeaderData.HotRegionSize,
8105 (ULONG64)codeHeaderData.ColdRegionStart, codeHeaderData.ColdRegionSize);
8109 ExtOut("Begin %p, size %x\n", (ULONG64)codeHeaderData.MethodStart, codeHeaderData.MethodSize);
8112 #if !defined(FEATURE_PAL)
8114 // Set up to mix gc info with the code if requested
8117 GCEncodingInfo gcEncodingInfo = {0};
8119 // The actual GC Encoding Table, this is updated during the course of the function.
8120 gcEncodingInfo.table = NULL;
8122 // The holder to make sure we clean up the memory for the table
8123 ArrayHolder<BYTE> table = NULL;
8127 // assume that GC encoding table is never more than 40 + methodSize * 2
8129 if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
8130 !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
8132 ExtOut("<integer overflow>\n");
8137 // Assign the new array to the mutable gcEncodingInfo table and to the
8138 // table ArrayHolder to clean this up when the function exits.
8139 table = gcEncodingInfo.table = new NOTHROW BYTE[tableSize];
8141 if (gcEncodingInfo.table == NULL)
8143 ExtOut("Could not allocate memory to read the gc info.\n");
8144 return E_OUTOFMEMORY;
8147 memset (gcEncodingInfo.table, 0, tableSize);
8148 // We avoid using move here, because we do not want to return
8149 if (!SafeReadMemory(TO_TADDR(codeHeaderData.GCInfo), gcEncodingInfo.table, tableSize, NULL))
8151 ExtOut("Could not read memory %p\n", (ULONG64)codeHeaderData.GCInfo);
8156 // Skip the info header
8158 gcEncodingInfo.methodSize = (unsigned int)codeHeaderData.MethodSize;
8161 // DumpGCTable will call gcPrintf for each encoding. We'd like a "give
8162 // me the next encoding" interface, but we're stuck with the callback.
8163 // To reconcile this without messing up too much code, we'll create a
8164 // fiber to dump the gc table. When we need the next gc encoding,
8165 // we'll switch to this fiber. The callback will note the next offset,
8166 // and switch back to the main fiber.
8169 gcEncodingInfo.ofs = -1;
8170 gcEncodingInfo.hotSizeToAdd = 0;
8172 gcEncodingInfo.pvMainFiber = ConvertThreadToFiber(NULL);
8173 if (!gcEncodingInfo.pvMainFiber && ERROR_ALREADY_FIBER == GetLastError())
8174 gcEncodingInfo.pvMainFiber = GetCurrentFiber();
8176 if (!gcEncodingInfo.pvMainFiber)
8179 gcEncodingInfo.pvGCTableFiber = CreateFiber(0, DumpGCTableFiberEntry, &gcEncodingInfo);
8180 if (!gcEncodingInfo.pvGCTableFiber)
8183 SwitchToFiber(gcEncodingInfo.pvGCTableFiber);
8187 SOSEHInfo *pInfo = NULL;
8190 pInfo = new NOTHROW SOSEHInfo;
8195 else if (g_sos->TraverseEHInfo(MethodDescData.NativeCodeAddr, gatherEh, (LPVOID)pInfo) != S_OK)
8197 ExtOut("Failed to gather EHInfo data\n");
8203 if (codeHeaderData.ColdRegionStart == NULL)
8205 g_targetMachine->Unassembly (
8206 (DWORD_PTR) codeHeaderData.MethodStart,
8207 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.MethodSize,
8209 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
8210 #if !defined(FEATURE_PAL)
8211 fWithGCInfo ? &gcEncodingInfo :
8221 ExtOut("Hot region:\n");
8222 g_targetMachine->Unassembly (
8223 (DWORD_PTR) codeHeaderData.MethodStart,
8224 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.HotRegionSize,
8226 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
8227 #if !defined(FEATURE_PAL)
8228 fWithGCInfo ? &gcEncodingInfo :
8236 ExtOut("Cold region:\n");
8238 #if !defined(FEATURE_PAL)
8239 // Displaying gcinfo for a cold region requires knowing the size of
8240 // the hot region preceeding.
8241 gcEncodingInfo.hotSizeToAdd = codeHeaderData.HotRegionSize;
8243 g_targetMachine->Unassembly (
8244 (DWORD_PTR) codeHeaderData.ColdRegionStart,
8245 ((DWORD_PTR)codeHeaderData.ColdRegionStart) + codeHeaderData.ColdRegionSize,
8247 ((DWORD_PTR) MethodDescData.GCStressCodeCopy) + codeHeaderData.HotRegionSize,
8248 #if !defined(FEATURE_PAL)
8249 fWithGCInfo ? &gcEncodingInfo :
8265 #if !defined(FEATURE_PAL)
8267 DeleteFiber(gcEncodingInfo.pvGCTableFiber);
8273 /**********************************************************************\
8274 * Routine Description: *
8276 * This function is called to dump the in-memory stress log *
8277 * !DumpLog [filename] *
8278 * will dump the stress log corresponding to the clr.dll *
8279 * loaded in the debuggee's VAS *
8280 * !DumpLog -addr <addr_of_StressLog::theLog> [filename] *
8281 * will dump the stress log associated with any DLL linked *
8282 * against utilcode.lib, most commonly mscordbi.dll *
8283 * (e.g. !DumpLog -addr mscordbi!StressLog::theLog) *
8285 \**********************************************************************/
8286 DECLARE_API(DumpLog)
8288 INIT_API_NO_RET_ON_FAILURE();
8290 MINIDUMP_NOT_SUPPORTED();
8292 const char* fileName = "StressLog.txt";
8294 CLRDATA_ADDRESS StressLogAddress = NULL;
8296 StringHolder sFileName, sLogAddr;
8297 CMDOption option[] =
8298 { // name, vptr, type, hasValue
8299 {"-addr", &sLogAddr.data, COSTRING, TRUE}
8303 {&sFileName.data, COSTRING}
8306 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
8310 if (nArg > 0 && sFileName.data != NULL)
8312 fileName = sFileName.data;
8315 // allow users to specify -addr mscordbdi!StressLog::theLog, for example.
8316 if (sLogAddr.data != NULL)
8318 StressLogAddress = GetExpression(sLogAddr.data);
8321 if (StressLogAddress == NULL)
8325 // Try to find stress log symbols
8326 DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
8327 StressLogAddress = dwAddr;
8329 else if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
8331 ExtOut("Unable to find stress log via DAC\n");
8336 if (StressLogAddress == NULL)
8338 ExtOut("Please provide the -addr argument for the address of the stress log, since no recognized runtime is loaded.\n");
8342 ExtOut("Attempting to dump Stress log to file '%s'\n", fileName);
8346 Status = StressLog::Dump(StressLogAddress, fileName, g_ExtData);
8349 ExtOut("SUCCESS: Stress log dumped\n");
8350 else if (Status == S_FALSE)
8351 ExtOut("No Stress log in the image, no file written\n");
8353 ExtOut("FAILURE: Stress log not dumped\n");
8360 DECLARE_API (DumpGCLog)
8363 MINIDUMP_NOT_SUPPORTED();
8366 if (GetEEFlavor() == UNKNOWNEE)
8368 ExtOut("CLR not loaded\n");
8372 const char* fileName = "GCLog.txt";
8374 while (isspace (*args))
8380 // Try to find stress log symbols
8381 DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_log_buffer");
8382 moveN (dwAddr, dwAddr);
8386 dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_log_buffer");
8387 moveN (dwAddr, dwAddr);
8390 ExtOut("Can't get either WKS or SVR GC's log file");
8395 ExtOut("Dumping GC log at %08x\n", dwAddr);
8397 g_bDacBroken = FALSE;
8399 ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
8403 HANDLE hGCLog = CreateFileA(
8409 FILE_ATTRIBUTE_NORMAL,
8412 if (hGCLog == INVALID_HANDLE_VALUE)
8414 ExtOut("failed to create file: %d\n", GetLastError());
8418 int iLogSize = 1024*1024;
8419 BYTE* bGCLog = new NOTHROW BYTE[iLogSize];
8426 memset (bGCLog, 0, iLogSize);
8427 if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
8429 ExtOut("failed to read memory from %08x\n", dwAddr);
8432 int iRealLogSize = iLogSize - 1;
8433 while (iRealLogSize >= 0)
8435 if (bGCLog[iRealLogSize] != '*')
8443 DWORD dwWritten = 0;
8444 WriteFile (hGCLog, bGCLog, iRealLogSize + 1, &dwWritten, NULL);
8449 if (hGCLog != INVALID_HANDLE_VALUE)
8451 CloseHandle (hGCLog);
8455 ExtOut("SUCCESS: Stress log dumped\n");
8456 else if (Status == S_FALSE)
8457 ExtOut("No Stress log in the image, no file written\n");
8459 ExtOut("FAILURE: Stress log not dumped\n");
8467 /**********************************************************************\
8468 * Routine Description: *
8470 * This function is called to dump the build number and type of the *
8473 \**********************************************************************/
8474 DECLARE_API (EEVersion)
8478 EEFLAVOR eef = GetEEFlavor();
8479 if (eef == UNKNOWNEE) {
8480 ExtOut("CLR not loaded\n");
8484 if (g_ExtSymbols2) {
8485 VS_FIXEDFILEINFO version;
8487 BOOL ret = GetEEVersion(&version);
8491 if (version.dwFileVersionMS != (DWORD)-1)
8493 ExtOut("%u.%u.%u.%u",
8494 HIWORD(version.dwFileVersionMS),
8495 LOWORD(version.dwFileVersionMS),
8496 HIWORD(version.dwFileVersionLS),
8497 LOWORD(version.dwFileVersionLS));
8498 if (version.dwFileFlags & VS_FF_DEBUG)
8500 ExtOut(" Checked or debug build");
8504 BOOL fRet = IsRetailBuild ((size_t)moduleInfo[eef].baseAddr);
8517 if (!InitializeHeapData ())
8518 ExtOut("GC Heap not initialized, so GC mode is not determined yet.\n");
8519 else if (IsServerBuild())
8520 ExtOut("Server mode with %d gc heaps\n", GetGcHeapCount());
8522 ExtOut("Workstation mode\n");
8524 if (!GetGcStructuresValid())
8526 ExtOut("In plan phase of garbage collection\n");
8529 // Print SOS version
8530 VS_FIXEDFILEINFO sosVersion;
8531 if (GetSOSVersion(&sosVersion))
8533 if (sosVersion.dwFileVersionMS != (DWORD)-1)
8535 ExtOut("SOS Version: %u.%u.%u.%u",
8536 HIWORD(sosVersion.dwFileVersionMS),
8537 LOWORD(sosVersion.dwFileVersionMS),
8538 HIWORD(sosVersion.dwFileVersionLS),
8539 LOWORD(sosVersion.dwFileVersionLS));
8540 if (sosVersion.dwFileFlags & VS_FF_DEBUG)
8542 ExtOut(" Checked or debug build");
8546 ExtOut(" retail build");
8554 #endif // FEATURE_PAL
8557 /**********************************************************************\
8558 * Routine Description: *
8560 * This function is called to print the environment setting for *
8561 * the current process. *
8563 \**********************************************************************/
8564 DECLARE_API (ProcInfo)
8567 MINIDUMP_NOT_SUPPORTED();
8571 ExtOut("!ProcInfo is not supported on a dump file.\n");
8575 #define INFO_ENV 0x00000001
8576 #define INFO_TIME 0x00000002
8577 #define INFO_MEM 0x00000004
8578 #define INFO_ALL 0xFFFFFFFF
8580 DWORD fProcInfo = INFO_ALL;
8582 if (_stricmp (args, "-env") == 0) {
8583 fProcInfo = INFO_ENV;
8586 if (_stricmp (args, "-time") == 0) {
8587 fProcInfo = INFO_TIME;
8590 if (_stricmp (args, "-mem") == 0) {
8591 fProcInfo = INFO_MEM;
8594 if (fProcInfo & INFO_ENV) {
8595 ExtOut("---------------------------------------\n");
8596 ExtOut("Environment\n");
8598 g_ExtSystem->GetCurrentProcessPeb(&pPeb);
8600 static ULONG Offset_ProcessParam = -1;
8601 static ULONG Offset_Environment = -1;
8602 if (Offset_ProcessParam == -1)
8606 if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
8609 if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB", &TypeId)))
8611 if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
8612 "ProcessParameters", &Offset_ProcessParam)))
8613 Offset_ProcessParam = -1;
8615 if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "_RTL_USER_PROCESS_PARAMETERS", &TypeId)))
8617 if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
8618 "Environment", &Offset_Environment)))
8619 Offset_Environment = -1;
8623 // We can not get it from PDB. Use the fixed one.
8624 if (Offset_ProcessParam == -1)
8625 Offset_ProcessParam = offsetof (DT_PEB, ProcessParameters);
8627 if (Offset_Environment == -1)
8628 Offset_Environment = offsetof (DT_RTL_USER_PROCESS_PARAMETERS, Environment);
8631 ULONG64 addr = pPeb + Offset_ProcessParam;
8633 g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
8634 addr = value + Offset_Environment;
8635 g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
8637 static WCHAR buffer[DT_OS_PAGE_SIZE/2];
8638 ULONG readBytes = DT_OS_PAGE_SIZE;
8640 if ((g_ExtData->ReadDebuggerData( DEBUG_DATA_MmPageSize, &Page, sizeof(Page), NULL)) == S_OK
8643 ULONG uPageSize = (ULONG)(ULONG_PTR)Page;
8644 if (readBytes > uPageSize) {
8645 readBytes = uPageSize;
8652 if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &buffer, readBytes, NULL)))
8657 while (pt < &buffer[DT_OS_PAGE_SIZE/2]) {
8658 end = wcschr (pt, L'\0');
8661 sprintf_s (format,_countof (format), "%dS", &buffer[DT_OS_PAGE_SIZE/2] - pt);
8665 else if (end == pt) {
8677 HANDLE hProcess = INVALID_HANDLE_VALUE;
8678 if (fProcInfo & (INFO_TIME | INFO_MEM)) {
8680 g_ExtSystem->GetCurrentProcessHandle(&handle);
8681 hProcess = (HANDLE)handle;
8684 if (!IsDumpFile() && fProcInfo & INFO_TIME) {
8685 FILETIME CreationTime;
8687 FILETIME KernelTime;
8690 typedef BOOL (WINAPI *FntGetProcessTimes)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
8691 static FntGetProcessTimes pFntGetProcessTimes = (FntGetProcessTimes)-1;
8692 if (pFntGetProcessTimes == (FntGetProcessTimes)-1) {
8693 HINSTANCE hstat = LoadLibrary ("Kernel32.dll");
8696 pFntGetProcessTimes = (FntGetProcessTimes)GetProcAddress (hstat, "GetProcessTimes");
8697 FreeLibrary (hstat);
8700 pFntGetProcessTimes = NULL;
8703 if (pFntGetProcessTimes && pFntGetProcessTimes (hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime)) {
8704 ExtOut("---------------------------------------\n");
8705 ExtOut("Process Times\n");
8706 static char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
8707 "Oct", "Nov", "Dec"};
8708 SYSTEMTIME SystemTime;
8709 FILETIME LocalFileTime;
8710 if (FileTimeToLocalFileTime (&CreationTime,&LocalFileTime)
8711 && FileTimeToSystemTime (&LocalFileTime,&SystemTime)) {
8712 ExtOut("Process Started at: %4d %s %2d %d:%d:%d.%02d\n",
8713 SystemTime.wYear, Month[SystemTime.wMonth-1], SystemTime.wDay,
8714 SystemTime.wHour, SystemTime.wMinute,
8715 SystemTime.wSecond, SystemTime.wMilliseconds/10);
8726 totalTime = KernelTime.dwLowDateTime + (((ULONG64)KernelTime.dwHighDateTime) << 32);
8727 nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
8728 totalTime %= 24*3600*10000000ui64;
8729 nHour = (DWORD)(totalTime/(3600*10000000ui64));
8730 totalTime %= 3600*10000000ui64;
8731 nMin = (DWORD)(totalTime/(60*10000000));
8732 totalTime %= 60*10000000;
8733 nSec = (DWORD)(totalTime/10000000);
8734 totalTime %= 10000000;
8735 nHundred = (DWORD)(totalTime/100000);
8736 ExtOut("Kernel CPU time : %d days %02d:%02d:%02d.%02d\n",
8737 nDay, nHour, nMin, nSec, nHundred);
8740 DWORD sHour = nHour;
8743 DWORD sHundred = nHundred;
8745 totalTime = UserTime.dwLowDateTime + (((ULONG64)UserTime.dwHighDateTime) << 32);
8746 nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
8747 totalTime %= 24*3600*10000000ui64;
8748 nHour = (DWORD)(totalTime/(3600*10000000ui64));
8749 totalTime %= 3600*10000000ui64;
8750 nMin = (DWORD)(totalTime/(60*10000000));
8751 totalTime %= 60*10000000;
8752 nSec = (DWORD)(totalTime/10000000);
8753 totalTime %= 10000000;
8754 nHundred = (DWORD)(totalTime/100000);
8755 ExtOut("User CPU time : %d days %02d:%02d:%02d.%02d\n",
8756 nDay, nHour, nMin, nSec, nHundred);
8762 sHundred += nHundred;
8763 if (sHundred >= 100) {
8764 sSec += sHundred/100;
8779 ExtOut("Total CPU time : %d days %02d:%02d:%02d.%02d\n",
8780 sDay, sHour, sMin, sSec, sHundred);
8784 if (!IsDumpFile() && fProcInfo & INFO_MEM) {
8788 *FntNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
8790 static FntNtQueryInformationProcess pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)-1;
8791 if (pFntNtQueryInformationProcess == (FntNtQueryInformationProcess)-1) {
8792 HINSTANCE hstat = LoadLibrary ("ntdll.dll");
8795 pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)GetProcAddress (hstat, "NtQueryInformationProcess");
8796 FreeLibrary (hstat);
8799 pFntNtQueryInformationProcess = NULL;
8802 if (pFntNtQueryInformationProcess &&
8803 NT_SUCCESS (pFntNtQueryInformationProcess (hProcess,ProcessVmCounters,&memory,sizeof(memory),NULL))) {
8804 ExtOut("---------------------------------------\n");
8805 ExtOut("Process Memory\n");
8806 ExtOut("WorkingSetSize: %8d KB PeakWorkingSetSize: %8d KB\n",
8807 memory.WorkingSetSize/1024, memory.PeakWorkingSetSize/1024);
8808 ExtOut("VirtualSize: %8d KB PeakVirtualSize: %8d KB\n",
8809 memory.VirtualSize/1024, memory.PeakVirtualSize/1024);
8810 ExtOut("PagefileUsage: %8d KB PeakPagefileUsage: %8d KB\n",
8811 memory.PagefileUsage/1024, memory.PeakPagefileUsage/1024);
8814 MEMORYSTATUS memstat;
8815 GlobalMemoryStatus (&memstat);
8816 ExtOut("---------------------------------------\n");
8817 ExtOut("%ld percent of memory is in use.\n\n",
8818 memstat.dwMemoryLoad);
8819 ExtOut("Memory Availability (Numbers in MB)\n\n");
8820 ExtOut(" %8s %8s\n", "Total", "Avail");
8821 ExtOut("Physical Memory %8d %8d\n", memstat.dwTotalPhys/1024/1024, memstat.dwAvailPhys/1024/1024);
8822 ExtOut("Page File %8d %8d\n", memstat.dwTotalPageFile/1024/1024, memstat.dwAvailPageFile/1024/1024);
8823 ExtOut("Virtual Memory %8d %8d\n", memstat.dwTotalVirtual/1024/1024, memstat.dwAvailVirtual/1024/1024);
8828 #endif // FEATURE_PAL
8830 /**********************************************************************\
8831 * Routine Description: *
8833 * This function is called to find the address of EE data for a *
8836 \**********************************************************************/
8837 DECLARE_API(Token2EE)
8840 MINIDUMP_NOT_SUPPORTED();
8842 StringHolder DllName;
8846 CMDOption option[] =
8847 { // name, vptr, type, hasValue
8848 {"/d", &dml, COBOOL, FALSE},
8853 {&DllName.data, COSTRING},
8858 if (!GetCMDOption(args,option, _countof(option), arg, _countof(arg), &nArg))
8864 ExtOut("Usage: !Token2EE module_name mdToken\n");
8865 ExtOut(" You can pass * for module_name to search all modules.\n");
8869 EnableDMLHolder dmlHolder(dml);
8871 ArrayHolder<DWORD_PTR> moduleList = NULL;
8873 if (strcmp(DllName.data, "*") == 0)
8875 moduleList = ModuleFromName(NULL, &numModule);
8879 moduleList = ModuleFromName(DllName.data, &numModule);
8882 if (moduleList == NULL)
8884 ExtOut("Failed to request module list.\n");
8888 for (int i = 0; i < numModule; i ++)
8895 ExtOut("--------------------------------------\n");
8898 DWORD_PTR dwAddr = moduleList[i];
8899 WCHAR FileName[MAX_PATH];
8900 FileNameForModule(dwAddr, FileName);
8902 // We'd like a short form for this output
8903 LPWSTR pszFilename = wcsrchr (FileName, L'\\');
8904 if (pszFilename == NULL)
8906 pszFilename = FileName;
8910 pszFilename++; // skip past the last "\" character
8913 DMLOut("Module: %s\n", DMLModule(dwAddr));
8914 ExtOut("Assembly: %S\n", pszFilename);
8916 GetInfoFromModule(dwAddr, (ULONG)token);
8924 /**********************************************************************\
8925 * Routine Description: *
8927 * This function is called to find the address of EE data for a *
8930 \**********************************************************************/
8931 DECLARE_API(Name2EE)
8934 MINIDUMP_NOT_SUPPORTED();
8936 StringHolder DllName, TypeName;
8939 CMDOption option[] =
8940 { // name, vptr, type, hasValue
8941 {"/d", &dml, COBOOL, FALSE},
8946 {&DllName.data, COSTRING},
8947 {&TypeName.data, COSTRING}
8951 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
8956 EnableDMLHolder dmlHolder(dml);
8960 // The input may be in the form <modulename>!<type>
8961 // If so, do some surgery on the input params.
8963 // There should be only 1 ! character
8964 LPSTR pszSeperator = strchr (DllName.data, '!');
8965 if (pszSeperator != NULL)
8967 if (strchr (pszSeperator + 1, '!') == NULL)
8969 size_t capacity_TypeName_data = strlen(pszSeperator + 1) + 1;
8970 TypeName.data = new NOTHROW char[capacity_TypeName_data];
8973 // get the type name,
8974 strcpy_s (TypeName.data, capacity_TypeName_data, pszSeperator + 1);
8975 // and truncate DllName
8976 *pszSeperator = '\0';
8978 // Do some extra validation
8979 if (strlen (DllName.data) >= 1 &&
8980 strlen (TypeName.data) > 1)
8991 ExtOut("Usage: !Name2EE module_name item_name\n");
8992 ExtOut(" or !Name2EE module_name!item_name\n");
8993 ExtOut(" use * for module_name to search all loaded modules\n");
8994 ExtOut("Examples: !Name2EE mscorlib.dll System.String.ToString\n");
8995 ExtOut(" !Name2EE *!System.String\n");
9000 ArrayHolder<DWORD_PTR> moduleList = NULL;
9001 if (strcmp(DllName.data, "*") == 0)
9003 moduleList = ModuleFromName(NULL, &numModule);
9007 moduleList = ModuleFromName(DllName.data, &numModule);
9011 if (moduleList == NULL)
9013 ExtOut("Failed to request module list.\n", DllName.data);
9017 for (int i = 0; i < numModule; i ++)
9024 ExtOut("--------------------------------------\n");
9027 DWORD_PTR dwAddr = moduleList[i];
9028 WCHAR FileName[MAX_PATH];
9029 FileNameForModule (dwAddr, FileName);
9031 // We'd like a short form for this output
9032 LPWSTR pszFilename = wcsrchr (FileName, L'\\');
9033 if (pszFilename == NULL)
9035 pszFilename = FileName;
9039 pszFilename++; // skip past the last "\" character
9042 DMLOut("Module: %s\n", DMLModule(dwAddr));
9043 ExtOut("Assembly: %S\n", pszFilename);
9044 GetInfoFromName(dwAddr, TypeName.data);
9055 MINIDUMP_NOT_SUPPORTED();
9057 DWORD_PTR root = NULL;
9058 DWORD_PTR target = NULL;
9062 CMDOption option[] =
9063 { // name, vptr, type, hasValue
9064 {"/d", &dml, COBOOL, FALSE},
9071 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9076 if (root == 0 || target == 0)
9078 ExtOut("Invalid argument %s\n", args);
9083 bool result = gcroot.PrintPathToObject(root, target);
9086 ExtOut("Did not find a path from %p to %p.\n", SOS_PTR(root), SOS_PTR(target));
9094 /**********************************************************************\
9095 * Routine Description: *
9097 * This function finds all roots (on stack or in handles) for a *
9100 \**********************************************************************/
9105 MINIDUMP_NOT_SUPPORTED();
9107 BOOL bNoStacks = FALSE;
9108 DWORD_PTR obj = NULL;
9113 CMDOption option[] =
9114 { // name, vptr, type, hasValue
9115 {"-nostacks", &bNoStacks, COBOOL, FALSE},
9116 {"-all", &all, COBOOL, FALSE},
9117 {"/d", &dml, COBOOL, FALSE},
9124 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9130 ExtOut("Invalid argument %s\n", args);
9134 EnableDMLHolder dmlHolder(dml);
9136 int i = gcroot.PrintRootsForObject(obj, all == TRUE, bNoStacks == TRUE);
9139 ExtOut("Interrupted, data may be incomplete.\n");
9142 ExtOut("Found %d roots.\n", i);
9144 ExtOut("Found %d unique roots (run '!GCRoot -all' to see all roots).\n", i);
9152 DECLARE_API(GCWhere)
9155 MINIDUMP_NOT_SUPPORTED();
9163 CMDOption option[] =
9164 { // name, vptr, type, hasValue
9165 {"-brick", &bGetBrick, COBOOL, FALSE},
9166 {"-card", &bGetCard, COBOOL, FALSE},
9167 {"/d", &dml, COBOOL, FALSE},
9173 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9178 EnableDMLHolder dmlHolder(dml);
9179 // Obtain allocation context for each managed thread.
9180 AllocInfo allocInfo;
9183 TADDR_SEGINFO trngSeg = { 0, 0, 0 };
9184 TADDR_RANGE allocCtx = { 0, 0 };
9186 BOOL bLarge = FALSE;
9187 BOOL bFound = FALSE;
9190 if (sos::IsObject(taddrObj))
9193 BOOL bContainsPointers;
9194 if(FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
9195 !GetSizeEfficient(taddrObj, taddrMT, FALSE, size, bContainsPointers))
9197 ExtWarn("Couldn't get size for object %#p: possible heap corruption.\n",
9202 if (!IsServerBuild())
9204 DacpGcHeapDetails heapDetails;
9205 if (heapDetails.Request(g_sos) != S_OK)
9207 ExtOut("Error requesting gc heap details\n");
9211 if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
9213 ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated " WIN64_8SPACES " size\n");
9214 ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n",
9215 (ULONG64)taddrObj, gen, 0, (ULONG64)trngSeg.segAddr, (ULONG64)trngSeg.start, (ULONG64)trngSeg.end, size, size);
9221 DacpGcHeapData gcheap;
9222 if (gcheap.Request(g_sos) != S_OK)
9224 ExtOut("Error requesting GC Heap data\n");
9229 DWORD dwNHeaps = gcheap.HeapCount;
9230 if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
9232 ExtOut("Failed to get GCHeaps: integer overflow\n");
9236 CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
9237 if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
9239 ExtOut("Failed to get GCHeaps\n");
9243 for (DWORD n = 0; n < dwNHeaps; n ++)
9245 DacpGcHeapDetails heapDetails;
9246 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
9248 ExtOut("Error requesting details\n");
9252 if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
9254 ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated" WIN64_8SPACES " size\n");
9255 ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n",
9256 (ULONG64)taddrObj, gen, n, (ULONG64)trngSeg.segAddr, (ULONG64)trngSeg.start, (ULONG64)trngSeg.end, size, size);
9265 ExtOut("Address %#p not found in the managed heap.\n", (ULONG64)taddrObj);
9271 DECLARE_API(FindRoots)
9275 MINIDUMP_NOT_SUPPORTED();
9279 ExtOut("!FindRoots is not supported on a dump file.\n");
9283 LONG_PTR gen = -100; // initialized outside the legal range: [-1, 2]
9289 CMDOption option[] =
9290 { // name, vptr, type, hasValue
9291 {"-gen", &sgen.data, COSTRING, TRUE},
9292 {"/d", &dml, COBOOL, FALSE},
9298 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9303 EnableDMLHolder dmlHolder(dml);
9304 if (sgen.data != NULL)
9306 if (_stricmp(sgen.data, "any") == 0)
9312 gen = GetExpression(sgen.data);
9315 if ((gen < -1 || gen > 2) && (taObj == 0))
9317 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");
9321 if (gen >= -1 && gen <= 2)
9323 IXCLRDataProcess2* idp2 = NULL;
9324 if (FAILED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
9326 ExtOut("Your version of the runtime/DAC do not support this command.\n");
9330 // Request GC_MARK_END notifications from debuggee
9331 GcEvtArgs gea = { GC_MARK_END, { ((gen == -1) ? 7 : (1 << gen)) } };
9332 idp2->SetGcNotification(gea);
9333 // ... and register the notification handler
9334 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
9335 // the above notification is removed in CNotification::OnGcEvent()
9339 // verify that the last event in the debugger was indeed a CLRN exception
9340 DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
9341 CNotification Notification;
9343 if (!CheckCLRNotificationEvent(&dle))
9345 ExtOut("The command !FindRoots can only be used after the debugger stopped on a CLRN GC notification.\n");
9346 ExtOut("At this time !GCRoot should be used instead.\n");
9349 // validate argument
9350 if (!g_snapshot.Build())
9352 ExtOut("Unable to build snapshot of the garbage collector state\n");
9356 if (g_snapshot.GetHeap(taObj) == NULL)
9358 ExtOut("Address %#p is not in the managed heap.\n", (ULONG64) taObj);
9362 int ogen = g_snapshot.GetGeneration(taObj);
9363 if (ogen > CNotification::GetCondemnedGen())
9365 DMLOut("Object %s will survive this collection:\n\tgen(%#p) = %d > %d = condemned generation.\n",
9366 DMLObject(taObj), (ULONG64)taObj, ogen, CNotification::GetCondemnedGen());
9371 int roots = gcroot.FindRoots(CNotification::GetCondemnedGen(), taObj);
9373 ExtOut("Found %d roots.\n", roots);
9382 class GCHandleStatsForDomains
9385 const static int SHARED_DOMAIN_INDEX = 0;
9386 const static int SYSTEM_DOMAIN_INDEX = 1;
9388 GCHandleStatsForDomains()
9389 : m_singleDomainMode(FALSE), m_numDomains(0), m_pStatistics(NULL), m_pDomainPointers(NULL)
9393 ~GCHandleStatsForDomains()
9397 if (m_singleDomainMode)
9398 delete m_pStatistics;
9400 delete [] m_pStatistics;
9403 if (m_pDomainPointers)
9404 delete [] m_pDomainPointers;
9407 BOOL Init(BOOL singleDomainMode)
9409 m_singleDomainMode = singleDomainMode;
9410 if (m_singleDomainMode)
9413 m_pStatistics = new NOTHROW GCHandleStatistics();
9414 if (m_pStatistics == NULL)
9419 DacpAppDomainStoreData adsData;
9420 if (adsData.Request(g_sos) != S_OK)
9423 m_numDomains = adsData.DomainCount + 2;
9424 ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount + 2];
9428 pArray[SHARED_DOMAIN_INDEX] = adsData.sharedDomain;
9429 pArray[SYSTEM_DOMAIN_INDEX] = adsData.systemDomain;
9431 if (g_sos->GetAppDomainList(adsData.DomainCount, pArray+2, NULL) != S_OK)
9434 m_pDomainPointers = pArray.Detach();
9435 m_pStatistics = new NOTHROW GCHandleStatistics[adsData.DomainCount + 2];
9436 if (m_pStatistics == NULL)
9443 GCHandleStatistics *LookupStatistics(CLRDATA_ADDRESS appDomainPtr) const
9445 if (m_singleDomainMode)
9447 // You can pass NULL appDomainPtr if you are in singleDomainMode
9448 return m_pStatistics;
9452 for (int i=0; i < m_numDomains; i++)
9453 if (m_pDomainPointers[i] == appDomainPtr)
9454 return m_pStatistics + i;
9461 GCHandleStatistics *GetStatistics(int appDomainIndex) const
9463 SOS_Assert(appDomainIndex >= 0);
9464 SOS_Assert(appDomainIndex < m_numDomains);
9466 return m_singleDomainMode ? m_pStatistics : m_pStatistics + appDomainIndex;
9469 int GetNumDomains() const
9471 return m_numDomains;
9474 CLRDATA_ADDRESS GetDomain(int index) const
9476 SOS_Assert(index >= 0);
9477 SOS_Assert(index < m_numDomains);
9478 return m_pDomainPointers[index];
9482 BOOL m_singleDomainMode;
9484 GCHandleStatistics *m_pStatistics;
9485 CLRDATA_ADDRESS *m_pDomainPointers;
9491 GCHandlesImpl(PCSTR args)
9492 : mPerDomain(FALSE), mStat(FALSE), mDML(FALSE), mType((int)~0)
9494 ArrayHolder<char> type = NULL;
9495 CMDOption option[] =
9497 {"-perdomain", &mPerDomain, COBOOL, FALSE},
9498 {"-stat", &mStat, COBOOL, FALSE},
9499 {"-type", &type, COSTRING, TRUE},
9500 {"/d", &mDML, COBOOL, FALSE},
9503 if (!GetCMDOption(args,option,_countof(option),NULL,0,NULL))
9504 sos::Throw<sos::Exception>("Failed to parse command line arguments.");
9507 if (_stricmp(type, "Pinned") == 0)
9508 mType = HNDTYPE_PINNED;
9509 else if (_stricmp(type, "RefCounted") == 0)
9510 mType = HNDTYPE_REFCOUNTED;
9511 else if (_stricmp(type, "WeakShort") == 0)
9512 mType = HNDTYPE_WEAK_SHORT;
9513 else if (_stricmp(type, "WeakLong") == 0)
9514 mType = HNDTYPE_WEAK_LONG;
9515 else if (_stricmp(type, "Strong") == 0)
9516 mType = HNDTYPE_STRONG;
9517 else if (_stricmp(type, "Variable") == 0)
9518 mType = HNDTYPE_VARIABLE;
9519 else if (_stricmp(type, "AsyncPinned") == 0)
9520 mType = HNDTYPE_ASYNCPINNED;
9521 else if (_stricmp(type, "SizedRef") == 0)
9522 mType = HNDTYPE_SIZEDREF;
9523 else if (_stricmp(type, "Dependent") == 0)
9524 mType = HNDTYPE_DEPENDENT;
9525 else if (_stricmp(type, "WeakWinRT") == 0)
9526 mType = HNDTYPE_WEAK_WINRT;
9528 sos::Throw<sos::Exception>("Unknown handle type '%s'.", type.GetPtr());
9533 EnableDMLHolder dmlHolder(mDML);
9535 mOut.ReInit(6, POINTERSIZE_HEX, AlignRight);
9536 mOut.SetWidths(5, POINTERSIZE_HEX, 11, POINTERSIZE_HEX, 8, POINTERSIZE_HEX);
9537 mOut.SetColAlignment(1, AlignLeft);
9539 if (mHandleStat.Init(!mPerDomain) == FALSE)
9540 sos::Throw<sos::Exception>("Error getting per-appdomain handle information");
9543 mOut.WriteRow("Handle", "Type", "Object", "Size", "Data", "Type");
9547 for (int i=0; (i < mHandleStat.GetNumDomains()) && !IsInterrupt(); i++)
9549 GCHandleStatistics *pStats = mHandleStat.GetStatistics(i);
9553 Print( "------------------------------------------------------------------------------\n");
9554 Print("GC Handle Statistics for AppDomain ", AppDomainPtr(mHandleStat.GetDomain(i)));
9556 if (i == GCHandleStatsForDomains::SHARED_DOMAIN_INDEX)
9557 Print(" (Shared Domain)\n");
9558 else if (i == GCHandleStatsForDomains::SYSTEM_DOMAIN_INDEX)
9559 Print(" (System Domain)\n");
9566 PrintGCStat(&pStats->hs);
9568 // Don't print handle stats if the user has filtered by type. All handles will be the same
9569 // type, and the total count will be displayed by PrintGCStat.
9570 if (mType == (unsigned int)~0)
9573 PrintGCHandleStats(pStats);
9581 ToRelease<ISOSHandleEnum> handles;
9582 if (FAILED(g_sos->GetHandleEnum(&handles)))
9584 if (IsMiniDumpFile())
9585 sos::Throw<sos::Exception>("Unable to display GC handles.\nA minidump without full memory may not have this information.");
9587 sos::Throw<sos::Exception>("Failed to walk the handle table.");
9590 // GCC can't handle stacks which are too large.
9592 SOSHandleData data[256];
9594 SOSHandleData data[4];
9597 unsigned int fetched = 0;
9601 if (FAILED(hr = handles->Next(_countof(data), data, &fetched)))
9603 ExtOut("Error %x while walking the handle table.\n", hr);
9607 WalkHandles(data, fetched);
9608 } while (_countof(data) == fetched);
9611 void WalkHandles(SOSHandleData data[], unsigned int count)
9613 for (unsigned int i = 0; i < count; ++i)
9615 sos::CheckInterrupt();
9617 if (mType != (unsigned int)~0 && mType != data[i].Type)
9620 GCHandleStatistics *pStats = mHandleStat.LookupStatistics(data[i].AppDomain);
9624 const wchar_t *mtName = 0;
9625 const char *type = 0;
9627 if (FAILED(MOVE(objAddr, data[i].Handle)))
9630 mtName = L"<error>";
9634 sos::Object obj(TO_TADDR(objAddr));
9635 mtAddr = obj.GetMT();
9636 if (sos::MethodTable::IsFreeMT(mtAddr))
9640 else if (!sos::MethodTable::IsValid(mtAddr))
9642 mtName = L"<error>";
9646 size = obj.GetSize();
9647 if (mType == (unsigned int)~0 || mType == data[i].Type)
9648 pStats->hs.Add(obj.GetMT(), (DWORD)size);
9652 switch(data[i].Type)
9654 case HNDTYPE_PINNED:
9656 if (pStats) pStats->pinnedHandleCount++;
9658 case HNDTYPE_REFCOUNTED:
9659 type = "RefCounted";
9660 if (pStats) pStats->refCntHandleCount++;
9662 case HNDTYPE_STRONG:
9664 if (pStats) pStats->strongHandleCount++;
9666 case HNDTYPE_WEAK_SHORT:
9668 if (pStats) pStats->weakShortHandleCount++;
9670 case HNDTYPE_WEAK_LONG:
9672 if (pStats) pStats->weakLongHandleCount++;
9674 case HNDTYPE_ASYNCPINNED:
9675 type = "AsyncPinned";
9676 if (pStats) pStats->asyncPinnedHandleCount++;
9678 case HNDTYPE_VARIABLE:
9680 if (pStats) pStats->variableCount++;
9682 case HNDTYPE_SIZEDREF:
9684 if (pStats) pStats->sizedRefCount++;
9686 case HNDTYPE_DEPENDENT:
9688 if (pStats) pStats->dependentCount++;
9690 case HNDTYPE_WEAK_WINRT:
9692 if (pStats) pStats->weakWinRTHandleCount++;
9697 pStats->unknownHandleCount++;
9703 sos::MethodTable mt = mtAddr;
9705 mtName = mt.GetName();
9707 if (data[i].Type == HNDTYPE_REFCOUNTED)
9708 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Decimal(data[i].RefCount), mtName);
9709 else if (data[i].Type == HNDTYPE_DEPENDENT)
9710 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), ObjectPtr(data[i].Secondary), mtName);
9711 else if (data[i].Type == HNDTYPE_WEAK_WINRT)
9712 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Pointer(data[i].Secondary), mtName);
9714 mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), "", mtName);
9719 inline void PrintHandleRow(const char *text, int count)
9722 mOut.WriteRow(text, Decimal(count));
9725 void PrintGCHandleStats(GCHandleStatistics *pStats)
9727 Print("Handles:\n");
9728 mOut.ReInit(2, 21, AlignLeft, 4);
9730 PrintHandleRow("Strong Handles:", pStats->strongHandleCount);
9731 PrintHandleRow("Pinned Handles:", pStats->pinnedHandleCount);
9732 PrintHandleRow("Async Pinned Handles:", pStats->asyncPinnedHandleCount);
9733 PrintHandleRow("Ref Count Handles:", pStats->refCntHandleCount);
9734 PrintHandleRow("Weak Long Handles:", pStats->weakLongHandleCount);
9735 PrintHandleRow("Weak Short Handles:", pStats->weakShortHandleCount);
9736 PrintHandleRow("Weak WinRT Handles:", pStats->weakWinRTHandleCount);
9737 PrintHandleRow("Variable Handles:", pStats->variableCount);
9738 PrintHandleRow("SizedRef Handles:", pStats->sizedRefCount);
9739 PrintHandleRow("Dependent Handles:", pStats->dependentCount);
9740 PrintHandleRow("Other Handles:", pStats->unknownHandleCount);
9744 BOOL mPerDomain, mStat, mDML;
9747 GCHandleStatsForDomains mHandleStat;
9750 /**********************************************************************\
9751 * Routine Description: *
9753 * This function dumps GC Handle statistics *
9755 \**********************************************************************/
9756 DECLARE_API(GCHandles)
9759 MINIDUMP_NOT_SUPPORTED();
9763 GCHandlesImpl gchandles(args);
9766 catch(const sos::Exception &e)
9774 BOOL derivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPWSTR baseString)
9776 // We want to follow back until we get the mt for System.Exception
9777 DacpMethodTableData dmtd;
9778 CLRDATA_ADDRESS walkMT = mtObj;
9779 while(walkMT != NULL)
9781 if (dmtd.Request(g_sos, walkMT) != S_OK)
9785 NameForMT_s (TO_TADDR(walkMT), g_mdName, mdNameLen);
9786 if (wcscmp (baseString, g_mdName) == 0)
9790 walkMT = dmtd.ParentMethodTable;
9795 // This is an experimental and undocumented SOS API that attempts to step through code
9796 // stopping once jitted code is reached. It currently has some issues - it can take arbitrarily long
9797 // to reach jitted code and canceling it is terrible. At best it doesn't cancel, at worst it
9798 // kills the debugger. IsInterrupt() doesn't work nearly as nicely as one would hope :/
9800 DECLARE_API(TraceToCode)
9804 static ULONG64 g_clrBaseAddr = 0;
9811 ExtOut("Interrupted\n");
9816 g_ExtRegisters->GetInstructionOffset(&Offset);
9820 CLRDATA_ADDRESS cdaStart = TO_CDADDR(Offset);
9821 DacpMethodDescData MethodDescData;
9822 if(g_ExtSymbols->GetModuleByOffset(Offset, 0, NULL, &base) == S_OK)
9824 if(g_clrBaseAddr == 0)
9826 g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL,
9829 if(g_clrBaseAddr == base)
9831 ExtOut("Compiled code in CLR\n");
9836 ExtOut("Compiled code in module @ 0x%I64x\n", base);
9840 else if (g_sos != NULL || LoadClrDebugDll()==S_OK)
9842 CLRDATA_ADDRESS addr;
9843 if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
9845 WCHAR wszNameBuffer[1024]; // should be large enough
9847 // get the MethodDesc name
9848 if ((g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK) &&
9849 wcsncmp(L"DomainBoundILStubClass", wszNameBuffer, 22)==0)
9856 ExtOut("Jitted code\n");
9862 ExtOut("Not compiled or jitted, assuming stub\n");
9868 // not compiled but CLR isn't loaded... some other code generator?
9878 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "thr; .echo wait" ,0);
9881 ExtOut("Error tracing instruction\n");
9892 // This is an experimental and undocumented API that sets a debugger pseudo-register based
9893 // on the type of code at the given IP. It can be used in scripts to keep stepping until certain
9894 // kinds of code have been reached. Presumbably its slower than !TraceToCode but at least it
9895 // cancels much better
9897 DECLARE_API(GetCodeTypeFlags)
9902 char buffer[100+mdNameLen];
9909 {&PReg.data, COSTRING}
9912 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
9917 size_t preg = 1; // by default
9920 preg = GetExpression(PReg.data);
9923 ExtOut("Pseudo-register number must be between 0 and 19\n");
9928 sprintf_s(buffer,_countof (buffer),
9931 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
9934 ExtOut("Error initialized register $t%d to zero\n", preg);
9939 CLRDATA_ADDRESS cdaStart = TO_CDADDR(ip);
9941 CLRDATA_ADDRESS addr;
9942 if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
9944 WCHAR wszNameBuffer[1024]; // should be large enough
9946 // get the MethodDesc name
9947 if (g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK &&
9948 wcsncmp(L"DomainBoundILStubClass", wszNameBuffer, 22)==0)
9955 ExtOut("Jitted code");
9959 else if(g_ExtSymbols->GetModuleByOffset (ip, 0, NULL, &base) == S_OK)
9961 ULONG64 clrBaseAddr = 0;
9962 if(SUCCEEDED(g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL, &clrBaseAddr)) && base==clrBaseAddr)
9964 ExtOut("Compiled code in CLR");
9969 ExtOut("Compiled code in module @ 0x%I64x\n", base);
9975 ExtOut("Not compiled or jitted, assuming stub\n");
9979 sprintf_s(buffer,_countof (buffer),
9982 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
9985 ExtOut("Error setting register $t%d\n", preg);
9993 DECLARE_API(StopOnException)
9996 MINIDUMP_NOT_SUPPORTED();
9999 char buffer[100+mdNameLen];
10001 BOOL fDerived = FALSE;
10002 BOOL fCreate1 = FALSE;
10003 BOOL fCreate2 = FALSE;
10005 CMDOption option[] = {
10006 // name, vptr, type, hasValue
10007 {"-derived", &fDerived, COBOOL, FALSE}, // catch derived exceptions
10008 {"-create", &fCreate1, COBOOL, FALSE}, // create 1st chance handler
10009 {"-create2", &fCreate2, COBOOL, FALSE}, // create 2nd chance handler
10012 StringHolder TypeName,PReg;
10016 {&TypeName.data, COSTRING},
10017 {&PReg.data, COSTRING}
10020 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
10026 ExtOut("Live debugging session required\n");
10029 if (nArg < 1 || nArg > 2)
10031 ExtOut("usage: StopOnException [-derived] [-create | -create2] <type name>\n");
10032 ExtOut(" [<pseudo-register number for result>]\n");
10033 ExtOut("ex: StopOnException -create System.OutOfMemoryException 1\n");
10037 size_t preg = 1; // by default
10040 preg = GetExpression(PReg.data);
10043 ExtOut("Pseudo-register number must be between 0 and 19\n");
10048 sprintf_s(buffer,_countof (buffer),
10051 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
10052 if (FAILED(Status))
10054 ExtOut("Error initialized register $t%d to zero\n", preg);
10058 if (fCreate1 || fCreate2)
10060 sprintf_s(buffer,_countof (buffer),
10061 "sxe %s \"!soe %s %s %d;.if(@$t%d==0) {g} .else {.echo '%s hit'}\" %x",
10062 fCreate1 ? "-c" : "-c2",
10063 fDerived ? "-derived" : "",
10071 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
10072 if (FAILED(Status))
10074 ExtOut("Error setting breakpoint: %s\n", buffer);
10078 ExtOut("Breakpoint set\n");
10082 // Find the last thrown exception on this thread.
10083 // Does it match? If so, set the register.
10084 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
10085 DacpThreadData Thread;
10087 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
10089 ExtOut("The current thread is unmanaged\n");
10094 if (!SafeReadMemory(Thread.lastThrownObjectHandle,
10096 sizeof(taLTOH), NULL))
10098 ExtOut("There is no current managed exception on this thread\n");
10104 LPWSTR typeNameWide = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
10105 MultiByteToWideChar(CP_ACP,0,TypeName.data,-1,typeNameWide,mdNameLen);
10108 if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
10110 NameForMT_s (taMT, g_mdName, mdNameLen);
10111 if ((wcscmp(g_mdName,typeNameWide) == 0) ||
10112 (fDerived && derivedFrom(taMT, typeNameWide)))
10114 sprintf_s(buffer,_countof (buffer),
10117 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
10118 if (FAILED(Status))
10120 ExtOut("Failed to execute the following command: %s\n", buffer);
10129 /**********************************************************************\
10130 * Routine Description: *
10132 * This function finds the size of an object or all roots. *
10134 \**********************************************************************/
10135 DECLARE_API(ObjSize)
10137 #ifndef FEATURE_PAL
10139 MINIDUMP_NOT_SUPPORTED();
10142 StringHolder str_Object;
10145 CMDOption option[] =
10146 { // name, vptr, type, hasValue
10147 {"/d", &dml, COBOOL, FALSE},
10151 {&str_Object.data, COSTRING}
10154 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
10159 EnableDMLHolder dmlHolder(dml);
10160 TADDR obj = GetExpression(str_Object.data);
10169 if(!sos::IsObject(obj))
10171 ExtOut("%p is not a valid object.\n", SOS_PTR(obj));
10175 size_t size = gcroot.ObjSize(obj);
10178 sos::MethodTable methodTable = mt;
10179 ExtOut("sizeof(%p) = %d (0x%x) bytes (%S)\n", SOS_PTR(obj), size, size, methodTable.GetName());
10188 #ifndef FEATURE_PAL
10189 // For FEATURE_PAL, MEMORY_BASIC_INFORMATION64 doesn't exist yet. TODO?
10190 DECLARE_API(GCHandleLeaks)
10193 MINIDUMP_NOT_SUPPORTED();
10195 ExtOut("-------------------------------------------------------------------------------\n");
10196 ExtOut("GCHandleLeaks will report any GCHandles that couldn't be found in memory. \n");
10197 ExtOut("Strong and Pinned GCHandles are reported at this time. You can safely abort the\n");
10198 ExtOut("memory scan with Control-C or Control-Break. \n");
10199 ExtOut("-------------------------------------------------------------------------------\n");
10201 static DWORD_PTR array[2000];
10205 CMDOption option[] =
10206 { // name, vptr, type, hasValue
10207 {"/d", &dml, COBOOL, FALSE},
10210 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
10215 EnableDMLHolder dmlHolder(dml);
10217 UINT iFinal = FindAllPinnedAndStrong(array,sizeof(array)/sizeof(DWORD_PTR));
10218 ExtOut("Found %d handles:\n",iFinal);
10219 for (i=1;i<=iFinal;i++)
10221 ExtOut("%p\t",(ULONG64)array[i-1]);
10226 ExtOut("\nSearching memory\n");
10227 // Now search memory for this:
10228 DWORD_PTR buffer[1024];
10229 ULONG64 memCur = 0x0;
10230 BOOL bAbort = FALSE;
10232 //find out memory used by stress log
10233 StressLogMem stressLog;
10234 CLRDATA_ADDRESS StressLogAddress = NULL;
10235 if (LoadClrDebugDll() != S_OK)
10237 // Try to find stress log symbols
10238 DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
10239 StressLogAddress = dwAddr;
10240 g_bDacBroken = TRUE;
10244 if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
10246 ExtOut("Unable to find stress log via DAC\n");
10248 g_bDacBroken = FALSE;
10251 if (stressLog.Init (StressLogAddress, g_ExtData))
10253 ExtOut("Reference found in stress log will be ignored\n");
10257 ExtOut("Failed to read whole or part of stress log, some references may come from stress log\n");
10264 MEMORY_BASIC_INFORMATION64 memInfo;
10266 status = g_ExtData2->QueryVirtual(UL64_TO_CDA(memCur), &memInfo);
10268 if( !NT_SUCCESS(status) )
10273 if (memInfo.State == MEM_COMMIT)
10275 for (ULONG64 memIter = memCur; memIter < (memCur + memInfo.RegionSize); memIter+=sizeof(buffer))
10279 ExtOut("Quitting at %p due to user abort\n",(ULONG64)memIter);
10284 if ((memIter % 0x10000000)==0x0)
10286 ExtOut("Searching %p...\n",(ULONG64)memIter);
10291 ret = g_ExtData->ReadVirtual(UL64_TO_CDA(memIter), buffer, sizeof(buffer), &size);
10294 for (UINT x=0;x<1024;x++)
10296 DWORD_PTR value = buffer[x];
10297 // We don't care about the low bit. Also, the GCHandle class turns on the
10298 // low bit for pinned handles, so without the statement below, we wouldn't
10299 // notice pinned handles.
10300 value = value & ~1;
10301 for (i=0;i<iFinal;i++)
10303 ULONG64 addrInDebugee = (ULONG64)memIter+(x*sizeof(DWORD_PTR));
10304 if ((array[i] & ~1) == value)
10306 if (stressLog.IsInStressLog (addrInDebugee))
10308 ExtOut("Found %p in stress log at location %p, reference not counted\n", (ULONG64)value, addrInDebugee);
10312 ExtOut("Found %p at location %p\n",(ULONG64)value, addrInDebugee);
10323 ExtOut("only read %x bytes at %p\n",size,(ULONG64)memIter);
10329 memCur += memInfo.RegionSize;
10332 int numNotFound = 0;
10333 for (i=0;i<iFinal;i++)
10335 if ((array[i] & 0x1) == 0)
10338 // ExtOut("WARNING: %p not found\n",(ULONG64)array[i]);
10342 if (numNotFound > 0)
10344 ExtOut("------------------------------------------------------------------------------\n");
10345 ExtOut("Some handles were not found. If the number of not-found handles grows over the\n");
10346 ExtOut("lifetime of your application, you may have a GCHandle leak. This will cause \n");
10347 ExtOut("the GC Heap to grow larger as objects are being kept alive, referenced only \n");
10348 ExtOut("by the orphaned handle. If the number doesn't grow over time, note that there \n");
10349 ExtOut("may be some noise in this output, as an unmanaged application may be storing \n");
10350 ExtOut("the handle in a non-standard way, perhaps with some bits flipped. The memory \n");
10351 ExtOut("scan wouldn't be able to find those. \n");
10352 ExtOut("------------------------------------------------------------------------------\n");
10354 ExtOut("Didn't find %d handles:\n", numNotFound);
10356 for (i=0;i<iFinal;i++)
10358 if ((array[i] & 0x1) == 0)
10361 ExtOut("%p\t",(ULONG64)array[i]);
10362 if ((numPrinted % 4) == 0)
10370 ExtOut("------------------------------------------------------------------------------\n");
10371 ExtOut("All handles found");
10373 ExtOut(" even though you aborted.\n");
10376 ExtOut("A leak may still exist because in a general scan of process memory SOS can't \n");
10377 ExtOut("differentiate between garbage and valid structures, so you may have false \n");
10378 ExtOut("positives. If you still suspect a leak, use this function over time to \n");
10379 ExtOut("identify a possible trend. \n");
10380 ExtOut("------------------------------------------------------------------------------\n");
10385 #endif // FEATURE_PAL
10389 class ClrStackImplWithICorDebug
10392 static HRESULT ClrStackFromPublicInterface(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, WCHAR* varToExpand = NULL, int onlyShowFrame = -1)
10394 ExtOut("Not supported on this platform.\n");
10399 #else // FEATURE_PAL NOT defined below
10401 class ClrStackImplWithICorDebug
10405 static HRESULT DereferenceAndUnboxValue(ICorDebugValue * pValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = NULL)
10407 HRESULT Status = S_OK;
10408 *ppOutputValue = NULL;
10409 if(pIsNull != NULL) *pIsNull = FALSE;
10411 ToRelease<ICorDebugReferenceValue> pReferenceValue;
10412 Status = pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue);
10413 if (SUCCEEDED(Status))
10415 BOOL isNull = FALSE;
10416 IfFailRet(pReferenceValue->IsNull(&isNull));
10419 ToRelease<ICorDebugValue> pDereferencedValue;
10420 IfFailRet(pReferenceValue->Dereference(&pDereferencedValue));
10421 return DereferenceAndUnboxValue(pDereferencedValue, ppOutputValue);
10425 if(pIsNull != NULL) *pIsNull = TRUE;
10426 *ppOutputValue = pValue;
10427 (*ppOutputValue)->AddRef();
10432 ToRelease<ICorDebugBoxValue> pBoxedValue;
10433 Status = pValue->QueryInterface(IID_ICorDebugBoxValue, (LPVOID*) &pBoxedValue);
10434 if (SUCCEEDED(Status))
10436 ToRelease<ICorDebugObjectValue> pUnboxedValue;
10437 IfFailRet(pBoxedValue->GetObject(&pUnboxedValue));
10438 return DereferenceAndUnboxValue(pUnboxedValue, ppOutputValue);
10440 *ppOutputValue = pValue;
10441 (*ppOutputValue)->AddRef();
10444 static BOOL ShouldExpandVariable(__in_z WCHAR* varToExpand, __in_z WCHAR* currentExpansion)
10446 if(currentExpansion == NULL || varToExpand == NULL) return FALSE;
10448 size_t varToExpandLen = wcslen(varToExpand);
10449 size_t currentExpansionLen = wcslen(currentExpansion);
10450 if(currentExpansionLen > varToExpandLen) return FALSE;
10451 if(currentExpansionLen < varToExpandLen && varToExpand[currentExpansionLen] != L'.') return FALSE;
10452 if(wcsncmp(currentExpansion, varToExpand, currentExpansionLen) != 0) return FALSE;
10456 static BOOL IsEnum(ICorDebugValue * pInputValue)
10458 ToRelease<ICorDebugValue> pValue;
10459 if(FAILED(DereferenceAndUnboxValue(pInputValue, &pValue, NULL))) return FALSE;
10461 WCHAR baseTypeName[mdNameLen];
10462 ToRelease<ICorDebugValue2> pValue2;
10463 ToRelease<ICorDebugType> pType;
10464 ToRelease<ICorDebugType> pBaseType;
10466 if(FAILED(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2))) return FALSE;
10467 if(FAILED(pValue2->GetExactType(&pType))) return FALSE;
10468 if(FAILED(pType->GetBase(&pBaseType)) || pBaseType == NULL) return FALSE;
10469 if(FAILED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen))) return FALSE;
10471 return (wcsncmp(baseTypeName, L"System.Enum", 11) == 0);
10473 static HRESULT AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
10475 bool isFirst = true;
10476 ToRelease<ICorDebugTypeEnum> pTypeEnum;
10477 if(SUCCEEDED(pType->EnumerateTypeParameters(&pTypeEnum)))
10479 ULONG numTypes = 0;
10480 ToRelease<ICorDebugType> pCurrentTypeParam;
10482 while(SUCCEEDED(pTypeEnum->Next(1, &pCurrentTypeParam, &numTypes)))
10484 if(numTypes == 0) break;
10489 wcsncat_s(typeName, typeNameLen, L"<", typeNameLen);
10491 else wcsncat_s(typeName, typeNameLen, L",", typeNameLen);
10493 WCHAR typeParamName[mdNameLen];
10494 typeParamName[0] = L'\0';
10495 GetTypeOfValue(pCurrentTypeParam, typeParamName, mdNameLen);
10496 wcsncat_s(typeName, typeNameLen, typeParamName, typeNameLen);
10499 wcsncat_s(typeName, typeNameLen, L">", typeNameLen);
10504 static HRESULT GetTypeOfValue(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
10506 HRESULT Status = S_OK;
10508 CorElementType corElemType;
10509 IfFailRet(pType->GetType(&corElemType));
10511 switch (corElemType)
10513 //List of unsupported CorElementTypes:
10514 //ELEMENT_TYPE_END = 0x0,
10515 //ELEMENT_TYPE_VAR = 0x13, // a class type variable VAR <U1>
10516 //ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
10517 //ELEMENT_TYPE_TYPEDBYREF = 0x16, // TYPEDREF (it takes no args) a typed referece to some other type
10518 //ELEMENT_TYPE_MVAR = 0x1e, // a method type variable MVAR <U1>
10519 //ELEMENT_TYPE_CMOD_REQD = 0x1F, // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef>
10520 //ELEMENT_TYPE_CMOD_OPT = 0x20, // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
10521 //ELEMENT_TYPE_INTERNAL = 0x21, // INTERNAL <typehandle>
10522 //ELEMENT_TYPE_MAX = 0x22, // first invalid element type
10523 //ELEMENT_TYPE_MODIFIER = 0x40,
10524 //ELEMENT_TYPE_SENTINEL = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs
10525 //ELEMENT_TYPE_PINNED = 0x05 | ELEMENT_TYPE_MODIFIER,
10526 //ELEMENT_TYPE_R4_HFA = 0x06 | ELEMENT_TYPE_MODIFIER, // used only internally for R4 HFA types
10527 //ELEMENT_TYPE_R8_HFA = 0x07 | ELEMENT_TYPE_MODIFIER, // used only internally for R8 HFA types
10529 swprintf_s(typeName, typeNameLen, L"(Unhandled CorElementType: 0x%x)\0", corElemType);
10532 case ELEMENT_TYPE_VALUETYPE:
10533 case ELEMENT_TYPE_CLASS:
10535 //Defaults in case we fail...
10536 if(corElemType == ELEMENT_TYPE_VALUETYPE) swprintf_s(typeName, typeNameLen, L"struct\0");
10537 else swprintf_s(typeName, typeNameLen, L"class\0");
10540 ToRelease<ICorDebugClass> pClass;
10541 if(SUCCEEDED(pType->GetClass(&pClass)) && SUCCEEDED(pClass->GetToken(&typeDef)))
10543 ToRelease<ICorDebugModule> pModule;
10544 IfFailRet(pClass->GetModule(&pModule));
10546 ToRelease<IUnknown> pMDUnknown;
10547 ToRelease<IMetaDataImport> pMD;
10548 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
10549 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
10551 if(SUCCEEDED(NameForToken_s(TokenFromRid(typeDef, mdtTypeDef), pMD, g_mdName, mdNameLen, false)))
10552 swprintf_s(typeName, typeNameLen, L"%s\0", g_mdName);
10554 AddGenericArgs(pType, typeName, typeNameLen);
10557 case ELEMENT_TYPE_VOID:
10558 swprintf_s(typeName, typeNameLen, L"void\0");
10560 case ELEMENT_TYPE_BOOLEAN:
10561 swprintf_s(typeName, typeNameLen, L"bool\0");
10563 case ELEMENT_TYPE_CHAR:
10564 swprintf_s(typeName, typeNameLen, L"char\0");
10566 case ELEMENT_TYPE_I1:
10567 swprintf_s(typeName, typeNameLen, L"signed byte\0");
10569 case ELEMENT_TYPE_U1:
10570 swprintf_s(typeName, typeNameLen, L"byte\0");
10572 case ELEMENT_TYPE_I2:
10573 swprintf_s(typeName, typeNameLen, L"short\0");
10575 case ELEMENT_TYPE_U2:
10576 swprintf_s(typeName, typeNameLen, L"unsigned short\0");
10578 case ELEMENT_TYPE_I4:
10579 swprintf_s(typeName, typeNameLen, L"int\0");
10581 case ELEMENT_TYPE_U4:
10582 swprintf_s(typeName, typeNameLen, L"unsigned int\0");
10584 case ELEMENT_TYPE_I8:
10585 swprintf_s(typeName, typeNameLen, L"long\0");
10587 case ELEMENT_TYPE_U8:
10588 swprintf_s(typeName, typeNameLen, L"unsigned long\0");
10590 case ELEMENT_TYPE_R4:
10591 swprintf_s(typeName, typeNameLen, L"float\0");
10593 case ELEMENT_TYPE_R8:
10594 swprintf_s(typeName, typeNameLen, L"double\0");
10596 case ELEMENT_TYPE_OBJECT:
10597 swprintf_s(typeName, typeNameLen, L"object\0");
10599 case ELEMENT_TYPE_STRING:
10600 swprintf_s(typeName, typeNameLen, L"string\0");
10602 case ELEMENT_TYPE_I:
10603 swprintf_s(typeName, typeNameLen, L"IntPtr\0");
10605 case ELEMENT_TYPE_U:
10606 swprintf_s(typeName, typeNameLen, L"UIntPtr\0");
10608 case ELEMENT_TYPE_SZARRAY:
10609 case ELEMENT_TYPE_ARRAY:
10610 case ELEMENT_TYPE_BYREF:
10611 case ELEMENT_TYPE_PTR:
10613 ToRelease<ICorDebugType> pFirstParameter;
10614 if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)))
10615 GetTypeOfValue(pFirstParameter, typeName, typeNameLen);
10617 swprintf_s(typeName, typeNameLen, L"<unknown>\0");
10619 switch(corElemType)
10621 case ELEMENT_TYPE_SZARRAY:
10622 wcsncat_s(typeName, typeNameLen, L"[]\0", typeNameLen);
10624 case ELEMENT_TYPE_ARRAY:
10627 pType->GetRank(&rank);
10628 wcsncat_s(typeName, typeNameLen, L"[", typeNameLen);
10629 for(ULONG32 i = 0; i < rank - 1; i++)
10632 wcsncat_s(typeName, typeNameLen, L",", typeNameLen);
10634 wcsncat_s(typeName, typeNameLen, L"]\0", typeNameLen);
10637 case ELEMENT_TYPE_BYREF:
10638 wcsncat_s(typeName, typeNameLen, L"&\0", typeNameLen);
10640 case ELEMENT_TYPE_PTR:
10641 wcsncat_s(typeName, typeNameLen, L"*\0", typeNameLen);
10646 case ELEMENT_TYPE_FNPTR:
10647 swprintf_s(typeName, typeNameLen, L"*(...)\0");
10649 case ELEMENT_TYPE_TYPEDBYREF:
10650 swprintf_s(typeName, typeNameLen, L"typedbyref\0");
10655 static HRESULT GetTypeOfValue(ICorDebugValue * pValue, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
10657 HRESULT Status = S_OK;
10659 CorElementType corElemType;
10660 IfFailRet(pValue->GetType(&corElemType));
10662 ToRelease<ICorDebugType> pType;
10663 ToRelease<ICorDebugValue2> pValue2;
10664 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugValue2, (void**) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
10665 return GetTypeOfValue(pType, typeName, typeNameLen);
10667 swprintf_s(typeName, typeNameLen, L"<unknown>\0");
10671 static HRESULT PrintEnumValue(ICorDebugValue* pInputValue, BYTE* enumValue)
10673 HRESULT Status = S_OK;
10675 ToRelease<ICorDebugValue> pValue;
10676 IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, NULL));
10678 mdTypeDef currentTypeDef;
10679 ToRelease<ICorDebugClass> pClass;
10680 ToRelease<ICorDebugValue2> pValue2;
10681 ToRelease<ICorDebugType> pType;
10682 ToRelease<ICorDebugModule> pModule;
10683 IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
10684 IfFailRet(pValue2->GetExactType(&pType));
10685 IfFailRet(pType->GetClass(&pClass));
10686 IfFailRet(pClass->GetModule(&pModule));
10687 IfFailRet(pClass->GetToken(¤tTypeDef));
10689 ToRelease<IUnknown> pMDUnknown;
10690 ToRelease<IMetaDataImport> pMD;
10691 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
10692 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
10695 //First, we need to figure out the underlying enum type so that we can correctly type cast the raw values of each enum constant
10696 //We get that from the non-static field of the enum variable (I think the field is called __value or something similar)
10697 ULONG numFields = 0;
10698 HCORENUM fEnum = NULL;
10699 mdFieldDef fieldDef;
10700 CorElementType enumUnderlyingType = ELEMENT_TYPE_END;
10701 while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
10703 DWORD fieldAttr = 0;
10704 PCCOR_SIGNATURE pSignatureBlob = NULL;
10705 ULONG sigBlobLength = 0;
10706 if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, NULL, 0, NULL, &fieldAttr, &pSignatureBlob, &sigBlobLength, NULL, NULL, NULL)))
10708 if((fieldAttr & fdStatic) == 0)
10710 CorSigUncompressCallingConv(pSignatureBlob);
10711 enumUnderlyingType = CorSigUncompressElementType(pSignatureBlob);
10716 pMD->CloseEnum(fEnum);
10719 //Now that we know the underlying enum type, let's decode the enum variable into OR-ed, human readable enum contants
10721 bool isFirst = true;
10722 ULONG64 remainingValue = *((ULONG64*)enumValue);
10723 while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
10726 DWORD fieldAttr = 0;
10727 WCHAR mdName[mdNameLen];
10728 WCHAR typeName[mdNameLen];
10729 UVCP_CONSTANT pRawValue = NULL;
10730 ULONG rawValueLength = 0;
10731 if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, &pRawValue, &rawValueLength)))
10733 DWORD enumValueRequiredAttributes = fdPublic | fdStatic | fdLiteral | fdHasDefault;
10734 if((fieldAttr & enumValueRequiredAttributes) != enumValueRequiredAttributes)
10737 ULONG64 currentConstValue = 0;
10738 switch (enumUnderlyingType)
10740 case ELEMENT_TYPE_CHAR:
10741 case ELEMENT_TYPE_I1:
10742 currentConstValue = (ULONG64)(*((CHAR*)pRawValue));
10744 case ELEMENT_TYPE_U1:
10745 currentConstValue = (ULONG64)(*((BYTE*)pRawValue));
10747 case ELEMENT_TYPE_I2:
10748 currentConstValue = (ULONG64)(*((SHORT*)pRawValue));
10750 case ELEMENT_TYPE_U2:
10751 currentConstValue = (ULONG64)(*((USHORT*)pRawValue));
10753 case ELEMENT_TYPE_I4:
10754 currentConstValue = (ULONG64)(*((INT32*)pRawValue));
10756 case ELEMENT_TYPE_U4:
10757 currentConstValue = (ULONG64)(*((UINT32*)pRawValue));
10759 case ELEMENT_TYPE_I8:
10760 currentConstValue = (ULONG64)(*((LONG*)pRawValue));
10762 case ELEMENT_TYPE_U8:
10763 currentConstValue = (ULONG64)(*((ULONG*)pRawValue));
10765 case ELEMENT_TYPE_I:
10766 currentConstValue = (ULONG64)(*((int*)pRawValue));
10768 case ELEMENT_TYPE_U:
10769 case ELEMENT_TYPE_R4:
10770 case ELEMENT_TYPE_R8:
10771 // Technically U and the floating-point ones are options in the CLI, but not in the CLS or C#, so these are NYI
10773 currentConstValue = 0;
10776 if((currentConstValue == remainingValue) || ((currentConstValue != 0) && ((currentConstValue & remainingValue) == currentConstValue)))
10778 remainingValue &= ~currentConstValue;
10781 ExtOut(" = %S", mdName);
10784 else ExtOut(" | %S", mdName);
10788 pMD->CloseEnum(fEnum);
10792 static HRESULT PrintStringValue(ICorDebugValue * pValue)
10796 ToRelease<ICorDebugStringValue> pStringValue;
10797 IfFailRet(pValue->QueryInterface(IID_ICorDebugStringValue, (LPVOID*) &pStringValue));
10800 IfFailRet(pStringValue->GetLength(&cchValue));
10801 cchValue++; // Allocate one more for null terminator
10803 CQuickString quickString;
10804 quickString.Alloc(cchValue);
10806 ULONG32 cchValueReturned;
10807 IfFailRet(pStringValue->GetString(
10810 quickString.String()));
10812 ExtOut(" = \"%S\"\n", quickString.String());
10816 static HRESULT PrintSzArrayValue(ICorDebugValue * pValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
10818 HRESULT Status = S_OK;
10820 ToRelease<ICorDebugArrayValue> pArrayValue;
10821 IfFailRet(pValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue));
10824 IfFailRet(pArrayValue->GetRank(&nRank));
10827 return E_UNEXPECTED;
10831 IfFailRet(pArrayValue->GetCount(&cElements));
10833 if (cElements == 0) ExtOut(" (empty)\n");
10834 else if (cElements == 1) ExtOut(" (1 element)\n");
10835 else ExtOut(" (%d elements)\n", cElements);
10837 if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
10838 size_t currentExpansionLen = wcslen(currentExpansion);
10840 for (ULONG32 i=0; i < cElements; i++)
10842 for(int j = 0; j <= indent; j++) ExtOut(" ");
10843 currentExpansion[currentExpansionLen] = L'\0';
10844 swprintf_s(currentExpansion, mdNameLen, L"%s.[%d]\0", currentExpansion, i);
10846 bool printed = false;
10847 CorElementType corElemType;
10848 ToRelease<ICorDebugType> pFirstParameter;
10849 ToRelease<ICorDebugValue2> pValue2;
10850 ToRelease<ICorDebugType> pType;
10851 if(SUCCEEDED(pArrayValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
10853 if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)) && SUCCEEDED(pFirstParameter->GetType(&corElemType)))
10855 switch(corElemType)
10857 //If the array element is something that we can expand with !clrstack, show information about the type of this element
10858 case ELEMENT_TYPE_VALUETYPE:
10859 case ELEMENT_TYPE_CLASS:
10860 case ELEMENT_TYPE_SZARRAY:
10862 WCHAR typeOfElement[mdNameLen];
10863 GetTypeOfValue(pFirstParameter, typeOfElement, mdNameLen);
10864 DMLOut(" |- %s = %S", DMLManagedVar(currentExpansion, currentFrame, i), typeOfElement);
10871 if(!printed) DMLOut(" |- %s", DMLManagedVar(currentExpansion, currentFrame, i));
10873 ToRelease<ICorDebugValue> pElementValue;
10874 IfFailRet(pArrayValue->GetElementAtPosition(i, &pElementValue));
10875 IfFailRet(PrintValue(pElementValue, pILFrame, pMD, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame));
10880 static HRESULT PrintValue(ICorDebugValue * pInputValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
10882 HRESULT Status = S_OK;
10884 BOOL isNull = TRUE;
10885 ToRelease<ICorDebugValue> pValue;
10886 IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
10890 ExtOut(" = null\n");
10895 IfFailRet(pValue->GetSize(&cbSize));
10896 ArrayHolder<BYTE> rgbValue = new NOTHROW BYTE[cbSize];
10897 if (rgbValue == NULL)
10900 return E_OUTOFMEMORY;
10903 memset(rgbValue.GetPtr(), 0, cbSize * sizeof(BYTE));
10905 CorElementType corElemType;
10906 IfFailRet(pValue->GetType(&corElemType));
10907 if (corElemType == ELEMENT_TYPE_STRING)
10909 return PrintStringValue(pValue);
10912 if (corElemType == ELEMENT_TYPE_SZARRAY)
10914 return PrintSzArrayValue(pValue, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
10917 ToRelease<ICorDebugGenericValue> pGenericValue;
10918 IfFailRet(pValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue));
10919 IfFailRet(pGenericValue->GetValue((LPVOID) &(rgbValue[0])));
10923 Status = PrintEnumValue(pValue, rgbValue);
10928 switch (corElemType)
10931 ExtOut(" (Unhandled CorElementType: 0x%x)\n", corElemType);
10934 case ELEMENT_TYPE_PTR:
10935 ExtOut(" = <pointer>\n");
10938 case ELEMENT_TYPE_FNPTR:
10940 CORDB_ADDRESS addr = 0;
10941 ToRelease<ICorDebugReferenceValue> pReferenceValue = NULL;
10942 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue)))
10943 pReferenceValue->GetValue(&addr);
10944 ExtOut(" = <function pointer 0x%x>\n", addr);
10948 case ELEMENT_TYPE_VALUETYPE:
10949 case ELEMENT_TYPE_CLASS:
10950 CORDB_ADDRESS addr;
10951 if(SUCCEEDED(pValue->GetAddress(&addr)))
10953 ExtOut(" @ 0x%I64x\n", addr);
10959 ProcessFields(pValue, NULL, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
10962 case ELEMENT_TYPE_BOOLEAN:
10963 ExtOut(" = %s\n", rgbValue[0] == 0 ? "false" : "true");
10966 case ELEMENT_TYPE_CHAR:
10967 ExtOut(" = '%C'\n", *(WCHAR *) &(rgbValue[0]));
10970 case ELEMENT_TYPE_I1:
10971 ExtOut(" = %d\n", *(char*) &(rgbValue[0]));
10974 case ELEMENT_TYPE_U1:
10975 ExtOut(" = %d\n", *(unsigned char*) &(rgbValue[0]));
10978 case ELEMENT_TYPE_I2:
10979 ExtOut(" = %hd\n", *(short*) &(rgbValue[0]));
10982 case ELEMENT_TYPE_U2:
10983 ExtOut(" = %hu\n", *(unsigned short*) &(rgbValue[0]));
10986 case ELEMENT_TYPE_I:
10987 ExtOut(" = %d\n", *(int*) &(rgbValue[0]));
10990 case ELEMENT_TYPE_U:
10991 ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0]));
10994 case ELEMENT_TYPE_I4:
10995 ExtOut(" = %d\n", *(int*) &(rgbValue[0]));
10998 case ELEMENT_TYPE_U4:
10999 ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0]));
11002 case ELEMENT_TYPE_I8:
11003 ExtOut(" = %I64d\n", *(__int64*) &(rgbValue[0]));
11006 case ELEMENT_TYPE_U8:
11007 ExtOut(" = %I64u\n", *(unsigned __int64*) &(rgbValue[0]));
11010 case ELEMENT_TYPE_R4:
11011 ExtOut(" = %f\n", (double) *(float*) &(rgbValue[0]));
11014 case ELEMENT_TYPE_R8:
11015 ExtOut(" = %f\n", *(double*) &(rgbValue[0]));
11018 case ELEMENT_TYPE_OBJECT:
11019 ExtOut(" = object\n");
11022 // TODO: The following corElementTypes are not yet implemented here. Array
11023 // might be interesting to add, though the others may be of rather limited use:
11024 // ELEMENT_TYPE_ARRAY = 0x14, // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ...
11026 // ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
11031 static HRESULT PrintParameters(BOOL bParams, BOOL bLocals, IMetaDataImport * pMD, mdTypeDef typeDef, mdMethodDef methodDef, ICorDebugILFrame * pILFrame, ICorDebugModule * pModule, __in_z WCHAR* varToExpand, int currentFrame)
11033 HRESULT Status = S_OK;
11036 ToRelease<ICorDebugValueEnum> pParamEnum;
11037 IfFailRet(pILFrame->EnumerateArguments(&pParamEnum));
11038 IfFailRet(pParamEnum->GetCount(&cParams));
11039 if (cParams > 0 && bParams)
11041 DWORD methAttr = 0;
11042 IfFailRet(pMD->GetMethodProps(methodDef, NULL, NULL, 0, NULL, &methAttr, NULL, NULL, NULL, NULL));
11044 ExtOut("\nPARAMETERS:\n");
11045 for (ULONG i=0; i < cParams; i++)
11047 ULONG paramNameLen = 0;
11048 mdParamDef paramDef;
11049 WCHAR paramName[mdNameLen] = L"\0";
11051 if(i == 0 && (methAttr & mdStatic) == 0)
11052 swprintf_s(paramName, mdNameLen, L"this\0");
11055 int idx = ((methAttr & mdStatic) == 0)? i : (i + 1);
11056 if(SUCCEEDED(pMD->GetParamForMethodIndex(methodDef, idx, ¶mDef)))
11057 pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, ¶mNameLen, NULL, NULL, NULL, NULL);
11059 if(wcslen(paramName) == 0)
11060 swprintf_s(paramName, mdNameLen, L"param_%d\0", i);
11062 ToRelease<ICorDebugValue> pValue;
11063 ULONG cArgsFetched;
11064 Status = pParamEnum->Next(1, &pValue, &cArgsFetched);
11066 if (FAILED(Status))
11068 ExtOut(" + (Error 0x%x retrieving parameter '%S')\n", Status, paramName);
11072 if (Status == S_FALSE)
11077 WCHAR typeName[mdNameLen] = L"\0";
11078 GetTypeOfValue(pValue, typeName, mdNameLen);
11079 DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
11081 ToRelease<ICorDebugReferenceValue> pRefValue;
11082 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
11084 BOOL bIsNull = TRUE;
11085 pRefValue->IsNull(&bIsNull);
11088 ExtOut(" = null\n");
11093 WCHAR currentExpansion[mdNameLen];
11094 swprintf_s(currentExpansion, mdNameLen, L"%s\0", paramName);
11095 if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
11096 ExtOut(" + (Error 0x%x printing parameter %d)\n", Status, i);
11099 else if (cParams == 0 && bParams)
11100 ExtOut("\nPARAMETERS: (none)\n");
11103 ToRelease<ICorDebugValueEnum> pLocalsEnum;
11104 IfFailRet(pILFrame->EnumerateLocalVariables(&pLocalsEnum));
11105 IfFailRet(pLocalsEnum->GetCount(&cLocals));
11106 if (cLocals > 0 && bLocals)
11108 bool symbolsAvailable = false;
11109 SymbolReader symReader;
11110 if(SUCCEEDED(symReader.LoadSymbols(pMD, pModule)))
11111 symbolsAvailable = true;
11113 ExtOut("\nLOCALS:\n");
11114 for (ULONG i=0; i < cLocals; i++)
11116 ULONG paramNameLen = 0;
11117 WCHAR paramName[mdNameLen] = L"\0";
11119 ToRelease<ICorDebugValue> pValue;
11120 if(symbolsAvailable)
11122 Status = symReader.GetNamedLocalVariable(pILFrame, i, paramName, mdNameLen, &pValue);
11126 ULONG cArgsFetched;
11127 Status = pLocalsEnum->Next(1, &pValue, &cArgsFetched);
11129 if(wcslen(paramName) == 0)
11130 swprintf_s(paramName, mdNameLen, L"local_%d\0", i);
11132 if (FAILED(Status))
11134 ExtOut(" + (Error 0x%x retrieving local variable '%S')\n", Status, paramName);
11138 if (Status == S_FALSE)
11143 WCHAR typeName[mdNameLen] = L"\0";
11144 GetTypeOfValue(pValue, typeName, mdNameLen);
11145 DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
11147 ToRelease<ICorDebugReferenceValue> pRefValue = NULL;
11148 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
11150 BOOL bIsNull = TRUE;
11151 pRefValue->IsNull(&bIsNull);
11154 ExtOut(" = null\n");
11159 WCHAR currentExpansion[mdNameLen];
11160 swprintf_s(currentExpansion, mdNameLen, L"%s\0", paramName);
11161 if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
11162 ExtOut(" + (Error 0x%x printing local variable %d)\n", Status, i);
11165 else if (cLocals == 0 && bLocals)
11166 ExtOut("\nLOCALS: (none)\n");
11168 if(bParams || bLocals)
11174 static HRESULT ProcessFields(ICorDebugValue* pInputValue, ICorDebugType* pTypeCast, ICorDebugILFrame * pILFrame, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
11176 if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
11177 size_t currentExpansionLen = wcslen(currentExpansion);
11179 HRESULT Status = S_OK;
11181 BOOL isNull = FALSE;
11182 ToRelease<ICorDebugValue> pValue;
11183 IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
11185 if(isNull) return S_OK;
11187 mdTypeDef currentTypeDef;
11188 ToRelease<ICorDebugClass> pClass;
11189 ToRelease<ICorDebugValue2> pValue2;
11190 ToRelease<ICorDebugType> pType;
11191 ToRelease<ICorDebugModule> pModule;
11192 IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
11193 if(pTypeCast == NULL)
11194 IfFailRet(pValue2->GetExactType(&pType));
11200 IfFailRet(pType->GetClass(&pClass));
11201 IfFailRet(pClass->GetModule(&pModule));
11202 IfFailRet(pClass->GetToken(¤tTypeDef));
11204 ToRelease<IUnknown> pMDUnknown;
11205 ToRelease<IMetaDataImport> pMD;
11206 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11207 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11209 WCHAR baseTypeName[mdNameLen] = L"\0";
11210 ToRelease<ICorDebugType> pBaseType;
11211 if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen)))
11213 if(wcsncmp(baseTypeName, L"System.Enum", 11) == 0)
11215 else if(wcsncmp(baseTypeName, L"System.Object", 13) != 0 && wcsncmp(baseTypeName, L"System.ValueType", 16) != 0)
11217 currentExpansion[currentExpansionLen] = L'\0';
11218 wcscat_s(currentExpansion, currentExpansionSize, L".\0");
11219 wcscat_s(currentExpansion, currentExpansionSize, L"[basetype]");
11220 for(int i = 0; i < indent; i++) ExtOut(" ");
11221 DMLOut(" |- %S %s\n", baseTypeName, DMLManagedVar(currentExpansion, currentFrame, L"[basetype]"));
11223 if(ShouldExpandVariable(varToExpand, currentExpansion))
11224 ProcessFields(pInputValue, pBaseType, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11229 ULONG numFields = 0;
11230 HCORENUM fEnum = NULL;
11231 mdFieldDef fieldDef;
11232 while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
11235 DWORD fieldAttr = 0;
11236 WCHAR mdName[mdNameLen];
11237 WCHAR typeName[mdNameLen];
11238 if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, NULL, NULL)))
11240 currentExpansion[currentExpansionLen] = L'\0';
11241 wcscat_s(currentExpansion, currentExpansionSize, L".\0");
11242 wcscat_s(currentExpansion, currentExpansionSize, mdName);
11244 ToRelease<ICorDebugValue> pFieldVal;
11245 if(fieldAttr & fdLiteral)
11247 //TODO: Is it worth it??
11248 //ExtOut(" |- const %S", mdName);
11252 for(int i = 0; i < indent; i++) ExtOut(" ");
11254 if (fieldAttr & fdStatic)
11255 pType->GetStaticFieldValue(fieldDef, pILFrame, &pFieldVal);
11258 ToRelease<ICorDebugObjectValue> pObjValue;
11259 if (SUCCEEDED(pValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue)))
11260 pObjValue->GetFieldValue(pClass, fieldDef, &pFieldVal);
11263 if(pFieldVal != NULL)
11265 typeName[0] = L'\0';
11266 GetTypeOfValue(pFieldVal, typeName, mdNameLen);
11267 DMLOut(" |- %S %s", typeName, DMLManagedVar(currentExpansion, currentFrame, mdName));
11268 PrintValue(pFieldVal, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11270 else if(!(fieldAttr & fdLiteral))
11271 ExtOut(" |- < unknown type > %S\n", mdName);
11275 pMD->CloseEnum(fEnum);
11286 // This is the main worker function used if !clrstack is called with "-i" to indicate
11287 // that the public ICorDebug* should be used instead of the private DAC interface. NOTE:
11288 // Currently only bParams is supported. NOTE: This is a work in progress and the
11289 // following would be good to do:
11290 // * More thorough testing with interesting stacks, especially with transitions into
11291 // and out of managed code.
11292 // * Consider interleaving this code back into the main body of !clrstack if it turns
11293 // out that there's a lot of duplication of code between these two functions.
11294 // (Still unclear how things will look once locals is implemented.)
11295 static HRESULT ClrStackFromPublicInterface(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, __in_z WCHAR* varToExpand = NULL, int onlyShowFrame = -1)
11299 IfFailRet(InitCorDebugInterface());
11301 ExtOut("\n\n\nDumping managed stack and managed variables using ICorDebug.\n");
11302 ExtOut("=============================================================================\n");
11304 ToRelease<ICorDebugThread> pThread;
11305 ToRelease<ICorDebugThread3> pThread3;
11306 ToRelease<ICorDebugStackWalk> pStackWalk;
11307 ULONG ulThreadID = 0;
11308 g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID);
11310 IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread));
11311 IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3));
11312 IfFailRet(pThread3->CreateStackWalk(&pStackWalk));
11314 InternalFrameManager internalFrameManager;
11315 IfFailRet(internalFrameManager.Init(pThread3));
11317 #if defined(_AMD64_)
11318 ExtOut("%-16s %-16s %s\n", "Child SP", "IP", "Call Site");
11319 #elif defined(_X86_)
11320 ExtOut("%-8s %-8s %s\n", "Child SP", "IP", "Call Site");
11323 int currentFrame = -1;
11325 for (Status = S_OK; ; Status = pStackWalk->Next())
11329 if (Status == CORDBG_S_AT_END_OF_STACK)
11331 ExtOut("Stack walk complete.\n");
11338 ExtOut("<interrupted>\n");
11342 CROSS_PLATFORM_CONTEXT context;
11343 ULONG32 cbContextActual;
11344 if ((Status=pStackWalk->GetContext(
11348 (BYTE *)&context))!=S_OK)
11350 ExtOut("GetFrameContext failed: %lx\n",Status);
11354 // First find the info for the Frame object, if the current frame has an associated clr!Frame.
11355 CLRDATA_ADDRESS sp = GetSP(context);
11356 CLRDATA_ADDRESS ip = GetIP(context);
11358 ToRelease<ICorDebugFrame> pFrame;
11359 IfFailRet(pStackWalk->GetFrame(&pFrame));
11360 if (Status == S_FALSE)
11362 DMLOut("%p %s [NativeStackFrame]\n", SOS_PTR(sp), DMLIP(ip));
11366 // TODO: What about internal frames preceding the above native stack frame?
11367 // Should I just exclude the above native stack frame from the output?
11368 // TODO: Compare caller frame (instead of current frame) against internal frame,
11369 // to deal with issues of current frame's current SP being closer to leaf than
11370 // EE Frames it pushes. By "caller" I mean not just managed caller, but the
11371 // very next non-internal frame dbi would return (native or managed). OR...
11372 // perhaps I should use GetStackRange() instead, to see if the internal frame
11373 // appears leafier than the base-part of the range of the currently iterated
11374 // stack frame? I think I like that better.
11375 _ASSERTE(pFrame != NULL);
11376 IfFailRet(internalFrameManager.PrintPrecedingInternalFrames(pFrame));
11378 // Print the stack and instruction pointers.
11379 DMLOut("%p %s ", SOS_PTR(sp), DMLIP(ip));
11381 ToRelease<ICorDebugRuntimeUnwindableFrame> pRuntimeUnwindableFrame;
11382 Status = pFrame->QueryInterface(IID_ICorDebugRuntimeUnwindableFrame, (LPVOID *) &pRuntimeUnwindableFrame);
11383 if (SUCCEEDED(Status))
11385 ExtOut("[RuntimeUnwindableFrame]\n");
11389 // Print the method/Frame info
11391 // TODO: IS THE FOLLOWING NECESSARY, OR AM I GUARANTEED THAT ALL INTERNAL FRAMES
11392 // CAN BE FOUND VIA GetActiveInternalFrames?
11393 ToRelease<ICorDebugInternalFrame> pInternalFrame;
11394 Status = pFrame->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame);
11395 if (SUCCEEDED(Status))
11397 // This is a clr!Frame.
11398 LPCWSTR pwszFrameName = L"TODO: Implement GetFrameName";
11399 ExtOut("[%S: p] ", pwszFrameName);
11402 // Print the frame's associated function info, if it has any.
11403 ToRelease<ICorDebugILFrame> pILFrame;
11404 HRESULT hrILFrame = pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame);
11406 if (SUCCEEDED(hrILFrame))
11408 ToRelease<ICorDebugFunction> pFunction;
11409 Status = pFrame->GetFunction(&pFunction);
11410 if (FAILED(Status))
11412 // We're on a JITted frame, but there's no Function for it. So it must
11414 ExtOut("[IL Stub or LCG]\n");
11418 ToRelease<ICorDebugClass> pClass;
11419 ToRelease<ICorDebugModule> pModule;
11420 mdMethodDef methodDef;
11421 IfFailRet(pFunction->GetClass(&pClass));
11422 IfFailRet(pFunction->GetModule(&pModule));
11423 IfFailRet(pFunction->GetToken(&methodDef));
11425 WCHAR wszModuleName[100];
11426 ULONG32 cchModuleNameActual;
11427 IfFailRet(pModule->GetName(_countof(wszModuleName), &cchModuleNameActual, wszModuleName));
11429 ToRelease<IUnknown> pMDUnknown;
11430 ToRelease<IMetaDataImport> pMD;
11431 ToRelease<IMDInternalImport> pMDInternal;
11432 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11433 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11434 IfFailRet(GetMDInternalFromImport(pMD, &pMDInternal));
11437 IfFailRet(pClass->GetToken(&typeDef));
11439 // Note that we don't need to pretty print the class, as class name is
11440 // already printed from GetMethodName below
11442 CQuickBytes functionName;
11443 // TODO: WARNING: GetMethodName() appears to include lots of unexercised
11444 // code, as evidenced by some fundamental bugs I found. It should either be
11445 // thoroughly reviewed, or some other more exercised code path to grab the
11446 // name should be used.
11447 // TODO: If we do stay with GetMethodName, it should be updated to print
11448 // generics properly. Today, it does not show generic type parameters, and
11449 // if any arguments have a generic type, those arguments are just shown as
11450 // "__Canon", even when they're value types.
11451 GetMethodName(methodDef, pMD, &functionName);
11453 DMLOut(DMLManagedVar(L"-a", currentFrame, (LPWSTR)functionName.Ptr()));
11454 ExtOut(" (%S)\n", wszModuleName);
11456 if (SUCCEEDED(hrILFrame) && (bParams || bLocals))
11458 if(onlyShowFrame == -1 || (onlyShowFrame >= 0 && currentFrame == onlyShowFrame))
11459 IfFailRet(PrintParameters(bParams, bLocals, pMD, typeDef, methodDef, pILFrame, pModule, varToExpand, currentFrame));
11463 ExtOut("=============================================================================\n");
11470 #endif // FEATURE_PAL
11472 WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj)
11476 if (ref.HasRegisterInformation)
11479 HRESULT hr = g_sos->GetRegisterName(ref.Register, _countof(reg), reg, NULL);
11483 res = L"<unknown register>";
11487 int offset = ref.Offset;
11498 res += Hex(offset);
11505 res += WString(Pointer(ref.Address));
11512 res += WString(ObjectPtr(ref.Object));
11515 if (ref.Flags & SOSRefPinned)
11517 res += L" (pinned)";
11520 if (ref.Flags & SOSRefInterior)
11522 res += L" (interior)";
11528 void PrintRef(const SOSStackRefData &ref, TableOutput &out)
11530 WString res = BuildRegisterOutput(ref);
11532 if (ref.Object && (ref.Flags & SOSRefInterior) == 0)
11535 sos::BuildTypeWithExtraInfo(TO_TADDR(ref.Object), _countof(type), type);
11537 res += WString(L" - ") + type;
11540 out.WriteColumn(2, res);
11546 static void PrintThread(ULONG osID, BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC)
11548 // Symbols variables
11549 ULONG symlines = 0; // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
11550 if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
11552 symlines &= SYMOPT_LOAD_LINES;
11556 bSuppressLines = TRUE;
11558 ToRelease<IXCLRDataStackWalk> pStackWalk;
11560 HRESULT hr = CreateStackWalk(osID, &pStackWalk);
11561 if (FAILED(hr) || pStackWalk == NULL)
11563 ExtOut("Failed to start stack walk: %lx\n", hr);
11567 unsigned int refCount = 0, errCount = 0;
11568 ArrayHolder<SOSStackRefData> pRefs = NULL;
11569 ArrayHolder<SOSStackRefError> pErrs = NULL;
11570 if (bGC && FAILED(GetGCRefs(osID, &pRefs, &refCount, &pErrs, &errCount)))
11573 TableOutput out(3, POINTERSIZE_HEX, AlignRight);
11574 out.WriteRow("Child SP", "IP", "Call Site");
11580 ExtOut("<interrupted>\n");
11583 CLRDATA_ADDRESS ip = 0, sp = 0;
11584 hr = GetFrameLocation(pStackWalk, &ip, &sp);
11586 DacpFrameData FrameData;
11587 HRESULT frameDataResult = FrameData.Request(pStackWalk);
11588 if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
11589 sp = FrameData.frameAddr;
11591 // Print the stack and instruction pointers.
11592 out.WriteColumn(0, sp);
11593 out.WriteColumn(1, InstructionPtr(ip));
11595 // Print the method/Frame info
11596 if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
11598 // This is a clr!Frame.
11599 out.WriteColumn(2, GetFrameFromAddress(TO_TADDR(FrameData.frameAddr), pStackWalk));
11601 // Print out gc references for the Frame.
11602 for (unsigned int i = 0; i < refCount; ++i)
11603 if (pRefs[i].Source == sp)
11604 PrintRef(pRefs[i], out);
11606 // Print out an error message if we got one.
11607 for (unsigned int i = 0; i < errCount; ++i)
11608 if (pErrs[i].Source == sp)
11609 out.WriteColumn(2, "Failed to enumerate GC references.");
11613 out.WriteColumn(2, MethodNameFromIP(ip, bSuppressLines));
11615 // Print out gc references. refCount will be zero if bGC is false (or if we
11616 // failed to fetch gc reference information).
11617 for (unsigned int i = 0; i < refCount; ++i)
11618 if (pRefs[i].Source == ip && pRefs[i].StackPointer == sp)
11619 PrintRef(pRefs[i], out);
11621 // Print out an error message if we got one.
11622 for (unsigned int i = 0; i < errCount; ++i)
11623 if (pErrs[i].Source == sp)
11624 out.WriteColumn(2, "Failed to enumerate GC references.");
11626 if (bParams || bLocals)
11627 PrintArgsAndLocals(pStackWalk, bParams, bLocals);
11630 } while (pStackWalk->Next() == S_OK);
11633 static HRESULT GetFrameLocation(IXCLRDataStackWalk *pStackWalk, CLRDATA_ADDRESS *ip, CLRDATA_ADDRESS *sp)
11635 CROSS_PLATFORM_CONTEXT context;
11636 HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
11637 if (FAILED(hr) || hr == S_FALSE)
11639 // GetFrameContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us.
11640 ExtOut("GetFrameContext failed: %lx\n", hr);
11644 // First find the info for the Frame object, if the current frame has an associated clr!Frame.
11645 *ip = GetIP(context);
11646 *sp = GetSP(context);
11648 if (IsDbgTargetArm())
11649 *ip = *ip & ~THUMB_CODE;
11654 static void PrintCurrentThread(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC)
11659 g_ExtSystem->GetCurrentThreadSystemId(&osid);
11660 ExtOut("OS Thread Id: 0x%x ", osid);
11661 g_ExtSystem->GetCurrentThreadId(&id);
11662 ExtOut("(%d)\n", id);
11664 PrintThread(osid, bParams, bLocals, bSuppressLines, bGC);
11668 static HRESULT CreateStackWalk(ULONG osID, IXCLRDataStackWalk **ppStackwalk)
11671 ToRelease<IXCLRDataTask> pTask;
11673 if ((hr = g_ExtSystem->GetCurrentThreadSystemId(&osID)) != S_OK ||
11674 (hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK)
11676 ExtOut("Unable to walk the managed stack. The current thread is likely not a \n");
11677 ExtOut("managed thread. You can run !threads to get a list of managed threads in\n");
11678 ExtOut("the process\n");
11682 return pTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
11683 CLRDATA_SIMPFRAME_MANAGED_METHOD |
11684 CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
11685 CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
11689 /* Prints the args and locals of for a thread's stack.
11691 * pStackWalk - the stack we are printing
11692 * bArgs - whether to print args
11693 * bLocals - whether to print locals
11695 static void PrintArgsAndLocals(IXCLRDataStackWalk *pStackWalk, BOOL bArgs, BOOL bLocals)
11697 ToRelease<IXCLRDataFrame> pFrame;
11698 ToRelease<IXCLRDataValue> pVal;
11699 ULONG32 argCount = 0;
11700 ULONG32 localCount = 0;
11703 hr = pStackWalk->GetFrame(&pFrame);
11706 if (SUCCEEDED(hr) && bArgs)
11707 hr = pFrame->GetNumArguments(&argCount);
11709 if (SUCCEEDED(hr) && bArgs)
11710 hr = ShowArgs(argCount, pFrame, pVal);
11713 if (SUCCEEDED(hr) && bLocals)
11714 hr = pFrame->GetNumLocalVariables(&localCount);
11716 if (SUCCEEDED(hr) && bLocals)
11717 ShowLocals(localCount, pFrame, pVal);
11724 /* Displays the arguments to a function
11726 * argy - the number of arguments the function has
11727 * pFramey - the frame we are inspecting
11728 * pVal - a pointer to the CLRDataValue we use to query for info about the args
11730 static HRESULT ShowArgs(ULONG32 argy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
11732 CLRDATA_ADDRESS addr = 0;
11733 BOOL fPrintedLocation = FALSE;
11734 ULONG64 outVar = 0;
11738 ArrayHolder<WCHAR> argName = new NOTHROW WCHAR[mdNameLen];
11745 for (ULONG32 i=0; i < argy; i++)
11749 ExtOut(" PARAMETERS:\n");
11752 hr = pFramey->GetArgumentByIndex(i,
11763 if (argName[0] != L'\0')
11765 ExtOut("%S ", argName.GetPtr());
11768 // At times we cannot print the value of a parameter (most
11769 // common case being a non-primitive value type). In these
11770 // cases we need to print the location of the parameter,
11771 // so that we can later examine it (e.g. using !dumpvc)
11773 bool result = SUCCEEDED(pVal->GetNumLocations(&tmp)) && tmp == 1;
11775 result = SUCCEEDED(pVal->GetLocationByIndex(0, &tmp, &addr));
11779 if (tmp == CLRDATA_VLOC_REGISTER)
11781 ExtOut("(<CLR reg>) ");
11785 ExtOut("(0x%p) ", CDA_TO_UL64(addr));
11787 fPrintedLocation = TRUE;
11791 if (argName[0] != L'\0' || fPrintedLocation)
11796 if (HRESULT_CODE(pVal->GetBytes(0,&tmp,NULL)) == ERROR_BUFFER_OVERFLOW)
11798 ArrayHolder<BYTE> pByte = new NOTHROW BYTE[tmp + 1];
11805 hr = pVal->GetBytes(tmp, &tmp, pByte);
11809 ExtOut("<unable to retrieve data>\n");
11815 case 1: outVar = *((BYTE *)pByte.GetPtr()); break;
11816 case 2: outVar = *((short *)pByte.GetPtr()); break;
11817 case 4: outVar = *((DWORD *)pByte.GetPtr()); break;
11818 case 8: outVar = *((ULONG64 *)pByte.GetPtr()); break;
11819 default: outVar = 0;
11823 DMLOut("0x%s\n", DMLObject(outVar));
11825 ExtOut("0x%p\n", (ULONG64)outVar);
11831 ExtOut("<no data>\n");
11841 /* Prints the locals of a frame.
11843 * localy - the number of locals in the frame
11844 * pFramey - the frame we are inspecting
11845 * pVal - a pointer to the CLRDataValue we use to query for info about the args
11847 static HRESULT ShowLocals(ULONG32 localy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
11849 for (ULONG32 i=0; i < localy; i++)
11852 ExtOut(" LOCALS:\n");
11857 // local names don't work in Whidbey.
11858 hr = pFramey->GetLocalVariableByIndex(i, &pVal, mdNameLen, NULL, g_mdName);
11864 ULONG32 numLocations;
11865 if (SUCCEEDED(pVal->GetNumLocations(&numLocations)) &&
11869 CLRDATA_ADDRESS addr;
11870 if (SUCCEEDED(pVal->GetLocationByIndex(0, &flags, &addr)))
11872 if (flags == CLRDATA_VLOC_REGISTER)
11874 ExtOut("<CLR reg> ");
11878 ExtOut("0x%p ", CDA_TO_UL64(addr));
11882 // Can I get a name for the item?
11886 ULONG32 dwSize = 0;
11887 hr = pVal->GetBytes(0, &dwSize, NULL);
11889 if (HRESULT_CODE(hr) == ERROR_BUFFER_OVERFLOW)
11891 ArrayHolder<BYTE> pByte = new NOTHROW BYTE[dwSize + 1];
11898 hr = pVal->GetBytes(dwSize,&dwSize,pByte);
11902 ExtOut("<unable to retrieve data>\n");
11906 ULONG64 outVar = 0;
11909 case 1: outVar = *((BYTE *) pByte.GetPtr()); break;
11910 case 2: outVar = *((short *) pByte.GetPtr()); break;
11911 case 4: outVar = *((DWORD *) pByte.GetPtr()); break;
11912 case 8: outVar = *((ULONG64 *) pByte.GetPtr()); break;
11913 default: outVar = 0;
11917 DMLOut("0x%s\n", DMLObject(outVar));
11919 ExtOut("0x%p\n", (ULONG64)outVar);
11924 ExtOut("<no data>\n");
11935 #ifndef FEATURE_PAL
11936 WatchCmd g_watchCmd;
11938 // The grand new !Watch command, private to Apollo for now
11942 BOOL bExpression = FALSE;
11943 StringHolder addExpression;
11944 StringHolder aExpression;
11945 StringHolder saveName;
11946 StringHolder sName;
11947 StringHolder expression;
11948 StringHolder filterName;
11949 StringHolder renameOldName;
11950 size_t expandIndex = -1;
11951 size_t removeIndex = -1;
11952 BOOL clear = FALSE;
11955 CMDOption option[] =
11956 { // name, vptr, type, hasValue
11957 {"-add", &addExpression.data, COSTRING, TRUE},
11958 {"-a", &aExpression.data, COSTRING, TRUE},
11959 {"-save", &saveName.data, COSTRING, TRUE},
11960 {"-s", &sName.data, COSTRING, TRUE},
11961 {"-clear", &clear, COBOOL, FALSE},
11962 {"-c", &clear, COBOOL, FALSE},
11963 {"-expand", &expandIndex, COSIZE_T, TRUE},
11964 {"-filter", &filterName.data, COSTRING, TRUE},
11965 {"-r", &removeIndex, COSIZE_T, TRUE},
11966 {"-remove", &removeIndex, COSIZE_T, TRUE},
11967 {"-rename", &renameOldName.data, COSTRING, TRUE},
11972 {&expression.data, COSTRING}
11974 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
11979 if(addExpression.data != NULL || aExpression.data != NULL)
11981 WCHAR pAddExpression[MAX_EXPRESSION];
11982 memset(pAddExpression, 0, MAX_EXPRESSION);
11983 swprintf_s(pAddExpression, MAX_EXPRESSION, L"%S", addExpression.data != NULL ? addExpression.data : aExpression.data);
11984 Status = g_watchCmd.Add(pAddExpression);
11986 else if(removeIndex != -1)
11988 if(removeIndex <= 0)
11990 ExtOut("Index must be a postive decimal number\n");
11994 Status = g_watchCmd.Remove((int)removeIndex);
11996 ExtOut("Watch expression #%d has been removed\n", removeIndex);
11997 else if(Status == S_FALSE)
11998 ExtOut("There is no watch expression with index %d\n", removeIndex);
12000 ExtOut("Unknown failure 0x%x removing watch expression\n", Status);
12003 else if(saveName.data != NULL || sName.data != NULL)
12005 WCHAR pSaveName[MAX_EXPRESSION];
12006 memset(pSaveName, 0, MAX_EXPRESSION);
12007 swprintf_s(pSaveName, MAX_EXPRESSION, L"%S", saveName.data != NULL ? saveName.data : sName.data);
12008 Status = g_watchCmd.SaveList(pSaveName);
12012 g_watchCmd.Clear();
12014 else if(renameOldName.data != NULL)
12018 ExtOut("Must provide an old and new name. Usage: !watch -rename <old_name> <new_name>.\n");
12021 WCHAR pOldName[MAX_EXPRESSION];
12022 memset(pOldName, 0, MAX_EXPRESSION);
12023 swprintf_s(pOldName, MAX_EXPRESSION, L"%S", renameOldName.data);
12024 WCHAR pNewName[MAX_EXPRESSION];
12025 memset(pNewName, 0, MAX_EXPRESSION);
12026 swprintf_s(pNewName, MAX_EXPRESSION, L"%S", expression.data);
12027 g_watchCmd.RenameList(pOldName, pNewName);
12029 // print the tree, possibly with filtering and/or expansion
12030 else if(expandIndex != -1 || expression.data == NULL)
12032 WCHAR pExpression[MAX_EXPRESSION];
12033 memset(pExpression, 0, MAX_EXPRESSION);
12035 if(expandIndex != -1)
12037 if(expression.data != NULL)
12039 swprintf_s(pExpression, MAX_EXPRESSION, L"%S", expression.data);
12043 ExtOut("No expression was provided. Usage !watch -expand <index> <expression>\n");
12047 WCHAR pFilterName[MAX_EXPRESSION];
12048 memset(pFilterName, 0, MAX_EXPRESSION);
12049 if(filterName.data != NULL)
12051 swprintf_s(pFilterName, MAX_EXPRESSION, L"%S", filterName.data);
12054 g_watchCmd.Print((int)expandIndex, pExpression, pFilterName);
12058 ExtOut("Unrecognized argument: %s\n", expression.data);
12065 DECLARE_API(ClrStack)
12070 BOOL bParams = FALSE;
12071 BOOL bLocals = FALSE;
12072 BOOL bSuppressLines = FALSE;
12073 BOOL bICorDebug = FALSE;
12076 DWORD frameToDumpVariablesFor = -1;
12077 StringHolder cvariableName;
12078 ArrayHolder<WCHAR> wvariableName = new NOTHROW WCHAR[mdNameLen];
12079 if (wvariableName == NULL)
12082 return E_OUTOFMEMORY;
12085 memset(wvariableName, 0, sizeof(wvariableName));
12088 CMDOption option[] =
12089 { // name, vptr, type, hasValue
12090 {"-a", &bAll, COBOOL, FALSE},
12091 {"-p", &bParams, COBOOL, FALSE},
12092 {"-l", &bLocals, COBOOL, FALSE},
12093 {"-n", &bSuppressLines, COBOOL, FALSE},
12094 {"-i", &bICorDebug, COBOOL, FALSE},
12095 {"-gc", &bGC, COBOOL, FALSE},
12096 {"/d", &dml, COBOOL, FALSE},
12100 {&cvariableName.data, COSTRING},
12101 {&frameToDumpVariablesFor, COSIZE_T},
12103 if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
12108 EnableDMLHolder dmlHolder(dml);
12109 if (bAll || bParams || bLocals)
12111 // No parameter or local supports for minidump case!
12112 MINIDUMP_NOT_SUPPORTED();
12117 bParams = bLocals = TRUE;
12124 bool firstParamIsNumber = true;
12125 for(DWORD i = 0; i < strlen(cvariableName.data); i++)
12126 firstParamIsNumber = firstParamIsNumber && isdigit(cvariableName.data[i]);
12128 if(firstParamIsNumber && nArg == 1)
12130 frameToDumpVariablesFor = (DWORD)GetExpression(cvariableName.data);
12131 cvariableName.data[0] = '\0';
12134 if(cvariableName.data != NULL && strlen(cvariableName.data) > 0)
12135 swprintf_s(wvariableName, mdNameLen, L"%S\0", cvariableName.data);
12137 if(wcslen(wvariableName) > 0)
12138 bParams = bLocals = TRUE;
12140 EnableDMLHolder dmlHolder(TRUE);
12141 return ClrStackImplWithICorDebug::ClrStackFromPublicInterface(bParams, bLocals, FALSE, wvariableName, frameToDumpVariablesFor);
12144 ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC);
12149 BOOL IsMemoryInfoAvailable()
12153 g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
12154 if (Qualifier == DEBUG_DUMP_SMALL)
12156 g_ExtControl->GetDumpFormatFlags(&Qualifier);
12157 if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0)
12159 if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY_INFO) == 0)
12168 DECLARE_API( VMMap )
12172 if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
12174 ExtOut("!VMMap requires a full memory dump (.dump /ma) or a live process.\n");
12182 } // DECLARE_API( vmmap )
12184 DECLARE_API( SOSFlush )
12188 g_clrData->Flush();
12191 } // DECLARE_API( SOSFlush )
12193 DECLARE_API( VMStat )
12197 if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
12199 ExtOut("!VMStat requires a full memory dump (.dump /ma) or a live process.\n");
12207 } // DECLARE_API( vmmap )
12209 /**********************************************************************\
12210 * Routine Description: *
12212 * This function saves a dll to a file. *
12214 \**********************************************************************/
12215 DECLARE_API(SaveModule)
12218 MINIDUMP_NOT_SUPPORTED();
12220 #ifndef FEATURE_PAL
12222 StringHolder Location;
12223 DWORD_PTR moduleAddr = NULL;
12228 {&moduleAddr, COHEX},
12229 {&Location.data, COSTRING}
12232 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
12238 ExtOut("Usage: SaveModule <address> <file to save>\n");
12241 if (moduleAddr == 0) {
12242 ExtOut ("Invalid arg\n");
12246 char* ptr = Location.data;
12248 DWORD_PTR dllBase = 0;
12250 if (g_ExtSymbols->GetModuleByOffset(TO_CDADDR(moduleAddr),0,NULL,&base) == S_OK)
12252 dllBase = TO_TADDR(base);
12254 else if (IsModule(moduleAddr))
12256 DacpModuleData module;
12257 module.Request(g_sos, TO_CDADDR(moduleAddr));
12258 dllBase = TO_TADDR(module.ilBase);
12261 ExtOut ("Module does not have base address\n");
12267 ExtOut ("%p is not a Module or base address\n", (ULONG64)moduleAddr);
12271 MEMORY_BASIC_INFORMATION64 mbi;
12272 if (FAILED(g_ExtData2->QueryVirtual(TO_CDADDR(dllBase), &mbi)))
12274 ExtOut("Failed to retrieve information about segment %p", (ULONG64)dllBase);
12278 // module loaded as an image or mapped as a flat file?
12279 bIsImage = (mbi.Type == MEM_IMAGE);
12281 IMAGE_DOS_HEADER DosHeader;
12282 if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
12285 IMAGE_NT_HEADERS Header;
12286 if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK)
12289 DWORD_PTR sectionAddr = dllBase + DosHeader.e_lfanew + offsetof(IMAGE_NT_HEADERS,OptionalHeader)
12290 + Header.FileHeader.SizeOfOptionalHeader;
12292 IMAGE_SECTION_HEADER section;
12297 DWORD_PTR FileAddr;
12298 DWORD_PTR FileSize;
12301 int nSection = Header.FileHeader.NumberOfSections;
12302 ExtOut("%u sections in file\n",nSection);
12303 MemLocation *memLoc = (MemLocation*)_alloca(nSection*sizeof(MemLocation));
12306 for (int n = 0; n < nSection; n++)
12308 if (g_ExtData->ReadVirtual(TO_CDADDR(sectionAddr), §ion, sizeof(section), NULL) == S_OK)
12310 for (slot = 0; slot <= indxSec; slot ++)
12311 if (section.PointerToRawData < memLoc[slot].FileAddr)
12314 for (int k = indxSec; k >= slot; k --)
12315 memcpy(&memLoc[k+1], &memLoc[k], sizeof(MemLocation));
12317 memLoc[slot].VAAddr = section.VirtualAddress;
12318 memLoc[slot].VASize = section.Misc.VirtualSize;
12319 memLoc[slot].FileAddr = section.PointerToRawData;
12320 memLoc[slot].FileSize = section.SizeOfRawData;
12321 ExtOut("section %d - VA=%x, VASize=%x, FileAddr=%x, FileSize=%x\n",
12322 n, memLoc[slot].VAAddr,memLoc[slot]. VASize,memLoc[slot].FileAddr,
12323 memLoc[slot].FileSize);
12328 ExtOut("Fail to read PE section info\n");
12331 sectionAddr += sizeof(section);
12334 if (ptr[0] == '\0')
12336 ExtOut ("File not specified\n");
12341 ptr += strlen(ptr)-1;
12342 while (isspace(*ptr))
12348 HANDLE hFile = CreateFileA(file,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
12349 if (hFile == INVALID_HANDLE_VALUE)
12351 ExtOut ("Fail to create file %s\n", file);
12355 ULONG pageSize = OSPageSize();
12356 char *buffer = (char *)_alloca(pageSize);
12361 TADDR dwAddr = dllBase;
12362 TADDR dwEnd = dllBase + Header.OptionalHeader.SizeOfHeaders;
12363 while (dwAddr < dwEnd)
12366 if (dwEnd - dwAddr < nRead)
12367 nRead = (ULONG)(dwEnd - dwAddr);
12369 if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
12371 WriteFile(hFile,buffer,nRead,&nWrite,NULL);
12375 ExtOut ("Fail to read memory\n");
12381 for (slot = 0; slot <= indxSec; slot ++)
12383 dwAddr = dllBase + (bIsImage ? memLoc[slot].VAAddr : memLoc[slot].FileAddr);
12384 dwEnd = memLoc[slot].FileSize + dwAddr - 1;
12386 while (dwAddr <= dwEnd)
12389 if (dwEnd - dwAddr + 1 < pageSize)
12390 nRead = (ULONG)(dwEnd - dwAddr + 1);
12392 if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
12394 WriteFile(hFile,buffer,nRead,&nWrite,NULL);
12398 ExtOut ("Fail to read memory\n");
12401 dwAddr += pageSize;
12405 CloseHandle (hFile);
12413 #endif // FEATURE_PAL
12417 DECLARE_API(dbgout)
12423 CMDOption option[] =
12424 { // name, vptr, type, hasValue
12425 {"-off", &bOff, COBOOL, FALSE},
12428 if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
12433 Output::SetDebugOutputEnabled(!bOff);
12436 DECLARE_API(filthint)
12441 DWORD_PTR filter = 0;
12443 CMDOption option[] =
12444 { // name, vptr, type, hasValue
12445 {"-off", &bOff, COBOOL, FALSE},
12452 if (!GetCMDOption(args, option, _countof(option),
12453 arg, _countof(arg), &nArg))
12463 g_filterHint = filter;
12468 static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp,
12469 ULONG64 IPAddr, StringOutput& so)
12471 #define DOAPPEND(str) \
12473 if (!so.Append((str))) { \
12474 return E_OUTOFMEMORY; \
12477 // Should we skip explicit frames? They are characterized by Esp = 0, && Eip = 0 or 1.
12478 // See comment in FormatGeneratedException() for explanation why on non_IA64 Eip is 1, and not 0
12479 if (!(Flags & SOS_STACKTRACE_SHOWEXPLICITFRAMES) && (Esp == 0) && (IPAddr == 1))
12484 DacpMethodDescData MethodDescData;
12485 if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
12490 static WCHAR wszNameBuffer[1024]; // should be large enough
12492 if (Flags & SOS_STACKTRACE_SHOWADDRESSES)
12494 _snwprintf_s(wszNameBuffer, _countof(wszNameBuffer), _countof(wszNameBuffer)-1, L"%p %p ",
12495 (void*)(size_t) Esp,
12496 (void*)(size_t) IPAddr); // _TRUNCATE
12498 DOAPPEND(wszNameBuffer);
12501 DacpModuleData dmd;
12502 BOOL bModuleNameWorked = FALSE;
12503 ULONG64 addrInModule = IPAddr;
12504 if (dmd.Request(g_sos, MethodDescData.ModulePtr) == S_OK)
12506 CLRDATA_ADDRESS base = 0;
12507 if (g_sos->GetPEFileBase(dmd.File, &base) == S_OK)
12511 addrInModule = base;
12517 if (g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index,
12520 CHAR ModuleName[MAX_PATH+1];
12522 if (g_ExtSymbols->GetModuleNames (Index, base,
12524 ModuleName, MAX_PATH, NULL,
12525 NULL, 0, NULL) == S_OK)
12527 MultiByteToWideChar (CP_ACP,
12532 ARRAYSIZE(wszNameBuffer));
12533 DOAPPEND (wszNameBuffer);
12534 bModuleNameWorked = TRUE;
12538 // Under certain circumstances DacpMethodDescData::GetMethodName()
12539 // returns a module qualified method name
12540 HRESULT hr = g_sos->GetMethodDescName(dwStartAddr, 1024, wszNameBuffer, NULL);
12542 WCHAR* pwszMethNameBegin = (hr != S_OK ? NULL : wcschr(wszNameBuffer, L'!'));
12543 if (!bModuleNameWorked && hr == S_OK && pwszMethNameBegin != NULL)
12545 // if we weren't able to get the module name, but GetMethodName returned
12546 // the module as part of the returned method name, use this data
12547 DOAPPEND(wszNameBuffer);
12551 if (!bModuleNameWorked)
12553 DOAPPEND (L"UNKNOWN");
12558 // the module name we retrieved above from debugger will take
12559 // precedence over the name possibly returned by GetMethodName()
12560 DOAPPEND(pwszMethNameBegin != NULL ? (pwszMethNameBegin+1) : wszNameBuffer);
12564 DOAPPEND(L"UNKNOWN");
12568 ULONG64 Displacement = (IPAddr - MethodDescData.NativeCodeAddr);
12571 _snwprintf_s(wszNameBuffer,_countof (wszNameBuffer), _countof (wszNameBuffer)-1, L"+%#x", Displacement); // _TRUNCATE
12572 DOAPPEND (wszNameBuffer);
12579 BOOL AppendContext(LPVOID pTransitionContexts, size_t maxCount, size_t *pcurCount, size_t uiSizeOfContext,
12580 CROSS_PLATFORM_CONTEXT *context)
12582 if (pTransitionContexts == NULL || *pcurCount >= maxCount)
12587 if (uiSizeOfContext == sizeof(StackTrace_SimpleContext))
12589 StackTrace_SimpleContext *pSimple = (StackTrace_SimpleContext *) pTransitionContexts;
12590 g_targetMachine->FillSimpleContext(&pSimple[*pcurCount], context);
12592 else if (uiSizeOfContext == g_targetMachine->GetContextSize())
12594 // FillTargetContext ensures we only write uiSizeOfContext bytes in pTransitionContexts
12595 // and not sizeof(CROSS_PLATFORM_CONTEXT) bytes (which would overrun).
12596 g_targetMachine->FillTargetContext(pTransitionContexts, context, (int)(*pcurCount));
12606 HRESULT CALLBACK ImplementEFNStackTrace(
12607 PDEBUG_CLIENT Client,
12608 __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
12609 size_t *puiTextLength,
12610 LPVOID pTransitionContexts,
12611 size_t *puiTransitionContextCount,
12612 size_t uiSizeOfContext,
12616 #define DOAPPEND(str) if (!so.Append((str))) { \
12617 Status = E_OUTOFMEMORY; \
12621 HRESULT Status = E_FAIL;
12623 size_t transitionContextCount = 0;
12625 if (puiTextLength == NULL)
12627 return E_INVALIDARG;
12630 if (pTransitionContexts)
12632 if (puiTransitionContextCount == NULL)
12634 return E_INVALIDARG;
12637 // Do error checking on context size
12638 if ((uiSizeOfContext != g_targetMachine->GetContextSize()) &&
12639 (uiSizeOfContext != sizeof(StackTrace_SimpleContext)))
12641 return E_INVALIDARG;
12645 IXCLRDataStackWalk *pStackWalk = NULL;
12646 IXCLRDataTask* Task;
12649 if ((Status = g_ExtSystem->GetCurrentThreadSystemId(&ThreadId)) != S_OK ||
12650 (Status = g_clrData->GetTaskByOSThreadID(ThreadId, &Task)) != S_OK)
12652 // Not a managed thread.
12653 return SOS_E_NOMANAGEDCODE;
12656 Status = Task->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
12657 CLRDATA_SIMPFRAME_MANAGED_METHOD |
12658 CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
12659 CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
12664 if (Status != S_OK)
12666 if (Status == E_FAIL)
12668 return SOS_E_NOMANAGEDCODE;
12673 #ifdef _TARGET_WIN64_
12675 ULONG numFrames = 0;
12676 PDEBUG_CONTROL4 g_ExtControl4 = NULL;
12677 // Do we have advanced capability?
12678 if ((Status = g_ExtControl->QueryInterface(__uuidof(IDebugControl4),
12679 (void **)&g_ExtControl4)) == S_OK)
12681 // GetContextStackTrace fills g_X64FrameContexts as an array of
12682 // contexts packed as target architecture contexts. We cannot
12683 // safely cast this as an array of CROSS_PLATFORM_CONTEXT, since
12684 // sizeof(CROSS_PLATFORM_CONTEXT) != sizeof(TGT_CONTEXT)
12685 Status = g_ExtControl4->GetContextStackTrace(
12690 g_X64FrameContexts,
12691 MAX_STACK_FRAMES*g_targetMachine->GetContextSize(),
12692 g_targetMachine->GetContextSize(),
12698 // The new interface is required for context information
12702 if (FAILED(Status))
12707 BOOL bInNative = TRUE;
12708 for (ULONG i=0; i < numFrames; i++)
12710 PDEBUG_STACK_FRAME pCur = g_Frames + i;
12712 CLRDATA_ADDRESS pMD;
12713 if (g_sos->GetMethodDescPtrFromIP(pCur->InstructionOffset, &pMD) == S_OK)
12715 if (bInNative || transitionContextCount==0)
12717 // We only want to list one transition frame if there are multiple frames.
12720 DOAPPEND (L"(TransitionMU)\n");
12721 // For each transition, we need to store the context information
12722 if (puiTransitionContextCount)
12724 // below we cast the i-th AMD64_CONTEXT to CROSS_PLATFORM_CONTEXT
12725 AppendContext (pTransitionContexts, *puiTransitionContextCount,
12726 &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
12730 transitionContextCount++;
12734 Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
12735 pCur->StackOffset, pCur->InstructionOffset, so);
12736 if (FAILED(Status))
12740 else if (Status == S_OK)
12744 // for S_FALSE do not append anything
12751 // We only want to list one transition frame if there are multiple frames.
12754 DOAPPEND (L"(TransitionUM)\n");
12755 // For each transition, we need to store the context information
12756 if (puiTransitionContextCount)
12758 AppendContext (pTransitionContexts, *puiTransitionContextCount,
12759 &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
12763 transitionContextCount++;
12772 g_ExtControl4->Release();
12773 g_ExtControl4 = NULL;
12775 #else // _TARGET_WIN64_
12778 size_t prevLength = 0;
12779 static WCHAR wszNameBuffer[1024]; // should be large enough
12780 wcscpy_s(wszNameBuffer, 1024, L"Frame"); // default value
12783 BOOL bInNative = TRUE;
12785 UINT frameCount = 0;
12788 DacpFrameData FrameData;
12789 if ((Status = FrameData.Request(pStackWalk)) != S_OK)
12794 CROSS_PLATFORM_CONTEXT context;
12795 if ((Status=pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(),
12796 NULL, (BYTE *)&context))!=S_OK)
12801 ExtDbgOut ( " * Ctx[BSI]: %08x %08x %08x ", GetBP(context), GetSP(context), GetIP(context) );
12803 CLRDATA_ADDRESS pMD;
12804 if (!FrameData.frameAddr)
12806 if (bInNative || transitionContextCount==0)
12808 // We only want to list one transition frame if there are multiple frames.
12811 DOAPPEND (L"(TransitionMU)\n");
12812 // For each transition, we need to store the context information
12813 if (puiTransitionContextCount)
12815 AppendContext (pTransitionContexts, *puiTransitionContextCount,
12816 &transitionContextCount, uiSizeOfContext, &context);
12820 transitionContextCount++;
12824 // we may have a method, try to get the methoddesc
12825 if (g_sos->GetMethodDescPtrFromIP(GetIP(context), &pMD)==S_OK)
12827 Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
12828 GetSP(context), GetIP(context), so);
12829 if (FAILED(Status))
12833 else if (Status == S_OK)
12837 // for S_FALSE do not append anything
12843 if (Output::IsDebugOutputEnabled())
12846 MOVE(vtAddr, TO_TADDR(FrameData.frameAddr));
12847 if (g_sos->GetFrameName(vtAddr, 1024, wszNameBuffer, NULL) == S_OK)
12848 ExtDbgOut("[%ls: %08x] ", wszNameBuffer, FrameData.frameAddr);
12850 ExtDbgOut("[Frame: %08x] ", FrameData.frameAddr);
12855 // We only want to list one transition frame if there are multiple frames.
12858 DOAPPEND (L"(TransitionUM)\n");
12859 // For each transition, we need to store the context information
12860 if (puiTransitionContextCount)
12862 AppendContext (pTransitionContexts, *puiTransitionContextCount,
12863 &transitionContextCount, uiSizeOfContext, &context);
12867 transitionContextCount++;
12873 if (so.Length() > prevLength)
12875 ExtDbgOut ( "%ls", so.String()+prevLength );
12876 prevLength = so.Length();
12879 ExtDbgOut ( "\n" );
12883 while ((frameCount++) < MAX_STACK_FRAMES && pStackWalk->Next()==S_OK);
12888 #endif // _TARGET_WIN64_
12892 pStackWalk->Release();
12896 // We have finished. Does the user want to copy this data to a buffer?
12897 if (Status == S_OK)
12901 // They want at least partial output
12902 wcsncpy_s (wszTextOut, *puiTextLength, so.String(), *puiTextLength-1); // _TRUNCATE
12906 *puiTextLength = wcslen (so.String()) + 1;
12909 if (puiTransitionContextCount)
12911 *puiTransitionContextCount = transitionContextCount;
12918 #ifndef FEATURE_PAL
12919 // TODO: Convert PAL_TRY_NAKED to something that works on the Mac.
12920 HRESULT CALLBACK ImplementEFNStackTraceTry(
12921 PDEBUG_CLIENT Client,
12922 __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
12923 size_t *puiTextLength,
12924 LPVOID pTransitionContexts,
12925 size_t *puiTransitionContextCount,
12926 size_t uiSizeOfContext,
12929 HRESULT Status = E_FAIL;
12933 Status = ImplementEFNStackTrace(Client, wszTextOut, puiTextLength,
12934 pTransitionContexts, puiTransitionContextCount,
12935 uiSizeOfContext, Flags);
12937 PAL_EXCEPT_NAKED (EXCEPTION_EXECUTE_HANDLER)
12945 // See sos_stacktrace.h for the contract with the callers regarding the LPVOID arguments.
12946 HRESULT CALLBACK _EFN_StackTrace(
12947 PDEBUG_CLIENT Client,
12948 __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
12949 size_t *puiTextLength,
12950 __out_bcount_opt(uiSizeOfContext*(*puiTransitionContextCount)) LPVOID pTransitionContexts,
12951 size_t *puiTransitionContextCount,
12952 size_t uiSizeOfContext,
12957 Status = ImplementEFNStackTraceTry(Client, wszTextOut, puiTextLength,
12958 pTransitionContexts, puiTransitionContextCount,
12959 uiSizeOfContext, Flags);
12963 #endif // !FEATURE_PAL
12965 BOOL FormatFromRemoteString(DWORD_PTR strObjPointer, __out_ecount(cchString) PWSTR wszBuffer, ULONG cchString)
12969 wszBuffer[0] = L'\0';
12971 DacpObjectData objData;
12972 if (objData.Request(g_sos, TO_CDADDR(strObjPointer))!=S_OK)
12979 if (MOVE(stInfo, strObjPointer) != S_OK)
12984 DWORD dwBufLength = 0;
12985 if (!ClrSafeInt<DWORD>::addition(stInfo.m_StringLength, 1, dwBufLength))
12987 ExtOut("<integer overflow>\n");
12991 LPWSTR pwszBuf = new NOTHROW WCHAR[dwBufLength];
12992 if (pwszBuf == NULL)
12997 if (g_sos->GetObjectStringData(TO_CDADDR(strObjPointer), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK)
13003 // String is in format
13004 // <SP><SP><SP>at <function name>(args,...)\n
13006 // Parse and copy just <function name>(args,...)
13008 LPWSTR pwszPointer = pwszBuf;
13010 WCHAR PSZSEP[] = L" at ";
13015 if (wcsncmp(pwszPointer, PSZSEP, _countof(PSZSEP)-1) != 0)
13021 pwszPointer += wcslen(PSZSEP);
13022 LPWSTR nextPos = wcsstr(pwszPointer, PSZSEP);
13023 if (nextPos == NULL)
13025 // Done! Note that we are leaving the function before we add the last
13026 // line of stack trace to the output string. This is on purpose because
13027 // this string needs to be merged with a real trace, and the last line
13028 // of the trace will be common to the real trace.
13031 WCHAR c = *nextPos;
13034 // Buffer is calculated for sprintf below (" %p %p %S\n");
13035 WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2];
13037 // Note that we don't add a newline because we have this embedded in wszLineBuffer
13038 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), L" %p %p %s", (void*)(size_t)-1, (void*)(size_t)-1, pwszPointer);
13039 Length += (UINT)wcslen(wszLineBuffer);
13043 wcsncat_s(wszBuffer, cchString, wszLineBuffer, _TRUNCATE);
13047 // Move to the next line.
13048 pwszPointer = nextPos;
13053 // Return TRUE only if the stack string had any information that was successfully parsed.
13054 // (Length > 0) is a good indicator of that.
13055 bRet = (Length > 0);
13059 HRESULT AppendExceptionInfo(CLRDATA_ADDRESS cdaObj,
13060 __out_ecount(cchString) PWSTR wszStackString,
13062 BOOL bNestedCase) // If bNestedCase is TRUE, the last frame of the computed stack is left off
13064 DacpObjectData objData;
13065 if (objData.Request(g_sos, cdaObj) != S_OK)
13070 // Make sure it is an exception object, and get the MT of Exception
13071 CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
13072 if (exceptionMT == NULL)
13074 return E_INVALIDARG;
13077 // First try to get exception object data using ISOSDacInterface2
13078 DacpExceptionObjectData excData;
13079 BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, cdaObj));
13082 // Is there a _remoteStackTraceString? We'll want to prepend that data.
13083 // We only have string data, so IP/SP info has to be set to -1.
13084 DWORD_PTR strPointer;
13087 strPointer = TO_TADDR(excData.RemoteStackTraceString);
13091 iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, L"_remoteStackTraceString");
13092 MOVE (strPointer, TO_TADDR(cdaObj) + iOffset);
13096 WCHAR *pwszBuffer = new NOTHROW WCHAR[cchString];
13097 if (pwszBuffer == NULL)
13099 return E_OUTOFMEMORY;
13102 if (FormatFromRemoteString(strPointer, pwszBuffer, cchString))
13104 // Prepend this stuff to the string for the user
13105 wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
13107 delete[] pwszBuffer;
13110 BOOL bAsync = bGotExcData ? IsAsyncException(excData)
13111 : IsAsyncException(TO_TADDR(cdaObj), TO_TADDR(objData.MethodTable));
13113 DWORD_PTR arrayPtr;
13116 arrayPtr = TO_TADDR(excData.StackTrace);
13120 iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, L"_stackTrace");
13121 MOVE (arrayPtr, TO_TADDR(cdaObj) + iOffset);
13127 MOVE (arrayLen, arrayPtr + sizeof(DWORD_PTR));
13131 #ifdef _TARGET_WIN64_
13132 DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
13134 DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD);
13135 #endif // _TARGET_WIN64_
13136 size_t stackTraceSize = 0;
13137 MOVE (stackTraceSize, dataPtr); // data length is stored at the beginning of the array in this case
13139 DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
13140 dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
13142 if (stackTraceSize != 0)
13144 size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, bNestedCase);
13145 WCHAR *pwszBuffer = new NOTHROW WCHAR[iLength + 1];
13148 FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer, iLength + 1, bAsync, bNestedCase);
13149 wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
13150 delete[] pwszBuffer;
13154 return E_OUTOFMEMORY;
13162 HRESULT ImplementEFNGetManagedExcepStack(
13163 CLRDATA_ADDRESS cdaStackObj,
13164 __out_ecount(cchString) PWSTR wszStackString,
13167 HRESULT Status = E_FAIL;
13169 if (wszStackString == NULL || cchString == 0)
13171 return E_INVALIDARG;
13174 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
13175 DacpThreadData Thread;
13176 BOOL bCanUseThreadContext = TRUE;
13178 ZeroMemory(&Thread, sizeof(DacpThreadData));
13180 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
13182 // The current thread is unmanaged
13183 bCanUseThreadContext = FALSE;
13186 if (cdaStackObj == NULL)
13188 if (!bCanUseThreadContext)
13190 return E_INVALIDARG;
13193 TADDR taLTOH = NULL;
13194 if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
13196 sizeof(taLTOH), NULL)) || (taLTOH==NULL))
13202 cdaStackObj = TO_CDADDR(taLTOH);
13206 // Put the stack trace header on
13207 AddExceptionHeader(wszStackString, cchString);
13209 // First is there a nested exception?
13210 if (bCanUseThreadContext && Thread.firstNestedException)
13212 CLRDATA_ADDRESS obj = 0, next = 0;
13213 CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
13216 Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
13218 // deal with the inability to read a nested exception gracefully
13219 if (Status != S_OK)
13224 Status = AppendExceptionInfo(obj, wszStackString, cchString, TRUE);
13225 currentNested = next;
13227 while(currentNested != NULL);
13230 Status = AppendExceptionInfo(cdaStackObj, wszStackString, cchString, FALSE);
13235 #ifndef FEATURE_PAL
13236 // TODO: Enable this when ImplementEFNStackTraceTry is fixed.
13237 // This function, like VerifyDAC, exists for the purpose of testing
13238 // hard-to-get-to SOS APIs.
13239 DECLARE_API(VerifyStackTrace)
13243 BOOL bVerifyManagedExcepStack = FALSE;
13244 CMDOption option[] =
13245 { // name, vptr, type, hasValue
13246 {"-ManagedExcepStack", &bVerifyManagedExcepStack, COBOOL, FALSE},
13249 if (!GetCMDOption(args, option, _countof(option), NULL,0,NULL))
13254 if (bVerifyManagedExcepStack)
13256 CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
13257 DacpThreadData Thread;
13259 TADDR taExc = NULL;
13260 if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
13262 ExtOut("The current thread is unmanaged\n");
13266 TADDR taLTOH = NULL;
13267 if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
13269 sizeof(taLTOH), NULL)) || (taLTOH == NULL))
13271 ExtOut("There is no current managed exception on this thread\n");
13279 const SIZE_T cchStr = 4096;
13280 WCHAR *wszStr = (WCHAR *)alloca(cchStr * sizeof(WCHAR));
13281 if (ImplementEFNGetManagedExcepStack(TO_CDADDR(taExc), wszStr, cchStr) != S_OK)
13283 ExtOut("Error!\n");
13287 ExtOut("_EFN_GetManagedExcepStack(%P, wszStr, sizeof(wszStr)) returned:\n", (ULONG64) taExc);
13288 ExtOut("%S\n", wszStr);
13290 if (ImplementEFNGetManagedExcepStack((ULONG64)NULL, wszStr, cchStr) != S_OK)
13292 ExtOut("Error!\n");
13296 ExtOut("_EFN_GetManagedExcepStack(NULL, wszStr, sizeof(wszStr)) returned:\n");
13297 ExtOut("%S\n", wszStr);
13301 size_t textLength = 0;
13302 size_t contextLength = 0;
13303 Status = ImplementEFNStackTraceTry(Client,
13311 if (Status != S_OK)
13313 ExtOut("Error: %lx\n", Status);
13317 ExtOut("Number of characters requested: %d\n", textLength);
13318 WCHAR *wszBuffer = new NOTHROW WCHAR[textLength + 1];
13319 if (wszBuffer == NULL)
13325 // For the transition contexts buffer the callers are expected to allocate
13326 // contextLength * sizeof(TARGET_CONTEXT), and not
13327 // contextLength * sizeof(CROSS_PLATFORM_CONTEXT). See sos_stacktrace.h for
13329 LPBYTE pContexts = new NOTHROW BYTE[contextLength * g_targetMachine->GetContextSize()];
13331 if (pContexts == NULL)
13334 delete[] wszBuffer;
13338 Status = ImplementEFNStackTrace(Client,
13343 g_targetMachine->GetContextSize(),
13346 if (Status != S_OK)
13348 ExtOut("Error: %lx\n", Status);
13349 delete[] wszBuffer;
13350 delete [] pContexts;
13354 ExtOut("%S\n", wszBuffer);
13356 ExtOut("Context information:\n");
13357 if (IsDbgTargetX86())
13359 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13360 "Ebp", "Esp", "Eip");
13362 else if (IsDbgTargetAmd64())
13364 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13365 "Rbp", "Rsp", "Rip");
13367 else if (IsDbgTargetArm())
13369 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13374 ExtOut("Unsupported platform");
13375 delete [] pContexts;
13376 delete[] wszBuffer;
13380 for (size_t j=0; j < contextLength; j++)
13382 CROSS_PLATFORM_CONTEXT *pCtx = (CROSS_PLATFORM_CONTEXT*)(pContexts + j*g_targetMachine->GetContextSize());
13383 ExtOut("%p %p %p\n", GetBP(*pCtx), GetSP(*pCtx), GetIP(*pCtx));
13386 delete [] pContexts;
13388 StackTrace_SimpleContext *pSimple = new NOTHROW StackTrace_SimpleContext[contextLength];
13389 if (pSimple == NULL)
13392 delete[] wszBuffer;
13396 Status = ImplementEFNStackTrace(Client,
13401 sizeof(StackTrace_SimpleContext),
13404 if (Status != S_OK)
13406 ExtOut("Error: %lx\n", Status);
13407 delete[] wszBuffer;
13412 ExtOut("Simple Context information:\n");
13413 if (IsDbgTargetX86())
13414 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13415 "Ebp", "Esp", "Eip");
13416 else if (IsDbgTargetAmd64())
13417 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13418 "Rbp", "Rsp", "Rip");
13419 else if (IsDbgTargetArm())
13420 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13424 ExtOut("Unsupported platform");
13425 delete[] wszBuffer;
13429 for (size_t j=0; j < contextLength; j++)
13431 ExtOut("%p %p %p\n", (ULONG64) pSimple[j].FrameOffset,
13432 (ULONG64) pSimple[j].StackOffset,
13433 (ULONG64) pSimple[j].InstructionOffset);
13436 delete[] wszBuffer;
13441 #endif // !FEATURE_PAL
13445 // This is an internal-only Apollo extension to de-optimize the code
13446 DECLARE_API(SuppressJitOptimization)
13449 MINIDUMP_NOT_SUPPORTED();
13451 StringHolder onOff;
13454 {&onOff.data, COSTRING},
13457 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
13462 if(nArg == 1 && (_stricmp(onOff.data, "On") == 0))
13464 // if CLR is already loaded, try to change the flags now
13465 if(CheckEEDll() == S_OK)
13467 SetNGENCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION);
13470 if(!g_fAllowJitOptimization)
13471 ExtOut("JIT optimization is already suppressed\n");
13474 g_fAllowJitOptimization = FALSE;
13475 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
13476 ExtOut("JIT optimization will be suppressed\n");
13481 else if(nArg == 1 && (_stricmp(onOff.data, "Off") == 0))
13483 // if CLR is already loaded, try to change the flags now
13484 if(CheckEEDll() == S_OK)
13486 SetNGENCompilerFlags(CORDEBUG_JIT_DEFAULT);
13489 if(g_fAllowJitOptimization)
13490 ExtOut("JIT optimization is already permitted\n");
13493 g_fAllowJitOptimization = TRUE;
13494 ExtOut("JIT optimization will be permitted\n");
13499 ExtOut("Usage: !SuppressJitOptimization <on|off>\n");
13505 // Uses ICorDebug to set the state of desired NGEN compiler flags. This can suppress pre-jitted optimized
13507 HRESULT SetNGENCompilerFlags(DWORD flags)
13511 ToRelease<ICorDebugProcess2> proc2;
13512 if(FAILED(hr = InitCorDebugInterface()))
13514 ExtOut("SOS: warning, prejitted code optimizations could not be changed. Failed to load ICorDebug HR = 0x%x\n", hr);
13516 else if(FAILED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess2), (void**) &proc2)))
13518 if(flags != CORDEBUG_JIT_DEFAULT)
13520 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
13527 else if(FAILED(hr = proc2->SetDesiredNGENCompilerFlags(flags)))
13529 // Versions of CLR that don't have SetDesiredNGENCompilerFlags DAC-ized will return E_FAIL.
13530 // This was first supported in the clr_triton branch around 4/1/12, Apollo release
13531 // It will likely be supported in desktop CLR during Dev12
13534 if(flags != CORDEBUG_JIT_DEFAULT)
13536 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
13543 else if(hr == CORDBG_E_NGEN_NOT_SUPPORTED)
13545 if(flags != CORDEBUG_JIT_DEFAULT)
13547 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support NGEN\n");
13554 else if(hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS)
13556 DWORD currentFlags = 0;
13557 if(FAILED(hr = proc2->GetDesiredNGENCompilerFlags(¤tFlags)))
13559 ExtOut("SOS: warning, prejitted code optimizations could not be changed. GetDesiredNGENCompilerFlags failed hr=0x%x\n", hr);
13561 else if(currentFlags != flags)
13563 ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. This setting is fixed once CLR starts\n");
13572 ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. SetDesiredNGENCompilerFlags hr = 0x%x\n", hr);
13581 // This is an internal-only Apollo extension to save breakpoint/watch state
13582 #ifndef FEATURE_PAL
13583 DECLARE_API(SaveState)
13586 MINIDUMP_NOT_SUPPORTED();
13588 StringHolder filePath;
13591 {&filePath.data, COSTRING},
13594 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
13601 ExtOut("Usage: !SaveState <file_path>\n");
13605 errno_t error = fopen_s(&pFile, filePath.data, "w");
13608 ExtOut("Failed to open file %s, error=0x%x\n", filePath.data, error);
13612 g_bpoints.SaveBreakpoints(pFile);
13613 g_watchCmd.SaveListToFile(pFile);
13616 ExtOut("Session breakpoints and watch expressions saved to %s\n", filePath.data);
13622 DECLARE_API(StopOnCatch)
13625 MINIDUMP_NOT_SUPPORTED();
13628 sprintf_s(buffer, _countof(buffer), "sxe -c \"!HandleCLRN\" clrn");
13629 g_stopOnNextCatch = TRUE;
13631 g_clrData->GetOtherNotificationFlags(&flags);
13632 flags |= CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER;
13633 g_clrData->SetOtherNotificationFlags(flags);
13634 ExtOut("Debuggee will break the next time a managed exception is caught during execution\n");
13639 // This is an undocumented SOS extension command intended to help test SOS
13640 // It causes the Dml output to be printed to the console uninterpretted so
13641 // that a test script can read the commands which are hidden in the markup
13642 DECLARE_API(ExposeDML)
13644 Output::SetDMLExposed(true);
13648 // According to kksharma the Windows debuggers always sign-extend
13649 // arguments when calling externally, therefore StackObjAddr
13650 // conforms to CLRDATA_ADDRESS contract.
13652 _EFN_GetManagedExcepStack(
13653 PDEBUG_CLIENT Client,
13654 ULONG64 StackObjAddr,
13655 __out_ecount (cbString) PSTR szStackString,
13661 ArrayHolder<WCHAR> tmpStr = new NOTHROW WCHAR[cbString];
13662 if (tmpStr == NULL)
13665 return E_OUTOFMEMORY;
13668 if (FAILED(Status = ImplementEFNGetManagedExcepStack(StackObjAddr, tmpStr, cbString)))
13673 if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmpStr, -1, szStackString, cbString, NULL, NULL) == 0)
13681 // same as _EFN_GetManagedExcepStack, but returns the stack as a wide string.
13683 _EFN_GetManagedExcepStackW(
13684 PDEBUG_CLIENT Client,
13685 ULONG64 StackObjAddr,
13686 __out_ecount(cchString) PWSTR wszStackString,
13692 return ImplementEFNGetManagedExcepStack(StackObjAddr, wszStackString, cchString);
13695 // According to kksharma the Windows debuggers always sign-extend
13696 // arguments when calling externally, therefore objAddr
13697 // conforms to CLRDATA_ADDRESS contract.
13699 _EFN_GetManagedObjectName(
13700 PDEBUG_CLIENT Client,
13702 __out_ecount (cbName) PSTR szName,
13708 if (!sos::IsObject(objAddr, false))
13710 return E_INVALIDARG;
13713 sos::Object obj = TO_TADDR(objAddr);
13715 if (WideCharToMultiByte(CP_ACP, 0, obj.GetTypeName(), (int) (wcslen(obj.GetTypeName()) + 1),
13716 szName, cbName, NULL, NULL) == 0)
13723 // According to kksharma the Windows debuggers always sign-extend
13724 // arguments when calling externally, therefore objAddr
13725 // conforms to CLRDATA_ADDRESS contract.
13727 _EFN_GetManagedObjectFieldInfo(
13728 PDEBUG_CLIENT Client,
13730 __out_ecount (mdNameLen) PSTR szFieldName,
13736 DacpObjectData objData;
13737 LPWSTR fieldName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
13739 if (szFieldName == NULL || *szFieldName == '\0' ||
13745 if (pOffset == NULL && pValue == NULL)
13747 // One of these needs to be valid
13751 if (FAILED(objData.Request(g_sos, objAddr)))
13756 MultiByteToWideChar(CP_ACP,0,szFieldName,-1,fieldName,mdNameLen);
13758 int iOffset = GetObjFieldOffset (objAddr, objData.MethodTable, fieldName);
13766 *pOffset = (ULONG) iOffset;
13771 if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(objAddr + iOffset), pValue, sizeof(ULONG64), NULL)))
13780 void PrintHelp (__in_z LPCSTR pszCmdName)
13782 static LPSTR pText = NULL;
13784 if (pText == NULL) {
13785 #ifndef FEATURE_PAL
13786 HGLOBAL hResource = NULL;
13787 HRSRC hResInfo = FindResource (g_hInstance, TEXT ("DOCUMENTATION"), TEXT ("TEXT"));
13788 if (hResInfo) hResource = LoadResource (g_hInstance, hResInfo);
13789 if (hResource) pText = (LPSTR) LockResource (hResource);
13792 ExtOut("Error loading documentation resource\n");
13796 #define SOS_DOCUMENT_FILENAME "sosdocs.txt"
13798 char lpFilename[MAX_PATH+12]; // + 12 to make enough room for strcat function.
13799 DWORD nReturnedSize;
13800 nReturnedSize = GetModuleFileName(g_hInstance, lpFilename, MAX_PATH);
13801 if ( nReturnedSize == 0 || nReturnedSize == MAX_PATH ) {
13802 // We consider both of these cases as failed.
13803 ExtOut("Error getting the name for the current module\n");
13807 // Find the last "\" or "/" in the path.
13808 char * pChar = lpFilename + strlen(lpFilename) - 1;
13809 while ( pChar != lpFilename-1 && * pChar != '\\' && * pChar != '/' ) { * pChar-- = 0; }
13810 strcat(lpFilename, SOS_DOCUMENT_FILENAME);
13812 HANDLE hSosDocFile = CreateFileA(lpFilename,
13813 GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
13814 if (hSosDocFile == INVALID_HANDLE_VALUE) {
13815 ExtOut("Error finding documentation file\n");
13819 HANDLE hMappedSosDocFile = CreateFileMappingA(hSosDocFile,
13820 NULL, PAGE_READONLY, 0, 0, NULL);
13821 CloseHandle(hSosDocFile);
13822 if (hMappedSosDocFile == NULL) {
13823 ExtOut("Error mapping documentation file\n");
13827 pText = (LPSTR)MapViewOfFile(hMappedSosDocFile,
13828 FILE_MAP_READ, 0, 0, 0);
13829 CloseHandle(hMappedSosDocFile);
13832 ExtOut("Error loading documentation file\n");
13838 // Find our line in the text file
13839 char searchString[MAX_PATH];
13840 sprintf_s(searchString, _countof(searchString), "COMMAND: %s.", pszCmdName);
13842 LPSTR pStart = strstr(pText, searchString);
13846 ExtOut("Documentation for %s not found.\n", pszCmdName);
13850 // Go to the end of this line:
13851 pStart = strchr(pStart, '\n');
13854 ExtOut("Expected newline in documentation resource.\n");
13858 // Bypass the newline that pStart points to and setup pEnd for the loop below. We set
13859 // pEnd to be the old pStart since we add one to it when we call strstr.
13862 // Find the first occurrence of \\ followed by an \r or an \n on a line by itself.
13865 pEnd = strstr(pEnd+1, "\\\\");
13866 } while (pEnd && ((pEnd[-1] != '\r' && pEnd[-1] != '\n') || (pEnd[3] != '\r' && pEnd[3] != '\n')));
13870 // We have found a \\ followed by a \r or \n. Do not print out the character pEnd points
13871 // to, as this will be the first \ (this is why we don't add one to the second parameter).
13872 ExtOut("%.*s", pEnd - pStart, pStart);
13876 // If pEnd is false then we have run to the end of the document. However, we did find
13877 // the command to print, so we should simply print to the end of the file. We'll add
13878 // an extra newline here in case the file does not contain one.
13879 ExtOut("%s\n", pStart);
13883 /**********************************************************************\
13884 * Routine Description: *
13886 * This function displays the commands available in strike and the *
13887 * arguments passed into each.
13889 \**********************************************************************/
13890 extern "C" HRESULT CALLBACK
13891 Help(PDEBUG_CLIENT Client, PCSTR Args)
13893 // Call extension initialization functions directly, because we don't need the DAC dll to be initialized to get help.
13895 __ExtensionCleanUp __extensionCleanUp;
13896 if ((Status = ExtQuery(Client)) != S_OK) return Status;
13899 StringHolder commandName;
13902 {&commandName.data, COSTRING}
13905 if (!GetCMDOption(Args, NULL, 0, arg, _countof(arg), &nArg))
13910 ExtOut("-------------------------------------------------------------------------------\n");
13914 // Convert commandName to lower-case
13915 LPSTR curChar = commandName.data;
13916 while (*curChar != '\0')
13918 if ( ((unsigned) *curChar <= 0x7F) && isupper(*curChar))
13920 *curChar = (CHAR) tolower(*curChar);
13925 // Strip off leading "!" if the user put that.
13926 curChar = commandName.data;
13927 if (*curChar == '!')
13930 PrintHelp (curChar);
13934 PrintHelp ("contents");
13940 #endif // !FEATURE_PAL