diff options
Diffstat (limited to 'src/ToolBox/SOS/Strike/strike.cpp')
-rw-r--r-- | src/ToolBox/SOS/Strike/strike.cpp | 15640 |
1 files changed, 0 insertions, 15640 deletions
diff --git a/src/ToolBox/SOS/Strike/strike.cpp b/src/ToolBox/SOS/Strike/strike.cpp deleted file mode 100644 index ed9b4cf71a..0000000000 --- a/src/ToolBox/SOS/Strike/strike.cpp +++ /dev/null @@ -1,15640 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// ==++== -// - -// -// ==--== - -// =========================================================================== -// STRIKE.CPP -// =========================================================================== -// -// History: -// 09/07/99 Microsoft Created -// -//************************************************************************************************ -// SOS is the native debugging extension designed to support investigations into CLR (mis-) -// behavior by both users of the runtime as well as the code owners. It allows inspection of -// internal structures, of user visible entities, as well as execution control. -// -// This is the main SOS file hosting the implementation of all the exposed commands. A good -// starting point for understanding the semantics of these commands is the sosdocs.txt file. -// -// #CrossPlatformSOS -// SOS currently supports cross platform debugging from x86 to ARM. It takes a different approach -// from the DAC: whereas for the DAC we produce one binary for each supported host-target -// architecture pair, for SOS we produce only one binary for each host architecture; this one -// binary contains code for all supported target architectures. In doing this SOS depends on two -// assumptions: -// . that the debugger will load the appropriate DAC, and -// . that the host and target word size is identical. -// The second assumption is identical to the DAC assumption, and there will be considerable effort -// required (in the EE, the DAC, and SOS) if we ever need to remove it. -// -// In an ideal world SOS would be able to retrieve all platform specific information it needs -// either from the debugger or from DAC. However, SOS has taken some subtle and not so subtle -// dependencies on the CLR and the target platform. -// To resolve this problem, SOS now abstracts the target behind the IMachine interface, and uses -// calls on IMachine to take target-specific actions. It implements X86Machine, ARMMachine, and -// AMD64Machine. An instance of these exists in each appropriate host (e.g. the X86 version of SOS -// contains instances of X86Machine and ARMMachine, the ARM version contains an instance of -// ARMMachine, and the AMD64 version contains an instance of AMD64Machine). The code included in -// each version if determined by the SosTarget*** MSBuild symbols, and SOS_TARGET_*** conditional -// compilation symbols (as specified in sos.targets). -// -// Most of the target specific code is hosted in disasm.h/.cpp, and disasmX86.cpp, disasmARM.cpp. -// Some code currently under _TARGET_*** ifdefs may need to be reviewed/revisited. -// -// Issues: -// The one-binary-per-host decision does have some drawbacks: -// . Currently including system headers or even CLR headers will only account for the host -// target, IOW, when building the X86 version of SOS, CONTEXT will refer to the X86 CONTEXT -// structure, so we need to be careful when debugging ARM targets. The CONTEXT issue is -// partially resolved by CROSS_PLATFORM_CONTEXT (there is still a need to be very careful -// when handling arrays of CONTEXTs - see _EFN_StackTrace for details on this). -// . For larger includes (e.g. GC info), we will need to include files in specific namespaces, -// with specific _TARGET_*** macros defined in order to avoid name clashes and ensure correct -// system types are used. -// ----------------------------------------------------------------------------------------------- - -#define DO_NOT_DISABLE_RAND //this is a standalone tool, and can use rand() - -#include <windows.h> -#include <winver.h> -#include <winternl.h> -#include <psapi.h> -#ifndef FEATURE_PAL -#include <list> -#endif // !FEATURE_PAL -#include <wchar.h> - -#include "platformspecific.h" - -#define NOEXTAPI -#define KDEXT_64BIT -#include <wdbgexts.h> -#undef DECLARE_API -#undef StackTrace - -#include <dbghelp.h> - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stddef.h> - -#include "strike.h" -#include "sos.h" - -#ifndef STRESS_LOG -#define STRESS_LOG -#endif // STRESS_LOG -#define STRESS_LOG_READONLY -#include "stresslog.h" - -#include "util.h" - -#include "corhdr.h" -#include "cor.h" -#include "cordebug.h" -#include "dacprivate.h" -#include "corexcep.h" - -#define CORHANDLE_MASK 0x1 -#define SWITCHED_OUT_FIBER_OSID 0xbaadf00d; - -#define DEFINE_EXT_GLOBALS - -#include "data.h" -#include "disasm.h" - -#include "predeftlsslot.h" - -#include "hillclimbing.h" - -#include "sos_md.h" - -#ifndef FEATURE_PAL - -#include "ExpressionNode.h" -#include "WatchCmd.h" - -#include <algorithm> - -#include "tls.h" - -typedef struct _VM_COUNTERS { - SIZE_T PeakVirtualSize; - SIZE_T VirtualSize; - ULONG PageFaultCount; - SIZE_T PeakWorkingSetSize; - SIZE_T WorkingSetSize; - SIZE_T QuotaPeakPagedPoolUsage; - SIZE_T QuotaPagedPoolUsage; - SIZE_T QuotaPeakNonPagedPoolUsage; - SIZE_T QuotaNonPagedPoolUsage; - SIZE_T PagefileUsage; - SIZE_T PeakPagefileUsage; -} VM_COUNTERS; -typedef VM_COUNTERS *PVM_COUNTERS; - -const PROCESSINFOCLASS ProcessVmCounters = static_cast<PROCESSINFOCLASS>(3); - -#endif // !FEATURE_PAL - -#include <set> -#include <vector> -#include <map> - -BOOL CallStatus; -BOOL ControlC = FALSE; - -IMetaDataDispenserEx *pDisp = NULL; -WCHAR g_mdName[mdNameLen]; - -#ifndef FEATURE_PAL -HMODULE g_hInstance = NULL; -#include <algorithm> -#endif // !FEATURE_PAL - -#ifdef _MSC_VER -#pragma warning(disable:4244) // conversion from 'unsigned int' to 'unsigned short', possible loss of data -#pragma warning(disable:4189) // local variable is initialized but not referenced -#endif - -#ifdef FEATURE_PAL -#define SOSPrefix "" -#define SOSThreads "clrthreads" -#else -#define SOSPrefix "!" -#define SOSThreads "!threads" -#endif - -#if defined _X86_ && !defined FEATURE_PAL -// disable FPO for X86 builds -#pragma optimize("y", off) -#endif - -#undef assert - -#ifdef _MSC_VER -#pragma warning(default:4244) -#pragma warning(default:4189) -#endif - -#ifndef FEATURE_PAL -#include "ntinfo.h" -#endif // FEATURE_PAL - -#ifndef IfFailRet -#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0) -#endif - -#ifdef FEATURE_PAL - -#define NOTHROW -#define MINIDUMP_NOT_SUPPORTED() - -#else // !FEATURE_PAL - -#define MINIDUMP_NOT_SUPPORTED() \ - if (IsMiniDumpFile()) \ - { \ - ExtOut("This command is not supported in a minidump without full memory\n"); \ - ExtOut("To try the command anyway, run !MinidumpMode 0\n"); \ - return Status; \ - } - -#define NOTHROW (std::nothrow) - -#include "safemath.h" - -DECLARE_API (MinidumpMode) -{ - INIT_API (); - DWORD_PTR Value=0; - - CMDValue arg[] = - { // vptr, type - {&Value, COHEX} - }; - - size_t nArg; - if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) - { - return Status; - } - if (nArg == 0) - { - // Print status of current mode - ExtOut("Current mode: %s - unsafe minidump commands are %s.\n", - g_InMinidumpSafeMode ? "1" : "0", - g_InMinidumpSafeMode ? "disabled" : "enabled"); - } - else - { - if (Value != 0 && Value != 1) - { - ExtOut("Mode must be 0 or 1\n"); - return Status; - } - - g_InMinidumpSafeMode = (BOOL) Value; - ExtOut("Unsafe minidump commands are %s.\n", - g_InMinidumpSafeMode ? "disabled" : "enabled"); - } - - return Status; -} - -#endif // FEATURE_PAL - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to get the MethodDesc for a given eip * -* * -\**********************************************************************/ -DECLARE_API(IP2MD) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - BOOL dml = FALSE; - TADDR IP = 0; - CMDOption option[] = - { // name, vptr, type, hasValue -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - CMDValue arg[] = - { // vptr, type - {&IP, COHEX}, - }; - size_t nArg; - - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - EnableDMLHolder dmlHolder(dml); - - if (IP == 0) - { - ExtOut("%s is not IP\n", args); - return Status; - } - - CLRDATA_ADDRESS cdaStart = TO_CDADDR(IP); - CLRDATA_ADDRESS pMD; - - - if ((Status = g_sos->GetMethodDescPtrFromIP(cdaStart, &pMD)) != S_OK) - { - ExtOut("Failed to request MethodData, not in JIT code range\n"); - return Status; - } - - DMLOut("MethodDesc: %s\n", DMLMethodDesc(pMD)); - DumpMDInfo(TO_TADDR(pMD), cdaStart, FALSE /* fStackTraceFormat */); - - WCHAR filename[MAX_LONGPATH]; - ULONG linenum; - // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options - ULONG symlines = 0; - if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines))) - { - symlines &= SYMOPT_LOAD_LINES; - } - - if (symlines != 0 && - SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, _countof(filename)))) - { - ExtOut("Source file: %S @ %d\n", filename, linenum); - } - - return Status; -} - -// (MAX_STACK_FRAMES is also used by x86 to prevent infinite loops in _EFN_StackTrace) -#define MAX_STACK_FRAMES 1000 - -#if defined(_TARGET_WIN64_) -#define DEBUG_STACK_CONTEXT AMD64_CONTEXT -#elif defined(_TARGET_ARM_) // _TARGET_WIN64_ -#define DEBUG_STACK_CONTEXT ARM_CONTEXT -#elif defined(_TARGET_X86_) // _TARGET_ARM_ -#define DEBUG_STACK_CONTEXT X86_CONTEXT -#endif // _TARGET_X86_ - -#ifdef DEBUG_STACK_CONTEXT -// I use a global set of frames for stack walking on win64 because the debugger's -// GetStackTrace function doesn't provide a way to find out the total size of a stackwalk, -// and I'd like to have a reasonably big maximum without overflowing the stack by declaring -// the buffer locally and I also want to get a managed trace in a low memory environment -// (so no dynamic allocation if possible). -DEBUG_STACK_FRAME g_Frames[MAX_STACK_FRAMES]; -DEBUG_STACK_CONTEXT g_FrameContexts[MAX_STACK_FRAMES]; - -static HRESULT -GetContextStackTrace(ULONG osThreadId, PULONG pnumFrames) -{ - PDEBUG_CONTROL4 debugControl4; - HRESULT hr; - - // Do we have advanced capability? - if ((hr = g_ExtControl->QueryInterface(__uuidof(IDebugControl4), (void **)&debugControl4)) == S_OK) - { - ULONG oldId, id; - g_ExtSystem->GetCurrentThreadId(&oldId); - - if ((hr = g_ExtSystem->GetThreadIdBySystemId(osThreadId, &id)) != S_OK) { - return hr; - } - g_ExtSystem->SetCurrentThreadId(id); - - // GetContextStackTrace fills g_FrameContexts as an array of - // contexts packed as target architecture contexts. We cannot - // safely cast this as an array of CROSS_PLATFORM_CONTEXT, since - // sizeof(CROSS_PLATFORM_CONTEXT) != sizeof(TGT_CONTEXT) - hr = debugControl4->GetContextStackTrace( - NULL, - 0, - g_Frames, - MAX_STACK_FRAMES, - g_FrameContexts, - MAX_STACK_FRAMES*g_targetMachine->GetContextSize(), - g_targetMachine->GetContextSize(), - pnumFrames); - - g_ExtSystem->SetCurrentThreadId(oldId); - debugControl4->Release(); - } - return hr; -} - -#endif // DEBUG_STACK_CONTEXT - -/**********************************************************************\ -* Routine Description: * -* * -* This function displays the stack trace. It looks at each DWORD * -* on stack. If the DWORD is a return address, the symbol name or -* managed function name is displayed. * -* * -\**********************************************************************/ -void DumpStackInternal(DumpStackFlag *pDSFlag) -{ - ReloadSymbolWithLineInfo(); - - ULONG64 StackOffset; - g_ExtRegisters->GetStackOffset (&StackOffset); - if (pDSFlag->top == 0) { - pDSFlag->top = TO_TADDR(StackOffset); - } - size_t value; - while (g_ExtData->ReadVirtual(TO_CDADDR(pDSFlag->top), &value, sizeof(size_t), NULL) != S_OK) { - if (IsInterrupt()) - return; - pDSFlag->top = NextOSPageAddress(pDSFlag->top); - } - -#ifndef FEATURE_PAL - if (pDSFlag->end == 0) { - // Find the current stack range - NT_TIB teb; - ULONG64 dwTebAddr=0; - - g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr); - if (SafeReadMemory(TO_TADDR(dwTebAddr), &teb, sizeof(NT_TIB), NULL)) - { - if (pDSFlag->top > TO_TADDR(teb.StackLimit) - && pDSFlag->top <= TO_TADDR(teb.StackBase)) - { - if (pDSFlag->end == 0 || pDSFlag->end > TO_TADDR(teb.StackBase)) - pDSFlag->end = TO_TADDR(teb.StackBase); - } - } - } -#endif // FEATURE_PAL - - if (pDSFlag->end == 0) - { - ExtOut("TEB information is not available so a stack size of 0xFFFF is assumed\n"); - pDSFlag->end = pDSFlag->top + 0xFFFF; - } - - if (pDSFlag->end < pDSFlag->top) - { - ExtOut("Wrong option: stack selection wrong\n"); - return; - } - - DumpStackWorker(*pDSFlag); -} - -#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_) -static BOOL UnwindStackFrames(ULONG32 osThreadId); -#endif - -DECLARE_API(DumpStack) -{ - INIT_API_NO_RET_ON_FAILURE(); - - MINIDUMP_NOT_SUPPORTED(); - - DumpStackFlag DSFlag; - DSFlag.fEEonly = FALSE; - DSFlag.fSuppressSrcInfo = FALSE; - DSFlag.top = 0; - DSFlag.end = 0; - - BOOL unwind = FALSE; - BOOL dml = FALSE; - CMDOption option[] = { - // name, vptr, type, hasValue - {"-EE", &DSFlag.fEEonly, COBOOL, FALSE}, - {"-n", &DSFlag.fSuppressSrcInfo, COBOOL, FALSE}, - {"-unwind", &unwind, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE} -#endif - }; - CMDValue arg[] = { - // vptr, type - {&DSFlag.top, COHEX}, - {&DSFlag.end, COHEX} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - return Status; - - // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options - ULONG symlines = 0; - if (!DSFlag.fSuppressSrcInfo && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines))) - { - symlines &= SYMOPT_LOAD_LINES; - } - DSFlag.fSuppressSrcInfo = DSFlag.fSuppressSrcInfo || (symlines == 0); - - EnableDMLHolder enabledml(dml); - - ULONG sysId = 0, id = 0; - g_ExtSystem->GetCurrentThreadSystemId(&sysId); - ExtOut("OS Thread Id: 0x%x ", sysId); - g_ExtSystem->GetCurrentThreadId(&id); - ExtOut("(%d)\n", id); - -#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_) - if (unwind) - { - UnwindStackFrames(sysId); - } - else -#endif - { - DumpStackInternal(&DSFlag); - } - return Status; -} - - -/**********************************************************************\ -* Routine Description: * -* * -* This function displays the stack trace for threads that EE knows * -* from ThreadStore. * -* * -\**********************************************************************/ -DECLARE_API (EEStack) -{ - INIT_API(); - - MINIDUMP_NOT_SUPPORTED(); - - DumpStackFlag DSFlag; - DSFlag.fEEonly = FALSE; - DSFlag.fSuppressSrcInfo = FALSE; - DSFlag.top = 0; - DSFlag.end = 0; - - BOOL bShortList = FALSE; - BOOL dml = FALSE; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-EE", &DSFlag.fEEonly, COBOOL, FALSE}, - {"-short", &bShortList, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE} -#endif - }; - - if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) - { - return Status; - } - - EnableDMLHolder enableDML(dml); - - ULONG Tid; - g_ExtSystem->GetCurrentThreadId(&Tid); - - DacpThreadStoreData ThreadStore; - if ((Status = ThreadStore.Request(g_sos)) != S_OK) - { - ExtOut("Failed to request ThreadStore\n"); - return Status; - } - - CLRDATA_ADDRESS CurThread = ThreadStore.firstThread; - while (CurThread) - { - if (IsInterrupt()) - break; - - DacpThreadData Thread; - if ((Status = Thread.Request(g_sos, CurThread)) != S_OK) - { - ExtOut("Failed to request Thread at %p\n", CurThread); - return Status; - } - - ULONG id=0; - if (g_ExtSystem->GetThreadIdBySystemId (Thread.osThreadId, &id) != S_OK) - { - CurThread = Thread.nextThread; - continue; - } - - ExtOut("---------------------------------------------\n"); - ExtOut("Thread %3d\n", id); - BOOL doIt = FALSE; - - -#define TS_Hijacked 0x00000080 - - if (!bShortList) - { - doIt = TRUE; - } - else if ((Thread.lockCount > 0) || (Thread.state & TS_Hijacked)) - { - // TODO: bring back || (int)vThread.m_pFrame != -1 { - doIt = TRUE; - } - else - { - ULONG64 IP; - g_ExtRegisters->GetInstructionOffset (&IP); - JITTypes jitType; - TADDR methodDesc; - TADDR gcinfoAddr; - IP2MethodDesc (TO_TADDR(IP), methodDesc, jitType, gcinfoAddr); - if (methodDesc) - { - doIt = TRUE; - } - } - - if (doIt) - { - g_ExtSystem->SetCurrentThreadId(id); - DSFlag.top = 0; - DSFlag.end = 0; - DumpStackInternal(&DSFlag); - } - - CurThread = Thread.nextThread; - } - - g_ExtSystem->SetCurrentThreadId(Tid); - return Status; -} - -HRESULT DumpStackObjectsRaw(size_t nArg, __in_z LPSTR exprBottom, __in_z LPSTR exprTop, BOOL bVerify) -{ - size_t StackTop = 0; - size_t StackBottom = 0; - if (nArg==0) - { - ULONG64 StackOffset; - g_ExtRegisters->GetStackOffset(&StackOffset); - - StackTop = TO_TADDR(StackOffset); - } - else - { - StackTop = GetExpression(exprTop); - if (StackTop == 0) - { - ExtOut("wrong option: %s\n", exprTop); - return E_FAIL; - } - - if (nArg==2) - { - StackBottom = GetExpression(exprBottom); - if (StackBottom == 0) - { - ExtOut("wrong option: %s\n", exprBottom); - return E_FAIL; - } - } - } - -#ifndef FEATURE_PAL - NT_TIB teb; - ULONG64 dwTebAddr=0; - HRESULT hr = g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr); - if (SUCCEEDED(hr) && SafeReadMemory (TO_TADDR(dwTebAddr), &teb, sizeof (NT_TIB), NULL)) - { - if (StackTop > TO_TADDR(teb.StackLimit) && StackTop <= TO_TADDR(teb.StackBase)) - { - if (StackBottom == 0 || StackBottom > TO_TADDR(teb.StackBase)) - StackBottom = TO_TADDR(teb.StackBase); - } - } -#endif - - if (StackBottom == 0) - StackBottom = StackTop + 0xFFFF; - - if (StackBottom < StackTop) - { - ExtOut("Wrong option: stack selection wrong\n"); - return E_FAIL; - } - - // We can use the gc snapshot to eliminate object addresses that are - // not on the gc heap. - if (!g_snapshot.Build()) - { - ExtOut("Unable to determine bounds of gc heap\n"); - return E_FAIL; - } - - // Print thread ID. - ULONG id = 0; - g_ExtSystem->GetCurrentThreadSystemId (&id); - ExtOut("OS Thread Id: 0x%x ", id); - g_ExtSystem->GetCurrentThreadId (&id); - ExtOut("(%d)\n", id); - - DumpStackObjectsHelper(StackTop, StackBottom, bVerify); - return S_OK; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the address and name of all * -* Managed Objects on the stack. * -* * -\**********************************************************************/ -DECLARE_API(DumpStackObjects) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - StringHolder exprTop, exprBottom; - - BOOL bVerify = FALSE; - BOOL dml = FALSE; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-verify", &bVerify, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE} -#endif - }; - CMDValue arg[] = - { // vptr, type - {&exprTop.data, COSTRING}, - {&exprBottom.data, COSTRING} - }; - size_t nArg; - - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder enableDML(dml); - - return DumpStackObjectsRaw(nArg, exprBottom.data, exprTop.data, bVerify); -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of a MethodDesc * -* for a given address * -* * -\**********************************************************************/ -DECLARE_API(DumpMD) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - DWORD_PTR dwStartAddr = NULL; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - CMDValue arg[] = - { // vptr, type - {&dwStartAddr, COHEX}, - }; - size_t nArg; - - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - - DumpMDInfo(dwStartAddr); - - return Status; -} - -BOOL GatherDynamicInfo(TADDR DynamicMethodObj, DacpObjectData *codeArray, - DacpObjectData *tokenArray, TADDR *ptokenArrayAddr) -{ - BOOL bRet = FALSE; - int iOffset; - DacpObjectData objData; // temp object - - if (codeArray == NULL || tokenArray == NULL) - return bRet; - - if (objData.Request(g_sos, TO_CDADDR(DynamicMethodObj)) != S_OK) - return bRet; - - iOffset = GetObjFieldOffset(TO_CDADDR(DynamicMethodObj), objData.MethodTable, W("m_resolver")); - if (iOffset <= 0) - return bRet; - - TADDR resolverPtr; - if (FAILED(MOVE(resolverPtr, DynamicMethodObj + iOffset))) - return bRet; - - if (objData.Request(g_sos, TO_CDADDR(resolverPtr)) != S_OK) - return bRet; - - iOffset = GetObjFieldOffset(TO_CDADDR(resolverPtr), objData.MethodTable, W("m_code")); - if (iOffset <= 0) - return bRet; - - TADDR codePtr; - if (FAILED(MOVE(codePtr, resolverPtr + iOffset))) - return bRet; - - if (codeArray->Request(g_sos, TO_CDADDR(codePtr)) != S_OK) - return bRet; - - if (codeArray->dwComponentSize != 1) - return bRet; - - // We also need the resolution table - iOffset = GetObjFieldOffset (TO_CDADDR(resolverPtr), objData.MethodTable, W("m_scope")); - if (iOffset <= 0) - return bRet; - - TADDR scopePtr; - if (FAILED(MOVE(scopePtr, resolverPtr + iOffset))) - return bRet; - - if (objData.Request(g_sos, TO_CDADDR(scopePtr)) != S_OK) - return bRet; - - iOffset = GetObjFieldOffset (TO_CDADDR(scopePtr), objData.MethodTable, W("m_tokens")); - if (iOffset <= 0) - return bRet; - - TADDR tokensPtr; - if (FAILED(MOVE(tokensPtr, scopePtr + iOffset))) - return bRet; - - if (objData.Request(g_sos, TO_CDADDR(tokensPtr)) != S_OK) - return bRet; - - iOffset = GetObjFieldOffset(TO_CDADDR(tokensPtr), objData.MethodTable, W("_items")); - if (iOffset <= 0) - return bRet; - - TADDR itemsPtr; - MOVE (itemsPtr, tokensPtr + iOffset); - - *ptokenArrayAddr = itemsPtr; - - if (tokenArray->Request(g_sos, TO_CDADDR(itemsPtr)) != S_OK) - return bRet; - - bRet = TRUE; // whew. - return bRet; -} - -DECLARE_API(DumpIL) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - DWORD_PTR dwStartAddr = NULL; - DWORD_PTR dwDynamicMethodObj = NULL; - BOOL dml = FALSE; - BOOL fILPointerDirectlySpecified = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE}, - {"/i", &fILPointerDirectlySpecified, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&dwStartAddr, COHEX}, - }; - size_t nArg; - - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - if (dwStartAddr == NULL) - { - ExtOut("Must pass a valid expression\n"); - return Status; - } - - if (fILPointerDirectlySpecified) - { - return DecodeILFromAddress(NULL, dwStartAddr); - } - - if (!g_snapshot.Build()) - { - ExtOut("Unable to build snapshot of the garbage collector state\n"); - return Status; - } - - if (g_snapshot.GetHeap(dwStartAddr) != NULL) - { - dwDynamicMethodObj = dwStartAddr; - } - - if (dwDynamicMethodObj == NULL) - { - // We have been given a MethodDesc - DacpMethodDescData MethodDescData; - if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK) - { - ExtOut("%p is not a MethodDesc\n", SOS_PTR(dwStartAddr)); - return Status; - } - - if (MethodDescData.bIsDynamic && MethodDescData.managedDynamicMethodObject) - { - dwDynamicMethodObj = TO_TADDR(MethodDescData.managedDynamicMethodObject); - if (dwDynamicMethodObj == NULL) - { - ExtOut("Unable to print IL for DynamicMethodDesc %p\n", SOS_PTR(dwDynamicMethodObj)); - return Status; - } - } - else - { - // This is not a dynamic method, print the IL for it. - // Get the module - DacpModuleData dmd; - if (dmd.Request(g_sos, MethodDescData.ModulePtr) != S_OK) - { - ExtOut("Unable to get module\n"); - return Status; - } - - ToRelease<IMetaDataImport> pImport = MDImportForModule(&dmd); - if (pImport == NULL) - { - ExtOut("bad import\n"); - return Status; - } - - ULONG pRva; - DWORD dwFlags; - if (pImport->GetRVA(MethodDescData.MDToken, &pRva, &dwFlags) != S_OK) - { - ExtOut("error in import\n"); - return Status; - } - - CLRDATA_ADDRESS ilAddrClr; - if (g_sos->GetILForModule(MethodDescData.ModulePtr, pRva, &ilAddrClr) != S_OK) - { - ExtOut("FindIL failed\n"); - return Status; - } - - TADDR ilAddr = TO_TADDR(ilAddrClr); - IfFailRet(DecodeILFromAddress(pImport, ilAddr)); - } - } - - if (dwDynamicMethodObj != NULL) - { - // We have a DynamicMethod managed object, let us visit the town and paint. - DacpObjectData codeArray; - DacpObjectData tokenArray; - DWORD_PTR tokenArrayAddr; - if (!GatherDynamicInfo (dwDynamicMethodObj, &codeArray, &tokenArray, &tokenArrayAddr)) - { - DMLOut("Error gathering dynamic info from object at %s.\n", DMLObject(dwDynamicMethodObj)); - return Status; - } - - // Read the memory into a local buffer - BYTE *pArray = new NOTHROW BYTE[(SIZE_T)codeArray.dwNumComponents]; - if (pArray == NULL) - { - ExtOut("Not enough memory to read IL\n"); - return Status; - } - - Status = g_ExtData->ReadVirtual(UL64_TO_CDA(codeArray.ArrayDataPtr), pArray, (ULONG)codeArray.dwNumComponents, NULL); - if (Status != S_OK) - { - ExtOut("Failed to read memory\n"); - delete [] pArray; - return Status; - } - - // Now we have a local copy of the IL, and a managed array for token resolution. - // Visit our IL parser with this info. - ExtOut("This is dynamic IL. Exception info is not reported at this time.\n"); - ExtOut("If a token is unresolved, run \"!do <addr>\" on the addr given\n"); - ExtOut("in parenthesis. You can also look at the token table yourself, by\n"); - ExtOut("running \"!DumpArray %p\".\n\n", SOS_PTR(tokenArrayAddr)); - DecodeDynamicIL(pArray, (ULONG)codeArray.dwNumComponents, tokenArray); - - delete [] pArray; - } - return Status; -} - -void DumpSigWorker ( - DWORD_PTR dwSigAddr, - DWORD_PTR dwModuleAddr, - BOOL fMethod) -{ - // - // Find the length of the signature and copy it into the debugger process. - // - - ULONG cbSig = 0; - const ULONG cbSigInc = 256; - ArrayHolder<COR_SIGNATURE> pSig = new NOTHROW COR_SIGNATURE[cbSigInc]; - if (pSig == NULL) - { - ReportOOM(); - return; - } - - CQuickBytes sigString; - for (;;) - { - if (IsInterrupt()) - return; - - ULONG cbCopied; - if (!SafeReadMemory(TO_TADDR(dwSigAddr + cbSig), pSig + cbSig, cbSigInc, &cbCopied)) - return; - cbSig += cbCopied; - - sigString.ReSize(0); - GetSignatureStringResults result; - if (fMethod) - result = GetMethodSignatureString(pSig, cbSig, dwModuleAddr, &sigString); - else - result = GetSignatureString(pSig, cbSig, dwModuleAddr, &sigString); - - if (GSS_ERROR == result) - return; - - if (GSS_SUCCESS == result) - break; - - // If we didn't get the full amount back, and we failed to parse the - // signature, it's not valid because of insufficient data - if (cbCopied < 256) - { - ExtOut("Invalid signature\n"); - return; - } - -#ifdef _PREFAST_ -#pragma warning(push) -#pragma warning(disable:6280) // "Suppress PREFast warning about mismatch alloc/free" -#endif - - PCOR_SIGNATURE pSigNew = (PCOR_SIGNATURE)realloc(pSig, cbSig+cbSigInc); - -#ifdef _PREFAST_ -#pragma warning(pop) -#endif - - if (pSigNew == NULL) - { - ExtOut("Out of memory\n"); - return; - } - - pSig = pSigNew; - } - - ExtOut("%S\n", (PCWSTR)sigString.Ptr()); -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump a signature object. * -* * -\**********************************************************************/ -DECLARE_API(DumpSig) -{ - INIT_API(); - - MINIDUMP_NOT_SUPPORTED(); - - // - // Fetch arguments - // - - StringHolder sigExpr; - StringHolder moduleExpr; - CMDValue arg[] = - { - {&sigExpr.data, COSTRING}, - {&moduleExpr.data, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) - { - return Status; - } - if (nArg != 2) - { - ExtOut("!DumpSig <sigaddr> <moduleaddr>\n"); - return Status; - } - - DWORD_PTR dwSigAddr = GetExpression(sigExpr.data); - DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data); - - if (dwSigAddr == 0 || dwModuleAddr == 0) - { - ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data); - return Status; - } - - DumpSigWorker(dwSigAddr, dwModuleAddr, TRUE); - return Status; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump a portion of a signature object. * -* * -\**********************************************************************/ -DECLARE_API(DumpSigElem) -{ - INIT_API(); - - MINIDUMP_NOT_SUPPORTED(); - - - // - // Fetch arguments - // - - StringHolder sigExpr; - StringHolder moduleExpr; - CMDValue arg[] = - { - {&sigExpr.data, COSTRING}, - {&moduleExpr.data, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) - { - return Status; - } - - if (nArg != 2) - { - ExtOut("!DumpSigElem <sigaddr> <moduleaddr>\n"); - return Status; - } - - DWORD_PTR dwSigAddr = GetExpression(sigExpr.data); - DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data); - - if (dwSigAddr == 0 || dwModuleAddr == 0) - { - ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data); - return Status; - } - - DumpSigWorker(dwSigAddr, dwModuleAddr, FALSE); - return Status; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of an EEClass from * -* a given address -* * -\**********************************************************************/ -DECLARE_API(DumpClass) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - DWORD_PTR dwStartAddr = 0; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - CMDValue arg[] = - { // vptr, type - {&dwStartAddr, COHEX} - }; - - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - if (nArg == 0) - { - ExtOut("Missing EEClass address\n"); - return Status; - } - - EnableDMLHolder dmlHolder(dml); - - CLRDATA_ADDRESS methodTable; - if ((Status=g_sos->GetMethodTableForEEClass(TO_CDADDR(dwStartAddr), &methodTable)) != S_OK) - { - ExtOut("Invalid EEClass address\n"); - return Status; - } - - DacpMethodTableData mtdata; - if ((Status=mtdata.Request(g_sos, TO_CDADDR(methodTable)))!=S_OK) - { - ExtOut("EEClass has an invalid MethodTable address\n"); - return Status; - } - - sos::MethodTable mt = TO_TADDR(methodTable); - ExtOut("Class Name: %S\n", mt.GetName()); - - WCHAR fileName[MAX_LONGPATH]; - FileNameForModule(TO_TADDR(mtdata.Module), fileName); - ExtOut("mdToken: %p\n", mtdata.cl); - ExtOut("File: %S\n", fileName); - - CLRDATA_ADDRESS ParentEEClass = NULL; - if (mtdata.ParentMethodTable) - { - DacpMethodTableData mtdataparent; - if ((Status=mtdataparent.Request(g_sos, TO_CDADDR(mtdata.ParentMethodTable)))!=S_OK) - { - ExtOut("EEClass has an invalid MethodTable address\n"); - return Status; - } - ParentEEClass = mtdataparent.Class; - } - - DMLOut("Parent Class: %s\n", DMLClass(ParentEEClass)); - DMLOut("Module: %s\n", DMLModule(mtdata.Module)); - DMLOut("Method Table: %s\n", DMLMethodTable(methodTable)); - ExtOut("Vtable Slots: %x\n", mtdata.wNumVirtuals); - ExtOut("Total Method Slots: %x\n", mtdata.wNumVtableSlots); - ExtOut("Class Attributes: %x ", mtdata.dwAttrClass); - - if (IsTdInterface(mtdata.dwAttrClass)) - ExtOut("Interface, "); - if (IsTdAbstract(mtdata.dwAttrClass)) - ExtOut("Abstract, "); - if (IsTdImport(mtdata.dwAttrClass)) - ExtOut("ComImport, "); - - ExtOut("\n"); - - DacpMethodTableFieldData vMethodTableFields; - if (SUCCEEDED(vMethodTableFields.Request(g_sos, methodTable))) - { - ExtOut("NumInstanceFields: %x\n", vMethodTableFields.wNumInstanceFields); - ExtOut("NumStaticFields: %x\n", vMethodTableFields.wNumStaticFields); - - if (vMethodTableFields.wNumThreadStaticFields != 0) - { - ExtOut("NumThreadStaticFields: %x\n", vMethodTableFields.wNumThreadStaticFields); - } - - if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0) - { - DisplayFields(methodTable, &mtdata, &vMethodTableFields, NULL, TRUE, FALSE); - } - } - - return Status; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of a MethodTable * -* from a given address * -* * -\**********************************************************************/ -DECLARE_API(DumpMT) -{ - DWORD_PTR dwStartAddr=0; - DWORD_PTR dwOriginalAddr; - - INIT_API(); - - MINIDUMP_NOT_SUPPORTED(); - - BOOL bDumpMDTable = FALSE; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-MD", &bDumpMDTable, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE} -#endif - }; - CMDValue arg[] = - { // vptr, type - {&dwStartAddr, COHEX} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - TableOutput table(2, 16, AlignLeft, false); - - if (nArg == 0) - { - Print("Missing MethodTable address\n"); - return Status; - } - - dwOriginalAddr = dwStartAddr; - dwStartAddr = dwStartAddr&~3; - - if (!IsMethodTable(dwStartAddr)) - { - Print(dwOriginalAddr, " is not a MethodTable\n"); - return Status; - } - - DacpMethodTableData vMethTable; - vMethTable.Request(g_sos, TO_CDADDR(dwStartAddr)); - - if (vMethTable.bIsFree) - { - Print("Free MethodTable\n"); - return Status; - } - - DacpMethodTableCollectibleData vMethTableCollectible; - vMethTableCollectible.Request(g_sos, TO_CDADDR(dwStartAddr)); - - table.WriteRow("EEClass:", EEClassPtr(vMethTable.Class)); - - table.WriteRow("Module:", ModulePtr(vMethTable.Module)); - - sos::MethodTable mt = (TADDR)dwStartAddr; - table.WriteRow("Name:", mt.GetName()); - - WCHAR fileName[MAX_LONGPATH]; - FileNameForModule(TO_TADDR(vMethTable.Module), fileName); - table.WriteRow("mdToken:", Pointer(vMethTable.cl)); - table.WriteRow("File:", fileName[0] ? fileName : W("Unknown Module")); - - if (vMethTableCollectible.LoaderAllocatorObjectHandle != NULL) - { - TADDR loaderAllocator; - if (SUCCEEDED(MOVE(loaderAllocator, vMethTableCollectible.LoaderAllocatorObjectHandle))) - { - table.WriteRow("LoaderAllocator:", ObjectPtr(loaderAllocator)); - } - } - - table.WriteRow("BaseSize:", PrefixHex(vMethTable.BaseSize)); - table.WriteRow("ComponentSize:", PrefixHex(vMethTable.ComponentSize)); - table.WriteRow("Slots in VTable:", Decimal(vMethTable.wNumMethods)); - - table.SetColWidth(0, 29); - table.WriteRow("Number of IFaces in IFaceMap:", Decimal(vMethTable.wNumInterfaces)); - - if (bDumpMDTable) - { - table.ReInit(4, POINTERSIZE_HEX, AlignRight); - table.SetColAlignment(3, AlignLeft); - table.SetColWidth(2, 6); - - Print("--------------------------------------\n"); - Print("MethodDesc Table\n"); - - table.WriteRow("Entry", "MethodDesc", "JIT", "Name"); - - for (DWORD n = 0; n < vMethTable.wNumMethods; n++) - { - JITTypes jitType; - DWORD_PTR methodDesc=0; - DWORD_PTR gcinfoAddr; - - CLRDATA_ADDRESS entry; - if (g_sos->GetMethodTableSlot(dwStartAddr, n, &entry) != S_OK) - { - PrintLn("<error getting slot ", Decimal(n), ">"); - continue; - } - - IP2MethodDesc((DWORD_PTR)entry, methodDesc, jitType, gcinfoAddr); - table.WriteColumn(0, entry); - table.WriteColumn(1, MethodDescPtr(methodDesc)); - - if (jitType == TYPE_UNKNOWN && methodDesc != NULL) - { - // We can get a more accurate jitType from NativeCodeAddr of the methoddesc, - // because the methodtable entry hasn't always been patched. - DacpMethodDescData tmpMethodDescData; - if (tmpMethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK) - { - DacpCodeHeaderData codeHeaderData; - if (codeHeaderData.Request(g_sos,tmpMethodDescData.NativeCodeAddr) == S_OK) - { - jitType = (JITTypes) codeHeaderData.JITType; - } - } - } - - const char *pszJitType = "NONE"; - if (jitType == TYPE_JIT) - pszJitType = "JIT"; - else if (jitType == TYPE_PJIT) - pszJitType = "PreJIT"; - else - { - DacpMethodDescData MethodDescData; - if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK) - { - // Is it an fcall? - if ((TO_TADDR(MethodDescData.NativeCodeAddr) >= TO_TADDR(moduleInfo[MSCORWKS].baseAddr)) && - ((TO_TADDR(MethodDescData.NativeCodeAddr) < TO_TADDR(moduleInfo[MSCORWKS].baseAddr + moduleInfo[MSCORWKS].size)))) - { - pszJitType = "FCALL"; - } - } - } - - table.WriteColumn(2, pszJitType); - - NameForMD_s(methodDesc,g_mdName,mdNameLen); - table.WriteColumn(3, g_mdName); - } - } - return Status; -} - -extern size_t Align (size_t nbytes); - -HRESULT PrintVC(TADDR taMT, TADDR taObject, BOOL bPrintFields = TRUE) -{ - HRESULT Status; - DacpMethodTableData mtabledata; - if ((Status = mtabledata.Request(g_sos, TO_CDADDR(taMT)))!=S_OK) - return Status; - - size_t size = mtabledata.BaseSize; - if ((Status=g_sos->GetMethodTableName(TO_CDADDR(taMT), mdNameLen, g_mdName, NULL))!=S_OK) - return Status; - - ExtOut("Name: %S\n", g_mdName); - DMLOut("MethodTable: %s\n", DMLMethodTable(taMT)); - DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class)); - ExtOut("Size: %d(0x%x) bytes\n", size, size); - - FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName); - ExtOut("File: %S\n", g_mdName[0] ? g_mdName : W("Unknown Module")); - - if (bPrintFields) - { - DacpMethodTableFieldData vMethodTableFields; - if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(taMT)))!=S_OK) - return Status; - - ExtOut("Fields:\n"); - - if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0) - DisplayFields(TO_CDADDR(taMT), &mtabledata, &vMethodTableFields, taObject, TRUE, TRUE); - } - - return S_OK; -} - -void PrintRuntimeTypeInfo(TADDR p_rtObject, const DacpObjectData & rtObjectData) -{ - // Get the method table - int iOffset = GetObjFieldOffset(TO_CDADDR(p_rtObject), rtObjectData.MethodTable, W("m_handle")); - if (iOffset > 0) - { - TADDR mtPtr; - if (SUCCEEDED(GetMTOfObject(p_rtObject + iOffset, &mtPtr))) - { - sos::MethodTable mt = mtPtr; - ExtOut("Type Name: %S\n", mt.GetName()); - DMLOut("Type MT: %s\n", DMLMethodTable(mtPtr)); - } - } -} - -HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE) -{ - if (!sos::IsObject(taObj, true)) - { - ExtOut("<Note: this object has an invalid CLASS field>\n"); - } - - DacpObjectData objData; - HRESULT Status; - if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK) - { - ExtOut("Invalid object\n"); - return Status; - } - - if (objData.ObjectType==OBJ_FREE) - { - ExtOut("Free Object\n"); - DWORD_PTR size = (DWORD_PTR)objData.Size; - ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size); - return S_OK; - } - - sos::Object obj = taObj; - ExtOut("Name: %S\n", obj.GetTypeName()); - DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable)); - - - DacpMethodTableData mtabledata; - if ((Status=mtabledata.Request(g_sos,objData.MethodTable)) == S_OK) - { - DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class)); - } - else - { - ExtOut("Invalid EEClass address\n"); - return Status; - } - - if (objData.RCW != NULL) - { - DMLOut("RCW: %s\n", DMLRCWrapper(objData.RCW)); - } - if (objData.CCW != NULL) - { - DMLOut("CCW: %s\n", DMLCCWrapper(objData.CCW)); - } - - DWORD_PTR size = (DWORD_PTR)objData.Size; - ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size); - - if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType")) == 0) - { - PrintRuntimeTypeInfo(taObj, objData); - } - - if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType+RuntimeTypeCache")) == 0) - { - // Get the method table - int iOffset = GetObjFieldOffset (TO_CDADDR(taObj), objData.MethodTable, W("m_runtimeType")); - if (iOffset > 0) - { - TADDR rtPtr; - if (MOVE(rtPtr, taObj + iOffset) == S_OK) - { - DacpObjectData rtObjectData; - if ((Status=rtObjectData.Request(g_sos, TO_CDADDR(rtPtr))) != S_OK) - { - ExtOut("Error when reading RuntimeType field\n"); - return Status; - } - - PrintRuntimeTypeInfo(rtPtr, rtObjectData); - } - } - } - - if (objData.ObjectType==OBJ_ARRAY) - { - ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s", - objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType)); - - IfDMLOut(" (<exec cmd=\"!DumpArray /d %p\">Print Array</exec>)", SOS_PTR(taObj)); - ExtOut("\n"); - - if (objData.ElementType == ELEMENT_TYPE_I1 || - objData.ElementType == ELEMENT_TYPE_U1 || - objData.ElementType == ELEMENT_TYPE_CHAR) - { - bool wide = objData.ElementType == ELEMENT_TYPE_CHAR; - - // Get the size of the character array, but clamp it to a reasonable length. - TADDR pos = taObj + (2 * sizeof(DWORD_PTR)); - DWORD_PTR num; - moveN(num, taObj + sizeof(DWORD_PTR)); - - if (IsDMLEnabled()) - DMLOut("<exec cmd=\"%s %x L%x\">Content</exec>: ", (wide) ? "dw" : "db", pos, num); - else - ExtOut("Content: "); - CharArrayContent(pos, (ULONG)(num <= 128 ? num : 128), wide); - ExtOut("\n"); - } - } - else - { - FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName); - ExtOut("File: %S\n", g_mdName[0] ? g_mdName : W("Unknown Module")); - } - - if (objData.ObjectType == OBJ_STRING) - { - ExtOut("String: "); - StringObjectContent(taObj); - ExtOut("\n"); - } - else if (objData.ObjectType == OBJ_OBJECT) - { - ExtOut("Object\n"); - } - - if (bPrintFields) - { - DacpMethodTableFieldData vMethodTableFields; - if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(objData.MethodTable)))!=S_OK) - return Status; - - ExtOut("Fields:\n"); - if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0) - { - DisplayFields(objData.MethodTable, &mtabledata, &vMethodTableFields, taObj, TRUE, FALSE); - } - else - { - ExtOut("None\n"); - } - } - - sos::ThinLockInfo lockInfo; - if (obj.GetThinLock(lockInfo)) - { - ExtOut("ThinLock owner %x (%p), Recursive %x\n", lockInfo.ThreadId, - SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion); - } - - return S_OK; -} - -BOOL IndicesInRange (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank) -{ - int i = 0; - if (!ClrSafeInt<int>::subtraction((int)rank, 1, i)) - { - ExtOut("<integer underflow>\n"); - return FALSE; - } - - for (; i >= 0; i--) - { - if (indices[i] >= bounds[i] + lowerBounds[i]) - { - if (i == 0) - { - return FALSE; - } - - indices[i] = lowerBounds[i]; - indices[i - 1]++; - } - } - - return TRUE; -} - -void ExtOutIndices (DWORD * indices, DWORD rank) -{ - for (DWORD i = 0; i < rank; i++) - { - ExtOut("[%d]", indices[i]); - } -} - -size_t OffsetFromIndices (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank) -{ - _ASSERTE(rank >= 0); - size_t multiplier = 1; - size_t offset = 0; - int i = 0; - if (!ClrSafeInt<int>::subtraction((int)rank, 1, i)) - { - ExtOut("<integer underflow>\n"); - return 0; - } - - for (; i >= 0; i--) - { - DWORD curIndex = indices[i] - lowerBounds[i]; - offset += curIndex * multiplier; - multiplier *= bounds[i]; - } - - return offset; -} -HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint); -#ifdef _DEBUG -HRESULT PrintPermissionSet (TADDR p_PermSet) -{ - HRESULT Status = S_OK; - - DacpObjectData PermSetData; - if ((Status=PermSetData.Request(g_sos, TO_CDADDR(p_PermSet))) != S_OK) - { - ExtOut("Invalid object\n"); - return Status; - } - - - sos::MethodTable mt = TO_TADDR(PermSetData.MethodTable); - if (_wcscmp (W("System.Security.PermissionSet"), mt.GetName()) != 0 && _wcscmp(W("System.Security.NamedPermissionSet"), mt.GetName()) != 0) - { - ExtOut("Invalid PermissionSet object\n"); - return S_FALSE; - } - - ExtOut("PermissionSet object: %p\n", SOS_PTR(p_PermSet)); - - // Print basic info - - // Walk the fields, printing some fields in a special way. - - int iOffset = GetObjFieldOffset (TO_CDADDR(p_PermSet), PermSetData.MethodTable, W("m_Unrestricted")); - - if (iOffset > 0) - { - BYTE unrestricted; - MOVE(unrestricted, p_PermSet + iOffset); - if (unrestricted) - ExtOut("Unrestricted: TRUE\n"); - else - ExtOut("Unrestricted: FALSE\n"); - } - - iOffset = GetObjFieldOffset (TO_CDADDR(p_PermSet), PermSetData.MethodTable, W("m_permSet")); - if (iOffset > 0) - { - TADDR tbSetPtr; - MOVE(tbSetPtr, p_PermSet + iOffset); - if (tbSetPtr != NULL) - { - DacpObjectData tbSetData; - if ((Status=tbSetData.Request(g_sos, TO_CDADDR(tbSetPtr))) != S_OK) - { - ExtOut("Invalid object\n"); - return Status; - } - - iOffset = GetObjFieldOffset (TO_CDADDR(tbSetPtr), tbSetData.MethodTable, W("m_Set")); - if (iOffset > 0) - { - DWORD_PTR PermsArrayPtr; - MOVE(PermsArrayPtr, tbSetPtr + iOffset); - if (PermsArrayPtr != NULL) - { - // Print all the permissions in the array - DacpObjectData objData; - if ((Status=objData.Request(g_sos, TO_CDADDR(PermsArrayPtr))) != S_OK) - { - ExtOut("Invalid object\n"); - return Status; - } - DumpArrayFlags flags; - flags.bDetail = TRUE; - return PrintArray(objData, flags, TRUE); - } - } - - iOffset = GetObjFieldOffset (TO_CDADDR(tbSetPtr), tbSetData.MethodTable, W("m_Obj")); - if (iOffset > 0) - { - DWORD_PTR PermObjPtr; - MOVE(PermObjPtr, tbSetPtr + iOffset); - if (PermObjPtr != NULL) - { - // Print the permission object - return PrintObj(PermObjPtr); - } - } - - - } - } - return Status; -} - -#endif // _DEBUG - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of an object from a * -* given address -* * -\**********************************************************************/ -DECLARE_API(DumpArray) -{ - INIT_API(); - - DumpArrayFlags flags; - - MINIDUMP_NOT_SUPPORTED(); - - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-start", &flags.startIndex, COSIZE_T, TRUE}, - {"-length", &flags.Length, COSIZE_T, TRUE}, - {"-details", &flags.bDetail, COBOOL, FALSE}, - {"-nofields", &flags.bNoFieldsForElement, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - CMDValue arg[] = - { // vptr, type - {&flags.strObject, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - DWORD_PTR p_Object = GetExpression (flags.strObject); - if (p_Object == 0) - { - ExtOut("Invalid parameter %s\n", flags.strObject); - return Status; - } - - if (!sos::IsObject(p_Object, true)) - { - ExtOut("<Note: this object has an invalid CLASS field>\n"); - } - - DacpObjectData objData; - if ((Status=objData.Request(g_sos, TO_CDADDR(p_Object))) != S_OK) - { - ExtOut("Invalid object\n"); - return Status; - } - - if (objData.ObjectType != OBJ_ARRAY) - { - ExtOut("Not an array, please use !DumpObj instead\n"); - return S_OK; - } - return PrintArray(objData, flags, FALSE); -} - - -HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint) -{ - HRESULT Status = S_OK; - - if (objData.dwRank != 1 && (flags.Length != (DWORD_PTR)-1 ||flags.startIndex != 0)) - { - ExtOut("For multi-dimension array, length and start index are supported\n"); - return S_OK; - } - - if (flags.startIndex > objData.dwNumComponents) - { - ExtOut("Start index out of range\n"); - return S_OK; - } - - if (!flags.bDetail && flags.bNoFieldsForElement) - { - ExtOut("-nofields has no effect unless -details is specified\n"); - } - - DWORD i; - if (!isPermSetPrint) - { - // TODO: don't depend on this being a MethodTable - NameForMT_s(TO_TADDR(objData.ElementTypeHandle), g_mdName, mdNameLen); - - ExtOut("Name: %S[", g_mdName); - for (i = 1; i < objData.dwRank; i++) - ExtOut(","); - ExtOut("]\n"); - - DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable)); - - { - DacpMethodTableData mtdata; - if (SUCCEEDED(mtdata.Request(g_sos, objData.MethodTable))) - { - DMLOut("EEClass: %s\n", DMLClass(mtdata.Class)); - } - } - - DWORD_PTR size = (DWORD_PTR)objData.Size; - ExtOut("Size: %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size); - - ExtOut("Array: Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s\n", - objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType)); - DMLOut("Element Methodtable: %s\n", DMLMethodTable(objData.ElementTypeHandle)); - } - - BOOL isElementValueType = IsElementValueType(objData.ElementType); - - DWORD dwRankAllocSize; - if (!ClrSafeInt<DWORD>::multiply(sizeof(DWORD), objData.dwRank, dwRankAllocSize)) - { - ExtOut("Integer overflow on array rank\n"); - return Status; - } - - DWORD *lowerBounds = (DWORD *)alloca(dwRankAllocSize); - if (!SafeReadMemory(objData.ArrayLowerBoundsPtr, lowerBounds, dwRankAllocSize, NULL)) - { - ExtOut("Failed to read lower bounds info from the array\n"); - return S_OK; - } - - DWORD *bounds = (DWORD *)alloca(dwRankAllocSize); - if (!SafeReadMemory (objData.ArrayBoundsPtr, bounds, dwRankAllocSize, NULL)) - { - ExtOut("Failed to read bounds info from the array\n"); - return S_OK; - } - - //length is only supported for single-dimension array - if (objData.dwRank == 1 && flags.Length != (DWORD_PTR)-1) - { - bounds[0] = _min(bounds[0], (DWORD)(flags.Length + flags.startIndex) - lowerBounds[0]); - } - - DWORD *indices = (DWORD *)alloca(dwRankAllocSize); - for (i = 0; i < objData.dwRank; i++) - { - indices[i] = lowerBounds[i]; - } - - //start index is only supported for single-dimension array - if (objData.dwRank == 1) - { - indices[0] = (DWORD)flags.startIndex; - } - - //Offset should be calculated by OffsetFromIndices. However because of the way - //how we grow indices, incrementing offset by one happens to match indices in every iteration - for (size_t offset = OffsetFromIndices (indices, lowerBounds, bounds, objData.dwRank); - IndicesInRange (indices, lowerBounds, bounds, objData.dwRank); - indices[objData.dwRank - 1]++, offset++) - { - if (IsInterrupt()) - { - ExtOut("interrupted by user\n"); - break; - } - - TADDR elementAddress = TO_TADDR(objData.ArrayDataPtr + offset * objData.dwComponentSize); - TADDR p_Element = NULL; - if (isElementValueType) - { - p_Element = elementAddress; - } - else if (!SafeReadMemory (elementAddress, &p_Element, sizeof (p_Element), NULL)) - { - ExtOut("Failed to read element at "); - ExtOutIndices(indices, objData.dwRank); - ExtOut("\n"); - continue; - } - - if (p_Element) - { - ExtOutIndices(indices, objData.dwRank); - - if (isElementValueType) - { - DMLOut( " %s\n", DMLValueClass(objData.ElementTypeHandle, p_Element)); - } - else - { - DMLOut(" %s\n", DMLObject(p_Element)); - } - } - else if (!isPermSetPrint) - { - ExtOutIndices(indices, objData.dwRank); - ExtOut(" null\n"); - } - - if (flags.bDetail) - { - IncrementIndent(); - if (isElementValueType) - { - PrintVC(TO_TADDR(objData.ElementTypeHandle), elementAddress, !flags.bNoFieldsForElement); - } - else if (p_Element != NULL) - { - PrintObj(p_Element, !flags.bNoFieldsForElement); - } - DecrementIndent(); - } - } - - return S_OK; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of an object from a * -* given address -* * -\**********************************************************************/ -DECLARE_API(DumpObj) -{ - INIT_API(); - - MINIDUMP_NOT_SUPPORTED(); - - BOOL dml = FALSE; - BOOL bNoFields = FALSE; - BOOL bRefs = FALSE; - StringHolder str_Object; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-nofields", &bNoFields, COBOOL, FALSE}, - {"-refs", &bRefs, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - CMDValue arg[] = - { // vptr, type - {&str_Object.data, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - DWORD_PTR p_Object = GetExpression(str_Object.data); - EnableDMLHolder dmlHolder(dml); - if (p_Object == 0) - { - ExtOut("Invalid parameter %s\n", args); - return Status; - } - - Status = PrintObj(p_Object, !bNoFields); - - if (SUCCEEDED(Status) && bRefs) - { - ExtOut("GC Refs:\n"); - TableOutput out(2, POINTERSIZE_HEX, AlignRight, 4); - out.WriteRow("offset", "object"); - for (sos::RefIterator itr(TO_TADDR(p_Object)); itr; ++itr) - out.WriteRow(Hex(itr.GetOffset()), ObjectPtr(*itr)); - } - - return Status; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of a delegate from a * -* given address. * -* * -\**********************************************************************/ - -DECLARE_API(DumpDelegate) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - try - { - BOOL dml = FALSE; - DWORD_PTR dwAddr = 0; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE} - }; - CMDValue arg[] = - { // vptr, type - {&dwAddr, COHEX} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - if (nArg != 1) - { - ExtOut("Usage: !DumpDelegate <delegate object address>\n"); - return Status; - } - - EnableDMLHolder dmlHolder(dml); - CLRDATA_ADDRESS delegateAddr = TO_CDADDR(dwAddr); - - if (!sos::IsObject(delegateAddr)) - { - ExtOut("Invalid object.\n"); - } - else - { - sos::Object delegateObj = TO_TADDR(delegateAddr); - if (!IsDerivedFrom(TO_CDADDR(delegateObj.GetMT()), W("System.Delegate"))) - { - ExtOut("Object of type '%S' is not a delegate.", delegateObj.GetTypeName()); - } - else - { - ExtOut("Target Method Name\n"); - - std::vector<CLRDATA_ADDRESS> delegatesRemaining; - delegatesRemaining.push_back(delegateAddr); - while (delegatesRemaining.size() > 0) - { - delegateAddr = delegatesRemaining.back(); - delegatesRemaining.pop_back(); - delegateObj = TO_TADDR(delegateAddr); - - int offset; - if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_target"))) != 0) - { - CLRDATA_ADDRESS target; - MOVE(target, delegateObj.GetAddress() + offset); - - if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_invocationList"))) != 0) - { - CLRDATA_ADDRESS invocationList; - MOVE(invocationList, delegateObj.GetAddress() + offset); - - if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_invocationCount"))) != 0) - { - int invocationCount; - MOVE(invocationCount, delegateObj.GetAddress() + offset); - - if (invocationList == NULL) - { - CLRDATA_ADDRESS md; - DMLOut("%s ", DMLObject(target)); - if (TryGetMethodDescriptorForDelegate(delegateAddr, &md)) - { - DMLOut("%s ", DMLMethodDesc(md)); - NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen); - ExtOut("%S\n", g_mdName); - } - else - { - ExtOut("(unknown)\n"); - } - } - else if (sos::IsObject(invocationList, false)) - { - DacpObjectData objData; - if (objData.Request(g_sos, invocationList) == S_OK && - objData.ObjectType == OBJ_ARRAY && - invocationCount <= (int)objData.dwNumComponents) - { - for (int i = 0; i < invocationCount; i++) - { - CLRDATA_ADDRESS elementPtr; - MOVE(elementPtr, TO_CDADDR(objData.ArrayDataPtr + (i * objData.dwComponentSize))); - if (elementPtr != NULL && sos::IsObject(elementPtr, false)) - { - delegatesRemaining.push_back(elementPtr); - } - } - } - } - } - } - } - } - } - } - - return S_OK; - } - catch (const sos::Exception &e) - { - ExtOut("%s\n", e.what()); - return E_FAIL; - } -} - -CLRDATA_ADDRESS isExceptionObj(CLRDATA_ADDRESS mtObj) -{ - // We want to follow back until we get the mt for System.Exception - DacpMethodTableData dmtd; - CLRDATA_ADDRESS walkMT = mtObj; - while(walkMT != NULL) - { - if (dmtd.Request(g_sos, walkMT) != S_OK) - { - break; - } - if (walkMT == g_special_usefulGlobals.ExceptionMethodTable) - { - return walkMT; - } - walkMT = dmtd.ParentMethodTable; - } - return NULL; -} - -CLRDATA_ADDRESS isSecurityExceptionObj(CLRDATA_ADDRESS mtObj) -{ - // We want to follow back until we get the mt for System.Exception - DacpMethodTableData dmtd; - CLRDATA_ADDRESS walkMT = mtObj; - while(walkMT != NULL) - { - if (dmtd.Request(g_sos, walkMT) != S_OK) - { - break; - } - NameForMT_s(TO_TADDR(walkMT), g_mdName, mdNameLen); - if (_wcscmp(W("System.Security.SecurityException"), g_mdName) == 0) - { - return walkMT; - } - walkMT = dmtd.ParentMethodTable; - } - return NULL; -} - -// Fill the passed in buffer with a text header for generated exception information. -// Returns the number of characters in the wszBuffer array on exit. -// If NULL is passed for wszBuffer, just returns the number of characters needed. -size_t AddExceptionHeader (__out_ecount_opt(bufferLength) WCHAR *wszBuffer, size_t bufferLength) -{ -#ifdef _TARGET_WIN64_ - const WCHAR *wszHeader = W(" SP IP Function\n"); -#else - const WCHAR *wszHeader = W(" SP IP Function\n"); -#endif // _TARGET_WIN64_ - if (wszBuffer) - { - swprintf_s(wszBuffer, bufferLength, wszHeader); - } - return _wcslen(wszHeader); -} - -struct StackTraceElement -{ - UINT_PTR ip; - UINT_PTR sp; - DWORD_PTR pFunc; // MethodDesc - // TRUE if this element represents the last frame of the foreign - // exception stack trace. - BOOL fIsLastFrameFromForeignStackTrace; - -}; - -#include "sos_stacktrace.h" - -class StringOutput -{ -public: - CQuickString cs; - StringOutput() - { - cs.Alloc(1024); - cs.String()[0] = L'\0'; - } - - BOOL Append(__in_z LPCWSTR pszStr) - { - size_t iInputLen = _wcslen (pszStr); - size_t iCurLen = _wcslen (cs.String()); - if ((iCurLen + iInputLen + 1) > cs.Size()) - { - if (cs.ReSize(iCurLen + iInputLen + 1) != S_OK) - { - return FALSE; - } - } - - wcsncat_s (cs.String(), cs.Size(), pszStr, _TRUNCATE); - return TRUE; - } - - size_t Length() - { - return _wcslen(cs.String()); - } - - WCHAR *String() - { - return cs.String(); - } -}; - -static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp, ULONG64 IPAddr, StringOutput& so); - -// Using heuristics to determine if an exception object represented an async (hardware) or a -// managed exception -// We need to use these heuristics when the System.Exception object is not the active exception -// on some thread, but it's something found somewhere on the managed heap. - -// uses the MapWin32FaultToCOMPlusException to figure out how we map async exceptions -// to managed exceptions and their HRESULTs -static const HRESULT AsyncHResultValues[] = -{ - COR_E_ARITHMETIC, // kArithmeticException - COR_E_OVERFLOW, // kOverflowException - COR_E_DIVIDEBYZERO, // kDivideByZeroException - COR_E_FORMAT, // kFormatException - COR_E_NULLREFERENCE, // kNullReferenceException - E_POINTER, // kAccessViolationException - // the EE is raising the next exceptions more often than the OS will raise an async - // exception for these conditions, so in general treat these as Synchronous - // COR_E_INDEXOUTOFRANGE, // kIndexOutOfRangeException - // COR_E_OUTOFMEMORY, // kOutOfMemoryException - // COR_E_STACKOVERFLOW, // kStackOverflowException - COR_E_DATAMISALIGNED, // kDataMisalignedException - -}; -BOOL IsAsyncException(CLRDATA_ADDRESS taObj, CLRDATA_ADDRESS mtObj) -{ - // by default we'll treat exceptions as synchronous - UINT32 xcode = EXCEPTION_COMPLUS; - int iOffset = GetObjFieldOffset (taObj, mtObj, W("_xcode")); - if (iOffset > 0) - { - HRESULT hr = MOVE(xcode, taObj + iOffset); - if (hr != S_OK) - { - xcode = EXCEPTION_COMPLUS; - goto Done; - } - } - - if (xcode == EXCEPTION_COMPLUS) - { - HRESULT ehr = 0; - iOffset = GetObjFieldOffset (taObj, mtObj, W("_HResult")); - if (iOffset > 0) - { - HRESULT hr = MOVE(ehr, taObj + iOffset); - if (hr != S_OK) - { - xcode = EXCEPTION_COMPLUS; - goto Done; - } - for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx) - { - if (ehr == AsyncHResultValues[idx]) - { - xcode = ehr; - break; - } - } - } - } -Done: - return xcode != EXCEPTION_COMPLUS; -} - -// Overload that mirrors the code above when the ExceptionObjectData was already retrieved from LS -BOOL IsAsyncException(const DacpExceptionObjectData & excData) -{ - if ((DWORD)excData.XCode != EXCEPTION_COMPLUS) - return TRUE; - - HRESULT ehr = excData.HResult; - for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx) - { - if (ehr == AsyncHResultValues[idx]) - { - return TRUE; - } - } - - return FALSE; -} - - -#define SOS_STACKTRACE_SHOWEXPLICITFRAMES 0x00000002 -size_t FormatGeneratedException (DWORD_PTR dataPtr, - UINT bytes, - __out_ecount_opt(bufferLength) WCHAR *wszBuffer, - size_t bufferLength, - BOOL bAsync, - BOOL bNestedCase = FALSE, - BOOL bLineNumbers = FALSE) -{ - UINT count = bytes / sizeof(StackTraceElement); - size_t Length = 0; - - if (wszBuffer && bufferLength > 0) - { - wszBuffer[0] = L'\0'; - } - - // Buffer is calculated for sprintf below (" %p %p %S\n"); - WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2 + MAX_LONGPATH + 8]; - - if (count == 0) - { - return 0; - } - - if (bNestedCase) - { - // If we are computing the call stack for a nested exception, we - // don't want to print the last frame, because the outer exception - // will have that frame. - count--; - } - - for (UINT i = 0; i < count; i++) - { - StackTraceElement ste; - MOVE (ste, dataPtr + i*sizeof(StackTraceElement)); - - // ste.ip must be adjusted because of an ancient workaround in the exception - // infrastructure. The workaround is that the exception needs to have - // an ip address that will map to the line number where the exception was thrown. - // (It doesn't matter that it's not a valid instruction). (see /vm/excep.cpp) - // - // This "counterhack" is not 100% accurate - // The biggest issue is that !PrintException must work with exception objects - // that may not be currently active; as a consequence we cannot rely on the - // state of some "current thread" to infer whether the IP values stored in - // the exception object have been adjusted or not. If we could, we may examine - // the topmost "Frame" and make the decision based on whether it's a - // FaultingExceptionFrame or not. - // 1. On IA64 the IP values are never adjusted by the EE so there's nothing - // to adjust back. - // 2. On AMD64: - // (a) if the exception was an async (hardware) exception add 1 to all - // IP values in the exception object - // (b) if the exception was a managed exception (either raised by the - // EE or thrown by managed code) do not adjust any IP values - // 3. On X86: - // (a) if the exception was an async (hardware) exception add 1 to - // all but the topmost IP value in the exception object - // (b) if the exception was a managed exception (either raised by - // the EE or thrown by managed code) add 1 to all IP values in - // the exception object -#if defined(_TARGET_AMD64_) - if (bAsync) - { - ste.ip += 1; - } -#elif defined(_TARGET_X86_) - if (IsDbgTargetX86() && (!bAsync || i != 0)) - { - ste.ip += 1; - } -#endif // defined(_TARGET_AMD64_) || defined(_TARGET__X86_) - - StringOutput so; - HRESULT Status = DumpMDInfoBuffer(ste.pFunc, SOS_STACKTRACE_SHOWADDRESSES|SOS_STACKTRACE_SHOWEXPLICITFRAMES, ste.sp, ste.ip, so); - - // If DumpMDInfoBuffer failed (due to out of memory or missing metadata), - // or did not update so (when ste is an explicit frames), do not update wszBuffer - if (Status == S_OK) - { - WCHAR filename[MAX_LONGPATH] = W(""); - ULONG linenum = 0; - if (bLineNumbers && - SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, _countof(filename)))) - { - swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s [%s @ %d]\n"), so.String(), filename, linenum); - } - else - { - swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s\n"), so.String()); - } - - Length += _wcslen(wszLineBuffer); - - if (wszBuffer) - { - wcsncat_s(wszBuffer, bufferLength, wszLineBuffer, _TRUNCATE); - } - } - } - - return Length; -} - -// ExtOut has an internal limit for the string size -void SosExtOutLargeString(__inout_z __inout_ecount_opt(len) WCHAR * pwszLargeString, size_t len) -{ - const size_t chunkLen = 2048; - - WCHAR *pwsz = pwszLargeString; // beginning of a chunk - size_t count = len/chunkLen; - // write full chunks - for (size_t idx = 0; idx < count; ++idx) - { - WCHAR *pch = pwsz + chunkLen; // after the chunk - // zero terminate the chunk - WCHAR ch = *pch; - *pch = L'\0'; - - ExtOut("%S", pwsz); - - // restore whacked char - *pch = ch; - - // advance to next chunk - pwsz += chunkLen; - } - - // last chunk - ExtOut("%S", pwsz); -} - -HRESULT FormatException(CLRDATA_ADDRESS taObj, BOOL bLineNumbers = FALSE) -{ - HRESULT Status = S_OK; - - DacpObjectData objData; - if ((Status=objData.Request(g_sos, taObj)) != S_OK) - { - ExtOut("Invalid object\n"); - return Status; - } - - // Make sure it is an exception object, and get the MT of Exception - CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable); - if (exceptionMT == NULL) - { - ExtOut("Not a valid exception object\n"); - return Status; - } - - DMLOut("Exception object: %s\n", DMLObject(taObj)); - - if (NameForMT_s(TO_TADDR(objData.MethodTable), g_mdName, mdNameLen)) - { - ExtOut("Exception type: %S\n", g_mdName); - } - else - { - ExtOut("Exception type: <Unknown>\n"); - } - - // Print basic info - - // First try to get exception object data using ISOSDacInterface2 - DacpExceptionObjectData excData; - BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, taObj)); - - // Walk the fields, printing some fields in a special way. - // HR, InnerException, Message, StackTrace, StackTraceString - - { - TADDR taMsg = 0; - if (bGotExcData) - { - taMsg = TO_TADDR(excData.Message); - } - else - { - int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_message")); - if (iOffset > 0) - { - MOVE (taMsg, taObj + iOffset); - } - } - - ExtOut("Message: "); - - if (taMsg) - StringObjectContent(taMsg); - else - ExtOut("<none>"); - - ExtOut("\n"); - } - - { - TADDR taInnerExc = 0; - if (bGotExcData) - { - taInnerExc = TO_TADDR(excData.InnerException); - } - else - { - int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_innerException")); - if (iOffset > 0) - { - MOVE (taInnerExc, taObj + iOffset); - } - } - - ExtOut("InnerException: "); - if (taInnerExc) - { - TADDR taMT; - if (SUCCEEDED(GetMTOfObject(taInnerExc, &taMT))) - { - NameForMT_s(taMT, g_mdName, mdNameLen); - ExtOut("%S, ", g_mdName); - if (IsDMLEnabled()) - DMLOut("Use <exec cmd=\"!PrintException /d %p\">!PrintException %p</exec> to see more.\n", taInnerExc, taInnerExc); - else - ExtOut("Use !PrintException %p to see more.\n", SOS_PTR(taInnerExc)); - } - else - { - ExtOut("<invalid MethodTable of inner exception>"); - } - } - else - { - ExtOut("<none>\n"); - } - } - - BOOL bAsync = bGotExcData ? IsAsyncException(excData) - : IsAsyncException(taObj, objData.MethodTable); - - { - TADDR taStackTrace = 0; - if (bGotExcData) - { - taStackTrace = TO_TADDR(excData.StackTrace); - } - else - { - int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTrace")); - if (iOffset > 0) - { - MOVE(taStackTrace, taObj + iOffset); - } - } - - ExtOut("StackTrace (generated):\n"); - if (taStackTrace) - { - DWORD arrayLen; - HRESULT hr = MOVE(arrayLen, taStackTrace + sizeof(DWORD_PTR)); - - if (arrayLen != 0 && hr == S_OK) - { -#ifdef _TARGET_WIN64_ - DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD); -#else - DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD); -#endif // _TARGET_WIN64_ - size_t stackTraceSize = 0; - MOVE (stackTraceSize, dataPtr); - - DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement)); - dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data - - if (stackTraceSize == 0) - { - ExtOut("Unable to decipher generated stack trace\n"); - } - else - { - size_t iHeaderLength = AddExceptionHeader (NULL, 0); - size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, FALSE, bLineNumbers); - WCHAR *pwszBuffer = new NOTHROW WCHAR[iHeaderLength + iLength + 1]; - if (pwszBuffer) - { - AddExceptionHeader(pwszBuffer, iHeaderLength + 1); - FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer + iHeaderLength, iLength + 1, bAsync, FALSE, bLineNumbers); - SosExtOutLargeString(pwszBuffer, iHeaderLength + iLength + 1); - delete[] pwszBuffer; - } - ExtOut("\n"); - } - } - else - { - ExtOut("<Not Available>\n"); - } - } - else - { - ExtOut("<none>\n"); - } - } - - { - TADDR taStackString; - if (bGotExcData) - { - taStackString = TO_TADDR(excData.StackTraceString); - } - else - { - int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTraceString")); - MOVE (taStackString, taObj + iOffset); - } - - ExtOut("StackTraceString: "); - if (taStackString) - { - StringObjectContent(taStackString); - ExtOut("\n\n"); // extra newline looks better - } - else - { - ExtOut("<none>\n"); - } - } - - { - DWORD hResult; - if (bGotExcData) - { - hResult = excData.HResult; - } - else - { - int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_HResult")); - MOVE (hResult, taObj + iOffset); - } - - ExtOut("HResult: %lx\n", hResult); - } - - if (isSecurityExceptionObj(objData.MethodTable) != NULL) - { - // We have a SecurityException Object: print out the debugString if present - int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("m_debugString")); - if (iOffset > 0) - { - TADDR taDebugString; - MOVE (taDebugString, taObj + iOffset); - - if (taDebugString) - { - ExtOut("SecurityException Message: "); - StringObjectContent(taDebugString); - ExtOut("\n\n"); // extra newline looks better - } - } - } - - return Status; -} - -DECLARE_API(PrintException) -{ - INIT_API(); - - BOOL dml = FALSE; - BOOL bShowNested = FALSE; - BOOL bLineNumbers = FALSE; - BOOL bCCW = FALSE; - StringHolder strObject; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-nested", &bShowNested, COBOOL, FALSE}, - {"-lines", &bLineNumbers, COBOOL, FALSE}, - {"-l", &bLineNumbers, COBOOL, FALSE}, - {"-ccw", &bCCW, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE} -#endif - }; - CMDValue arg[] = - { // vptr, type - {&strObject, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - if (bLineNumbers) - { - ULONG symlines = 0; - if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines))) - { - symlines &= SYMOPT_LOAD_LINES; - } - if (symlines == 0) - { - ExtOut("In order for the option -lines to enable display of source information\n" - "the debugger must be configured to load the line number information from\n" - "the symbol files. Use the \".lines; .reload\" command to achieve this.\n"); - // don't even try - bLineNumbers = FALSE; - } - } - - EnableDMLHolder dmlHolder(dml); - DWORD_PTR p_Object = NULL; - if (nArg == 0) - { - if (bCCW) - { - ExtOut("No CCW pointer specified\n"); - return Status; - } - - // Look at the last exception object on this thread - - CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread(); - DacpThreadData Thread; - - if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK)) - { - ExtOut("The current thread is unmanaged\n"); - return Status; - } - - DWORD_PTR dwAddr = NULL; - if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle), - &dwAddr, - sizeof(dwAddr), NULL)) || (dwAddr==NULL)) - { - ExtOut("There is no current managed exception on this thread\n"); - } - else - { - p_Object = dwAddr; - } - } - else - { - p_Object = GetExpression(strObject.data); - if (p_Object == 0) - { - if (bCCW) - { - ExtOut("Invalid CCW pointer %s\n", args); - } - else - { - ExtOut("Invalid exception object %s\n", args); - } - return Status; - } - - if (bCCW) - { - // check if the address is a CCW pointer and then - // get the exception object from it - DacpCCWData ccwData; - if (ccwData.Request(g_sos, p_Object) == S_OK) - { - p_Object = TO_TADDR(ccwData.managedObject); - } - } - } - - if (p_Object) - { - FormatException(TO_CDADDR(p_Object), bLineNumbers); - } - - // Are there nested exceptions? - CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread(); - DacpThreadData Thread; - - if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK)) - { - ExtOut("The current thread is unmanaged\n"); - return Status; - } - - if (Thread.firstNestedException) - { - if (!bShowNested) - { - ExtOut("There are nested exceptions on this thread. Run with -nested for details\n"); - return Status; - } - - CLRDATA_ADDRESS currentNested = Thread.firstNestedException; - do - { - CLRDATA_ADDRESS obj = 0, next = 0; - Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next); - - if (Status != S_OK) - { - ExtOut("Error retrieving nested exception info %p\n", SOS_PTR(currentNested)); - return Status; - } - - if (IsInterrupt()) - { - ExtOut("<aborted>\n"); - return Status; - } - - ExtOut("\nNested exception -------------------------------------------------------------\n"); - Status = FormatException(obj, bLineNumbers); - if (Status != S_OK) - { - return Status; - } - - currentNested = next; - } - while(currentNested != NULL); - } - return Status; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of an object from a * -* given address -* * -\**********************************************************************/ -DECLARE_API(DumpVC) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - DWORD_PTR p_MT = NULL; - DWORD_PTR p_Object = NULL; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE} -#endif - }; - CMDValue arg[] = - { // vptr, type - {&p_MT, COHEX}, - {&p_Object, COHEX}, - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - if (nArg!=2) - { - ExtOut("Usage: !DumpVC <Method Table> <Value object start addr>\n"); - return Status; - } - - if (!IsMethodTable(p_MT)) - { - ExtOut("Not a managed object\n"); - return S_OK; - } - - return PrintVC(p_MT, p_Object); -} - -#ifndef FEATURE_PAL - -#ifdef FEATURE_COMINTEROP - -DECLARE_API(DumpRCW) -{ - INIT_API(); - - BOOL dml = FALSE; - StringHolder strObject; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE} - }; - CMDValue arg[] = - { // vptr, type - {&strObject, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - if (nArg == 0) - { - ExtOut("Missing RCW address\n"); - return Status; - } - else - { - DWORD_PTR p_RCW = GetExpression(strObject.data); - if (p_RCW == 0) - { - ExtOut("Invalid RCW %s\n", args); - } - else - { - DacpRCWData rcwData; - if ((Status = rcwData.Request(g_sos, p_RCW)) != S_OK) - { - ExtOut("Error requesting RCW data\n"); - return Status; - } - BOOL isDCOMProxy; - if (FAILED(rcwData.IsDCOMProxy(g_sos, p_RCW, &isDCOMProxy))) - { - isDCOMProxy = FALSE; - } - - DMLOut("Managed object: %s\n", DMLObject(rcwData.managedObject)); - DMLOut("Creating thread: %p\n", SOS_PTR(rcwData.creatorThread)); - ExtOut("IUnknown pointer: %p\n", SOS_PTR(rcwData.unknownPointer)); - ExtOut("COM Context: %p\n", SOS_PTR(rcwData.ctxCookie)); - ExtOut("Managed ref count: %d\n", rcwData.refCount); - ExtOut("IUnknown V-table pointer : %p (captured at RCW creation time)\n", SOS_PTR(rcwData.vtablePtr)); - - ExtOut("Flags: %s%s%s%s%s%s%s%s\n", - (rcwData.isDisconnected? "IsDisconnected " : ""), - (rcwData.supportsIInspectable? "SupportsIInspectable " : ""), - (rcwData.isAggregated? "IsAggregated " : ""), - (rcwData.isContained? "IsContained " : ""), - (rcwData.isJupiterObject? "IsJupiterObject " : ""), - (rcwData.isFreeThreaded? "IsFreeThreaded " : ""), - (rcwData.identityPointer == TO_CDADDR(p_RCW)? "IsUnique " : ""), - (isDCOMProxy ? "IsDCOMProxy " : "") - ); - - // Jupiter data hidden by default - if (rcwData.isJupiterObject) - { - ExtOut("IJupiterObject: %p\n", SOS_PTR(rcwData.jupiterObject)); - } - - ExtOut("COM interface pointers:\n"); - - ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[rcwData.interfaceCount]; - if (pArray == NULL) - { - ReportOOM(); - return Status; - } - - if ((Status = g_sos->GetRCWInterfaces(p_RCW, rcwData.interfaceCount, pArray, NULL)) != S_OK) - { - ExtOut("Error requesting COM interface pointers\n"); - return Status; - } - - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "Context", "MT"); - for (int i = 0; i < rcwData.interfaceCount; i++) - { - // Ignore any NULL MethodTable interface cache. At this point only IJupiterObject - // is saved as NULL MethodTable at first slot, and we've already printed outs its - // value earlier. - if (pArray[i].methodTable == NULL) - continue; - - NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen); - - DMLOut("%p %p %s %S\n", SOS_PTR(pArray[i].interfacePtr), SOS_PTR(pArray[i].comContext), DMLMethodTable(pArray[i].methodTable), g_mdName); - } - } - } - - return Status; -} - -DECLARE_API(DumpCCW) -{ - INIT_API(); - - BOOL dml = FALSE; - StringHolder strObject; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE} - }; - CMDValue arg[] = - { // vptr, type - {&strObject, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - if (nArg == 0) - { - ExtOut("Missing CCW address\n"); - return Status; - } - else - { - DWORD_PTR p_CCW = GetExpression(strObject.data); - if (p_CCW == 0) - { - ExtOut("Invalid CCW %s\n", args); - } - else - { - DacpCCWData ccwData; - if ((Status = ccwData.Request(g_sos, p_CCW)) != S_OK) - { - ExtOut("Error requesting CCW data\n"); - return Status; - } - - if (ccwData.ccwAddress != p_CCW) - ExtOut("CCW: %p\n", SOS_PTR(ccwData.ccwAddress)); - - DMLOut("Managed object: %s\n", DMLObject(ccwData.managedObject)); - ExtOut("Outer IUnknown: %p\n", SOS_PTR(ccwData.outerIUnknown)); - ExtOut("Ref count: %d%s\n", ccwData.refCount, ccwData.isNeutered ? " (NEUTERED)" : ""); - ExtOut("Flags: %s%s\n", - (ccwData.isExtendsCOMObject? "IsExtendsCOMObject " : ""), - (ccwData.isAggregated? "IsAggregated " : "") - ); - - // Jupiter information hidden by default - if (ccwData.jupiterRefCount > 0) - { - ExtOut("Jupiter ref count: %d%s%s%s%s\n", - ccwData.jupiterRefCount, - (ccwData.isPegged || ccwData.isGlobalPegged) ? ", Pegged by" : "", - ccwData.isPegged ? " Jupiter " : "", - (ccwData.isPegged && ccwData.isGlobalPegged) ? "&" : "", - ccwData.isGlobalPegged ? " CLR " : "" - ); - } - - ExtOut("RefCounted Handle: %p%s\n", - SOS_PTR(ccwData.handle), - (ccwData.hasStrongRef ? " (STRONG)" : " (WEAK)")); - - ExtOut("COM interface pointers:\n"); - - ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[ccwData.interfaceCount]; - if (pArray == NULL) - { - ReportOOM(); - return Status; - } - - if ((Status = g_sos->GetCCWInterfaces(p_CCW, ccwData.interfaceCount, pArray, NULL)) != S_OK) - { - ExtOut("Error requesting COM interface pointers\n"); - return Status; - } - - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "MT", "Type"); - for (int i = 0; i < ccwData.interfaceCount; i++) - { - if (pArray[i].methodTable == NULL) - { - wcscpy_s(g_mdName, mdNameLen, W("IDispatch/IUnknown")); - } - else - { - NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen); - } - - DMLOut("%p %s %S\n", pArray[i].interfacePtr, DMLMethodTable(pArray[i].methodTable), g_mdName); - } - } - } - - return Status; -} - -#endif // FEATURE_COMINTEROP - -#ifdef _DEBUG -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of a PermissionSet * -* from a given address. * -* * -\**********************************************************************/ -/* - COMMAND: dumppermissionset. - !DumpPermissionSet <PermissionSet object address> - - This command allows you to examine a PermissionSet object. Note that you can - also use DumpObj such an object in greater detail. DumpPermissionSet attempts - to extract all the relevant information from a PermissionSet that you might be - interested in when performing Code Access Security (CAS) related debugging. - - Here is a simple PermissionSet object: - - 0:000> !DumpPermissionSet 014615f4 - PermissionSet object: 014615f4 - Unrestricted: TRUE - - Note that this is an unrestricted PermissionSet object that does not contain - any individual permissions. - - Here is another example of a PermissionSet object, one that is not unrestricted - and contains a single permission: - - 0:003> !DumpPermissionSet 01469fa8 - PermissionSet object: 01469fa8 - Unrestricted: FALSE - Name: System.Security.Permissions.ReflectionPermission - MethodTable: 5b731308 - EEClass: 5b7e0d78 - Size: 12(0xc) bytes - (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0. - 0.0__b77a5c561934e089\mscorlib.dll) - - Fields: - MT Field Offset Type VT Attr Value Name - 5b73125c 4001d66 4 System.Int32 0 instance 2 m_flags - - Here is another example of an unrestricted PermissionSet, one that contains - several permissions. The numbers in parentheses before each Permission object - represents the index of that Permission in the PermissionSet. - - 0:003> !DumpPermissionSet 01467bd8 - PermissionSet object: 01467bd8 - Unrestricted: FALSE - [1] 01467e90 - Name: System.Security.Permissions.FileDialogPermission - MethodTable: 5b73023c - EEClass: 5b7dfb18 - Size: 12(0xc) bytes - (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll) - Fields: - MT Field Offset Type VT Attr Value Name - 5b730190 4001cc2 4 System.Int32 0 instance 1 access - [4] 014682a8 - Name: System.Security.Permissions.ReflectionPermission - MethodTable: 5b731308 - EEClass: 5b7e0d78 - Size: 12(0xc) bytes - (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll) - Fields: - MT Field Offset Type VT Attr Value Name - 5b73125c 4001d66 4 System.Int32 0 instance 0 m_flags - [17] 0146c060 - Name: System.Diagnostics.EventLogPermission - MethodTable: 569841c4 - EEClass: 56a03e5c - Size: 28(0x1c) bytes - (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll) - Fields: - MT Field Offset Type VT Attr Value Name - 5b6d65d4 4003078 4 System.Object[] 0 instance 0146c190 tagNames - 5b6c9ed8 4003079 8 System.Type 0 instance 0146c17c permissionAccessType - 5b6cd928 400307a 10 System.Boolean 0 instance 0 isUnrestricted - 5b6c45f8 400307b c ...ections.Hashtable 0 instance 0146c1a4 rootTable - 5b6c090c 4003077 bfc System.String 0 static 00000000 computerName - 56984434 40030e7 14 ...onEntryCollection 0 instance 00000000 innerCollection - [18] 0146ceb4 - Name: System.Net.WebPermission - MethodTable: 5696dfc4 - EEClass: 569e256c - Size: 20(0x14) bytes - (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll) - Fields: - MT Field Offset Type VT Attr Value Name - 5b6cd928 400238e c System.Boolean 0 instance 0 m_Unrestricted - 5b6cd928 400238f d System.Boolean 0 instance 0 m_UnrestrictedConnect - 5b6cd928 4002390 e System.Boolean 0 instance 0 m_UnrestrictedAccept - 5b6c639c 4002391 4 ...ections.ArrayList 0 instance 0146cf3c m_connectList - 5b6c639c 4002392 8 ...ections.ArrayList 0 instance 0146cf54 m_acceptList - 569476f8 4002393 8a4 ...Expressions.Regex 0 static 00000000 s_MatchAllRegex - [19] 0146a5fc - Name: System.Net.DnsPermission - MethodTable: 56966408 - EEClass: 569d3c08 - Size: 12(0xc) bytes - (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll) - Fields: - MT Field Offset Type VT Attr Value Name - 5b6cd928 4001d2c 4 System.Boolean 0 instance 1 m_noRestriction - [20] 0146d8ec - Name: System.Web.AspNetHostingPermission - MethodTable: 569831bc - EEClass: 56a02ccc - Size: 12(0xc) bytes - (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll) - Fields: - MT Field Offset Type VT Attr Value Name - 56983090 4003074 4 System.Int32 0 instance 600 _level - [21] 0146e394 - Name: System.Net.NetworkInformation.NetworkInformationPermission - MethodTable: 5697ac70 - EEClass: 569f7104 - Size: 16(0x10) bytes - (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll) - Fields: - MT Field Offset Type VT Attr Value Name - 5697ab38 4002c34 4 System.Int32 0 instance 0 access - 5b6cd928 4002c35 8 System.Boolean 0 instance 0 unrestricted - - - The abbreviation !dps can be used for brevity. - - \\ -*/ -DECLARE_API(DumpPermissionSet) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - DWORD_PTR p_Object = NULL; - - CMDValue arg[] = - { - {&p_Object, COHEX} - }; - size_t nArg; - if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) - { - return Status; - } - if (nArg!=1) - { - ExtOut("Usage: !DumpPermissionSet <PermissionSet object addr>\n"); - return Status; - } - - - return PrintPermissionSet(p_Object); -} - -#endif // _DEBUG - -void GCPrintGenerationInfo(DacpGcHeapDetails &heap); -void GCPrintSegmentInfo(DacpGcHeapDetails &heap, DWORD_PTR &total_size); - -#endif // FEATURE_PAL - -void DisplayInvalidStructuresMessage() -{ - ExtOut("The garbage collector data structures are not in a valid state for traversal.\n"); - ExtOut("It is either in the \"plan phase,\" where objects are being moved around, or\n"); - ExtOut("we are at the initialization or shutdown of the gc heap. Commands related to \n"); - ExtOut("displaying, finding or traversing objects as well as gc heap segments may not \n"); - ExtOut("work properly. !dumpheap and !verifyheap may incorrectly complain of heap \n"); - ExtOut("consistency errors.\n"); -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function dumps GC heap size. * -* * -\**********************************************************************/ -DECLARE_API(EEHeap) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - BOOL dml = FALSE; - BOOL showgc = FALSE; - BOOL showloader = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-gc", &showgc, COBOOL, FALSE}, - {"-loader", &showloader, COBOOL, FALSE}, - {"/d", &dml, COBOOL, FALSE}, - }; - - if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - if (showloader || !showgc) - { - // Loader heap. - DWORD_PTR allHeapSize = 0; - DWORD_PTR wasted = 0; - DacpAppDomainStoreData adsData; - if ((Status=adsData.Request(g_sos))!=S_OK) - { - ExtOut("Unable to get AppDomain information\n"); - return Status; - } - - // The first one is the system domain. - ExtOut("Loader Heap:\n"); - IfFailRet(PrintDomainHeapInfo("System Domain", adsData.systemDomain, &allHeapSize, &wasted)); - if (adsData.sharedDomain != NULL) - { - IfFailRet(PrintDomainHeapInfo("Shared Domain", adsData.sharedDomain, &allHeapSize, &wasted)); - } - - ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount]; - - if (pArray==NULL) - { - ReportOOM(); - return Status; - } - - if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK) - { - ExtOut("Unable to get the array of all AppDomains.\n"); - return Status; - } - - for (int n=0;n<adsData.DomainCount;n++) - { - if (IsInterrupt()) - break; - - char domain[16]; - sprintf_s(domain, _countof(domain), "Domain %d", n+1); - - IfFailRet(PrintDomainHeapInfo(domain, pArray[n], &allHeapSize, &wasted)); - - } - - // Jit code heap - ExtOut("--------------------------------------\n"); - ExtOut("Jit code heap:\n"); - - if (IsMiniDumpFile()) - { - ExtOut("<no information>\n"); - } - else - { - allHeapSize += JitHeapInfo(); - } - - - // Module Data - { - int numModule; - ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(NULL, &numModule); - if (moduleList == NULL) - { - ExtOut("Failed to request module list.\n"); - } - else - { - // Module Thunk Heaps - ExtOut("--------------------------------------\n"); - ExtOut("Module Thunk heaps:\n"); - allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_ThunkHeap, &wasted); - - // Module Lookup Table Heaps - ExtOut("--------------------------------------\n"); - ExtOut("Module Lookup Table heaps:\n"); - allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_LookupTableHeap, &wasted); - } - } - - ExtOut("--------------------------------------\n"); - ExtOut("Total LoaderHeap size: "); - PrintHeapSize(allHeapSize, wasted); - ExtOut("=======================================\n"); - } - - if (showgc || !showloader) - { - // GC Heap - DWORD dwNHeaps = 1; - - if (!GetGcStructuresValid()) - { - DisplayInvalidStructuresMessage(); - } - - DacpGcHeapData gcheap; - if (gcheap.Request(g_sos) != S_OK) - { - ExtOut("Error requesting GC Heap data\n"); - return Status; - } - - if (gcheap.bServerMode) - { - dwNHeaps = gcheap.HeapCount; - } - - ExtOut("Number of GC Heaps: %d\n", dwNHeaps); - DWORD_PTR totalSize = 0; - if (!gcheap.bServerMode) - { - DacpGcHeapDetails heapDetails; - if (heapDetails.Request(g_sos) != S_OK) - { - ExtOut("Error requesting details\n"); - return Status; - } - - GCHeapInfo (heapDetails, totalSize); - ExtOut("Total Size: "); - PrintHeapSize(totalSize, 0); - } - else - { - DWORD dwAllocSize; - if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize)) - { - ExtOut("Failed to get GCHeaps: integer overflow\n"); - return Status; - } - - CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize); - if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK) - { - ExtOut("Failed to get GCHeaps\n"); - return Status; - } - - DWORD n; - for (n = 0; n < dwNHeaps; n ++) - { - DacpGcHeapDetails heapDetails; - if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK) - { - ExtOut("Error requesting details\n"); - return Status; - } - ExtOut("------------------------------\n"); - ExtOut("Heap %d (%p)\n", n, SOS_PTR(heapAddrs[n])); - DWORD_PTR heapSize = 0; - GCHeapInfo (heapDetails, heapSize); - totalSize += heapSize; - ExtOut("Heap Size: " WIN86_8SPACES); - PrintHeapSize(heapSize, 0); - } - } - ExtOut("------------------------------\n"); - ExtOut("GC Heap Size: " WIN86_8SPACES); - PrintHeapSize(totalSize, 0); - } - return Status; -} - -void PrintGCStat(HeapStat *inStat, const char* label=NULL) -{ - if (inStat) - { - bool sorted = false; - try - { - inStat->Sort(); - sorted = true; - inStat->Print(label); - } - catch(...) - { - ExtOut("Exception occurred while trying to %s the GC stats.\n", sorted ? "print" : "sort"); - } - - inStat->Delete(); - } -} - -#ifndef FEATURE_PAL - -DECLARE_API(TraverseHeap) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - BOOL bXmlFormat = FALSE; - BOOL bVerify = FALSE; - StringHolder Filename; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-xml", &bXmlFormat, COBOOL, FALSE}, - {"-verify", &bVerify, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&Filename.data, COSTRING}, - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - if (nArg != 1) - { - ExtOut("usage: HeapTraverse [-xml] filename\n"); - return Status; - } - - if (!g_snapshot.Build()) - { - ExtOut("Unable to build snapshot of the garbage collector state\n"); - return Status; - } - - FILE* file = NULL; - if (fopen_s(&file, Filename.data, "w") != 0) { - ExtOut("Unable to open file\n"); - return Status; - } - - if (!bVerify) - ExtOut("Assuming a uncorrupted GC heap. If this is a crash dump consider -verify option\n"); - - HeapTraverser traverser(bVerify != FALSE); - - ExtOut("Writing %s format to file %s\n", bXmlFormat ? "Xml" : "CLRProfiler", Filename.data); - ExtOut("Gathering types...\n"); - - // TODO: there may be a canonical list of methodtables in the runtime that we can - // traverse instead of exploring the gc heap for that list. We could then simplify the - // tree structure to a sorted list of methodtables, and the index is the ID. - - // TODO: "Traversing object members" code should be generalized and shared between - // !gcroot and !traverseheap. Also !dumpheap can begin using GCHeapsTraverse. - - if (!traverser.Initialize()) - { - ExtOut("Error initializing heap traversal\n"); - fclose(file); - return Status; - } - - if (!traverser.CreateReport (file, bXmlFormat ? FORMAT_XML : FORMAT_CLRPROFILER)) - { - ExtOut("Unable to write heap report\n"); - fclose(file); - return Status; - } - - fclose(file); - ExtOut("\nfile %s saved\n", Filename.data); - - return Status; -} - -#endif // FEATURE_PAL - -struct PrintRuntimeTypeArgs -{ - DWORD_PTR mtOfRuntimeType; - int handleFieldOffset; - DacpAppDomainStoreData adstore; -}; - -void PrintRuntimeTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token) -{ - PrintRuntimeTypeArgs *pArgs = (PrintRuntimeTypeArgs *)token; - - if (pArgs->mtOfRuntimeType == NULL) - { - NameForMT_s(methodTable, g_mdName, mdNameLen); - - if (_wcscmp(g_mdName, W("System.RuntimeType")) == 0) - { - pArgs->mtOfRuntimeType = methodTable; - pArgs->handleFieldOffset = GetObjFieldOffset(TO_CDADDR(objAddr), TO_CDADDR(methodTable), W("m_handle")); - if (pArgs->handleFieldOffset <= 0) - ExtOut("Error getting System.RuntimeType.m_handle offset\n"); - - pArgs->adstore.Request(g_sos); - } - } - - if ((methodTable == pArgs->mtOfRuntimeType) && (pArgs->handleFieldOffset > 0)) - { - // Get the method table and display the information. - DWORD_PTR mtPtr; - if (MOVE(mtPtr, objAddr + pArgs->handleFieldOffset) == S_OK) - { - DMLOut(DMLObject(objAddr)); - - CLRDATA_ADDRESS appDomain = GetAppDomainForMT(mtPtr); - if (appDomain != NULL) - { - if (appDomain == pArgs->adstore.sharedDomain) - ExtOut(" %" POINTERSIZE "s", "Shared"); - - else if (appDomain == pArgs->adstore.systemDomain) - ExtOut(" %" POINTERSIZE "s", "System"); - else - DMLOut(" %s", DMLDomain(appDomain)); - } - else - { - ExtOut(" %" POINTERSIZE "s", "?"); - } - - NameForMT_s(mtPtr, g_mdName, mdNameLen); - DMLOut(" %s %S\n", DMLMethodTable(mtPtr), g_mdName); - } - } -} - - -DECLARE_API(DumpRuntimeTypes) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE}, - }; - - if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) - return Status; - - EnableDMLHolder dmlHolder(dml); - - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type Name \n", - "Address", "Domain", "MT"); - ExtOut("------------------------------------------------------------------------------\n"); - - PrintRuntimeTypeArgs pargs; - ZeroMemory(&pargs, sizeof(PrintRuntimeTypeArgs)); - - GCHeapsTraverse(PrintRuntimeTypes, (LPVOID)&pargs); - return Status; -} - -#define MIN_FRAGMENTATIONBLOCK_BYTES (1024*512) -namespace sos -{ - class FragmentationBlock - { - public: - FragmentationBlock(TADDR addr, size_t size, TADDR next, TADDR mt) - : mAddress(addr), mSize(size), mNext(next), mNextMT(mt) - { - } - - inline TADDR GetAddress() const - { - return mAddress; - } - inline size_t GetSize() const - { - return mSize; - } - - inline TADDR GetNextObject() const - { - return mNext; - } - - inline TADDR GetNextMT() const - { - return mNextMT; - } - - private: - TADDR mAddress; - size_t mSize; - TADDR mNext; - TADDR mNextMT; - }; -} - -class DumpHeapImpl -{ -public: - DumpHeapImpl(PCSTR args) - : mStart(0), mStop(0), mMT(0), mMinSize(0), mMaxSize(~0), - mStat(FALSE), mStrings(FALSE), mVerify(FALSE), - mThinlock(FALSE), mShort(FALSE), mDML(FALSE), - mLive(FALSE), mDead(FALSE), mType(NULL) - { - ArrayHolder<char> type = NULL; - - TADDR minTemp = 0; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-mt", &mMT, COHEX, TRUE}, // dump objects with a given MethodTable - {"-type", &type, COSTRING, TRUE}, // list objects of specified type - {"-stat", &mStat, COBOOL, FALSE}, // dump a summary of types and the number of instances of each - {"-strings", &mStrings, COBOOL, FALSE}, // dump a summary of string objects - {"-verify", &mVerify, COBOOL, FALSE}, // verify heap objects (!heapverify) - {"-thinlock", &mThinlock, COBOOL, FALSE},// list only thinlocks - {"-short", &mShort, COBOOL, FALSE}, // list only addresses - {"-min", &mMinSize, COHEX, TRUE}, // min size of objects to display - {"-max", &mMaxSize, COHEX, TRUE}, // max size of objects to display - {"-live", &mLive, COHEX, FALSE}, // only print live objects - {"-dead", &mDead, COHEX, FALSE}, // only print dead objects -#ifndef FEATURE_PAL - {"/d", &mDML, COBOOL, FALSE}, // Debugger Markup Language -#endif - }; - - CMDValue arg[] = - { // vptr, type - {&mStart, COHEX}, - {&mStop, COHEX} - }; - - size_t nArgs = 0; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArgs)) - sos::Throw<sos::Exception>("Failed to parse command line arguments."); - - if (mStart == 0) - mStart = minTemp; - - if (mStop == 0) - mStop = sos::GCHeap::HeapEnd; - - if (type && mMT) - { - sos::Throw<sos::Exception>("Cannot specify both -mt and -type"); - } - - if (mLive && mDead) - { - sos::Throw<sos::Exception>("Cannot specify both -live and -dead."); - } - - if (mMinSize > mMaxSize) - { - sos::Throw<sos::Exception>("wrong argument"); - } - - // If the user gave us a type, convert it to unicode and clean up "type". - if (type && !mStrings) - { - size_t iLen = strlen(type) + 1; - mType = new WCHAR[iLen]; - MultiByteToWideChar(CP_ACP, 0, type, -1, mType, (int)iLen); - } - } - - ~DumpHeapImpl() - { - if (mType) - delete [] mType; - } - - void Run() - { - // enable Debugger Markup Language - EnableDMLHolder dmlholder(mDML); - sos::GCHeap gcheap; - - if (!gcheap.AreGCStructuresValid()) - DisplayInvalidStructuresMessage(); - - if (IsMiniDumpFile()) - { - ExtOut("In a minidump without full memory, most gc heap structures will not be valid.\n"); - ExtOut("If you need this functionality, get a full memory dump with \".dump /ma mydump.dmp\"\n"); - } - -#ifndef FEATURE_PAL - if (mLive || mDead) - { - GCRootImpl gcroot; - mLiveness = gcroot.GetLiveObjects(); - } -#endif - - // Some of the "specialty" versions of DumpHeap have slightly - // different implementations than the standard version of DumpHeap. - // We seperate them out to not clutter the standard DumpHeap function. - if (mShort) - DumpHeapShort(gcheap); - else if (mThinlock) - DumpHeapThinlock(gcheap); - else if (mStrings) - DumpHeapStrings(gcheap); - else - DumpHeap(gcheap); - - if (mVerify) - ValidateSyncTable(gcheap); - } - - static bool ValidateSyncTable(sos::GCHeap &gcheap) - { - bool succeeded = true; - for (sos::SyncBlkIterator itr; itr; ++itr) - { - sos::CheckInterrupt(); - - if (!itr->IsFree()) - { - if (!sos::IsObject(itr->GetObject(), true)) - { - ExtOut("SyncBlock %d corrupted, points to invalid object %p\n", - itr->GetIndex(), SOS_PTR(itr->GetObject())); - succeeded = false; - } - else - { - // Does the object header point to this syncblock index? - sos::Object obj = itr->GetObject(); - ULONG header = 0; - - if (!obj.TryGetHeader(header)) - { - ExtOut("Failed to get object header for object %p while inspecting syncblock at index %d.\n", - SOS_PTR(itr->GetObject()), itr->GetIndex()); - succeeded = false; - } - else - { - bool valid = false; - if ((header & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0 && (header & BIT_SBLK_IS_HASHCODE) == 0) - { - ULONG index = header & MASK_SYNCBLOCKINDEX; - valid = (ULONG)itr->GetIndex() == index; - } - - if (!valid) - { - ExtOut("Object header for %p should have a SyncBlock index of %d.\n", - SOS_PTR(itr->GetObject()), itr->GetIndex()); - succeeded = false; - } - } - } - } - } - - return succeeded; - } -private: - DumpHeapImpl(const DumpHeapImpl &); - - bool Verify(const sos::ObjectIterator &itr) - { - if (mVerify) - { - char buffer[1024]; - if (!itr.Verify(buffer, _countof(buffer))) - { - ExtOut(buffer); - return false; - } - } - - return true; - } - - bool IsCorrectType(const sos::Object &obj) - { - if (mMT != NULL) - return mMT == obj.GetMT(); - - if (mType != NULL) - { - WString name = obj.GetTypeName(); - return _wcsstr(name.c_str(), mType) != NULL; - } - - return true; - } - - bool IsCorrectSize(const sos::Object &obj) - { - size_t size = obj.GetSize(); - return size >= mMinSize && size <= mMaxSize; - } - - bool IsCorrectLiveness(const sos::Object &obj) - { -#ifndef FEATURE_PAL - if (mLive && mLiveness.find(obj.GetAddress()) == mLiveness.end()) - return false; - - if (mDead && (mLiveness.find(obj.GetAddress()) != mLiveness.end() || obj.IsFree())) - return false; -#endif - return true; - } - - - - inline void PrintHeader() - { - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s\n", "Address", "MT", "Size"); - } - - void DumpHeap(sos::GCHeap &gcheap) - { - HeapStat stats; - - // For heap fragmentation tracking. - TADDR lastFreeObj = NULL; - size_t lastFreeSize = 0; - - if (!mStat) - PrintHeader(); - - for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr) - { - if (!Verify(itr)) - return; - - bool onLOH = itr.IsCurrObjectOnLOH(); - - // Check for free objects to report fragmentation - if (lastFreeObj != NULL) - ReportFreeObject(lastFreeObj, lastFreeSize, itr->GetAddress(), itr->GetMT()); - - if (!onLOH && itr->IsFree()) - { - lastFreeObj = *itr; - lastFreeSize = itr->GetSize(); - } - else - { - lastFreeObj = NULL; - } - - if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr)) - { - stats.Add((DWORD_PTR)itr->GetMT(), (DWORD)itr->GetSize()); - if (!mStat) - DMLOut("%s %s %8d%s\n", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize(), - itr->IsFree() ? " Free":" "); - } - } - - if (!mStat) - ExtOut("\n"); - - stats.Sort(); - stats.Print(); - - PrintFragmentationReport(); - } - - struct StringSetEntry - { - StringSetEntry() : count(0), size(0) - { - str[0] = 0; - } - - StringSetEntry(__in_ecount(64) WCHAR tmp[64], size_t _size) - : count(1), size(_size) - { - memcpy(str, tmp, sizeof(str)); - } - - void Add(size_t _size) const - { - count++; - size += _size; - } - - mutable size_t count; - mutable size_t size; - WCHAR str[64]; - - bool operator<(const StringSetEntry &rhs) const - { - return _wcscmp(str, rhs.str) < 0; - } - }; - - - static bool StringSetCompare(const StringSetEntry &a1, const StringSetEntry &a2) - { - return a1.size < a2.size; - } - - void DumpHeapStrings(sos::GCHeap &gcheap) - { -#ifdef FEATURE_PAL - ExtOut("Not implemented.\n"); -#else - const int offset = sos::Object::GetStringDataOffset(); - typedef std::set<StringSetEntry> Set; - Set set; // A set keyed off of the string's text - - StringSetEntry tmp; // Temp string used to keep track of the set - ULONG fetched = 0; - - TableOutput out(3, POINTERSIZE_HEX, AlignRight); - for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr) - { - if (IsInterrupt()) - break; - - if (itr->IsString() && IsCorrectSize(*itr) && IsCorrectLiveness(*itr)) - { - CLRDATA_ADDRESS addr = itr->GetAddress(); - size_t size = itr->GetSize(); - - if (!mStat) - out.WriteRow(ObjectPtr(addr), Pointer(itr->GetMT()), Decimal(size)); - - // Don't bother calculating the size of the string, just read the full 64 characters of the buffer. The null - // terminator we read will terminate the string. - HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(addr+offset), tmp.str, sizeof(WCHAR)*(_countof(tmp.str)-1), &fetched); - if (SUCCEEDED(hr)) - { - // Ensure we null terminate the string. Note that this will not overrun the buffer as we only - // wrote a max of 63 characters into the 64 character buffer. - tmp.str[fetched/sizeof(WCHAR)] = 0; - Set::iterator sitr = set.find(tmp); - if (sitr == set.end()) - { - tmp.size = size; - tmp.count = 1; - set.insert(tmp); - } - else - { - sitr->Add(size); - } - } - } - } - - ExtOut("\n"); - - // Now flatten the set into a vector. This is much faster than keeping two sets, or using a multimap. - typedef std::vector<StringSetEntry> Vect; - Vect v(set.begin(), set.end()); - std::sort(v.begin(), v.end(), &DumpHeapImpl::StringSetCompare); - - // Now print out the data. The call to Flatten ensures that we don't print newlines to break up the - // output in strange ways. - for (Vect::iterator vitr = v.begin(); vitr != v.end(); ++vitr) - { - if (IsInterrupt()) - break; - - Flatten(vitr->str, (unsigned int)_wcslen(vitr->str)); - out.WriteRow(Decimal(vitr->size), Decimal(vitr->count), vitr->str); - } -#endif // FEATURE_PAL - } - - void DumpHeapShort(sos::GCHeap &gcheap) - { - for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr) - { - if (!Verify(itr)) - return; - - if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr)) - DMLOut("%s\n", DMLObject(itr->GetAddress())); - } - } - - void DumpHeapThinlock(sos::GCHeap &gcheap) - { - int count = 0; - - PrintHeader(); - for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr) - { - if (!Verify(itr)) - return; - - sos::ThinLockInfo lockInfo; - if (IsCorrectType(*itr) && itr->GetThinLock(lockInfo)) - { - DMLOut("%s %s %8d", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize()); - ExtOut(" ThinLock owner %x (%p) Recursive %x\n", lockInfo.ThreadId, - SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion); - - count++; - } - } - - ExtOut("Found %d objects.\n", count); - } - -private: - TADDR mStart, - mStop, - mMT, - mMinSize, - mMaxSize; - - BOOL mStat, - mStrings, - mVerify, - mThinlock, - mShort, - mDML, - mLive, - mDead; - - - WCHAR *mType; - -private: -#if !defined(FEATURE_PAL) - // Windows only - std::unordered_set<TADDR> mLiveness; - typedef std::list<sos::FragmentationBlock> FragmentationList; - FragmentationList mFrag; - - void InitFragmentationList() - { - mFrag.clear(); - } - - void ReportFreeObject(TADDR addr, size_t size, TADDR next, TADDR mt) - { - if (size >= MIN_FRAGMENTATIONBLOCK_BYTES) - mFrag.push_back(sos::FragmentationBlock(addr, size, next, mt)); - } - - void PrintFragmentationReport() - { - if (mFrag.size() > 0) - { - ExtOut("Fragmented blocks larger than 0.5 MB:\n"); - ExtOut("%" POINTERSIZE "s %8s %16s\n", "Addr", "Size", "Followed by"); - - for (FragmentationList::const_iterator itr = mFrag.begin(); itr != mFrag.end(); ++itr) - { - sos::MethodTable mt = itr->GetNextMT(); - ExtOut("%p %6.1fMB " WIN64_8SPACES "%p %S\n", - SOS_PTR(itr->GetAddress()), - ((double)itr->GetSize()) / 1024.0 / 1024.0, - SOS_PTR(itr->GetNextObject()), - mt.GetName()); - } - } - } -#else - void InitFragmentationList() {} - void ReportFreeObject(TADDR, TADDR, size_t, TADDR) {} - void PrintFragmentationReport() {} -#endif -}; - -/**********************************************************************\ -* Routine Description: * -* * -* This function dumps async state machines on GC heap, * -* displaying details about each async operation found. * -* (May not work if GC is in progress.) * -* * -\**********************************************************************/ - -void ResolveContinuation(CLRDATA_ADDRESS* contAddr) -{ - // Ideally this continuation is itself an async method box. - sos::Object contObj = TO_TADDR(*contAddr); - if (GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("StateMachine")) == 0) - { - // It was something else. - - // If it's a standard task continuation, get its task field. - int offset; - if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("m_task"))) != 0) - { - MOVE(*contAddr, contObj.GetAddress() + offset); - if (sos::IsObject(*contAddr, false)) - { - contObj = TO_TADDR(*contAddr); - } - } - else - { - // If it's storing an action wrapper, try to follow to that action's target. - if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("m_action"))) != 0) - { - MOVE(*contAddr, contObj.GetAddress() + offset); - if (sos::IsObject(*contAddr, false)) - { - contObj = TO_TADDR(*contAddr); - } - } - - // If we now have an Action, try to follow through to the delegate's target. - if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_target"))) != 0) - { - MOVE(*contAddr, contObj.GetAddress() + offset); - if (sos::IsObject(*contAddr, false)) - { - contObj = TO_TADDR(*contAddr); - - // In some cases, the delegate's target might be a ContinuationWrapper, in which case we want to unwrap that as well. - if (_wcsncmp(contObj.GetTypeName(), W("System.Runtime.CompilerServices.AsyncMethodBuilderCore+ContinuationWrapper"), 74) == 0 && - (offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_continuation"))) != 0) - { - MOVE(*contAddr, contObj.GetAddress() + offset); - if (sos::IsObject(*contAddr, false)) - { - contObj = TO_TADDR(*contAddr); - if ((offset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_target"))) != 0) - { - MOVE(*contAddr, contObj.GetAddress() + offset); - if (sos::IsObject(*contAddr, false)) - { - contObj = TO_TADDR(*contAddr); - } - } - } - } - } - } - } - - // Use whatever object we ended with. - *contAddr = contObj.GetAddress(); - } -} - -bool TryGetContinuation(CLRDATA_ADDRESS addr, CLRDATA_ADDRESS mt, CLRDATA_ADDRESS* contAddr) -{ - // Get the continuation field from the task. - int offset = GetObjFieldOffset(addr, mt, W("m_continuationObject")); - if (offset != 0) - { - DWORD_PTR contObjPtr; - MOVE(contObjPtr, addr + offset); - if (sos::IsObject(contObjPtr, false)) - { - *contAddr = TO_CDADDR(contObjPtr); - ResolveContinuation(contAddr); - return true; - } - } - - return false; -} - -struct AsyncRecord -{ - CLRDATA_ADDRESS Address; - CLRDATA_ADDRESS MT; - DWORD Size; - CLRDATA_ADDRESS StateMachineAddr; - CLRDATA_ADDRESS StateMachineMT; - BOOL FilteredByOptions; - BOOL IsStateMachine; - BOOL IsValueType; - BOOL IsTopLevel; - int TaskStateFlags; - int StateValue; - std::vector<CLRDATA_ADDRESS> Continuations; -}; - -bool AsyncRecordIsCompleted(AsyncRecord& ar) -{ - const int TASK_STATE_COMPLETED_MASK = 0x1600000; - return (ar.TaskStateFlags & TASK_STATE_COMPLETED_MASK) != 0; -} - -const char* GetAsyncRecordStatusDescription(AsyncRecord& ar) -{ - const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000; - const int TASK_STATE_FAULTED = 0x200000; - const int TASK_STATE_CANCELED = 0x400000; - - if ((ar.TaskStateFlags & TASK_STATE_RAN_TO_COMPLETION) != 0) return "Success"; - if ((ar.TaskStateFlags & TASK_STATE_FAULTED) != 0) return "Failed"; - if ((ar.TaskStateFlags & TASK_STATE_CANCELED) != 0) return "Canceled"; - return "Pending"; -} - -void ExtOutTaskDelegateMethod(sos::Object& obj) -{ - DacpFieldDescData actionField; - int offset = GetObjFieldOffset(obj.GetAddress(), obj.GetMT(), W("m_action"), TRUE, &actionField); - if (offset != 0) - { - CLRDATA_ADDRESS actionAddr; - MOVE(actionAddr, obj.GetAddress() + offset); - CLRDATA_ADDRESS actionMD; - if (actionAddr != NULL && TryGetMethodDescriptorForDelegate(actionAddr, &actionMD)) - { - NameForMD_s((DWORD_PTR)actionMD, g_mdName, mdNameLen); - ExtOut("(%S) ", g_mdName); - } - } -} - -void ExtOutTaskStateFlagsDescription(int stateFlags) -{ - if (stateFlags == 0) return; - - ExtOut("State Flags: "); - - // TaskCreationOptions.* - if ((stateFlags & 0x01) != 0) ExtOut("PreferFairness "); - if ((stateFlags & 0x02) != 0) ExtOut("LongRunning "); - if ((stateFlags & 0x04) != 0) ExtOut("AttachedToParent "); - if ((stateFlags & 0x08) != 0) ExtOut("DenyChildAttach "); - if ((stateFlags & 0x10) != 0) ExtOut("HideScheduler "); - if ((stateFlags & 0x40) != 0) ExtOut("RunContinuationsAsynchronously "); - - // InternalTaskOptions.* - if ((stateFlags & 0x0200) != 0) ExtOut("ContinuationTask "); - if ((stateFlags & 0x0400) != 0) ExtOut("PromiseTask "); - if ((stateFlags & 0x1000) != 0) ExtOut("LazyCancellation "); - if ((stateFlags & 0x2000) != 0) ExtOut("QueuedByRuntime "); - if ((stateFlags & 0x4000) != 0) ExtOut("DoNotDispose "); - - // TASK_STATE_* - if ((stateFlags & 0x10000) != 0) ExtOut("STARTED "); - if ((stateFlags & 0x20000) != 0) ExtOut("DELEGATE_INVOKED "); - if ((stateFlags & 0x40000) != 0) ExtOut("DISPOSED "); - if ((stateFlags & 0x80000) != 0) ExtOut("EXCEPTIONOBSERVEDBYPARENT "); - if ((stateFlags & 0x100000) != 0) ExtOut("CANCELLATIONACKNOWLEDGED "); - if ((stateFlags & 0x200000) != 0) ExtOut("FAULTED "); - if ((stateFlags & 0x400000) != 0) ExtOut("CANCELED "); - if ((stateFlags & 0x800000) != 0) ExtOut("WAITING_ON_CHILDREN "); - if ((stateFlags & 0x1000000) != 0) ExtOut("RAN_TO_COMPLETION "); - if ((stateFlags & 0x2000000) != 0) ExtOut("WAITINGFORACTIVATION "); - if ((stateFlags & 0x4000000) != 0) ExtOut("COMPLETION_RESERVED "); - if ((stateFlags & 0x8000000) != 0) ExtOut("THREAD_WAS_ABORTED "); - if ((stateFlags & 0x10000000) != 0) ExtOut("WAIT_COMPLETION_NOTIFICATION "); - if ((stateFlags & 0x20000000) != 0) ExtOut("EXECUTIONCONTEXT_IS_NULL "); - if ((stateFlags & 0x40000000) != 0) ExtOut("TASKSCHEDULED_WAS_FIRED "); - - ExtOut("\n"); -} - -void ExtOutStateMachineFields(AsyncRecord& ar) -{ - DacpMethodTableData mtabledata; - DacpMethodTableFieldData vMethodTableFields; - if (mtabledata.Request(g_sos, ar.StateMachineMT) == S_OK && - vMethodTableFields.Request(g_sos, ar.StateMachineMT) == S_OK && - vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0) - { - DisplayFields(ar.StateMachineMT, &mtabledata, &vMethodTableFields, (DWORD_PTR)ar.StateMachineAddr, TRUE, ar.IsValueType); - } -} - -void FindStateMachineTypes(DWORD_PTR* corelibModule, mdTypeDef* stateMachineBox, mdTypeDef* debugStateMachineBox) -{ - int numModule; - ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(const_cast<LPSTR>("System.Private.CoreLib.dll"), &numModule); - if (moduleList != NULL && numModule == 1) - { - *corelibModule = moduleList[0]; - GetInfoFromName(*corelibModule, "System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1", stateMachineBox); - GetInfoFromName(*corelibModule, "System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+DebugFinalizableAsyncStateMachineBox`1", debugStateMachineBox); - } - else - { - *corelibModule = 0; - *stateMachineBox = 0; - *debugStateMachineBox = 0; - } -} - -DECLARE_API(DumpAsync) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - if (!g_snapshot.Build()) - { - ExtOut("Unable to build snapshot of the garbage collector state\n"); - return E_FAIL; - } - - try - { - // Process command-line arguments. - size_t nArg = 0; - TADDR mt = NULL, addr = NULL; - ArrayHolder<char> ansiType = NULL; - ArrayHolder<WCHAR> type = NULL; - BOOL dml = FALSE, includeCompleted = FALSE, includeStacks = FALSE, includeRoots = FALSE, includeAllTasks = FALSE, dumpFields = FALSE; - CMDOption option[] = - { // name, vptr, type, hasValue - { "-addr", &addr, COHEX, TRUE }, // dump only the async object at the specified address - { "-mt", &mt, COHEX, TRUE }, // dump only async objects with a given MethodTable - { "-type", &ansiType, COSTRING, TRUE }, // dump only async objects that contain the specified type substring - { "-tasks", &includeAllTasks, COBOOL, FALSE }, // include all tasks that can be found on the heap, not just async methods - { "-completed", &includeCompleted, COBOOL, FALSE }, // include async objects that are in a completed state - { "-fields", &dumpFields, COBOOL, FALSE }, // show relevant fields of found async objects - { "-stacks", &includeStacks, COBOOL, FALSE }, // gather and output continuation/stack information - { "-roots", &includeRoots, COBOOL, FALSE }, // gather and output GC root information -#ifndef FEATURE_PAL - { "/d", &dml, COBOOL, FALSE }, // Debugger Markup Language -#endif - }; - if (!GetCMDOption(args, option, _countof(option), NULL, 0, &nArg) || nArg != 0) - { - sos::Throw<sos::Exception>( - "Usage: DumpAsync [-addr ObjectAddr] [-mt MethodTableAddr] [-type TypeName] [-tasks] [-completed] [-fields] [-stacks] [-roots]\n" - "[-addr ObjectAddr] => Only display the async object at the specified address.\n" - "[-mt MethodTableAddr] => Only display top-level async objects with the specified method table address.\n" - "[-type TypeName] => Only display top-level async objects whose type name includes the specified substring.\n" - "[-tasks] => Include Task and Task-derived objects, in addition to any state machine objects found.\n" - "[-completed] => Include async objects that represent completed operations but that are still on the heap.\n" - "[-fields] => Show the fields of state machines.\n" - "[-stacks] => Gather, output, and consolidate based on continuation chains / async stacks for discovered async objects.\n" - "[-roots] => Perform a gcroot on each rendered async object.\n" - ); - } - if (ansiType != NULL) - { - size_t ansiTypeLen = strlen(ansiType) + 1; - type = new WCHAR[ansiTypeLen]; - MultiByteToWideChar(CP_ACP, 0, ansiType, -1, type, (int)ansiTypeLen); - } - - EnableDMLHolder dmlHolder(dml); - BOOL hasTypeFilter = mt != NULL || ansiType != NULL || addr != NULL; - - // Display a message if the heap isn't verified. - sos::GCHeap gcheap; - if (!gcheap.AreGCStructuresValid()) - { - DisplayInvalidStructuresMessage(); - } - - // Find the state machine types - DWORD_PTR corelibModule; - mdTypeDef stateMachineBoxMd, debugStateMachineBoxMd; - FindStateMachineTypes(&corelibModule, &stateMachineBoxMd, &debugStateMachineBoxMd); - - // Walk each heap object looking for async state machine objects. As we're targeting .NET Core 2.1+, all such objects - // will be Task or Task-derived types. - std::map<CLRDATA_ADDRESS, AsyncRecord> asyncRecords; - for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr) - { - // Skip objects too small to be state machines or tasks, avoiding some compiler-generated caching data structures. - if (itr->GetSize() <= 24) - { - continue; - } - - // Match only async objects. - if (includeAllTasks) - { - // If the user has selected to include all tasks and not just async state machine boxes, we simply need to validate - // that this is Task or Task-derived, and if it's not, skip it. - if (!IsDerivedFrom(itr->GetMT(), W("System.Threading.Tasks.Task"))) - { - continue; - } - } - else - { - // Otherwise, we only care about AsyncStateMachineBox`1 as well as the DebugFinalizableAsyncStateMachineBox`1 - // that's used when certain ETW events are set. - DacpMethodTableData mtdata; - if (mtdata.Request(g_sos, TO_TADDR(itr->GetMT())) != S_OK || - mtdata.Module != corelibModule || - (mtdata.cl != stateMachineBoxMd && mtdata.cl != debugStateMachineBoxMd)) - { - continue; - } - } - - // Create an AsyncRecord to store the state for this instance. We're likely going to keep the object at this point, - // though we may still discard/skip it with a few checks later; to do that, though, we'll need some of the info - // gathered here, so we construct the record to store the data. - AsyncRecord ar; - ar.Address = itr->GetAddress(); - ar.MT = itr->GetMT(); - ar.Size = (DWORD)itr->GetSize(); - ar.StateMachineAddr = itr->GetAddress(); - ar.StateMachineMT = itr->GetMT(); - ar.IsValueType = false; - ar.IsTopLevel = true; - ar.IsStateMachine = false; - ar.TaskStateFlags = 0; - ar.StateValue = 0; - ar.FilteredByOptions = // we process all objects to support forming proper chains, but then only display ones that match the user's request - (mt == NULL || mt == itr->GetMT()) && // Match only MTs the user requested. - (type == NULL || _wcsstr(itr->GetTypeName(), type) != NULL) && // Match only type name substrings the user requested. - (addr == NULL || addr == itr->GetAddress()); // Match only the object at the specified address. - - // Get the state flags for the task. This is used to determine whether async objects are completed (and thus should - // be culled by default). It avoids our needing to depend on interpreting the compiler's "<>1__state" field, and also lets - // us display state information for non-async state machine objects. - DacpFieldDescData stateFlagsField; - int offset = GetObjFieldOffset(ar.Address, ar.MT, W("m_stateFlags"), TRUE, &stateFlagsField); - if (offset != 0) - { - MOVE(ar.TaskStateFlags, ar.Address + offset); - } - - // Get the async state machine object's StateMachine field. - DacpFieldDescData stateMachineField; - int stateMachineFieldOffset = GetObjFieldOffset(TO_CDADDR(itr->GetAddress()), itr->GetMT(), W("StateMachine"), TRUE, &stateMachineField); - if (stateMachineFieldOffset != 0) - { - ar.IsStateMachine = true; - ar.IsValueType = stateMachineField.Type == ELEMENT_TYPE_VALUETYPE; - - // Get the address and method table of the state machine. While it'll generally be a struct, it is valid for it to be a - // class (the C# compiler generates a class in debug builds to better support Edit-And-Continue), so we accommodate both. - DacpFieldDescData stateField; - int stateFieldOffset = -1; - if (ar.IsValueType) - { - ar.StateMachineAddr = itr->GetAddress() + stateMachineFieldOffset; - ar.StateMachineMT = stateMachineField.MTOfType; - stateFieldOffset = GetValueFieldOffset(ar.StateMachineMT, W("<>1__state"), &stateField); - } - else - { - MOVE(ar.StateMachineAddr, itr->GetAddress() + stateMachineFieldOffset); - DacpObjectData objData; - if (objData.Request(g_sos, ar.StateMachineAddr) == S_OK) - { - ar.StateMachineMT = objData.MethodTable; // update from Canon to actual type - stateFieldOffset = GetObjFieldOffset(ar.StateMachineAddr, ar.StateMachineMT, W("<>1__state"), TRUE, &stateField); - } - } - - if (stateFieldOffset >= 0 && (ar.IsValueType || stateFieldOffset != 0)) - { - MOVE(ar.StateValue, ar.StateMachineAddr + stateFieldOffset); - } - } - - // If we only want to include incomplete async objects, skip this one if it's completed. - if (!includeCompleted && AsyncRecordIsCompleted(ar)) - { - continue; - } - - // If the user has asked to include "async stacks" information, resolve any continuation - // that might be registered with it. This could be a single continuation, or it could - // be a list of continuations in the case of the same task being awaited multiple times. - CLRDATA_ADDRESS nextAddr; - if (includeStacks && TryGetContinuation(itr->GetAddress(), itr->GetMT(), &nextAddr)) - { - sos::Object contObj = TO_TADDR(nextAddr); - if (_wcsncmp(contObj.GetTypeName(), W("System.Collections.Generic.List`1"), 33) == 0) - { - // The continuation is a List<object>. Iterate through its internal object[] - // looking for non-null objects, and adding each one as a continuation. - int itemsOffset = GetObjFieldOffset(contObj.GetAddress(), contObj.GetMT(), W("_items")); - if (itemsOffset != 0) - { - DWORD_PTR listItemsPtr; - MOVE(listItemsPtr, contObj.GetAddress() + itemsOffset); - if (sos::IsObject(listItemsPtr, false)) - { - DacpObjectData objData; - if (objData.Request(g_sos, TO_CDADDR(listItemsPtr)) == S_OK && objData.ObjectType == OBJ_ARRAY) - { - for (SIZE_T i = 0; i < objData.dwNumComponents; i++) - { - CLRDATA_ADDRESS elementPtr; - MOVE(elementPtr, TO_CDADDR(objData.ArrayDataPtr + (i * objData.dwComponentSize))); - if (elementPtr != NULL && sos::IsObject(elementPtr, false)) - { - ResolveContinuation(&elementPtr); - ar.Continuations.push_back(elementPtr); - } - } - } - } - } - } - else - { - ar.Continuations.push_back(contObj.GetAddress()); - } - } - - // We've gathered all of the needed information for this heap object. Add it to our list of async records. - asyncRecords.insert(std::pair<CLRDATA_ADDRESS, AsyncRecord>(ar.Address, ar)); - } - - // As with DumpHeap, output a summary table about all of the objects we found. In contrast, though, his is based on the filtered - // list of async records we gathered rather than everything on the heap. - if (addr == NULL) // no point in stats if we're only targeting a single object - { - HeapStat stats; - for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt) - { - if (!hasTypeFilter || arIt->second.FilteredByOptions) - { - stats.Add((DWORD_PTR)arIt->second.MT, (DWORD)arIt->second.Size); - } - } - stats.Sort(); - stats.Print(); - } - - // If the user has asked for "async stacks" and if there's not MT/type name filter, look through all of our async records - // to find the "top-level" nodes that start rather than that are a part of a continuation chain. When we then iterate through - // async records, we only print ones out that are still classified as top-level. We don't do this if there's a type filter - // because in that case we consider those and only those objects to be top-level. - if (includeStacks && !hasTypeFilter) - { - size_t uniqueChains = asyncRecords.size(); - for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt) - { - for (std::vector<CLRDATA_ADDRESS>::iterator contIt = arIt->second.Continuations.begin(); contIt != arIt->second.Continuations.end(); ++contIt) - { - std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator found = asyncRecords.find(*contIt); - if (found != asyncRecords.end()) - { - if (found->second.IsTopLevel) - { - found->second.IsTopLevel = false; - uniqueChains--; - } - } - } - } - - ExtOut("In %d chains.\n", uniqueChains); - } - - // Print out header for the main line of each result. - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s ", "Address", "MT", "Size"); - if (includeCompleted) ExtOut("%8s ", "Status"); - ExtOut("%10s %s\n", "State", "Description"); - - // Output each top-level async record. - int counter = 0; - for (std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator arIt = asyncRecords.begin(); arIt != asyncRecords.end(); ++arIt) - { - if (!arIt->second.IsTopLevel || (hasTypeFilter && !arIt->second.FilteredByOptions)) - { - continue; - } - - // Output the state machine's details as a single line. - sos::Object obj = TO_TADDR(arIt->second.Address); - if (arIt->second.IsStateMachine) - { - // This has a StateMachine. Output its details. - sos::MethodTable mt = TO_TADDR(arIt->second.StateMachineMT); - DMLOut("%s %s %8d ", DMLAsync(obj.GetAddress()), DMLDumpHeapMT(obj.GetMT()), obj.GetSize()); - if (includeCompleted) ExtOut("%8s ", GetAsyncRecordStatusDescription(arIt->second)); - ExtOut("%10d %S\n", arIt->second.StateValue, mt.GetName()); - if (dumpFields) ExtOutStateMachineFields(arIt->second); - } - else - { - // This does not have a StateMachine. Output the details of the Task itself. - DMLOut("%s %s %8d ", DMLAsync(obj.GetAddress()), DMLDumpHeapMT(obj.GetMT()), obj.GetSize()); - if (includeCompleted) ExtOut("%8s ", GetAsyncRecordStatusDescription(arIt->second)); - ExtOut("[%08x] %S ", arIt->second.TaskStateFlags, obj.GetTypeName()); - ExtOutTaskDelegateMethod(obj); - ExtOut("\n"); - if (dumpFields) ExtOutTaskStateFlagsDescription(arIt->second.TaskStateFlags); - } - - // If we gathered any continuations for this record, output the chains now. - if (includeStacks && arIt->second.Continuations.size() > 0) - { - ExtOut(includeAllTasks ? "Continuation chains:\n" : "Async \"stack\":\n"); - std::vector<std::pair<int, CLRDATA_ADDRESS>> continuationChainToExplore; - continuationChainToExplore.push_back(std::pair<int, CLRDATA_ADDRESS>(1, obj.GetAddress())); - - // Do a depth-first traversal of continuations, outputting each continuation found and then - // looking in our gathered objects list for its continuations. - std::set<CLRDATA_ADDRESS> seen; - while (continuationChainToExplore.size() > 0) - { - // Pop the next continuation from the stack. - std::pair<int, CLRDATA_ADDRESS> cur = continuationChainToExplore.back(); - continuationChainToExplore.pop_back(); - - // Get the async record for this continuation. It should be one we already know about. - std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator curAsyncRecord = asyncRecords.find(cur.second); - if (curAsyncRecord == asyncRecords.end()) - { - continue; - } - - // Make sure to avoid cycles in the rare case where async records may refer to each other. - if (seen.find(cur.second) != seen.end()) - { - continue; - } - seen.insert(cur.second); - - // Iterate through all continuations from this object. - for (std::vector<CLRDATA_ADDRESS>::iterator contIt = curAsyncRecord->second.Continuations.begin(); contIt != curAsyncRecord->second.Continuations.end(); ++contIt) - { - sos::Object cont = TO_TADDR(*contIt); - - // Print out the depth of the continuation with dots, then its address. - for (int i = 0; i < cur.first; i++) ExtOut("."); - DMLOut("%s ", DMLObject(cont.GetAddress())); - - // Print out the name of the method for this task's delegate if it has one (state machines won't, but others tasks may). - ExtOutTaskDelegateMethod(cont); - - // Find the async record for this continuation, and output its name. If it's a state machine, - // also output its current state value so that a user can see at a glance its status. - std::map<CLRDATA_ADDRESS, AsyncRecord>::iterator contAsyncRecord = asyncRecords.find(cont.GetAddress()); - if (contAsyncRecord != asyncRecords.end()) - { - sos::MethodTable contMT = TO_TADDR(contAsyncRecord->second.StateMachineMT); - if (contAsyncRecord->second.IsStateMachine) ExtOut("(%d) ", contAsyncRecord->second.StateValue); - ExtOut("%S\n", contMT.GetName()); - if (contAsyncRecord->second.IsStateMachine && dumpFields) ExtOutStateMachineFields(contAsyncRecord->second); - } - else - { - ExtOut("%S\n", cont.GetTypeName()); - } - - // Add this continuation to the stack to explore. - continuationChainToExplore.push_back(std::pair<int, CLRDATA_ADDRESS>(cur.first + 1, *contIt)); - } - } - } - - // Finally, output gcroots, as they can serve as alternative/more detailed "async stacks", and also help to highlight - // state machines that aren't being kept alive. However, they're more expensive to compute, so they're opt-in. - if (includeRoots) - { - ExtOut("GC roots:\n"); - IncrementIndent(); - GCRootImpl gcroot; - int numRoots = gcroot.PrintRootsForObject(obj.GetAddress(), FALSE, FALSE); - DecrementIndent(); - if (numRoots == 0 && !AsyncRecordIsCompleted(arIt->second)) - { - ExtOut("Incomplete state machine or task with 0 roots.\n"); - } - } - - // If we're rendering more than one line per entry, output a separator to help distinguish the entries. - if (dumpFields || includeStacks || includeRoots) - { - ExtOut("--------------------------------------------------------------------------------\n"); - } - } - - return S_OK; - } - catch (const sos::Exception &e) - { - ExtOut("%s\n", e.what()); - return E_FAIL; - } -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function dumps all objects on GC heap. It also displays * -* statistics of objects. If GC heap is corrupted, it will stop at -* the bad place. (May not work if GC is in progress.) * -* * -\**********************************************************************/ -DECLARE_API(DumpHeap) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - if (!g_snapshot.Build()) - { - ExtOut("Unable to build snapshot of the garbage collector state\n"); - return E_FAIL; - } - - try - { - DumpHeapImpl dumpHeap(args); - dumpHeap.Run(); - - return S_OK; - } - catch(const sos::Exception &e) - { - ExtOut("%s\n", e.what()); - return E_FAIL; - } -} - -DECLARE_API(VerifyHeap) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - if (!g_snapshot.Build()) - { - ExtOut("Unable to build snapshot of the garbage collector state\n"); - return E_FAIL; - } - - try - { - bool succeeded = true; - char buffer[1024]; - sos::GCHeap gcheap; - sos::ObjectIterator itr = gcheap.WalkHeap(); - - while (itr) - { - if (itr.Verify(buffer, _countof(buffer))) - { - ++itr; - } - else - { - succeeded = false; - ExtOut(buffer); - itr.MoveToNextObjectCarefully(); - } - } - - if (!DumpHeapImpl::ValidateSyncTable(gcheap)) - succeeded = false; - - if (succeeded) - ExtOut("No heap corruption detected.\n"); - - return S_OK; - } - catch(const sos::Exception &e) - { - ExtOut("%s\n", e.what()); - return E_FAIL; - } -} - -#ifndef FEATURE_PAL - -enum failure_get_memory -{ - fgm_no_failure = 0, - fgm_reserve_segment = 1, - fgm_commit_segment_beg = 2, - fgm_commit_eph_segment = 3, - fgm_grow_table = 4, - fgm_commit_table = 5 -}; - -enum oom_reason -{ - oom_no_failure = 0, - oom_budget = 1, - oom_cant_commit = 2, - oom_cant_reserve = 3, - oom_loh = 4, - oom_low_mem = 5, - oom_unproductive_full_gc = 6 -}; - -static const char *const str_oom[] = -{ - "There was no managed OOM due to allocations on the GC heap", // oom_no_failure - "This is likely to be a bug in GC", // oom_budget - "Didn't have enough memory to commit", // oom_cant_commit - "This is likely to be a bug in GC", // oom_cant_reserve - "Didn't have enough memory to allocate an LOH segment", // oom_loh - "Low on memory during GC", // oom_low_mem - "Could not do a full GC" // oom_unproductive_full_gc -}; - -static const char *const str_fgm[] = -{ - "There was no failure to allocate memory", // fgm_no_failure - "Failed to reserve memory", // fgm_reserve_segment - "Didn't have enough memory to commit beginning of the segment", // fgm_commit_segment_beg - "Didn't have enough memory to commit the new ephemeral segment", // fgm_commit_eph_segment - "Didn't have enough memory to grow the internal GC data structures", // fgm_grow_table - "Didn't have enough memory to commit the internal GC data structures", // fgm_commit_table -}; - -void PrintOOMInfo(DacpOomData* oomData) -{ - ExtOut("Managed OOM occurred after GC #%d (Requested to allocate %d bytes)\n", - oomData->gc_index, oomData->alloc_size); - - if ((oomData->reason == oom_budget) || - (oomData->reason == oom_cant_reserve)) - { - // TODO: This message needs to be updated with more precious info. - ExtOut("%s, please contact PSS\n", str_oom[oomData->reason]); - } - else - { - ExtOut("Reason: %s\n", str_oom[oomData->reason]); - } - - // Now print out the more detailed memory info if any. - if (oomData->fgm != fgm_no_failure) - { - ExtOut("Detail: %s: %s (%d bytes)", - (oomData->loh_p ? "LOH" : "SOH"), - str_fgm[oomData->fgm], - oomData->size); - - if ((oomData->fgm == fgm_commit_segment_beg) || - (oomData->fgm == fgm_commit_eph_segment) || - (oomData->fgm == fgm_grow_table) || - (oomData->fgm == fgm_commit_table)) - { - // If it's a commit error (fgm_grow_table can indicate a reserve - // or a commit error since we make one VirtualAlloc call to - // reserve and commit), we indicate the available commit - // space if we recorded it. - if (oomData->available_pagefile_mb) - { - ExtOut(" - on GC entry available commit space was %d MB", - oomData->available_pagefile_mb); - } - } - - ExtOut("\n"); - } -} - -DECLARE_API(AnalyzeOOM) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - -#ifndef FEATURE_PAL - - if (!InitializeHeapData ()) - { - ExtOut("GC Heap not initialized yet.\n"); - return S_OK; - } - - BOOL bHasManagedOOM = FALSE; - DacpOomData oomData; - memset (&oomData, 0, sizeof(oomData)); - if (!IsServerBuild()) - { - if (oomData.Request(g_sos) != S_OK) - { - ExtOut("Error requesting OOM data\n"); - return E_FAIL; - } - if (oomData.reason != oom_no_failure) - { - bHasManagedOOM = TRUE; - PrintOOMInfo(&oomData); - } - } - else - { - DWORD dwNHeaps = GetGcHeapCount(); - DWORD dwAllocSize; - if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize)) - { - ExtOut("Failed to get GCHeaps: integer overflow\n"); - return Status; - } - - CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize); - if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK) - { - ExtOut("Failed to get GCHeaps\n"); - return Status; - } - - for (DWORD n = 0; n < dwNHeaps; n ++) - { - if (oomData.Request(g_sos, heapAddrs[n]) != S_OK) - { - ExtOut("Heap %d: Error requesting OOM data\n", n); - return E_FAIL; - } - if (oomData.reason != oom_no_failure) - { - if (!bHasManagedOOM) - { - bHasManagedOOM = TRUE; - } - ExtOut("---------Heap %#-2d---------\n", n); - PrintOOMInfo(&oomData); - } - } - } - - if (!bHasManagedOOM) - { - ExtOut("%s\n", str_oom[oomData.reason]); - } - - return S_OK; -#else - _ASSERTE(false); - return E_FAIL; -#endif // FEATURE_PAL -} - -DECLARE_API(VerifyObj) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - TADDR taddrObj = 0; - TADDR taddrMT; - size_t objSize; - - BOOL bValid = FALSE; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&taddrObj, COHEX} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - BOOL bContainsPointers; - - if (FAILED(GetMTOfObject(taddrObj, &taddrMT)) || - !GetSizeEfficient(taddrObj, taddrMT, FALSE, objSize, bContainsPointers)) - { - ExtOut("object %#p does not have valid method table\n", SOS_PTR(taddrObj)); - goto Exit; - } - - // we need to build g_snapshot as it is later used in GetGeneration - if (!g_snapshot.Build()) - { - ExtOut("Unable to build snapshot of the garbage collector state\n"); - goto Exit; - } - { - DacpGcHeapDetails *pheapDetails = g_snapshot.GetHeap(taddrObj); - bValid = VerifyObject(*pheapDetails, taddrObj, taddrMT, objSize, TRUE); - } - -Exit: - if (bValid) - { - ExtOut("object %#p is a valid object\n", SOS_PTR(taddrObj)); - } - - return Status; -} - -void LNODisplayOutput(LPCWSTR tag, TADDR pMT, TADDR currentObj, size_t size) -{ - sos::Object obj(currentObj, pMT); - DMLOut("%S %s %12d (0x%x)\t%S\n", tag, DMLObject(currentObj), size, size, obj.GetTypeName()); -} - -DECLARE_API(ListNearObj) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - -#if !defined(FEATURE_PAL) - - TADDR taddrArg = 0; - TADDR taddrObj = 0; - // we may want to provide a more exact version of searching for the - // previous object in the heap, using the brick table, instead of - // looking for what may be valid method tables... - //BOOL bExact; - //CMDOption option[] = - //{ - // // name, vptr, type, hasValue - // {"-exact", &bExact, COBOOL, FALSE} - //}; - - BOOL dml = FALSE; - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE}, - }; - CMDValue arg[] = - { - // vptr, type - {&taddrArg, COHEX} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || nArg != 1) - { - ExtOut("Usage: !ListNearObj <obj_address>\n"); - return Status; - } - - EnableDMLHolder dmlHolder(dml); - - if (!g_snapshot.Build()) - { - ExtOut("Unable to build snapshot of the garbage collector state\n"); - return Status; - } - - taddrObj = Align(taddrArg); - - DacpGcHeapDetails *heap = g_snapshot.GetHeap(taddrArg); - if (heap == NULL) - { - ExtOut("Address %p does not lie in the managed heap\n", SOS_PTR(taddrObj)); - return Status; - } - - TADDR_SEGINFO trngSeg = {0, 0, 0}; - TADDR_RANGE allocCtx = {0, 0}; - BOOL bLarge; - int gen; - if (!GCObjInHeap(taddrObj, *heap, trngSeg, gen, allocCtx, bLarge)) - { - ExtOut("Failed to find the segment of the managed heap where the object %p resides\n", - SOS_PTR(taddrObj)); - return Status; - } - - TADDR objMT = NULL; - size_t objSize = 0; - BOOL bObj = FALSE; - TADDR taddrCur; - TADDR curMT = 0; - size_t curSize = 0; - BOOL bCur = FALSE; - TADDR taddrNxt; - TADDR nxtMT = 0; - size_t nxtSize = 0; - BOOL bNxt = FALSE; - BOOL bContainsPointers; - - std::vector<TADDR> candidate; - candidate.reserve(10); - - // since we'll be reading back I'll prime the read cache to a buffer before the current address - MOVE(taddrCur, _max(trngSeg.start, taddrObj-DT_OS_PAGE_SIZE)); - - // ===== Look for a good candidate preceeding taddrObj - - for (taddrCur = taddrObj - sizeof(TADDR); taddrCur >= trngSeg.start; taddrCur -= sizeof(TADDR)) - { - // currently we don't pay attention to allocation contexts. if this - // proves to be an issue we need to reconsider the code below - if (SUCCEEDED(GetMTOfObject(taddrCur, &curMT)) && - GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers)) - { - // remember this as one of the possible "good" objects preceeding taddrObj - candidate.push_back(taddrCur); - - std::vector<TADDR>::iterator it = - std::find(candidate.begin(), candidate.end(), taddrCur+curSize); - if (it != candidate.end()) - { - // We found a chain of two objects preceeding taddrObj. We'll - // trust this is a good indication that the two objects are valid. - // What is not valid is possibly the object following the second - // one... - taddrCur = *it; - GetMTOfObject(taddrCur, &curMT); - GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers); - bCur = TRUE; - break; - } - } - } - - if (!bCur && !candidate.empty()) - { - // pick the closest object to taddrObj - taddrCur = *(candidate.begin()); - GetMTOfObject(taddrCur, &curMT); - GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers); - // we have a candidate, even if not confirmed - bCur = TRUE; - } - - taddrNxt = taddrObj; - if (taddrArg == taddrObj) - { - taddrNxt += sizeof(TADDR); - } - - // ===== Now look at taddrObj - if (taddrObj == taddrArg) - { - // only look at taddrObj if it's the same as what user passed in, meaning it's aligned. - if (SUCCEEDED(GetMTOfObject(taddrObj, &objMT)) && - GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers)) - { - bObj = TRUE; - taddrNxt = taddrObj+objSize; - } - } - - if ((taddrCur + curSize > taddrArg) && taddrCur + curSize < trngSeg.end) - { - if (SUCCEEDED(GetMTOfObject(taddrCur + curSize, &nxtMT)) && - GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers)) - { - taddrNxt = taddrCur+curSize; - } - } - - // ===== And finally move on to elements following taddrObj - - for (; taddrNxt < trngSeg.end; taddrNxt += sizeof(TADDR)) - { - if (SUCCEEDED(GetMTOfObject(taddrNxt, &nxtMT)) && - GetSizeEfficient(taddrNxt, nxtMT, bLarge, nxtSize, bContainsPointers)) - { - bNxt = TRUE; - break; - } - } - - if (bCur) - LNODisplayOutput(W("Before: "), curMT, taddrCur, curSize); - else - ExtOut("Before: couldn't find any object between %#p and %#p\n", - SOS_PTR(trngSeg.start), SOS_PTR(taddrArg)); - - if (bObj) - LNODisplayOutput(W("Current:"), objMT, taddrObj, objSize); - - if (bNxt) - LNODisplayOutput(W("After: "), nxtMT, taddrNxt, nxtSize); - else - ExtOut("After: couldn't find any object between %#p and %#p\n", - SOS_PTR(taddrArg), SOS_PTR(trngSeg.end)); - - if (bCur && bNxt && - (((taddrCur+curSize == taddrObj) && (taddrObj+objSize == taddrNxt)) || (taddrCur+curSize == taddrNxt))) - { - ExtOut("Heap local consistency confirmed.\n"); - } - else - { - ExtOut("Heap local consistency not confirmed.\n"); - } - - return Status; - -#else - - _ASSERTE(false); - return E_FAIL; - -#endif // FEATURE_PAL -} - - -DECLARE_API(GCHeapStat) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - -#ifndef FEATURE_PAL - - BOOL bIncUnreachable = FALSE; - BOOL dml = FALSE; - - CMDOption option[] = { - // name, vptr, type, hasValue - {"-inclUnrooted", &bIncUnreachable, COBOOL, FALSE}, - {"-iu", &bIncUnreachable, COBOOL, FALSE}, - {"/d", &dml, COBOOL, FALSE} - }; - - if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - ExtOut("%-8s %12s %12s %12s %12s\n", "Heap", "Gen0", "Gen1", "Gen2", "LOH"); - - if (!IsServerBuild()) - { - float tempf; - DacpGcHeapDetails heapDetails; - if (heapDetails.Request(g_sos) != S_OK) - { - ExtErr("Error requesting gc heap details\n"); - return Status; - } - - HeapUsageStat hpUsage; - if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage)) - { - ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", 0, - hpUsage.genUsage[0].allocd, hpUsage.genUsage[1].allocd, - hpUsage.genUsage[2].allocd, hpUsage.genUsage[3].allocd); - ExtOut("\nFree space: Percentage\n"); - ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u ", 0, - hpUsage.genUsage[0].freed, hpUsage.genUsage[1].freed, - hpUsage.genUsage[2].freed, hpUsage.genUsage[3].freed); - tempf = ((float)(hpUsage.genUsage[0].freed+hpUsage.genUsage[1].freed+hpUsage.genUsage[2].freed)) / - (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd); - ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf), - (int)(100*((float)hpUsage.genUsage[3].freed) / (hpUsage.genUsage[3].allocd))); - if (bIncUnreachable) - { - ExtOut("\nUnrooted objects: Percentage\n"); - ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u ", 0, - hpUsage.genUsage[0].unrooted, hpUsage.genUsage[1].unrooted, - hpUsage.genUsage[2].unrooted, hpUsage.genUsage[3].unrooted); - tempf = ((float)(hpUsage.genUsage[0].unrooted+hpUsage.genUsage[1].unrooted+hpUsage.genUsage[2].unrooted)) / - (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd); - ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf), - (int)(100*((float)hpUsage.genUsage[3].unrooted) / (hpUsage.genUsage[3].allocd))); - } - } - } - else - { - float tempf; - DacpGcHeapData gcheap; - if (gcheap.Request(g_sos) != S_OK) - { - ExtErr("Error requesting GC Heap data\n"); - return Status; - } - - DWORD dwAllocSize; - DWORD dwNHeaps = gcheap.HeapCount; - if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize)) - { - ExtErr("Failed to get GCHeaps: integer overflow\n"); - return Status; - } - - CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize); - if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK) - { - ExtErr("Failed to get GCHeaps\n"); - return Status; - } - - ArrayHolder<HeapUsageStat> hpUsage = new NOTHROW HeapUsageStat[dwNHeaps]; - if (hpUsage == NULL) - { - ReportOOM(); - return Status; - } - - // aggregate stats across heaps / generation - GenUsageStat genUsageStat[4] = {0, 0, 0, 0}; - - for (DWORD n = 0; n < dwNHeaps; n ++) - { - DacpGcHeapDetails heapDetails; - if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK) - { - ExtErr("Error requesting gc heap details\n"); - return Status; - } - - if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage[n])) - { - for (int i = 0; i < 4; ++i) - { - genUsageStat[i].allocd += hpUsage[n].genUsage[i].allocd; - genUsageStat[i].freed += hpUsage[n].genUsage[i].freed; - if (bIncUnreachable) - { - genUsageStat[i].unrooted += hpUsage[n].genUsage[i].unrooted; - } - } - } - } - - for (DWORD n = 0; n < dwNHeaps; n ++) - { - ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", n, - hpUsage[n].genUsage[0].allocd, hpUsage[n].genUsage[1].allocd, - hpUsage[n].genUsage[2].allocd, hpUsage[n].genUsage[3].allocd); - } - ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", - genUsageStat[0].allocd, genUsageStat[1].allocd, - genUsageStat[2].allocd, genUsageStat[3].allocd); - - ExtOut("\nFree space: Percentage\n"); - for (DWORD n = 0; n < dwNHeaps; n ++) - { - ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u ", n, - hpUsage[n].genUsage[0].freed, hpUsage[n].genUsage[1].freed, - hpUsage[n].genUsage[2].freed, hpUsage[n].genUsage[3].freed); - - tempf = ((float)(hpUsage[n].genUsage[0].freed+hpUsage[n].genUsage[1].freed+hpUsage[n].genUsage[2].freed)) / - (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd); - ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf), - (int)(100*((float)hpUsage[n].genUsage[3].freed) / (hpUsage[n].genUsage[3].allocd)) - ); - } - ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", - genUsageStat[0].freed, genUsageStat[1].freed, - genUsageStat[2].freed, genUsageStat[3].freed); - - if (bIncUnreachable) - { - ExtOut("\nUnrooted objects: Percentage\n"); - for (DWORD n = 0; n < dwNHeaps; n ++) - { - ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u ", n, - hpUsage[n].genUsage[0].unrooted, hpUsage[n].genUsage[1].unrooted, - hpUsage[n].genUsage[2].unrooted, hpUsage[n].genUsage[3].unrooted); - - tempf = ((float)(hpUsage[n].genUsage[0].unrooted+hpUsage[n].genUsage[1].unrooted+hpUsage[n].genUsage[2].unrooted)) / - (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd); - ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf), - (int)(100*((float)hpUsage[n].genUsage[3].unrooted) / (hpUsage[n].genUsage[3].allocd))); - } - ExtOut("Total %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", - genUsageStat[0].unrooted, genUsageStat[1].unrooted, - genUsageStat[2].unrooted, genUsageStat[3].unrooted); - } - - } - - return Status; - -#else - - _ASSERTE(false); - return E_FAIL; - -#endif // FEATURE_PAL -} - -#endif // FEATURE_PAL - -/**********************************************************************\ -* Routine Description: * -* * -* This function dumps what is in the syncblock cache. By default * -* it dumps all active syncblocks. Using -all to dump all syncblocks -* * -\**********************************************************************/ -DECLARE_API(SyncBlk) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - BOOL bDumpAll = FALSE; - size_t nbAsked = 0; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-all", &bDumpAll, COBOOL, FALSE}, - {"/d", &dml, COBOOL, FALSE} - }; - CMDValue arg[] = - { // vptr, type - {&nbAsked, COSIZE_T} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - DacpSyncBlockData syncBlockData; - if (syncBlockData.Request(g_sos,1) != S_OK) - { - ExtOut("Error requesting SyncBlk data\n"); - return Status; - } - - DWORD dwCount = syncBlockData.SyncBlockCount; - - ExtOut("Index" WIN64_8SPACES " SyncBlock MonitorHeld Recursion Owning Thread Info" WIN64_8SPACES " SyncBlock Owner\n"); - ULONG freeCount = 0; - ULONG CCWCount = 0; - ULONG RCWCount = 0; - ULONG CFCount = 0; - for (DWORD nb = 1; nb <= dwCount; nb++) - { - if (IsInterrupt()) - return Status; - - if (nbAsked && nb != nbAsked) - { - continue; - } - - if (syncBlockData.Request(g_sos,nb) != S_OK) - { - ExtOut("SyncBlock %d is invalid%s\n", nb, - (nb != nbAsked) ? ", continuing..." : ""); - continue; - } - - BOOL bPrint = (bDumpAll || nb == nbAsked || (syncBlockData.MonitorHeld > 0 && !syncBlockData.bFree)); - - if (bPrint) - { - ExtOut("%5d ", nb); - if (!syncBlockData.bFree || nb != nbAsked) - { - ExtOut("%p ", syncBlockData.SyncBlockPointer); - ExtOut("%11d ", syncBlockData.MonitorHeld); - ExtOut("%9d ", syncBlockData.Recursion); - ExtOut("%p ", syncBlockData.HoldingThread); - - if (syncBlockData.HoldingThread == ~0ul) - { - ExtOut(" orphaned "); - } - else if (syncBlockData.HoldingThread != NULL) - { - DacpThreadData Thread; - if ((Status = Thread.Request(g_sos, syncBlockData.HoldingThread)) != S_OK) - { - ExtOut("Failed to request Thread at %p\n", syncBlockData.HoldingThread); - return Status; - } - - DMLOut(DMLThreadID(Thread.osThreadId)); - ULONG id; - if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK) - { - ExtOut("%4d ", id); - } - else - { - ExtOut(" XXX "); - } - } - else - { - ExtOut(" none "); - } - - if (syncBlockData.bFree) - { - ExtOut(" %8d", 0); // TODO: do we need to print the free synctable list? - } - else - { - sos::Object obj = TO_TADDR(syncBlockData.Object); - DMLOut(" %s %S", DMLObject(syncBlockData.Object), obj.GetTypeName()); - } - } - } - - if (syncBlockData.bFree) - { - freeCount ++; - if (bPrint) { - ExtOut(" Free"); - } - } - else - { -#ifdef FEATURE_COMINTEROP - if (syncBlockData.COMFlags) { - switch (syncBlockData.COMFlags) { - case SYNCBLOCKDATA_COMFLAGS_CCW: - CCWCount ++; - break; - case SYNCBLOCKDATA_COMFLAGS_RCW: - RCWCount ++; - break; - case SYNCBLOCKDATA_COMFLAGS_CF: - CFCount ++; - break; - } - } -#endif // FEATURE_COMINTEROP - } - - if (syncBlockData.MonitorHeld > 1) - { - // TODO: implement this - /* - ExtOut(" "); - DWORD_PTR pHead = (DWORD_PTR)vSyncBlock.m_Link.m_pNext; - DWORD_PTR pNext = pHead; - Thread vThread; - - while (1) - { - if (IsInterrupt()) - return Status; - DWORD_PTR pWaitEventLink = pNext - offsetLinkSB; - WaitEventLink vWaitEventLink; - vWaitEventLink.Fill(pWaitEventLink); - if (!CallStatus) { - break; - } - DWORD_PTR dwAddr = (DWORD_PTR)vWaitEventLink.m_Thread; - ExtOut("%x ", dwAddr); - vThread.Fill (dwAddr); - if (!CallStatus) { - break; - } - if (bPrint) - DMLOut("%s,", DMLThreadID(vThread.m_OSThreadId)); - pNext = (DWORD_PTR)vWaitEventLink.m_LinkSB.m_pNext; - if (pNext == 0) - break; - } - */ - } - - if (bPrint) - ExtOut("\n"); - } - - ExtOut("-----------------------------\n"); - ExtOut("Total %d\n", dwCount); -#ifdef FEATURE_COMINTEROP - ExtOut("CCW %d\n", CCWCount); - ExtOut("RCW %d\n", RCWCount); - ExtOut("ComClassFactory %d\n", CFCount); -#endif - ExtOut("Free %d\n", freeCount); - - return Status; -} - -#ifdef FEATURE_COMINTEROP -struct VisitRcwArgs -{ - BOOL bDetail; - UINT MTACount; - UINT STACount; - ULONG FTMCount; -}; - -void VisitRcw(CLRDATA_ADDRESS RCW,CLRDATA_ADDRESS Context,CLRDATA_ADDRESS Thread, BOOL bIsFreeThreaded, LPVOID token) -{ - VisitRcwArgs *pArgs = (VisitRcwArgs *) token; - - if (pArgs->bDetail) - { - if (pArgs->MTACount == 0 && pArgs->STACount == 0 && pArgs->FTMCount == 0) - { - // First time, print a header - ExtOut("RuntimeCallableWrappers (RCW) to be cleaned:\n"); - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Apartment\n", - "RCW", "CONTEXT", "THREAD"); - } - LPCSTR szThreadApartment; - if (bIsFreeThreaded) - { - szThreadApartment = "(FreeThreaded)"; - pArgs->FTMCount++; - } - else if (Thread == NULL) - { - szThreadApartment = "(MTA)"; - pArgs->MTACount++; - } - else - { - szThreadApartment = "(STA)"; - pArgs->STACount++; - } - - ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %9s\n", - SOS_PTR(RCW), - SOS_PTR(Context), - SOS_PTR(Thread), - szThreadApartment); - } -} - -DECLARE_API(RCWCleanupList) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - DWORD_PTR p_CleanupList = GetExpression(args); - - VisitRcwArgs travArgs; - ZeroMemory(&travArgs,sizeof(VisitRcwArgs)); - travArgs.bDetail = TRUE; - - // We need to detect when !RCWCleanupList is called with an expression which evaluates to 0 - // (to print out an Invalid parameter message), but at the same time we need to allow an - // empty argument list which would result in p_CleanupList equaling 0. - if (p_CleanupList || strlen(args) == 0) - { - HRESULT hr = g_sos->TraverseRCWCleanupList(p_CleanupList, (VISITRCWFORCLEANUP)VisitRcw, &travArgs); - - if (SUCCEEDED(hr)) - { - ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount); - ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount); - ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount); - } - else - { - ExtOut("An error occurred while traversing the cleanup list.\n"); - } - } - else - { - ExtOut("Invalid parameter %s\n", args); - } - - return Status; -} -#endif // FEATURE_COMINTEROP - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of the finalizer * -* queue. * -* * -\**********************************************************************/ -DECLARE_API(FinalizeQueue) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - BOOL bDetail = FALSE; - BOOL bAllReady = FALSE; - BOOL bShort = FALSE; - BOOL dml = FALSE; - TADDR taddrMT = 0; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-detail", &bDetail, COBOOL, FALSE}, - {"-allReady", &bAllReady, COBOOL, FALSE}, - {"-short", &bShort, COBOOL, FALSE}, - {"/d", &dml, COBOOL, FALSE}, - {"-mt", &taddrMT, COHEX, TRUE}, - }; - - if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - if (!bShort) - { - DacpSyncBlockCleanupData dsbcd; - CLRDATA_ADDRESS sbCurrent = NULL; - ULONG cleanCount = 0; - while ((dsbcd.Request(g_sos,sbCurrent) == S_OK) && dsbcd.SyncBlockPointer) - { - if (bDetail) - { - if (cleanCount == 0) // print first time only - { - ExtOut("SyncBlocks to be cleaned by the finalizer thread:\n"); - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n", - "SyncBlock", "RCW", "CCW", "ComClassFactory"); - } - - ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p\n", - (ULONG64) dsbcd.SyncBlockPointer, - (ULONG64) dsbcd.blockRCW, - (ULONG64) dsbcd.blockCCW, - (ULONG64) dsbcd.blockClassFactory); - } - - cleanCount++; - sbCurrent = dsbcd.nextSyncBlock; - if (sbCurrent == NULL) - { - break; - } - } - - ExtOut("SyncBlocks to be cleaned up: %d\n", cleanCount); - -#ifdef FEATURE_COMINTEROP - VisitRcwArgs travArgs; - ZeroMemory(&travArgs,sizeof(VisitRcwArgs)); - travArgs.bDetail = bDetail; - g_sos->TraverseRCWCleanupList(0, (VISITRCWFORCLEANUP) VisitRcw, &travArgs); - ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount); - ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount); - ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount); -#endif // FEATURE_COMINTEROP - -// noRCW: - ExtOut("----------------------------------\n"); - } - - // GC Heap - DWORD dwNHeaps = GetGcHeapCount(); - - HeapStat hpStat; - - if (!IsServerBuild()) - { - DacpGcHeapDetails heapDetails; - if (heapDetails.Request(g_sos) != S_OK) - { - ExtOut("Error requesting details\n"); - return Status; - } - - GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort); - } - else - { - DWORD dwAllocSize; - if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize)) - { - ExtOut("Failed to get GCHeaps: integer overflow\n"); - return Status; - } - - CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize); - if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK) - { - ExtOut("Failed to get GCHeaps\n"); - return Status; - } - - for (DWORD n = 0; n < dwNHeaps; n ++) - { - DacpGcHeapDetails heapDetails; - if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK) - { - ExtOut("Error requesting details\n"); - return Status; - } - - ExtOut("------------------------------\n"); - ExtOut("Heap %d\n", n); - GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort); - } - } - - if (!bShort) - { - if (bAllReady) - { - PrintGCStat(&hpStat, "Statistics for all finalizable objects that are no longer rooted:\n"); - } - else - { - PrintGCStat(&hpStat, "Statistics for all finalizable objects (including all objects ready for finalization):\n"); - } - } - - return Status; -} - -enum { - // These are the values set in m_dwTransientFlags. - // Note that none of these flags survive a prejit save/restore. - - M_CRST_NOTINITIALIZED = 0x00000001, // Used to prevent destruction of garbage m_crst - M_LOOKUPCRST_NOTINITIALIZED = 0x00000002, - - SUPPORTS_UPDATEABLE_METHODS = 0x00000020, - CLASSES_FREED = 0x00000040, - HAS_PHONY_IL_RVAS = 0x00000080, - IS_EDIT_AND_CONTINUE = 0x00000200, -}; - -void ModuleMapTraverse(UINT index, CLRDATA_ADDRESS methodTable, LPVOID token) -{ - ULONG32 rid = (ULONG32)(size_t)token; - NameForMT_s(TO_TADDR(methodTable), g_mdName, mdNameLen); - - DMLOut("%s 0x%08x %S\n", DMLMethodTable(methodTable), (ULONG32)TokenFromRid(rid, index), g_mdName); -} - - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of a Module * -* for a given address * -* * -\**********************************************************************/ -DECLARE_API(DumpModule) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - - DWORD_PTR p_ModuleAddr = NULL; - BOOL bMethodTables = FALSE; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-mt", &bMethodTables, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE} -#endif - }; - CMDValue arg[] = - { // vptr, type - {&p_ModuleAddr, COHEX} - }; - - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - if (nArg != 1) - { - ExtOut("Usage: DumpModule [-mt] <Module Address>\n"); - return Status; - } - - EnableDMLHolder dmlHolder(dml); - DacpModuleData module; - if ((Status=module.Request(g_sos, TO_CDADDR(p_ModuleAddr)))!=S_OK) - { - ExtOut("Fail to fill Module %p\n", SOS_PTR(p_ModuleAddr)); - return Status; - } - - WCHAR FileName[MAX_LONGPATH]; - FileNameForModule (&module, FileName); - ExtOut("Name: %S\n", FileName[0] ? FileName : W("Unknown Module")); - - ExtOut("Attributes: "); - if (module.bIsPEFile) - ExtOut("PEFile "); - if (module.bIsReflection) - ExtOut("Reflection "); - if (module.dwTransientFlags & SUPPORTS_UPDATEABLE_METHODS) - ExtOut("SupportsUpdateableMethods"); - ExtOut("\n"); - - DMLOut("Assembly: %s\n", DMLAssembly(module.Assembly)); - - ExtOut("LoaderHeap: %p\n", SOS_PTR(module.pLookupTableHeap)); - ExtOut("TypeDefToMethodTableMap: %p\n", SOS_PTR(module.TypeDefToMethodTableMap)); - ExtOut("TypeRefToMethodTableMap: %p\n", SOS_PTR(module.TypeRefToMethodTableMap)); - ExtOut("MethodDefToDescMap: %p\n", SOS_PTR(module.MethodDefToDescMap)); - ExtOut("FieldDefToDescMap: %p\n", SOS_PTR(module.FieldDefToDescMap)); - ExtOut("MemberRefToDescMap: %p\n", SOS_PTR(module.MemberRefToDescMap)); - ExtOut("FileReferencesMap: %p\n", SOS_PTR(module.FileReferencesMap)); - ExtOut("AssemblyReferencesMap: %p\n", SOS_PTR(module.ManifestModuleReferencesMap)); - - if (module.ilBase && module.metadataStart) - ExtOut("MetaData start address: %p (%d bytes)\n", SOS_PTR(module.metadataStart), module.metadataSize); - - if (bMethodTables) - { - ExtOut("\nTypes defined in this module\n\n"); - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeDef", "Name"); - - ExtOut("------------------------------------------------------------------------------\n"); - g_sos->TraverseModuleMap(TYPEDEFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil); - - ExtOut("\nTypes referenced in this module\n\n"); - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeRef", "Name"); - - ExtOut("------------------------------------------------------------------------------\n"); - g_sos->TraverseModuleMap(TYPEREFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil); - } - - return Status; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of a Domain * -* for a given address * -* * -\**********************************************************************/ -DECLARE_API(DumpDomain) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - DWORD_PTR p_DomainAddr = 0; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - CMDValue arg[] = - { // vptr, type - {&p_DomainAddr, COHEX}, - }; - size_t nArg; - - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - - DacpAppDomainStoreData adsData; - if ((Status=adsData.Request(g_sos))!=S_OK) - { - ExtOut("Unable to get AppDomain information\n"); - return Status; - } - - if (p_DomainAddr) - { - DacpAppDomainData appDomain1; - if ((Status=appDomain1.Request(g_sos, TO_CDADDR(p_DomainAddr)))!=S_OK) - { - ExtOut("Fail to fill AppDomain\n"); - return Status; - } - - ExtOut("--------------------------------------\n"); - - if (p_DomainAddr == adsData.sharedDomain) - { - DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain)); - } - else if (p_DomainAddr == adsData.systemDomain) - { - DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain)); - } - else - { - DMLOut("Domain %d:%s %s\n", appDomain1.dwId, (appDomain1.dwId >= 10) ? "" : " ", DMLDomain(p_DomainAddr)); - } - - DomainInfo(&appDomain1); - return Status; - } - - ExtOut("--------------------------------------\n"); - DMLOut("System Domain: %s\n", DMLDomain(adsData.systemDomain)); - DacpAppDomainData appDomain; - if ((Status=appDomain.Request(g_sos,adsData.systemDomain))!=S_OK) - { - ExtOut("Unable to get system domain info.\n"); - return Status; - } - DomainInfo(&appDomain); - - if (adsData.sharedDomain != NULL) - { - ExtOut("--------------------------------------\n"); - DMLOut("Shared Domain: %s\n", DMLDomain(adsData.sharedDomain)); - if ((Status=appDomain.Request(g_sos, adsData.sharedDomain))!=S_OK) - { - ExtOut("Unable to get shared domain info\n"); - return Status; - } - DomainInfo(&appDomain); - } - - ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount]; - if (pArray==NULL) - { - ReportOOM(); - return Status; - } - - if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK) - { - ExtOut("Unable to get array of AppDomains\n"); - return Status; - } - - for (int n=0;n<adsData.DomainCount;n++) - { - if (IsInterrupt()) - break; - - if ((Status=appDomain.Request(g_sos, pArray[n])) != S_OK) - { - ExtOut("Failed to get appdomain %p, error %lx\n", SOS_PTR(pArray[n]), Status); - return Status; - } - - ExtOut("--------------------------------------\n"); - DMLOut("Domain %d:%s %s\n", appDomain.dwId, (appDomain.dwId >= 10) ? "" : " ", DMLDomain(pArray[n])); - DomainInfo(&appDomain); - } - - return Status; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the contents of a Assembly * -* for a given address * -* * -\**********************************************************************/ -DECLARE_API(DumpAssembly) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - DWORD_PTR p_AssemblyAddr = 0; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - CMDValue arg[] = - { // vptr, type - {&p_AssemblyAddr, COHEX}, - }; - size_t nArg; - - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - - if (p_AssemblyAddr == 0) - { - ExtOut("Invalid Assembly %s\n", args); - return Status; - } - - DacpAssemblyData Assembly; - if ((Status=Assembly.Request(g_sos, TO_CDADDR(p_AssemblyAddr)))!=S_OK) - { - ExtOut("Fail to fill Assembly\n"); - return Status; - } - DMLOut("Parent Domain: %s\n", DMLDomain(Assembly.ParentDomain)); - if (g_sos->GetAssemblyName(TO_CDADDR(p_AssemblyAddr), mdNameLen, g_mdName, NULL)==S_OK) - ExtOut("Name: %S\n", g_mdName); - else - ExtOut("Name: Unknown\n"); - - AssemblyInfo(&Assembly); - return Status; -} - - -String GetHostingCapabilities(DWORD hostConfig) -{ - String result; - - bool bAnythingPrinted = false; - -#define CHK_AND_PRINT(hType,hStr) \ - if (hostConfig & (hType)) { \ - if (bAnythingPrinted) result += ", "; \ - result += hStr; \ - bAnythingPrinted = true; \ - } - - CHK_AND_PRINT(CLRMEMORYHOSTED, "Memory"); - CHK_AND_PRINT(CLRTASKHOSTED, "Task"); - CHK_AND_PRINT(CLRSYNCHOSTED, "Sync"); - CHK_AND_PRINT(CLRTHREADPOOLHOSTED, "Threadpool"); - CHK_AND_PRINT(CLRIOCOMPLETIONHOSTED, "IOCompletion"); - CHK_AND_PRINT(CLRASSEMBLYHOSTED, "Assembly"); - CHK_AND_PRINT(CLRGCHOSTED, "GC"); - CHK_AND_PRINT(CLRSECURITYHOSTED, "Security"); - -#undef CHK_AND_PRINT - - return result; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the managed threads * -* * -\**********************************************************************/ -HRESULT PrintThreadsFromThreadStore(BOOL bMiniDump, BOOL bPrintLiveThreadsOnly) -{ - HRESULT Status; - - DacpThreadStoreData ThreadStore; - if ((Status = ThreadStore.Request(g_sos)) != S_OK) - { - Print("Failed to request ThreadStore\n"); - return Status; - } - - TableOutput table(2, 17); - - table.WriteRow("ThreadCount:", Decimal(ThreadStore.threadCount)); - table.WriteRow("UnstartedThread:", Decimal(ThreadStore.unstartedThreadCount)); - table.WriteRow("BackgroundThread:", Decimal(ThreadStore.backgroundThreadCount)); - table.WriteRow("PendingThread:", Decimal(ThreadStore.pendingThreadCount)); - table.WriteRow("DeadThread:", Decimal(ThreadStore.deadThreadCount)); - - if (ThreadStore.fHostConfig & ~CLRHOSTED) - { - String hosting = "yes"; - - hosting += " ("; - hosting += GetHostingCapabilities(ThreadStore.fHostConfig); - hosting += ")"; - - table.WriteRow("Hosted Runtime:", hosting); - } - else - { - table.WriteRow("Hosted Runtime:", "no"); - } - - const bool hosted = (ThreadStore.fHostConfig & CLRTASKHOSTED) != 0; - table.ReInit(hosted ? 12 : 11, POINTERSIZE_HEX); - table.SetWidths(10, 4, 4, 4, _max(9, POINTERSIZE_HEX), - 8, 11, 1+POINTERSIZE_HEX*2, POINTERSIZE_HEX, - 5, 3, POINTERSIZE_HEX); - - table.SetColAlignment(0, AlignRight); - table.SetColAlignment(1, AlignRight); - table.SetColAlignment(2, AlignRight); - table.SetColAlignment(4, AlignRight); - - table.WriteColumn(8, "Lock"); - table.WriteRow("", "ID", "OSID", "ThreadOBJ", "State", "GC Mode", "GC Alloc Context", "Domain", "Count", "Apt"); - - if (hosted) - table.WriteColumn("Fiber"); - - table.WriteColumn("Exception"); - - DacpThreadData Thread; - CLRDATA_ADDRESS CurThread = ThreadStore.firstThread; - while (CurThread) - { - if (IsInterrupt()) - break; - - if ((Status = Thread.Request(g_sos, CurThread)) != S_OK) - { - PrintLn("Failed to request Thread at ", Pointer(CurThread)); - return Status; - } - - BOOL bSwitchedOutFiber = Thread.osThreadId == SWITCHED_OUT_FIBER_OSID; - if (!IsKernelDebugger()) - { - ULONG id = 0; - - if (bSwitchedOutFiber) - { - table.WriteColumn(0, "<<<< "); - } - else if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK) - { - table.WriteColumn(0, Decimal(id)); - } - else if (bPrintLiveThreadsOnly) - { - CurThread = Thread.nextThread; - continue; - } - else - { - table.WriteColumn(0, "XXXX "); - } - } - - table.WriteColumn(1, Decimal(Thread.corThreadId)); - table.WriteColumn(2, ThreadID(bSwitchedOutFiber ? 0 : Thread.osThreadId)); - table.WriteColumn(3, Pointer(CurThread)); - table.WriteColumn(4, ThreadState(Thread.state)); - table.WriteColumn(5, Thread.preemptiveGCDisabled == 1 ? "Cooperative" : "Preemptive"); - table.WriteColumnFormat(6, "%p:%p", Thread.allocContextPtr, Thread.allocContextLimit); - - if (Thread.domain) - { - table.WriteColumn(7, AppDomainPtr(Thread.domain)); - } - else - { - CLRDATA_ADDRESS domain = 0; - if (FAILED(g_sos->GetDomainFromContext(Thread.context, &domain))) - table.WriteColumn(7, "<error>"); - else - table.WriteColumn(7, AppDomainPtr(domain)); - } - - table.WriteColumn(8, Decimal(Thread.lockCount)); - - // Apartment state -#ifndef FEATURE_PAL - DWORD_PTR OleTlsDataAddr; - if (!bSwitchedOutFiber - && SafeReadMemory(Thread.teb + offsetof(TEB, ReservedForOle), - &OleTlsDataAddr, - sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0) - { - DWORD AptState; - if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags), - &AptState, - sizeof(AptState), NULL)) - { - if (AptState & OLETLS_APARTMENTTHREADED) - table.WriteColumn(9, "STA"); - else if (AptState & OLETLS_MULTITHREADED) - table.WriteColumn(9, "MTA"); - else if (AptState & OLETLS_INNEUTRALAPT) - table.WriteColumn(9, "NTA"); - else - table.WriteColumn(9, "Ukn"); - } - else - { - table.WriteColumn(9, "Ukn"); - } - } - else -#endif // FEATURE_PAL - table.WriteColumn(9, "Ukn"); - - if (hosted) - table.WriteColumn(10, Thread.fiberData); - - WString lastCol; - if (CurThread == ThreadStore.finalizerThread) - lastCol += W("(Finalizer) "); - if (CurThread == ThreadStore.gcThread) - lastCol += W("(GC) "); - - const int TS_TPWorkerThread = 0x01000000; // is this a threadpool worker thread? - const int TS_CompletionPortThread = 0x08000000; // is this is a completion port thread? - - if (Thread.state & TS_TPWorkerThread) - lastCol += W("(Threadpool Worker) "); - else if (Thread.state & TS_CompletionPortThread) - lastCol += W("(Threadpool Completion Port) "); - - - TADDR taLTOH; - if (Thread.lastThrownObjectHandle && SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle), - &taLTOH, sizeof(taLTOH), NULL) && taLTOH) - { - TADDR taMT; - if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL)) - { - if (NameForMT_s(taMT, g_mdName, mdNameLen)) - lastCol += WString(g_mdName) + W(" ") + ExceptionPtr(taLTOH); - else - lastCol += WString(W("<Invalid Object> (")) + Pointer(taLTOH) + W(")"); - - // Print something if there are nested exceptions on the thread - if (Thread.firstNestedException) - lastCol += W(" (nested exceptions)"); - } - } - - table.WriteColumn(lastCol); - CurThread = Thread.nextThread; - } - - return Status; -} - -#ifndef FEATURE_PAL -HRESULT PrintSpecialThreads() -{ - Print("\n"); - - DWORD dwCLRTLSDataIndex = 0; - HRESULT Status = g_sos->GetTLSIndex(&dwCLRTLSDataIndex); - - if (!SUCCEEDED (Status)) - { - Print("Failed to retrieve Tls Data index\n"); - return Status; - } - - - ULONG ulOriginalThreadID = 0; - Status = g_ExtSystem->GetCurrentThreadId (&ulOriginalThreadID); - if (!SUCCEEDED (Status)) - { - Print("Failed to require current Thread ID\n"); - return Status; - } - - ULONG ulTotalThreads = 0; - Status = g_ExtSystem->GetNumberThreads (&ulTotalThreads); - if (!SUCCEEDED (Status)) - { - Print("Failed to require total thread number\n"); - return Status; - } - - TableOutput table(3, 4, AlignRight, 5); - table.WriteRow("", "OSID", "Special thread type"); - - for (ULONG ulThread = 0; ulThread < ulTotalThreads; ulThread++) - { - ULONG Id = 0; - ULONG SysId = 0; - HRESULT threadStatus = g_ExtSystem->GetThreadIdsByIndex(ulThread, 1, &Id, &SysId); - if (!SUCCEEDED (threadStatus)) - { - PrintLn("Failed to get thread ID for thread ", Decimal(ulThread)); - continue; - } - - threadStatus = g_ExtSystem->SetCurrentThreadId(Id); - if (!SUCCEEDED (threadStatus)) - { - PrintLn("Failed to switch to thread ", ThreadID(SysId)); - continue; - } - - CLRDATA_ADDRESS cdaTeb = 0; - threadStatus = g_ExtSystem->GetCurrentThreadTeb(&cdaTeb); - if (!SUCCEEDED (threadStatus)) - { - PrintLn("Failed to get Teb for Thread ", ThreadID(SysId)); - continue; - } - - TADDR CLRTLSDataAddr = 0; - - TADDR tlsArrayAddr = NULL; - if (!SafeReadMemory (TO_TADDR(cdaTeb) + WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer , &tlsArrayAddr, sizeof (void**), NULL)) - { - PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId)); - continue; - } - - if (tlsArrayAddr == NULL) - { - continue; - } - - TADDR moduleTlsDataAddr = 0; - if (!SafeReadMemory (tlsArrayAddr + sizeof (void*) * (dwCLRTLSDataIndex & 0xFFFF), &moduleTlsDataAddr, sizeof (void**), NULL)) - { - PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId)); - continue; - } - - CLRTLSDataAddr = moduleTlsDataAddr + ((dwCLRTLSDataIndex & 0x7FFF0000) >> 16) + OFFSETOF__TLS__tls_EETlsData; - - TADDR CLRTLSData = NULL; - if (!SafeReadMemory (CLRTLSDataAddr, &CLRTLSData, sizeof (TADDR), NULL)) - { - PrintLn("Failed to get CLR Tls data for thread ", ThreadID(SysId)); - continue; - } - - if (CLRTLSData == NULL) - { - continue; - } - - size_t ThreadType = 0; - if (!SafeReadMemory (CLRTLSData + sizeof (TADDR) * TlsIdx_ThreadType, &ThreadType, sizeof (size_t), NULL)) - { - PrintLn("Failed to get thread type info not found for thread ", ThreadID(SysId)); - continue; - } - - if (ThreadType == 0) - { - continue; - } - - table.WriteColumn(0, Decimal(Id)); - table.WriteColumn(1, ThreadID(SysId)); - - String type; - if (ThreadType & ThreadType_GC) - { - type += "GC "; - } - if (ThreadType & ThreadType_Timer) - { - type += "Timer "; - } - if (ThreadType & ThreadType_Gate) - { - type += "Gate "; - } - if (ThreadType & ThreadType_DbgHelper) - { - type += "DbgHelper "; - } - if (ThreadType & ThreadType_Shutdown) - { - type += "Shutdown "; - } - if (ThreadType & ThreadType_DynamicSuspendEE) - { - type += "SuspendEE "; - } - if (ThreadType & ThreadType_Finalizer) - { - type += "Finalizer "; - } - if (ThreadType & ThreadType_ShutdownHelper) - { - type += "ShutdownHelper "; - } - if (ThreadType & ThreadType_Threadpool_IOCompletion) - { - type += "IOCompletion "; - } - if (ThreadType & ThreadType_Threadpool_Worker) - { - type += "ThreadpoolWorker "; - } - if (ThreadType & ThreadType_Wait) - { - type += "Wait "; - } - if (ThreadType & ThreadType_ProfAPI_Attach) - { - type += "ProfilingAPIAttach "; - } - if (ThreadType & ThreadType_ProfAPI_Detach) - { - type += "ProfilingAPIDetach "; - } - - table.WriteColumn(2, type); - } - - Status = g_ExtSystem->SetCurrentThreadId (ulOriginalThreadID); - if (!SUCCEEDED (Status)) - { - ExtOut("Failed to switch to original thread\n"); - return Status; - } - - return Status; -} -#endif //FEATURE_PAL - -HRESULT SwitchToExceptionThread() -{ - HRESULT Status; - - DacpThreadStoreData ThreadStore; - if ((Status = ThreadStore.Request(g_sos)) != S_OK) - { - Print("Failed to request ThreadStore\n"); - return Status; - } - - DacpThreadData Thread; - CLRDATA_ADDRESS CurThread = ThreadStore.firstThread; - while (CurThread) - { - if (IsInterrupt()) - break; - - if ((Status = Thread.Request(g_sos, CurThread)) != S_OK) - { - PrintLn("Failed to request Thread at ", Pointer(CurThread)); - return Status; - } - - TADDR taLTOH; - if (Thread.lastThrownObjectHandle != NULL) - { - if (SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle), &taLTOH, sizeof(taLTOH), NULL)) - { - if (taLTOH != NULL) - { - ULONG id; - if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK) - { - if (g_ExtSystem->SetCurrentThreadId(id) == S_OK) - { - PrintLn("Found managed exception on thread ", ThreadID(Thread.osThreadId)); - break; - } - } - } - } - } - - CurThread = Thread.nextThread; - } - - return Status; -} - -struct ThreadStateTable -{ - unsigned int State; - const char * Name; -}; -static const struct ThreadStateTable ThreadStates[] = -{ - {0x1, "Thread Abort Requested"}, - {0x2, "GC Suspend Pending"}, - {0x4, "User Suspend Pending"}, - {0x8, "Debug Suspend Pending"}, - {0x10, "GC On Transitions"}, - {0x20, "Legal to Join"}, - {0x40, "Yield Requested"}, - {0x80, "Hijacked by the GC"}, - {0x100, "Blocking GC for Stack Overflow"}, - {0x200, "Background"}, - {0x400, "Unstarted"}, - {0x800, "Dead"}, - {0x1000, "CLR Owns"}, - {0x2000, "CoInitialized"}, - {0x4000, "In Single Threaded Apartment"}, - {0x8000, "In Multi Threaded Apartment"}, - {0x10000, "Reported Dead"}, - {0x20000, "Fully initialized"}, - {0x40000, "Task Reset"}, - {0x80000, "Sync Suspended"}, - {0x100000, "Debug Will Sync"}, - {0x200000, "Stack Crawl Needed"}, - {0x400000, "Suspend Unstarted"}, - {0x800000, "Aborted"}, - {0x1000000, "Thread Pool Worker Thread"}, - {0x2000000, "Interruptible"}, - {0x4000000, "Interrupted"}, - {0x8000000, "Completion Port Thread"}, - {0x10000000, "Abort Initiated"}, - {0x20000000, "Finalized"}, - {0x40000000, "Failed to Start"}, - {0x80000000, "Detached"}, -}; - -DECLARE_API(ThreadState) -{ - INIT_API_NODAC(); - - size_t state = GetExpression(args); - int count = 0; - - if (state) - { - - for (unsigned int i = 0; i < _countof(ThreadStates); ++i) - if (state & ThreadStates[i].State) - { - ExtOut(" %s\n", ThreadStates[i].Name); - count++; - } - } - - // If we did not find any thread states, print out a message to let the user - // know that the function is working correctly. - if (count == 0) - ExtOut(" No thread states for '%s'\n", args); - - return Status; -} - -DECLARE_API(Threads) -{ - INIT_API(); - - BOOL bPrintSpecialThreads = FALSE; - BOOL bPrintLiveThreadsOnly = FALSE; - BOOL bSwitchToManagedExceptionThread = FALSE; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-special", &bPrintSpecialThreads, COBOOL, FALSE}, - {"-live", &bPrintLiveThreadsOnly, COBOOL, FALSE}, - {"-managedexception", &bSwitchToManagedExceptionThread, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) - { - return Status; - } - - if (bSwitchToManagedExceptionThread) - { - return SwitchToExceptionThread(); - } - - // We need to support minidumps for this command. - BOOL bMiniDump = IsMiniDumpFile(); - - if (bMiniDump && bPrintSpecialThreads) - { - Print("Special thread information is not available in mini dumps.\n"); - } - - EnableDMLHolder dmlHolder(dml); - - try - { - Status = PrintThreadsFromThreadStore(bMiniDump, bPrintLiveThreadsOnly); - if (!bMiniDump && bPrintSpecialThreads) - { -#ifdef FEATURE_PAL - Print("\n-special not supported.\n"); -#else //FEATURE_PAL - HRESULT Status2 = PrintSpecialThreads(); - if (!SUCCEEDED(Status2)) - Status = Status2; -#endif //FEATURE_PAL - } - } - catch (sos::Exception &e) - { - ExtOut("%s\n", e.what()); - } - - return Status; -} - -#ifndef FEATURE_PAL -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the Watson Buckets. * -* * -\**********************************************************************/ -DECLARE_API(WatsonBuckets) -{ - INIT_API(); - - // We don't need to support minidumps for this command. - if (IsMiniDumpFile()) - { - ExtOut("Not supported on mini dumps.\n"); - } - - // Get the current managed thread. - CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread(); - DacpThreadData Thread; - - if ((threadAddr == NULL) || ((Status = Thread.Request(g_sos, threadAddr)) != S_OK)) - { - ExtOut("The current thread is unmanaged\n"); - return Status; - } - - // Get the definition of GenericModeBlock. -#include <msodw.h> - GenericModeBlock gmb; - - if ((Status = g_sos->GetClrWatsonBuckets(threadAddr, &gmb)) != S_OK) - { - ExtOut("Can't get Watson Buckets\n"); - return Status; - } - - ExtOut("Watson Bucket parameters:\n"); - ExtOut("b1: %S\n", gmb.wzP1); - ExtOut("b2: %S\n", gmb.wzP2); - ExtOut("b3: %S\n", gmb.wzP3); - ExtOut("b4: %S\n", gmb.wzP4); - ExtOut("b5: %S\n", gmb.wzP5); - ExtOut("b6: %S\n", gmb.wzP6); - ExtOut("b7: %S\n", gmb.wzP7); - ExtOut("b8: %S\n", gmb.wzP8); - ExtOut("b9: %S\n", gmb.wzP9); - - return Status; -} // WatsonBuckets() -#endif // FEATURE_PAL - -struct PendingBreakpoint -{ - WCHAR szModuleName[MAX_LONGPATH]; - WCHAR szFunctionName[mdNameLen]; - WCHAR szFilename[MAX_LONGPATH]; - DWORD lineNumber; - TADDR pModule; - DWORD ilOffset; - mdMethodDef methodToken; - void SetModule(TADDR module) - { - pModule = module; - } - - bool ModuleMatches(TADDR compare) - { - return (compare == pModule); - } - - PendingBreakpoint *pNext; - PendingBreakpoint() : lineNumber(0), ilOffset(0), methodToken(0), pNext(NULL) - { - szModuleName[0] = L'\0'; - szFunctionName[0] = L'\0'; - szFilename[0] = L'\0'; - } -}; - -void IssueDebuggerBPCommand ( CLRDATA_ADDRESS addr ) -{ - const int MaxBPsCached = 1024; - static CLRDATA_ADDRESS alreadyPlacedBPs[MaxBPsCached]; - static int curLimit = 0; - - // on ARM the debugger requires breakpoint addresses to be sanitized - if (IsDbgTargetArm()) -#ifndef FEATURE_PAL - addr &= ~THUMB_CODE; -#else - addr |= THUMB_CODE; // lldb expects thumb code bit set -#endif - - // if we overflowed our cache consider all new BPs unique... - BOOL bUnique = curLimit >= MaxBPsCached; - if (!bUnique) - { - bUnique = TRUE; - for (int i = 0; i < curLimit; ++i) - { - if (alreadyPlacedBPs[i] == addr) - { - bUnique = FALSE; - break; - } - } - } - if (bUnique) - { - char buffer[64]; // sufficient for "bp <pointersize>" - static WCHAR wszNameBuffer[1024]; // should be large enough - - // get the MethodDesc name - CLRDATA_ADDRESS pMD; - if (g_sos->GetMethodDescPtrFromIP(addr, &pMD) != S_OK - || g_sos->GetMethodDescName(pMD, 1024, wszNameBuffer, NULL) != S_OK) - { - wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN")); - } - -#ifndef FEATURE_PAL - sprintf_s(buffer, _countof(buffer), "bp %p", (void*) (size_t) addr); -#else - sprintf_s(buffer, _countof(buffer), "breakpoint set --address 0x%p", (void*) (size_t) addr); -#endif - ExtOut("Setting breakpoint: %s [%S]\n", buffer, wszNameBuffer); - g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0); - - if (curLimit < MaxBPsCached) - { - alreadyPlacedBPs[curLimit++] = addr; - } - } -} - -class Breakpoints -{ - PendingBreakpoint* m_breakpoints; -public: - Breakpoints() - { - m_breakpoints = NULL; - } - ~Breakpoints() - { - PendingBreakpoint *pCur = m_breakpoints; - while(pCur) - { - PendingBreakpoint *pNext = pCur->pNext; - delete pCur; - pCur = pNext; - } - m_breakpoints = NULL; - } - - void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod, DWORD ilOffset) - { - if (!IsIn(szModule, szName, mod)) - { - PendingBreakpoint *pNew = new PendingBreakpoint(); - wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule); - wcscpy_s(pNew->szFunctionName, mdNameLen, szName); - pNew->SetModule(mod); - pNew->ilOffset = ilOffset; - pNew->pNext = m_breakpoints; - m_breakpoints = pNew; - } - } - - void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, mdMethodDef methodToken, TADDR mod, DWORD ilOffset) - { - if (!IsIn(methodToken, mod, ilOffset)) - { - PendingBreakpoint *pNew = new PendingBreakpoint(); - wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule); - wcscpy_s(pNew->szFunctionName, mdNameLen, szName); - pNew->methodToken = methodToken; - pNew->SetModule(mod); - pNew->ilOffset = ilOffset; - pNew->pNext = m_breakpoints; - m_breakpoints = pNew; - } - } - - void Add(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod) - { - if (!IsIn(szFilename, lineNumber, mod)) - { - PendingBreakpoint *pNew = new PendingBreakpoint(); - wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename); - pNew->lineNumber = lineNumber; - pNew->SetModule(mod); - pNew->pNext = m_breakpoints; - m_breakpoints = pNew; - } - } - - void Add(__in_z LPWSTR szFilename, DWORD lineNumber, mdMethodDef methodToken, TADDR mod, DWORD ilOffset) - { - if (!IsIn(methodToken, mod, ilOffset)) - { - PendingBreakpoint *pNew = new PendingBreakpoint(); - wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename); - pNew->lineNumber = lineNumber; - pNew->methodToken = methodToken; - pNew->SetModule(mod); - pNew->ilOffset = ilOffset; - pNew->pNext = m_breakpoints; - m_breakpoints = pNew; - } - } - - //returns true if updates are still needed for this module, FALSE if all BPs are now bound - BOOL Update(TADDR mod, BOOL isNewModule) - { - BOOL bNeedUpdates = FALSE; - PendingBreakpoint *pCur = NULL; - - if(isNewModule) - { - SymbolReader symbolReader; - SymbolReader* pSymReader = &symbolReader; - if(LoadSymbolsForModule(mod, &symbolReader) != S_OK) - pSymReader = NULL; - - // Get tokens for any modules that match. If there was a change, - // update notifications. - pCur = m_breakpoints; - while(pCur) - { - PendingBreakpoint *pNext = pCur->pNext; - ResolvePendingNonModuleBoundBreakpoint(mod, pCur, pSymReader); - pCur = pNext; - } - } - - pCur = m_breakpoints; - while(pCur) - { - PendingBreakpoint *pNext = pCur->pNext; - if (ResolvePendingBreakpoint(mod, pCur)) - { - bNeedUpdates = TRUE; - } - pCur = pNext; - } - return bNeedUpdates; - } - - BOOL UpdateKnownCodeAddress(TADDR mod, CLRDATA_ADDRESS bpLocation) - { - PendingBreakpoint *pCur = m_breakpoints; - BOOL bpSet = FALSE; - - while(pCur) - { - PendingBreakpoint *pNext = pCur->pNext; - if (pCur->ModuleMatches(mod)) - { - IssueDebuggerBPCommand(bpLocation); - bpSet = TRUE; - break; - } - - pCur = pNext; - } - - return bpSet; - } - - void RemovePendingForModule(TADDR mod) - { - PendingBreakpoint *pCur = m_breakpoints; - while(pCur) - { - PendingBreakpoint *pNext = pCur->pNext; - if (pCur->ModuleMatches(mod)) - { - // Delete the current node, and keep going - Delete(pCur); - } - - pCur = pNext; - } - } - - void ListBreakpoints() - { - PendingBreakpoint *pCur = m_breakpoints; - size_t iBreakpointIndex = 1; - ExtOut(SOSPrefix "bpmd pending breakpoint list\n Breakpoint index - Location, ModuleID, Method Token\n"); - while(pCur) - { - //windbg likes to format %p as always being 64 bits - ULONG64 modulePtr = (ULONG64)pCur->pModule; - - if(pCur->szModuleName[0] != L'\0') - ExtOut("%d - %ws!%ws+%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset, modulePtr, pCur->methodToken); - else - ExtOut("%d - %ws:%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szFilename, pCur->lineNumber, modulePtr, pCur->methodToken); - iBreakpointIndex++; - pCur = pCur->pNext; - } - } - -#ifndef FEATURE_PAL - void SaveBreakpoints(FILE* pFile) - { - PendingBreakpoint *pCur = m_breakpoints; - while(pCur) - { - if(pCur->szModuleName[0] != L'\0') - fprintf_s(pFile, "!bpmd %ws %ws %d\n", pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset); - else - fprintf_s(pFile, "!bpmd %ws:%d\n", pCur->szFilename, pCur->lineNumber); - pCur = pCur->pNext; - } - } -#endif - - void CleanupNotifications() - { -#ifdef FEATURE_PAL - if (m_breakpoints == NULL) - { - g_ExtServices->ClearExceptionCallback(); - } -#endif - } - - void ClearBreakpoint(size_t breakPointToClear) - { - PendingBreakpoint *pCur = m_breakpoints; - size_t iBreakpointIndex = 1; - while(pCur) - { - if (breakPointToClear == iBreakpointIndex) - { - ExtOut("%d - %ws, %ws, %p\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->pModule); - ExtOut("Cleared\n"); - Delete(pCur); - break; - } - iBreakpointIndex++; - pCur = pCur->pNext; - } - - if (pCur == NULL) - { - ExtOut("Invalid pending breakpoint index.\n"); - } - CleanupNotifications(); - } - - void ClearAllBreakpoints() - { - size_t iBreakpointIndex = 1; - for (PendingBreakpoint *pCur = m_breakpoints; pCur != NULL; ) - { - PendingBreakpoint* pNext = pCur->pNext; - Delete(pCur); - iBreakpointIndex++; - pCur = pNext; - } - CleanupNotifications(); - - ExtOut("All pending breakpoints cleared.\n"); - } - - HRESULT LoadSymbolsForModule(TADDR mod, SymbolReader* pSymbolReader) - { - HRESULT Status = S_OK; - ToRelease<IXCLRDataModule> pModule; - IfFailRet(g_sos->GetModule(mod, &pModule)); - - ToRelease<IMetaDataImport> pMDImport = NULL; - IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport)); - - IfFailRet(pSymbolReader->LoadSymbols(pMDImport, pModule)); - - return S_OK; - } - - HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pFilename, DWORD lineNumber, TADDR mod, SymbolReader* pSymbolReader) - { - HRESULT Status = S_OK; - if(pSymbolReader == NULL) - return S_FALSE; // no symbols, can't bind here - - mdMethodDef methodDef; - ULONG32 ilOffset; - if(FAILED(Status = pSymbolReader->ResolveSequencePoint(pFilename, lineNumber, mod, &methodDef, &ilOffset))) - { - return S_FALSE; // not binding in a module is typical - } - - Add(pFilename, lineNumber, methodDef, mod, ilOffset); - return Status; - } - - HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pModuleName, __in_z WCHAR* pMethodName, TADDR mod, DWORD ilOffset) - { - HRESULT Status = S_OK; - char szName[mdNameLen]; - int numModule; - - ToRelease<IXCLRDataModule> module; - IfFailRet(g_sos->GetModule(mod, &module)); - - WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szName, mdNameLen, NULL, NULL); - - ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(szName, &numModule); - if (moduleList == NULL) - { - ExtOut("Failed to request module list.\n"); - return E_FAIL; - } - - for (int i = 0; i < numModule; i++) - { - // If any one entry in moduleList matches, then the current PendingBreakpoint - // is the right one. - if(moduleList[i] != TO_TADDR(mod)) - continue; - - CLRDATA_ENUM h; - if (module->StartEnumMethodDefinitionsByName(pMethodName, 0, &h) == S_OK) - { - IXCLRDataMethodDefinition *pMeth = NULL; - while (module->EnumMethodDefinitionByName(&h, &pMeth) == S_OK) - { - mdMethodDef methodToken; - ToRelease<IXCLRDataModule> pUnusedModule; - IfFailRet(pMeth->GetTokenAndScope(&methodToken, &pUnusedModule)); - - Add(pModuleName, pMethodName, methodToken, mod, ilOffset); - pMeth->Release(); - } - module->EndEnumMethodDefinitionsByName(h); - } - } - return S_OK; - } - - // Return TRUE if there might be more instances that will be JITTED later - static BOOL ResolveMethodInstances(IXCLRDataMethodDefinition *pMeth, DWORD ilOffset) - { - BOOL bFoundCode = FALSE; - BOOL bNeedDefer = FALSE; - CLRDATA_ENUM h1; - - if (pMeth->StartEnumInstances (NULL, &h1) == S_OK) - { - IXCLRDataMethodInstance *inst = NULL; - while (pMeth->EnumInstance (&h1, &inst) == S_OK) - { - BOOL foundByIlOffset = FALSE; - ULONG32 rangesNeeded = 0; - if(inst->GetAddressRangesByILOffset(ilOffset, 0, &rangesNeeded, NULL) == S_OK) - { - ArrayHolder<CLRDATA_ADDRESS_RANGE> ranges = new NOTHROW CLRDATA_ADDRESS_RANGE[rangesNeeded]; - if (ranges != NULL) - { - if (inst->GetAddressRangesByILOffset(ilOffset, rangesNeeded, NULL, ranges) == S_OK) - { - for (DWORD i = 0; i < rangesNeeded; i++) - { - IssueDebuggerBPCommand(ranges[i].startAddress); - bFoundCode = TRUE; - foundByIlOffset = TRUE; - } - } - } - } - - if (!foundByIlOffset && ilOffset == 0) - { - CLRDATA_ADDRESS addr = 0; - if (inst->GetRepresentativeEntryAddress(&addr) == S_OK) - { - IssueDebuggerBPCommand(addr); - bFoundCode = TRUE; - } - } - } - pMeth->EndEnumInstances (h1); - } - - // if this is a generic method we need to add a deferred bp - BOOL bGeneric = FALSE; - pMeth->HasClassOrMethodInstantiation(&bGeneric); - - bNeedDefer = !bFoundCode || bGeneric; - // This is down here because we only need to call SetCodeNofiication once. - if (bNeedDefer) - { - if (pMeth->SetCodeNotification (CLRDATA_METHNOTIFY_GENERATED) != S_OK) - { - bNeedDefer = FALSE; - ExtOut("Failed to set code notification\n"); - } - } - return bNeedDefer; - } - -private: - BOOL IsIn(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod) - { - PendingBreakpoint *pCur = m_breakpoints; - while(pCur) - { - if (pCur->ModuleMatches(mod) && - _wcsicmp(pCur->szModuleName, szModule) == 0 && - _wcscmp(pCur->szFunctionName, szName) == 0) - { - return TRUE; - } - pCur = pCur->pNext; - } - return FALSE; - } - - BOOL IsIn(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod) - { - PendingBreakpoint *pCur = m_breakpoints; - while(pCur) - { - if (pCur->ModuleMatches(mod) && - _wcsicmp(pCur->szFilename, szFilename) == 0 && - pCur->lineNumber == lineNumber) - { - return TRUE; - } - pCur = pCur->pNext; - } - return FALSE; - } - - BOOL IsIn(mdMethodDef token, TADDR mod, DWORD ilOffset) - { - PendingBreakpoint *pCur = m_breakpoints; - while(pCur) - { - if (pCur->ModuleMatches(mod) && - pCur->methodToken == token && - pCur->ilOffset == ilOffset) - { - return TRUE; - } - pCur = pCur->pNext; - } - return FALSE; - } - - void Delete(PendingBreakpoint *pDelete) - { - PendingBreakpoint *pCur = m_breakpoints; - PendingBreakpoint *pPrev = NULL; - while(pCur) - { - if (pCur == pDelete) - { - if (pPrev == NULL) - { - m_breakpoints = pCur->pNext; - } - else - { - pPrev->pNext = pCur->pNext; - } - delete pCur; - return; - } - pPrev = pCur; - pCur = pCur->pNext; - } - } - - - - HRESULT ResolvePendingNonModuleBoundBreakpoint(TADDR mod, PendingBreakpoint *pCur, SymbolReader* pSymbolReader) - { - // This function only works with pending breakpoints that are not module bound. - if (pCur->pModule == NULL) - { - if(pCur->szModuleName[0] != L'\0') - { - return ResolvePendingNonModuleBoundBreakpoint(pCur->szModuleName, pCur->szFunctionName, mod, pCur->ilOffset); - } - else - { - return ResolvePendingNonModuleBoundBreakpoint(pCur->szFilename, pCur->lineNumber, mod, pSymbolReader); - } - } - else - { - return S_OK; - } - } - - // Returns TRUE if further instances may be jitted, FALSE if all instances are now resolved - BOOL ResolvePendingBreakpoint(TADDR addr, PendingBreakpoint *pCur) - { - // Only go forward if the module matches the current PendingBreakpoint - if (!pCur->ModuleMatches(addr)) - { - return FALSE; - } - - ToRelease<IXCLRDataModule> mod; - if (FAILED(g_sos->GetModule(addr, &mod))) - { - return FALSE; - } - - if(pCur->methodToken == 0) - { - return FALSE; - } - - ToRelease<IXCLRDataMethodDefinition> pMeth = NULL; - mod->GetMethodDefinitionByToken(pCur->methodToken, &pMeth); - - // We may not need the code notification. Maybe it was ngen'd and we - // already have the method? - // We can delete the current entry if ResolveMethodInstances() set all BPs - return ResolveMethodInstances(pMeth, pCur->ilOffset); - } -}; - -Breakpoints g_bpoints; - -// Controls whether optimizations are disabled on module load and whether NGEN can be used -BOOL g_fAllowJitOptimization = TRUE; - -// Controls whether a one-shot breakpoint should be inserted the next time -// execution is about to enter a catch clause -BOOL g_stopOnNextCatch = FALSE; - -// According to the latest debuggers these callbacks will not get called -// unless the user (or an extension, like SOS :-)) had previously enabled -// clrn with "sxe clrn". -class CNotification : public IXCLRDataExceptionNotification5 -{ - static int s_condemnedGen; - - int m_count; - int m_dbgStatus; -public: - CNotification() - : m_count(0) - , m_dbgStatus(DEBUG_STATUS_NO_CHANGE) - {} - - int GetDebugStatus() - { - return m_dbgStatus; - } - - STDMETHODIMP QueryInterface (REFIID iid, void **ppvObject) - { - if (ppvObject == NULL) - return E_INVALIDARG; - - if (IsEqualIID(iid, IID_IUnknown) - || IsEqualIID(iid, IID_IXCLRDataExceptionNotification) - || IsEqualIID(iid, IID_IXCLRDataExceptionNotification2) - || IsEqualIID(iid, IID_IXCLRDataExceptionNotification3) - || IsEqualIID(iid, IID_IXCLRDataExceptionNotification4) - || IsEqualIID(iid, IID_IXCLRDataExceptionNotification5)) - { - *ppvObject = static_cast<IXCLRDataExceptionNotification5*>(this); - AddRef(); - return S_OK; - } - else - return E_NOINTERFACE; - - } - - STDMETHODIMP_(ULONG) AddRef(void) { return ++m_count; } - STDMETHODIMP_(ULONG) Release(void) - { - m_count--; - if (m_count < 0) - { - m_count = 0; - } - return m_count; - } - - - /* - * New code was generated or discarded for a method.: - */ - STDMETHODIMP OnCodeGenerated(IXCLRDataMethodInstance* method) - { - m_dbgStatus = DEBUG_STATUS_GO_HANDLED; - return S_OK; - } - - STDMETHODIMP OnCodeGenerated2(IXCLRDataMethodInstance* method, CLRDATA_ADDRESS nativeCodeLocation) - { - // Some method has been generated, make a breakpoint. - ULONG32 len = mdNameLen; - LPWSTR szModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR)); - if (method->GetName(0, mdNameLen, &len, g_mdName) == S_OK) - { - ToRelease<IXCLRDataModule> pMod; - HRESULT hr = method->GetTokenAndScope(NULL, &pMod); - if (SUCCEEDED(hr)) - { - len = mdNameLen; - if (pMod->GetName(mdNameLen, &len, szModuleName) == S_OK) - { - ExtOut("JITTED %S!%S\n", szModuleName, g_mdName); - - DacpGetModuleAddress dgma; - if (SUCCEEDED(dgma.Request(pMod))) - { - g_bpoints.UpdateKnownCodeAddress(TO_TADDR(dgma.ModulePtr), nativeCodeLocation); - } - else - { - ExtOut("Failed to request module address.\n"); - } - } - } - } - - m_dbgStatus = DEBUG_STATUS_GO_HANDLED; - return S_OK; - } - - STDMETHODIMP OnCodeDiscarded(IXCLRDataMethodInstance* method) - { - return E_NOTIMPL; - } - - /* - * The process or task reached the desired execution state. - */ - STDMETHODIMP OnProcessExecution(ULONG32 state) { return E_NOTIMPL; } - STDMETHODIMP OnTaskExecution(IXCLRDataTask* task, - ULONG32 state) { return E_NOTIMPL; } - - /* - * The given module was loaded or unloaded. - */ - STDMETHODIMP OnModuleLoaded(IXCLRDataModule* mod) - { - DacpGetModuleAddress dgma; - if (SUCCEEDED(dgma.Request(mod))) - { - g_bpoints.Update(TO_TADDR(dgma.ModulePtr), TRUE); - } - - if(!g_fAllowJitOptimization) - { - HRESULT hr; - ToRelease<IXCLRDataModule2> mod2; - if(FAILED(mod->QueryInterface(__uuidof(IXCLRDataModule2), (void**) &mod2))) - { - ExtOut("SOS: warning, optimizations for this module could not be suppressed because this CLR version doesn't support the functionality\n"); - } - else if(FAILED(hr = mod2->SetJITCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION))) - { - if(hr == CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE) - ExtOut("SOS: warning, optimizations for this module could not be surpressed because an optimized prejitted image was loaded\n"); - else - ExtOut("SOS: warning, optimizations for this module could not be surpressed hr=0x%x\n", hr); - } - } - - m_dbgStatus = DEBUG_STATUS_GO_HANDLED; - return S_OK; - } - - STDMETHODIMP OnModuleUnloaded(IXCLRDataModule* mod) - { - DacpGetModuleAddress dgma; - if (SUCCEEDED(dgma.Request(mod))) - { - g_bpoints.RemovePendingForModule(TO_TADDR(dgma.ModulePtr)); - } - - m_dbgStatus = DEBUG_STATUS_GO_HANDLED; - return S_OK; - } - - /* - * The given type was loaded or unloaded. - */ - STDMETHODIMP OnTypeLoaded(IXCLRDataTypeInstance* typeInst) - { return E_NOTIMPL; } - STDMETHODIMP OnTypeUnloaded(IXCLRDataTypeInstance* typeInst) - { return E_NOTIMPL; } - - STDMETHODIMP OnAppDomainLoaded(IXCLRDataAppDomain* domain) - { return E_NOTIMPL; } - STDMETHODIMP OnAppDomainUnloaded(IXCLRDataAppDomain* domain) - { return E_NOTIMPL; } - STDMETHODIMP OnException(IXCLRDataExceptionState* exception) - { return E_NOTIMPL; } - - STDMETHODIMP OnGcEvent(GcEvtArgs gcEvtArgs) -{ - // by default don't stop on these notifications... - m_dbgStatus = DEBUG_STATUS_GO_HANDLED; - - IXCLRDataProcess2* idp2 = NULL; - if (SUCCEEDED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2))) - { - if (gcEvtArgs.typ == GC_MARK_END) - { - // erase notification request - GcEvtArgs gea = { GC_MARK_END, { 0 } }; - idp2->SetGcNotification(gea); - - s_condemnedGen = bitidx(gcEvtArgs.condemnedGeneration); - - ExtOut("CLR notification: GC - Performing a gen %d collection. Determined surviving objects...\n", s_condemnedGen); - - // GC_MARK_END notification means: give the user a chance to examine the debuggee - m_dbgStatus = DEBUG_STATUS_BREAK; - } - } - - return S_OK; - } - - /* - * Catch is about to be entered - */ - STDMETHODIMP ExceptionCatcherEnter(IXCLRDataMethodInstance* method, DWORD catcherNativeOffset) - { - if(g_stopOnNextCatch) - { - CLRDATA_ADDRESS startAddr; - if(method->GetRepresentativeEntryAddress(&startAddr) == S_OK) - { - CHAR buffer[100]; -#ifndef FEATURE_PAL - sprintf_s(buffer, _countof(buffer), "bp /1 %p", (void*) (size_t) (startAddr+catcherNativeOffset)); -#else - sprintf_s(buffer, _countof(buffer), "breakpoint set --one-shot --address 0x%p", (void*) (size_t) (startAddr+catcherNativeOffset)); -#endif - g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0); - } - g_stopOnNextCatch = FALSE; - } - - m_dbgStatus = DEBUG_STATUS_GO_HANDLED; - return S_OK; - } - - static int GetCondemnedGen() - { - return s_condemnedGen; - } - -}; - -int CNotification::s_condemnedGen = -1; - -BOOL CheckCLRNotificationEvent(DEBUG_LAST_EVENT_INFO_EXCEPTION* pdle) -{ - ISOSDacInterface4 *psos4 = NULL; - CLRDATA_ADDRESS arguments[3]; - HRESULT Status; - - if (SUCCEEDED(Status = g_sos->QueryInterface(__uuidof(ISOSDacInterface4), (void**) &psos4))) - { - int count = _countof(arguments); - int countNeeded = 0; - - Status = psos4->GetClrNotification(arguments, count, &countNeeded); - psos4->Release(); - - if (SUCCEEDED(Status)) - { - memset(&pdle->ExceptionRecord, 0, sizeof(pdle->ExceptionRecord)); - pdle->FirstChance = TRUE; - pdle->ExceptionRecord.ExceptionCode = CLRDATA_NOTIFY_EXCEPTION; - - _ASSERTE(count <= EXCEPTION_MAXIMUM_PARAMETERS); - for (int i = 0; i < count; i++) - { - pdle->ExceptionRecord.ExceptionInformation[i] = arguments[i]; - } - // The rest of the ExceptionRecord isn't used by TranslateExceptionRecordToNotification - return TRUE; - } - // No pending exception notification - return FALSE; - } - - // The new DAC based interface doesn't exists so ask the debugger for the last exception - // information. NOTE: this function doesn't work on xplat version when the coreclr symbols - // have been stripped. - - ULONG Type, ProcessId, ThreadId; - ULONG ExtraInformationUsed; - Status = g_ExtControl->GetLastEventInformation( - &Type, - &ProcessId, - &ThreadId, - pdle, - sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION), - &ExtraInformationUsed, - NULL, - 0, - NULL); - - if (Status != S_OK || Type != DEBUG_EVENT_EXCEPTION) - { - return FALSE; - } - - if (!pdle->FirstChance || pdle->ExceptionRecord.ExceptionCode != CLRDATA_NOTIFY_EXCEPTION) - { - return FALSE; - } - - return TRUE; -} - -HRESULT HandleCLRNotificationEvent() -{ - /* - * Did we get module load notification? If so, check if any in our pending list - * need to be registered for jit notification. - * - * Did we get a jit notification? If so, check if any can be removed and - * real breakpoints be set. - */ - DEBUG_LAST_EVENT_INFO_EXCEPTION dle; - CNotification Notification; - - if (!CheckCLRNotificationEvent(&dle)) - { -#ifndef FEATURE_PAL - ExtOut("Expecting first chance CLRN exception\n"); - return E_FAIL; -#else - g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0); - return S_OK; -#endif - } - - // Notification only needs to live for the lifetime of the call below, so it's a non-static - // local. - HRESULT Status = g_clrData->TranslateExceptionRecordToNotification(&dle.ExceptionRecord, &Notification); - if (Status != S_OK) - { - ExtErr("Error processing exception notification\n"); - return Status; - } - else - { - switch (Notification.GetDebugStatus()) - { - case DEBUG_STATUS_GO: - case DEBUG_STATUS_GO_HANDLED: - case DEBUG_STATUS_GO_NOT_HANDLED: -#ifndef FEATURE_PAL - g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "g", 0); -#else - g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0); -#endif - break; - default: - break; - } - } - - return S_OK; -} - -#ifndef FEATURE_PAL - -DECLARE_API(HandleCLRN) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - return HandleCLRNotificationEvent(); -} - -#else // FEATURE_PAL - -HRESULT HandleExceptionNotification(ILLDBServices *client) -{ - INIT_API(); - return HandleCLRNotificationEvent(); -} - -#endif // FEATURE_PAL - -DECLARE_API(bpmd) -{ - INIT_API_NOEE(); - MINIDUMP_NOT_SUPPORTED(); - char buffer[1024]; - - if (IsDumpFile()) - { - ExtOut(SOSPrefix "bpmd is not supported on a dump file.\n"); - return Status; - } - - - // We keep a list of managed breakpoints the user wants to set, and display pending bps - // bpmd. If you call bpmd <module name> <method> we will set or update an existing bp. - // bpmd acts as a feeder of breakpoints to bp when the time is right. - // - - StringHolder DllName,TypeName; - int lineNumber = 0; - size_t Offset = 0; - - DWORD_PTR pMD = NULL; - BOOL fNoFutureModule = FALSE; - BOOL fList = FALSE; - size_t clearItem = 0; - BOOL fClearAll = FALSE; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-md", &pMD, COHEX, TRUE}, - {"-nofuturemodule", &fNoFutureModule, COBOOL, FALSE}, - {"-list", &fList, COBOOL, FALSE}, - {"-clear", &clearItem, COSIZE_T, TRUE}, - {"-clearall", &fClearAll, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&DllName.data, COSTRING}, - {&TypeName.data, COSTRING}, - {&Offset, COSIZE_T}, - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - bool fBadParam = false; - bool fIsFilename = false; - int commandsParsed = 0; - - if (pMD != NULL) - { - if (nArg != 0) - { - fBadParam = true; - } - commandsParsed++; - } - if (fList) - { - commandsParsed++; - if (nArg != 0) - { - fBadParam = true; - } - } - if (fClearAll) - { - commandsParsed++; - if (nArg != 0) - { - fBadParam = true; - } - } - if (clearItem != 0) - { - commandsParsed++; - if (nArg != 0) - { - fBadParam = true; - } - } - if (1 <= nArg && nArg <= 3) - { - commandsParsed++; - // did we get dll and type name or file:line#? Search for a colon in the first arg - // to see if it is in fact a file:line# - CHAR* pColon = strchr(DllName.data, ':'); -#ifndef FEATURE_PAL - if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A, 0, NULL, NULL))) { -#else - if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_DLL_NAME_A, 0, NULL, NULL))) { -#endif - ExtOut("%s not loaded yet\n", MAIN_CLR_DLL_NAME_A); - return Status; - } - - if(NULL != pColon) - { - fIsFilename = true; - *pColon = '\0'; - pColon++; - if(1 != sscanf_s(pColon, "%d", &lineNumber)) - { - ExtOut("Unable to parse line number\n"); - fBadParam = true; - } - else if(lineNumber < 0) - { - ExtOut("Line number must be positive\n"); - fBadParam = true; - } - if(nArg != 1) fBadParam = 1; - } - } - - if (fBadParam || (commandsParsed != 1)) - { - ExtOut("Usage: " SOSPrefix "bpmd -md <MethodDesc pointer>\n"); - ExtOut("Usage: " SOSPrefix "bpmd [-nofuturemodule] <module name> <managed function name> [<il offset>]\n"); - ExtOut("Usage: " SOSPrefix "bpmd <filename>:<line number>\n"); - ExtOut("Usage: " SOSPrefix "bpmd -list\n"); - ExtOut("Usage: " SOSPrefix "bpmd -clear <pending breakpoint number>\n"); - ExtOut("Usage: " SOSPrefix "bpmd -clearall\n"); -#ifdef FEATURE_PAL - ExtOut("See \"soshelp bpmd\" for more details.\n"); -#else - ExtOut("See \"!help bpmd\" for more details.\n"); -#endif - return Status; - } - - if (fList) - { - g_bpoints.ListBreakpoints(); - return Status; - } - if (clearItem != 0) - { - g_bpoints.ClearBreakpoint(clearItem); - return Status; - } - if (fClearAll) - { - g_bpoints.ClearAllBreakpoints(); - return Status; - } - // Add a breakpoint - // Do we already have this breakpoint? - // Or, before setting it, is the module perhaps already loaded and code - // is available? If so, don't add to our pending list, just go ahead and - // set the real breakpoint. - - LPWSTR ModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR)); - LPWSTR FunctionName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR)); - LPWSTR Filename = (LPWSTR)alloca(MAX_LONGPATH * sizeof(WCHAR)); - - BOOL bNeedNotificationExceptions = FALSE; - - if (pMD == NULL) - { - int numModule = 0; - int numMethods = 0; - - ArrayHolder<DWORD_PTR> moduleList = NULL; - - if(!fIsFilename) - { - MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, ModuleName, mdNameLen); - MultiByteToWideChar(CP_ACP, 0, TypeName.data, -1, FunctionName, mdNameLen); - } - else - { - MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, Filename, MAX_LONGPATH); - } - - // Get modules that may need a breakpoint bound - if ((Status = CheckEEDll()) == S_OK) - { - if ((Status = LoadClrDebugDll()) != S_OK) - { - // if the EE is loaded but DAC isn't we should stop. - DACMessage(Status); - return Status; - } - g_bDacBroken = FALSE; \ - - // Get the module list - moduleList = ModuleFromName(fIsFilename ? NULL : DllName.data, &numModule); - - // Its OK if moduleList is NULL - // There is a very normal case when checking for modules after clr is loaded - // but before any AppDomains or assemblies are created - // for example: - // >sxe ld:clr - // >g - // ... - // ModLoad: clr.dll - // >!bpmd Foo.dll Foo.Bar - } - // If LoadClrDebugDll() succeeded make sure we release g_clrData - ToRelease<IXCLRDataProcess> spIDP(g_clrData); - ToRelease<ISOSDacInterface> spISD(g_sos); - ResetGlobals(); - - // we can get here with EE not loaded => 0 modules - // EE is loaded => 0 or more modules - ArrayHolder<DWORD_PTR> pMDs = NULL; - for (int iModule = 0; iModule < numModule; iModule++) - { - ToRelease<IXCLRDataModule> ModDef; - if (g_sos->GetModule(moduleList[iModule], &ModDef) != S_OK) - { - continue; - } - - HRESULT symbolsLoaded = S_FALSE; - if(!fIsFilename) - { - g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, moduleList[iModule], (DWORD)Offset); - } - else - { - SymbolReader symbolReader; - symbolsLoaded = g_bpoints.LoadSymbolsForModule(moduleList[iModule], &symbolReader); - if(symbolsLoaded == S_OK && - g_bpoints.ResolvePendingNonModuleBoundBreakpoint(Filename, lineNumber, moduleList[iModule], &symbolReader) == S_OK) - { - // if we have symbols then get the function name so we can lookup the MethodDescs - mdMethodDef methodDefToken; - ULONG32 ilOffset; - if(SUCCEEDED(symbolReader.ResolveSequencePoint(Filename, lineNumber, moduleList[iModule], &methodDefToken, &ilOffset))) - { - ToRelease<IXCLRDataMethodDefinition> pMethodDef = NULL; - if (SUCCEEDED(ModDef->GetMethodDefinitionByToken(methodDefToken, &pMethodDef))) - { - ULONG32 nameLen = 0; - pMethodDef->GetName(0, mdNameLen, &nameLen, FunctionName); - - // get the size of the required buffer - int buffSize = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, 0, NULL, NULL); - - TypeName.data = new NOTHROW char[buffSize]; - if (TypeName.data != NULL) - { - int bytesWritten = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, buffSize, NULL, NULL); - _ASSERTE(bytesWritten == buffSize); - } - } - } - } - } - - HRESULT gotMethodDescs = GetMethodDescsFromName(moduleList[iModule], ModDef, TypeName.data, &pMDs, &numMethods); - if (FAILED(gotMethodDescs) && (!fIsFilename)) - { - // BPs via file name will enumerate through modules so there will be legitimate failures. - // for module/type name we already found a match so this shouldn't fail (this is the original behavior). - ExtOut("Error getting MethodDescs for module %p\n", moduleList[iModule]); - return Status; - } - - // for filename+line number only print extra info if symbols for this module are loaded (it can get quite noisy otherwise). - if ((!fIsFilename) || (fIsFilename && symbolsLoaded == S_OK)) - { - for (int i = 0; i < numMethods; i++) - { - if (pMDs[i] == MD_NOT_YET_LOADED) - { - continue; - } - ExtOut("MethodDesc = %p\n", SOS_PTR(pMDs[i])); - } - } - - if (g_bpoints.Update(moduleList[iModule], FALSE)) - { - bNeedNotificationExceptions = TRUE; - } - } - - if (!fNoFutureModule) - { - // add a pending breakpoint that will find future loaded modules, and - // wait for the module load notification. - if (!fIsFilename) - { - g_bpoints.Add(ModuleName, FunctionName, NULL, (DWORD)Offset); - } - else - { - g_bpoints.Add(Filename, lineNumber, NULL); - } - bNeedNotificationExceptions = TRUE; - - ULONG32 flags = 0; - g_clrData->GetOtherNotificationFlags(&flags); - flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD); - g_clrData->SetOtherNotificationFlags(flags); - } - } - else /* We were given a MethodDesc already */ - { - // if we've got an explicit MD, then we better have CLR and mscordacwks loaded - INIT_API_EE() - INIT_API_DAC(); - - DacpMethodDescData MethodDescData; - ExtOut("MethodDesc = %p\n", SOS_PTR(pMD)); - if (MethodDescData.Request(g_sos, TO_CDADDR(pMD)) != S_OK) - { - ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(pMD)); - return Status; - } - - if (MethodDescData.bHasNativeCode) - { - IssueDebuggerBPCommand((size_t) MethodDescData.NativeCodeAddr); - } - else if (MethodDescData.bIsDynamic) - { -#ifndef FEATURE_PAL - // Dynamic methods don't have JIT notifications. This is something we must - // fix in the next release. Until then, you have a cumbersome user experience. - ExtOut("This DynamicMethodDesc is not yet JITTED. Placing memory breakpoint at %p\n", - MethodDescData.AddressOfNativeCodeSlot); - - sprintf_s(buffer, _countof(buffer), -#ifdef _TARGET_WIN64_ - "ba w8" -#else - "ba w4" -#endif // _TARGET_WIN64_ - - " /1 %p \"bp poi(%p); g\"", - (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot, - (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot); - - Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0); - if (FAILED(Status)) - { - ExtOut("Unable to set breakpoint with IDebugControl::Execute: %x\n",Status); - ExtOut("Attempted to run: %s\n", buffer); - } -#else - ExtErr("This DynamicMethodDesc is not yet JITTED %p\n", MethodDescData.AddressOfNativeCodeSlot); -#endif // FEATURE_PAL - } - else - { - // Must issue a pending breakpoint. - if (g_sos->GetMethodDescName(pMD, mdNameLen, FunctionName, NULL) != S_OK) - { - ExtOut("Unable to get method name for MethodDesc %p\n", SOS_PTR(pMD)); - return Status; - } - - FileNameForModule ((DWORD_PTR) MethodDescData.ModulePtr, ModuleName); - - // We didn't find code, add a breakpoint. - g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, TO_TADDR(MethodDescData.ModulePtr), 0); - g_bpoints.Update(TO_TADDR(MethodDescData.ModulePtr), FALSE); - bNeedNotificationExceptions = TRUE; - } - } - - if (bNeedNotificationExceptions) - { - ExtOut("Adding pending breakpoints...\n"); -#ifndef FEATURE_PAL - sprintf_s(buffer, _countof(buffer), "sxe -c \"!HandleCLRN\" clrn"); - Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0); -#else - Status = g_ExtServices->SetExceptionCallback(HandleExceptionNotification); -#endif // FEATURE_PAL - } - - return Status; -} - -#ifndef FEATURE_PAL - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the managed threadpool * -* * -\**********************************************************************/ -DECLARE_API(ThreadPool) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - DacpThreadpoolData threadpool; - - if ((Status = threadpool.Request(g_sos)) == S_OK) - { - BOOL doHCDump = FALSE, doWorkItemDump = FALSE, dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-ti", &doHCDump, COBOOL, FALSE}, - {"-wi", &doWorkItemDump, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - - if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - - ExtOut ("CPU utilization: %d%%\n", threadpool.cpuUtilization); - ExtOut ("Worker Thread:"); - ExtOut (" Total: %d", threadpool.NumWorkingWorkerThreads + threadpool.NumIdleWorkerThreads + threadpool.NumRetiredWorkerThreads); - ExtOut (" Running: %d", threadpool.NumWorkingWorkerThreads); - ExtOut (" Idle: %d", threadpool.NumIdleWorkerThreads); - ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalWorkerThreads); - ExtOut (" MinLimit: %d", threadpool.MinLimitTotalWorkerThreads); - ExtOut ("\n"); - - int numWorkRequests = 0; - CLRDATA_ADDRESS workRequestPtr = threadpool.FirstUnmanagedWorkRequest; - DacpWorkRequestData workRequestData; - while (workRequestPtr) - { - if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK) - { - ExtOut(" Failed to examine a WorkRequest\n"); - return Status; - } - numWorkRequests++; - workRequestPtr = workRequestData.NextWorkRequest; - } - - ExtOut ("Work Request in Queue: %d\n", numWorkRequests); - workRequestPtr = threadpool.FirstUnmanagedWorkRequest; - while (workRequestPtr) - { - if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK) - { - ExtOut(" Failed to examine a WorkRequest\n"); - return Status; - } - - if (workRequestData.Function == threadpool.AsyncTimerCallbackCompletionFPtr) - ExtOut (" AsyncTimerCallbackCompletion TimerInfo@%p\n", SOS_PTR(workRequestData.Context)); - else - ExtOut (" Unknown Function: %p Context: %p\n", SOS_PTR(workRequestData.Function), - SOS_PTR(workRequestData.Context)); - - workRequestPtr = workRequestData.NextWorkRequest; - } - - if (doWorkItemDump && g_snapshot.Build()) - { - // Display a message if the heap isn't verified. - sos::GCHeap gcheap; - if (!gcheap.AreGCStructuresValid()) - { - DisplayInvalidStructuresMessage(); - } - - // Walk every heap item looking for the global queue and local queues. - ExtOut("\nQueued work items:\n%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "Queue", "Address", "Work Item"); - HeapStat stats; - for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr) - { - if (_wcscmp(itr->GetTypeName(), W("System.Threading.ThreadPoolWorkQueue")) == 0) - { - // We found a global queue (there should be only one, given one AppDomain). - // Get its workItems ConcurrentQueue<IThreadPoolWorkItem>. - int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("workItems")); - if (offset > 0) - { - DWORD_PTR workItemsConcurrentQueuePtr; - MOVE(workItemsConcurrentQueuePtr, itr->GetAddress() + offset); - if (sos::IsObject(workItemsConcurrentQueuePtr, false)) - { - // We got the ConcurrentQueue. Get its head segment. - sos::Object workItemsConcurrentQueue = TO_TADDR(workItemsConcurrentQueuePtr); - offset = GetObjFieldOffset(workItemsConcurrentQueue.GetAddress(), workItemsConcurrentQueue.GetMT(), W("_head")); - if (offset > 0) - { - // Now, walk from segment to segment, each of which contains an array of work items. - DWORD_PTR segmentPtr; - MOVE(segmentPtr, workItemsConcurrentQueue.GetAddress() + offset); - while (sos::IsObject(segmentPtr, false)) - { - sos::Object segment = TO_TADDR(segmentPtr); - - // Get the work items array. It's an array of Slot structs, which starts with the T. - offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_slots")); - if (offset <= 0) - { - break; - } - - DWORD_PTR slotsPtr; - MOVE(slotsPtr, segment.GetAddress() + offset); - if (!sos::IsObject(slotsPtr, false)) - { - break; - } - - // Walk every element in the array, outputting details on non-null work items. - DacpObjectData slotsArray; - if (slotsArray.Request(g_sos, TO_CDADDR(slotsPtr)) == S_OK && slotsArray.ObjectType == OBJ_ARRAY) - { - for (int i = 0; i < slotsArray.dwNumComponents; i++) - { - CLRDATA_ADDRESS workItemPtr; - MOVE(workItemPtr, TO_CDADDR(slotsArray.ArrayDataPtr + (i * slotsArray.dwComponentSize))); // the item object reference is at the beginning of the Slot - if (workItemPtr != NULL && sos::IsObject(workItemPtr, false)) - { - sos::Object workItem = TO_TADDR(workItemPtr); - stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize()); - DMLOut("%" POINTERSIZE "s %s %S", "[Global]", DMLObject(workItem.GetAddress()), workItem.GetTypeName()); - if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 || - (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0) - { - CLRDATA_ADDRESS delegatePtr; - MOVE(delegatePtr, workItem.GetAddress() + offset); - CLRDATA_ADDRESS md; - if (TryGetMethodDescriptorForDelegate(delegatePtr, &md)) - { - NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen); - ExtOut(" => %S", g_mdName); - } - } - ExtOut("\n"); - } - } - } - - // Move to the next segment. - DacpFieldDescData segmentField; - offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_nextSegment"), TRUE, &segmentField); - if (offset <= 0) - { - break; - } - - MOVE(segmentPtr, segment.GetAddress() + offset); - if (segmentPtr == NULL) - { - break; - } - } - } - } - } - } - else if (_wcscmp(itr->GetTypeName(), W("System.Threading.ThreadPoolWorkQueue+WorkStealingQueue")) == 0) - { - // We found a local queue. Get its work items array. - int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("m_array")); - if (offset > 0) - { - // Walk every element in the array, outputting details on non-null work items. - DWORD_PTR workItemArrayPtr; - MOVE(workItemArrayPtr, itr->GetAddress() + offset); - DacpObjectData workItemArray; - if (workItemArray.Request(g_sos, TO_CDADDR(workItemArrayPtr)) == S_OK && workItemArray.ObjectType == OBJ_ARRAY) - { - for (int i = 0; i < workItemArray.dwNumComponents; i++) - { - CLRDATA_ADDRESS workItemPtr; - MOVE(workItemPtr, TO_CDADDR(workItemArray.ArrayDataPtr + (i * workItemArray.dwComponentSize))); - if (workItemPtr != NULL && sos::IsObject(workItemPtr, false)) - { - sos::Object workItem = TO_TADDR(workItemPtr); - stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize()); - DMLOut("%s %s %S", DMLObject(itr->GetAddress()), DMLObject(workItem.GetAddress()), workItem.GetTypeName()); - if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 || - (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0) - { - CLRDATA_ADDRESS delegatePtr; - MOVE(delegatePtr, workItem.GetAddress() + offset); - CLRDATA_ADDRESS md; - if (TryGetMethodDescriptorForDelegate(delegatePtr, &md)) - { - NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen); - ExtOut(" => %S", g_mdName); - } - } - ExtOut("\n"); - } - } - } - } - } - } - - // Output a summary. - stats.Sort(); - stats.Print(); - ExtOut("\n"); - } - - if (doHCDump) - { - ExtOut ("--------------------------------------\n"); - ExtOut ("\nThread Injection History\n"); - if (threadpool.HillClimbingLogSize > 0) - { - static char const * const TransitionNames[] = - { - "Warmup", - "Initializing", - "RandomMove", - "ClimbingMove", - "ChangePoint", - "Stabilizing", - "Starvation", - "ThreadTimedOut", - "Undefined" - }; - - ExtOut("\n Time Transition New #Threads #Samples Throughput\n"); - DacpHillClimbingLogEntry entry; - - // get the most recent entry first, so we can calculate time offsets - - int index = (threadpool.HillClimbingLogFirstIndex + threadpool.HillClimbingLogSize-1) % HillClimbingLogCapacity; - CLRDATA_ADDRESS entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry)); - if ((Status = entry.Request(g_sos,entryPtr))!=S_OK) - { - ExtOut(" Failed to examine a HillClimbing log entry\n"); - return Status; - } - DWORD endTime = entry.TickCount; - - for (int i = 0; i < threadpool.HillClimbingLogSize; i++) - { - index = (i + threadpool.HillClimbingLogFirstIndex) % HillClimbingLogCapacity; - entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry)); - - if ((Status = entry.Request(g_sos,entryPtr))!=S_OK) - { - ExtOut(" Failed to examine a HillClimbing log entry\n"); - return Status; - } - - ExtOut("%8.2lf %-14s %12d %12d %11.2lf\n", - (double)(int)(entry.TickCount - endTime) / 1000.0, - TransitionNames[entry.Transition], - entry.NewControlSetting, - entry.LastHistoryCount, - entry.LastHistoryMean); - } - } - } - - ExtOut ("--------------------------------------\n"); - ExtOut ("Number of Timers: %d\n", threadpool.NumTimers); - ExtOut ("--------------------------------------\n"); - - ExtOut ("Completion Port Thread:"); - ExtOut ("Total: %d", threadpool.NumCPThreads); - ExtOut (" Free: %d", threadpool.NumFreeCPThreads); - ExtOut (" MaxFree: %d", threadpool.MaxFreeCPThreads); - ExtOut (" CurrentLimit: %d", threadpool.CurrentLimitTotalCPThreads); - ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalCPThreads); - ExtOut (" MinLimit: %d", threadpool.MinLimitTotalCPThreads); - ExtOut ("\n"); - } - else - { - ExtOut("Failed to request ThreadpoolMgr information\n"); - } - return Status; -} - -#endif // FEATURE_PAL - -DECLARE_API(FindAppDomain) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - DWORD_PTR p_Object = NULL; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - CMDValue arg[] = - { // vptr, type - {&p_Object, COHEX}, - }; - size_t nArg; - - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - - if ((p_Object == 0) || !sos::IsObject(p_Object)) - { - ExtOut("%p is not a valid object\n", SOS_PTR(p_Object)); - return Status; - } - - DacpAppDomainStoreData adstore; - if (adstore.Request(g_sos) != S_OK) - { - ExtOut("Error getting AppDomain information\n"); - return Status; - } - - CLRDATA_ADDRESS appDomain = GetAppDomain (TO_CDADDR(p_Object)); - - if (appDomain != NULL) - { - DMLOut("AppDomain: %s\n", DMLDomain(appDomain)); - if (appDomain == adstore.sharedDomain) - { - ExtOut("Name: Shared Domain\n"); - ExtOut("ID: (shared domain)\n"); - } - else if (appDomain == adstore.systemDomain) - { - ExtOut("Name: System Domain\n"); - ExtOut("ID: (system domain)\n"); - } - else - { - DacpAppDomainData domain; - if ((domain.Request(g_sos, appDomain) != S_OK) || - (g_sos->GetAppDomainName(appDomain,mdNameLen,g_mdName, NULL)!=S_OK)) - { - ExtOut("Error getting AppDomain %p.\n", SOS_PTR(appDomain)); - return Status; - } - - ExtOut("Name: %S\n", (g_mdName[0]!=L'\0') ? g_mdName : W("None")); - ExtOut("ID: %d\n", domain.dwId); - } - } - else - { - ExtOut("The type is declared in the shared domain and other\n"); - ExtOut("methods of finding the AppDomain failed. Try running\n"); - if (IsDMLEnabled()) - DMLOut("<exec cmd=\"!gcroot /d %p\">!gcroot %p</exec>, and if you find a root on a\n", p_Object, p_Object); - else - ExtOut(SOSPrefix "gcroot %p, and if you find a root on a\n", p_Object); - ExtOut("stack, check the AppDomain of that stack with " SOSThreads ".\n"); - ExtOut("Note that the Thread could have transitioned between\n"); - ExtOut("multiple AppDomains.\n"); - } - - return Status; -} - -#ifndef FEATURE_PAL - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to get the COM state (e.g. APT,contexe * -* activity. * -* * -\**********************************************************************/ -#ifdef FEATURE_COMINTEROP -DECLARE_API(COMState) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - - ULONG numThread; - ULONG maxId; - g_ExtSystem->GetTotalNumberThreads(&numThread,&maxId); - - ULONG curId; - g_ExtSystem->GetCurrentThreadId(&curId); - - SIZE_T AllocSize; - if (!ClrSafeInt<SIZE_T>::multiply(sizeof(ULONG), numThread, AllocSize)) - { - ExtOut(" Error! integer overflow on numThread 0x%08x\n", numThread); - return Status; - } - ULONG *ids = (ULONG*)alloca(AllocSize); - ULONG *sysIds = (ULONG*)alloca(AllocSize); - g_ExtSystem->GetThreadIdsByIndex(0,numThread,ids,sysIds); -#if defined(_TARGET_WIN64_) - ExtOut(" ID TEB APT APTId CallerTID Context\n"); -#else - ExtOut(" ID TEB APT APTId CallerTID Context\n"); -#endif - for (ULONG i = 0; i < numThread; i ++) { - g_ExtSystem->SetCurrentThreadId(ids[i]); - CLRDATA_ADDRESS cdaTeb; - g_ExtSystem->GetCurrentThreadTeb(&cdaTeb); - ExtOut("%3d %4x %p", ids[i], sysIds[i], SOS_PTR(CDA_TO_UL64(cdaTeb))); - // Apartment state - TADDR OleTlsDataAddr; - if (SafeReadMemory(TO_TADDR(cdaTeb) + offsetof(TEB,ReservedForOle), - &OleTlsDataAddr, - sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0) { - DWORD AptState; - if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags), - &AptState, - sizeof(AptState), NULL)) { - if (AptState & OLETLS_APARTMENTTHREADED) { - ExtOut(" STA"); - } - else if (AptState & OLETLS_MULTITHREADED) { - ExtOut(" MTA"); - } - else if (AptState & OLETLS_INNEUTRALAPT) { - ExtOut(" NTA"); - } - else { - ExtOut(" Ukn"); - } - - // Read these fields only if we were able to read anything of the SOleTlsData structure - DWORD dwApartmentID; - if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwApartmentID), - &dwApartmentID, - sizeof(dwApartmentID), NULL)) { - ExtOut(" %8x", dwApartmentID); - } - else - ExtOut(" %8x", 0); - - DWORD dwTIDCaller; - if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwTIDCaller), - &dwTIDCaller, - sizeof(dwTIDCaller), NULL)) { - ExtOut(" %8x", dwTIDCaller); - } - else - ExtOut(" %8x", 0); - - size_t Context; - if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,pCurrentCtx), - &Context, - sizeof(Context), NULL)) { - ExtOut(" %p", SOS_PTR(Context)); - } - else - ExtOut(" %p", SOS_PTR(0)); - - } - else - ExtOut(" Ukn"); - } - else - ExtOut(" Ukn"); - ExtOut("\n"); - } - - g_ExtSystem->SetCurrentThreadId(curId); - return Status; -} -#endif // FEATURE_COMINTEROP - -#endif // FEATURE_PAL - -BOOL traverseEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token) -{ - size_t methodStart = (size_t) token; - - if (IsInterrupt()) - { - return FALSE; - } - - ExtOut("EHHandler %d: %s ", clauseIndex, EHTypeName(pEHInfo->clauseType)); - - LPCWSTR typeName = EHTypedClauseTypeName(pEHInfo); - if (typeName != NULL) - { - ExtOut("catch(%S) ", typeName); - } - - if (IsClonedFinally(pEHInfo)) - ExtOut("(cloned finally)"); - else if (pEHInfo->isDuplicateClause) - ExtOut("(duplicate)"); - - ExtOut("\n"); - ExtOut("Clause: "); - - ULONG64 addrStart = pEHInfo->tryStartOffset + methodStart; - ULONG64 addrEnd = pEHInfo->tryEndOffset + methodStart; - -#ifdef _WIN64 - ExtOut("[%08x`%08x, %08x`%08x]", - (ULONG)(addrStart >> 32), (ULONG)addrStart, - (ULONG)(addrEnd >> 32), (ULONG)addrEnd); -#else - ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd); -#endif - - ExtOut(" [%x, %x]\n", - (UINT32) pEHInfo->tryStartOffset, - (UINT32) pEHInfo->tryEndOffset); - - ExtOut("Handler: "); - - addrStart = pEHInfo->handlerStartOffset + methodStart; - addrEnd = pEHInfo->handlerEndOffset + methodStart; - -#ifdef _WIN64 - ExtOut("[%08x`%08x, %08x`%08x]", - (ULONG)(addrStart >> 32), (ULONG)addrStart, - (ULONG)(addrEnd >> 32), (ULONG)addrEnd); -#else - ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd); -#endif - - ExtOut(" [%x, %x]\n", - (UINT32) pEHInfo->handlerStartOffset, - (UINT32) pEHInfo->handlerEndOffset); - - if (pEHInfo->clauseType == EHFilter) - { - ExtOut("Filter: "); - - addrStart = pEHInfo->filterOffset + methodStart; - -#ifdef _WIN64 - ExtOut("[%08x`%08x]", (ULONG)(addrStart >> 32), (ULONG)addrStart); -#else - ExtOut("[%08x]", (ULONG)addrStart); -#endif - - ExtOut(" [%x]\n", - (UINT32) pEHInfo->filterOffset); - } - - ExtOut("\n"); - return TRUE; -} - -DECLARE_API(EHInfo) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - DWORD_PTR dwStartAddr = NULL; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE}, - }; - - CMDValue arg[] = - { // vptr, type - {&dwStartAddr, COHEX}, - }; - - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - DWORD_PTR tmpAddr = dwStartAddr; - - if (!IsMethodDesc(dwStartAddr)) - { - JITTypes jitType; - DWORD_PTR methodDesc; - DWORD_PTR gcinfoAddr; - IP2MethodDesc (dwStartAddr, methodDesc, jitType, gcinfoAddr); - tmpAddr = methodDesc; - } - - DacpMethodDescData MD; - if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK)) - { - ExtOut("%p is not a MethodDesc\n", SOS_PTR(tmpAddr)); - return Status; - } - - if (1 == nArg && !MD.bHasNativeCode) - { - ExtOut("No EH info available\n"); - return Status; - } - - DacpCodeHeaderData codeHeaderData; - if (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK) - { - ExtOut("Unable to get codeHeader information\n"); - return Status; - } - - DMLOut("MethodDesc: %s\n", DMLMethodDesc(MD.MethodDescPtr)); - DumpMDInfo(TO_TADDR(MD.MethodDescPtr)); - - ExtOut("\n"); - Status = g_sos->TraverseEHInfo(TO_CDADDR(MD.NativeCodeAddr), traverseEh, (LPVOID)MD.NativeCodeAddr); - - if (Status == E_ABORT) - { - ExtOut("<user aborted>\n"); - } - else if (Status != S_OK) - { - ExtOut("Failed to perform EHInfo traverse\n"); - } - - return Status; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the GC encoding of a managed * -* function. * -* * -\**********************************************************************/ -DECLARE_API(GCInfo) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - - TADDR taStartAddr = NULL; - TADDR taGCInfoAddr; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&taStartAddr, COHEX}, - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - TADDR tmpAddr = taStartAddr; - - if (!IsMethodDesc(taStartAddr)) - { - JITTypes jitType; - TADDR methodDesc; - TADDR gcinfoAddr; - IP2MethodDesc(taStartAddr, methodDesc, jitType, gcinfoAddr); - tmpAddr = methodDesc; - } - - DacpMethodDescData MD; - if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK)) - { - ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(taStartAddr)); - return Status; - } - - if (1 == nArg && !MD.bHasNativeCode) - { - ExtOut("No GC info available\n"); - return Status; - } - - DacpCodeHeaderData codeHeaderData; - - if ( - // Try to get code header data from taStartAddr. This will get the code - // header corresponding to the IP address, even if the function was rejitted - (codeHeaderData.Request(g_sos, TO_CDADDR(taStartAddr)) != S_OK) && - - // If that didn't work, just try to use the code address that the MD - // points to. If the function was rejitted, this will only give you the - // original JITted code, but that's better than nothing - (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK) - ) - { - // We always used to emit this (before rejit support), even if we couldn't get - // the code header, so keep on doing so. - ExtOut("entry point %p\n", SOS_PTR(MD.NativeCodeAddr)); - - // And now the error.... - ExtOut("Unable to get codeHeader information\n"); - return Status; - } - - // We have the code header, so use it to determine the method start - - ExtOut("entry point %p\n", SOS_PTR(codeHeaderData.MethodStart)); - - if (codeHeaderData.JITType == TYPE_UNKNOWN) - { - ExtOut("unknown Jit\n"); - return Status; - } - else if (codeHeaderData.JITType == TYPE_JIT) - { - ExtOut("Normal JIT generated code\n"); - } - else if (codeHeaderData.JITType == TYPE_PJIT) - { - ExtOut("preJIT generated code\n"); - } - - taGCInfoAddr = TO_TADDR(codeHeaderData.GCInfo); - - ExtOut("GC info %p\n", SOS_PTR(taGCInfoAddr)); - - // assume that GC encoding table is never more than - // 40 + methodSize * 2 - int tableSize = 0; - if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) || - !ClrSafeInt<int>::addition(tableSize, 40, tableSize)) - { - ExtOut("<integer overflow>\n"); - return E_FAIL; - } - ArrayHolder<BYTE> table = new NOTHROW BYTE[tableSize]; - if (table == NULL) - { - ExtOut("Could not allocate memory to read the gc info.\n"); - return E_OUTOFMEMORY; - } - - memset(table, 0, tableSize); - // We avoid using move here, because we do not want to return - if (!SafeReadMemory(taGCInfoAddr, table, tableSize, NULL)) - { - ExtOut("Could not read memory %p\n", SOS_PTR(taGCInfoAddr)); - return Status; - } - - // Mutable table pointer since we need to pass the appropriate - // offset into the table to DumpGCTable. - GCInfoToken gcInfoToken = { table, GCINFO_VERSION }; - unsigned int methodSize = (unsigned int)codeHeaderData.MethodSize; - - g_targetMachine->DumpGCInfo(gcInfoToken, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/); - - return Status; -} - -#if !defined(FEATURE_PAL) - -void DecodeGCTableEntry (const char *fmt, ...) -{ - GCEncodingInfo *pInfo = (GCEncodingInfo*)GetFiberData(); - va_list va; - - // - // Append the new data to the buffer - // - - va_start(va, fmt); - - int cch = _vsnprintf_s(&pInfo->buf[pInfo->cch], _countof(pInfo->buf) - pInfo->cch, _countof(pInfo->buf) - pInfo->cch - 1, fmt, va); - if (cch >= 0) - pInfo->cch += cch; - - va_end(va); - - pInfo->buf[pInfo->cch] = '\0'; - - // - // If there are complete lines in the buffer, decode them. - // - - for (;;) - { - char *pNewLine = strchr(pInfo->buf, '\n'); - - if (!pNewLine) - break; - - // - // The line should start with a 16-bit (x86) or 32-bit (non-x86) hex - // offset. strtoul returns ULONG_MAX or 0 on failure. 0 is a valid - // offset for the first encoding, or while the last offset was 0. - // - - if (isxdigit(pInfo->buf[0])) - { - char *pEnd; - ULONG ofs = strtoul(pInfo->buf, &pEnd, 16); - - if ( isspace(*pEnd) - && -1 != ofs - && ( -1 == pInfo->ofs - || 0 == pInfo->ofs - || ofs > 0)) - { - pInfo->ofs = ofs; - *pNewLine = '\0'; - - SwitchToFiber(pInfo->pvMainFiber); - } - } - else if (0 == strncmp(pInfo->buf, "Untracked:", 10)) - { - pInfo->ofs = 0; - *pNewLine = '\0'; - - SwitchToFiber(pInfo->pvMainFiber); - } - - // - // Shift the remaining data to the start of the buffer - // - - strcpy_s(pInfo->buf, _countof(pInfo->buf), pNewLine+1); - pInfo->cch = (int)strlen(pInfo->buf); - } -} - - -VOID CALLBACK DumpGCTableFiberEntry (LPVOID pvGCEncodingInfo) -{ - GCEncodingInfo *pInfo = (GCEncodingInfo*)pvGCEncodingInfo; - GCInfoToken gcInfoToken = { pInfo->table, GCINFO_VERSION }; - g_targetMachine->DumpGCInfo(gcInfoToken, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/); - - pInfo->fDoneDecoding = true; - SwitchToFiber(pInfo->pvMainFiber); -} -#endif // !FEATURE_PAL - -BOOL gatherEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token) -{ - SOSEHInfo *pInfo = (SOSEHInfo *) token; - - if (pInfo == NULL) - { - return FALSE; - } - - if (pInfo->m_pInfos == NULL) - { - // First time, initialize structure - pInfo->EHCount = totalClauses; - pInfo->m_pInfos = new NOTHROW DACEHInfo[totalClauses]; - if (pInfo->m_pInfos == NULL) - { - ReportOOM(); - return FALSE; - } - } - - pInfo->m_pInfos[clauseIndex] = *((DACEHInfo*)pEHInfo); - return TRUE; -} - - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to unassembly a managed function. * -* It tries to print symbolic info for function call, contants... * -* * -\**********************************************************************/ -DECLARE_API(u) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - - DWORD_PTR dwStartAddr = NULL; - BOOL fWithGCInfo = FALSE; - BOOL fWithEHInfo = FALSE; - BOOL bSuppressLines = FALSE; - BOOL bDisplayOffsets = FALSE; - BOOL dml = FALSE; - size_t nArg; - - CMDOption option[] = - { // name, vptr, type, hasValue -#ifndef FEATURE_PAL - {"-gcinfo", &fWithGCInfo, COBOOL, FALSE}, -#endif - {"-ehinfo", &fWithEHInfo, COBOOL, FALSE}, - {"-n", &bSuppressLines, COBOOL, FALSE}, - {"-o", &bDisplayOffsets, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - CMDValue arg[] = - { // vptr, type - {&dwStartAddr, COHEX}, - }; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (nArg < 1)) - { - return Status; - } - // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options - ULONG symlines = 0; - if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines))) - { - symlines &= SYMOPT_LOAD_LINES; - } - bSuppressLines = bSuppressLines || (symlines == 0); - - EnableDMLHolder dmlHolder(dml); - // dwStartAddr is either some IP address or a MethodDesc. Start off assuming it's a - // MethodDesc. - DWORD_PTR methodDesc = dwStartAddr; - if (!IsMethodDesc(methodDesc)) - { - // Not a methodDesc, so gotta find it ourselves - DWORD_PTR tmpAddr = dwStartAddr; - JITTypes jt; - DWORD_PTR gcinfoAddr; - IP2MethodDesc (tmpAddr, methodDesc, jt, - gcinfoAddr); - if (!methodDesc || jt == TYPE_UNKNOWN) - { - // It is not managed code. - ExtOut("Unmanaged code\n"); - UnassemblyUnmanaged(dwStartAddr, bSuppressLines); - return Status; - } - } - - DacpMethodDescData MethodDescData; - if ((Status=MethodDescData.Request(g_sos, TO_CDADDR(methodDesc))) != S_OK) - { - ExtOut("Failed to get method desc for %p.\n", SOS_PTR(dwStartAddr)); - return Status; - } - - if (!MethodDescData.bHasNativeCode) - { - ExtOut("Not jitted yet\n"); - return Status; - } - - // Get the appropriate code header. If we were passed an MD, then use - // MethodDescData.NativeCodeAddr to find the code header; if we were passed an IP, use - // that IP to find the code header. This ensures that, for rejitted functions, we - // disassemble the rejit version that the user explicitly specified with their IP. - DacpCodeHeaderData codeHeaderData; - if (codeHeaderData.Request( - g_sos, - TO_CDADDR( - (dwStartAddr == methodDesc) ? MethodDescData.NativeCodeAddr : dwStartAddr) - ) != S_OK) - - { - ExtOut("Unable to get codeHeader information\n"); - return Status; - } - - if (codeHeaderData.MethodStart == 0) - { - ExtOut("not a valid MethodDesc\n"); - return Status; - } - - if (codeHeaderData.JITType == TYPE_UNKNOWN) - { - ExtOut("unknown Jit\n"); - return Status; - } - else if (codeHeaderData.JITType == TYPE_JIT) - { - ExtOut("Normal JIT generated code\n"); - } - else if (codeHeaderData.JITType == TYPE_PJIT) - { - ExtOut("preJIT generated code\n"); - } - - NameForMD_s(methodDesc, g_mdName, mdNameLen); - ExtOut("%S\n", g_mdName); - if (codeHeaderData.ColdRegionStart != NULL) - { - ExtOut("Begin %p, size %x. Cold region begin %p, size %x\n", - SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.HotRegionSize, - SOS_PTR(codeHeaderData.ColdRegionStart), codeHeaderData.ColdRegionSize); - } - else - { - ExtOut("Begin %p, size %x\n", SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.MethodSize); - } - -#if !defined(FEATURE_PAL) - // - // Set up to mix gc info with the code if requested - // - - GCEncodingInfo gcEncodingInfo = {0}; - - // The actual GC Encoding Table, this is updated during the course of the function. - gcEncodingInfo.table = NULL; - - // The holder to make sure we clean up the memory for the table - ArrayHolder<BYTE> table = NULL; - - if (fWithGCInfo) - { - // assume that GC encoding table is never more than 40 + methodSize * 2 - int tableSize = 0; - if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) || - !ClrSafeInt<int>::addition(tableSize, 40, tableSize)) - { - ExtOut("<integer overflow>\n"); - return E_FAIL; - } - - - // Assign the new array to the mutable gcEncodingInfo table and to the - // table ArrayHolder to clean this up when the function exits. - table = gcEncodingInfo.table = new NOTHROW BYTE[tableSize]; - - if (gcEncodingInfo.table == NULL) - { - ExtOut("Could not allocate memory to read the gc info.\n"); - return E_OUTOFMEMORY; - } - - memset (gcEncodingInfo.table, 0, tableSize); - // We avoid using move here, because we do not want to return - if (!SafeReadMemory(TO_TADDR(codeHeaderData.GCInfo), gcEncodingInfo.table, tableSize, NULL)) - { - ExtOut("Could not read memory %p\n", SOS_PTR(codeHeaderData.GCInfo)); - return Status; - } - - // - // Skip the info header - // - gcEncodingInfo.methodSize = (unsigned int)codeHeaderData.MethodSize; - - // - // DumpGCTable will call gcPrintf for each encoding. We'd like a "give - // me the next encoding" interface, but we're stuck with the callback. - // To reconcile this without messing up too much code, we'll create a - // fiber to dump the gc table. When we need the next gc encoding, - // we'll switch to this fiber. The callback will note the next offset, - // and switch back to the main fiber. - // - - gcEncodingInfo.ofs = -1; - gcEncodingInfo.hotSizeToAdd = 0; - - gcEncodingInfo.pvMainFiber = ConvertThreadToFiber(NULL); - if (!gcEncodingInfo.pvMainFiber && ERROR_ALREADY_FIBER == GetLastError()) - gcEncodingInfo.pvMainFiber = GetCurrentFiber(); - - if (!gcEncodingInfo.pvMainFiber) - return Status; - - gcEncodingInfo.pvGCTableFiber = CreateFiber(0, DumpGCTableFiberEntry, &gcEncodingInfo); - if (!gcEncodingInfo.pvGCTableFiber) - return Status; - - SwitchToFiber(gcEncodingInfo.pvGCTableFiber); - } -#endif - - SOSEHInfo *pInfo = NULL; - if (fWithEHInfo) - { - pInfo = new NOTHROW SOSEHInfo; - if (pInfo == NULL) - { - ReportOOM(); - } - else if (g_sos->TraverseEHInfo(MethodDescData.NativeCodeAddr, gatherEh, (LPVOID)pInfo) != S_OK) - { - ExtOut("Failed to gather EHInfo data\n"); - delete pInfo; - pInfo = NULL; - } - } - - if (codeHeaderData.ColdRegionStart == NULL) - { - g_targetMachine->Unassembly ( - (DWORD_PTR) codeHeaderData.MethodStart, - ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.MethodSize, - dwStartAddr, - (DWORD_PTR) MethodDescData.GCStressCodeCopy, -#if !defined(FEATURE_PAL) - fWithGCInfo ? &gcEncodingInfo : -#endif - NULL, - pInfo, - bSuppressLines, - bDisplayOffsets - ); - } - else - { - ExtOut("Hot region:\n"); - g_targetMachine->Unassembly ( - (DWORD_PTR) codeHeaderData.MethodStart, - ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.HotRegionSize, - dwStartAddr, - (DWORD_PTR) MethodDescData.GCStressCodeCopy, -#if !defined(FEATURE_PAL) - fWithGCInfo ? &gcEncodingInfo : -#endif - NULL, - pInfo, - bSuppressLines, - bDisplayOffsets - ); - - ExtOut("Cold region:\n"); - -#if !defined(FEATURE_PAL) - // Displaying gcinfo for a cold region requires knowing the size of - // the hot region preceeding. - gcEncodingInfo.hotSizeToAdd = codeHeaderData.HotRegionSize; -#endif - g_targetMachine->Unassembly ( - (DWORD_PTR) codeHeaderData.ColdRegionStart, - ((DWORD_PTR)codeHeaderData.ColdRegionStart) + codeHeaderData.ColdRegionSize, - dwStartAddr, - ((DWORD_PTR) MethodDescData.GCStressCodeCopy) + codeHeaderData.HotRegionSize, -#if !defined(FEATURE_PAL) - fWithGCInfo ? &gcEncodingInfo : -#endif - NULL, - pInfo, - bSuppressLines, - bDisplayOffsets - ); - - } - - if (pInfo) - { - delete pInfo; - pInfo = NULL; - } - -#if !defined(FEATURE_PAL) - if (fWithGCInfo) - DeleteFiber(gcEncodingInfo.pvGCTableFiber); -#endif - - return Status; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the in-memory stress log * -* !DumpLog [filename] * -* will dump the stress log corresponding to the clr.dll * -* loaded in the debuggee's VAS * -* !DumpLog -addr <addr_of_StressLog::theLog> [filename] * -* will dump the stress log associated with any DLL linked * -* against utilcode.lib, most commonly mscordbi.dll * -* (e.g. !DumpLog -addr mscordbi!StressLog::theLog) * -* * -\**********************************************************************/ -DECLARE_API(DumpLog) -{ - INIT_API_NO_RET_ON_FAILURE(); - - MINIDUMP_NOT_SUPPORTED(); - - const char* fileName = "StressLog.txt"; - - CLRDATA_ADDRESS StressLogAddress = NULL; - - StringHolder sFileName, sLogAddr; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-addr", &sLogAddr.data, COSTRING, TRUE} - }; - CMDValue arg[] = - { // vptr, type - {&sFileName.data, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - if (nArg > 0 && sFileName.data != NULL) - { - fileName = sFileName.data; - } - - // allow users to specify -addr mscordbdi!StressLog::theLog, for example. - if (sLogAddr.data != NULL) - { - StressLogAddress = GetExpression(sLogAddr.data); - } - - if (StressLogAddress == NULL) - { - if (g_bDacBroken) - { -#ifdef FEATURE_PAL - ExtOut("No stress log address. DAC is broken; can't get it\n"); - return E_FAIL; -#else - // Try to find stress log symbols - DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog"); - StressLogAddress = dwAddr; -#endif - } - else if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK) - { - ExtOut("Unable to find stress log via DAC\n"); - return E_FAIL; - } - } - - if (StressLogAddress == NULL) - { - ExtOut("Please provide the -addr argument for the address of the stress log, since no recognized runtime is loaded.\n"); - return E_FAIL; - } - - ExtOut("Attempting to dump Stress log to file '%s'\n", fileName); - - - - Status = StressLog::Dump(StressLogAddress, fileName, g_ExtData); - - if (Status == S_OK) - ExtOut("SUCCESS: Stress log dumped\n"); - else if (Status == S_FALSE) - ExtOut("No Stress log in the image, no file written\n"); - else - ExtOut("FAILURE: Stress log not dumped\n"); - - return Status; -} - -#ifndef FEATURE_PAL -DECLARE_API (DumpGCLog) -{ - INIT_API_NODAC(); - MINIDUMP_NOT_SUPPORTED(); - - if (GetEEFlavor() == UNKNOWNEE) - { - ExtOut("CLR not loaded\n"); - return Status; - } - - const char* fileName = "GCLog.txt"; - int iLogSize = 1024*1024; - BYTE* bGCLog = NULL; - int iRealLogSize = iLogSize - 1; - DWORD dwWritten = 0; - - while (isspace (*args)) - args ++; - - if (*args != 0) - fileName = args; - - DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_log_buffer"); - moveN (dwAddr, dwAddr); - - if (dwAddr == 0) - { - dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_log_buffer"); - moveN (dwAddr, dwAddr); - if (dwAddr == 0) - { - ExtOut("Can't get either WKS or SVR GC's log file"); - return E_FAIL; - } - } - - ExtOut("Dumping GC log at %08x\n", dwAddr); - - g_bDacBroken = FALSE; - - ExtOut("Attempting to dump GC log to file '%s'\n", fileName); - - Status = E_FAIL; - - HANDLE hGCLog = CreateFileA( - fileName, - GENERIC_WRITE, - FILE_SHARE_READ, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (hGCLog == INVALID_HANDLE_VALUE) - { - ExtOut("failed to create file: %d\n", GetLastError()); - goto exit; - } - - bGCLog = new NOTHROW BYTE[iLogSize]; - if (bGCLog == NULL) - { - ReportOOM(); - goto exit; - } - - memset (bGCLog, 0, iLogSize); - if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL)) - { - ExtOut("failed to read memory from %08x\n", dwAddr); - } - - while (iRealLogSize >= 0) - { - if (bGCLog[iRealLogSize] != '*') - { - break; - } - - iRealLogSize--; - } - - WriteFile (hGCLog, bGCLog, iRealLogSize + 1, &dwWritten, NULL); - - Status = S_OK; - -exit: - - if (bGCLog != NULL) - { - delete [] bGCLog; - } - - if (hGCLog != INVALID_HANDLE_VALUE) - { - CloseHandle (hGCLog); - } - - if (Status == S_OK) - ExtOut("SUCCESS: Stress log dumped\n"); - else if (Status == S_FALSE) - ExtOut("No Stress log in the image, no file written\n"); - else - ExtOut("FAILURE: Stress log not dumped\n"); - - return Status; -} - -DECLARE_API (DumpGCConfigLog) -{ - INIT_API(); -#ifdef GC_CONFIG_DRIVEN - MINIDUMP_NOT_SUPPORTED(); - - if (GetEEFlavor() == UNKNOWNEE) - { - ExtOut("CLR not loaded\n"); - return Status; - } - - const char* fileName = "GCConfigLog.txt"; - - while (isspace (*args)) - args ++; - - if (*args != 0) - fileName = args; - - if (!InitializeHeapData ()) - { - ExtOut("GC Heap not initialized yet.\n"); - return S_OK; - } - - BOOL fIsServerGC = IsServerBuild(); - - DWORD_PTR dwAddr = 0; - DWORD_PTR dwAddrOffset = 0; - - if (fIsServerGC) - { - dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer"); - dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer_offset"); - } - else - { - dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer"); - dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer_offset"); - } - - moveN (dwAddr, dwAddr); - moveN (dwAddrOffset, dwAddrOffset); - - if (dwAddr == 0) - { - ExtOut("Can't get either WKS or SVR GC's config log buffer"); - return E_FAIL; - } - - ExtOut("Dumping GC log at %08x\n", dwAddr); - - g_bDacBroken = FALSE; - - ExtOut("Attempting to dump GC log to file '%s'\n", fileName); - - Status = E_FAIL; - - HANDLE hGCLog = CreateFileA( - fileName, - GENERIC_WRITE, - FILE_SHARE_READ, - NULL, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (hGCLog == INVALID_HANDLE_VALUE) - { - ExtOut("failed to create file: %d\n", GetLastError()); - goto exit; - } - - { - int iLogSize = (int)dwAddrOffset; - - ArrayHolder<BYTE> bGCLog = new NOTHROW BYTE[iLogSize]; - if (bGCLog == NULL) - { - ReportOOM(); - goto exit; - } - - memset (bGCLog, 0, iLogSize); - if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL)) - { - ExtOut("failed to read memory from %08x\n", dwAddr); - } - - SetFilePointer (hGCLog, 0, 0, FILE_END); - DWORD dwWritten; - WriteFile (hGCLog, bGCLog, iLogSize, &dwWritten, NULL); - } - - Status = S_OK; - -exit: - - if (hGCLog != INVALID_HANDLE_VALUE) - { - CloseHandle (hGCLog); - } - - if (Status == S_OK) - ExtOut("SUCCESS: Stress log dumped\n"); - else if (Status == S_FALSE) - ExtOut("No Stress log in the image, no file written\n"); - else - ExtOut("FAILURE: Stress log not dumped\n"); - - return Status; -#else - ExtOut("Not implemented\n"); - return S_OK; -#endif //GC_CONFIG_DRIVEN -} -#endif // FEATURE_PAL - -#ifdef GC_CONFIG_DRIVEN -static const char * const str_interesting_data_points[] = -{ - "pre short", // 0 - "post short", // 1 - "merged pins", // 2 - "converted pins", // 3 - "pre pin", // 4 - "post pin", // 5 - "pre and post pin", // 6 - "pre short padded", // 7 - "post short padded", // 7 -}; - -static const char * const str_heap_compact_reasons[] = -{ - "low on ephemeral space", - "high fragmentation", - "couldn't allocate gaps", - "user specfied compact LOH", - "last GC before OOM", - "induced compacting GC", - "fragmented gen0 (ephemeral GC)", - "high memory load (ephemeral GC)", - "high memory load and frag", - "very high memory load and frag", - "no gc mode" -}; - -static BOOL gc_heap_compact_reason_mandatory_p[] = -{ - TRUE, //compact_low_ephemeral = 0, - FALSE, //compact_high_frag = 1, - TRUE, //compact_no_gaps = 2, - TRUE, //compact_loh_forced = 3, - TRUE, //compact_last_gc = 4 - TRUE, //compact_induced_compacting = 5, - FALSE, //compact_fragmented_gen0 = 6, - FALSE, //compact_high_mem_load = 7, - TRUE, //compact_high_mem_frag = 8, - TRUE, //compact_vhigh_mem_frag = 9, - TRUE //compact_no_gc_mode = 10 -}; - -static const char * const str_heap_expand_mechanisms[] = -{ - "reused seg with normal fit", - "reused seg with best fit", - "expand promoting eph", - "expand with a new seg", - "no memory for a new seg", - "expand in next full GC" -}; - -static const char * const str_bit_mechanisms[] = -{ - "using mark list", - "demotion" -}; - -static const char * const str_gc_global_mechanisms[] = -{ - "concurrent GCs", - "compacting GCs", - "promoting GCs", - "GCs that did demotion", - "card bundles", - "elevation logic" -}; - -void PrintInterestingGCInfo(DacpGCInterestingInfoData* dataPerHeap) -{ - ExtOut("Interesting data points\n"); - size_t* data = dataPerHeap->interestingDataPoints; - for (int i = 0; i < DAC_NUM_GC_DATA_POINTS; i++) - { - ExtOut("%20s: %d\n", str_interesting_data_points[i], data[i]); - } - - ExtOut("\nCompacting reasons\n"); - data = dataPerHeap->compactReasons; - for (int i = 0; i < DAC_MAX_COMPACT_REASONS_COUNT; i++) - { - ExtOut("[%s]%35s: %d\n", (gc_heap_compact_reason_mandatory_p[i] ? "M" : "W"), str_heap_compact_reasons[i], data[i]); - } - - ExtOut("\nExpansion mechanisms\n"); - data = dataPerHeap->expandMechanisms; - for (int i = 0; i < DAC_MAX_EXPAND_MECHANISMS_COUNT; i++) - { - ExtOut("%30s: %d\n", str_heap_expand_mechanisms[i], data[i]); - } - - ExtOut("\nOther mechanisms enabled\n"); - data = dataPerHeap->bitMechanisms; - for (int i = 0; i < DAC_MAX_GC_MECHANISM_BITS_COUNT; i++) - { - ExtOut("%20s: %d\n", str_bit_mechanisms[i], data[i]); - } -} -#endif //GC_CONFIG_DRIVEN - -DECLARE_API(DumpGCData) -{ - INIT_API(); - -#ifdef GC_CONFIG_DRIVEN - MINIDUMP_NOT_SUPPORTED(); - - if (!InitializeHeapData ()) - { - ExtOut("GC Heap not initialized yet.\n"); - return S_OK; - } - - DacpGCInterestingInfoData interestingInfo; - interestingInfo.RequestGlobal(g_sos); - for (int i = 0; i < DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT; i++) - { - ExtOut("%-30s: %d\n", str_gc_global_mechanisms[i], interestingInfo.globalMechanisms[i]); - } - - ExtOut("\n[info per heap]\n"); - - if (!IsServerBuild()) - { - if (interestingInfo.Request(g_sos) != S_OK) - { - ExtOut("Error requesting interesting GC info\n"); - return E_FAIL; - } - - PrintInterestingGCInfo(&interestingInfo); - } - else - { - DWORD dwNHeaps = GetGcHeapCount(); - DWORD dwAllocSize; - if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize)) - { - ExtOut("Failed to get GCHeaps: integer overflow\n"); - return Status; - } - - CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize); - if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK) - { - ExtOut("Failed to get GCHeaps\n"); - return Status; - } - - for (DWORD n = 0; n < dwNHeaps; n ++) - { - if (interestingInfo.Request(g_sos, heapAddrs[n]) != S_OK) - { - ExtOut("Heap %d: Error requesting interesting GC info\n", n); - return E_FAIL; - } - - ExtOut("--------info for heap %d--------\n", n); - PrintInterestingGCInfo(&interestingInfo); - ExtOut("\n"); - } - } - - return S_OK; -#else - ExtOut("Not implemented\n"); - return S_OK; -#endif //GC_CONFIG_DRIVEN -} - -#ifndef FEATURE_PAL -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to dump the build number and type of the * -* mscoree.dll * -* * -\**********************************************************************/ -DECLARE_API (EEVersion) -{ - INIT_API(); - - EEFLAVOR eef = GetEEFlavor(); - if (eef == UNKNOWNEE) { - ExtOut("CLR not loaded\n"); - return Status; - } - - if (g_ExtSymbols2) { - VS_FIXEDFILEINFO version; - - BOOL ret = GetEEVersion(&version); - - if (ret) - { - if (version.dwFileVersionMS != (DWORD)-1) - { - ExtOut("%u.%u.%u.%u", - HIWORD(version.dwFileVersionMS), - LOWORD(version.dwFileVersionMS), - HIWORD(version.dwFileVersionLS), - LOWORD(version.dwFileVersionLS)); - if (version.dwFileFlags & VS_FF_DEBUG) - { - ExtOut(" Checked or debug build"); - } - else - { - BOOL fRet = IsRetailBuild ((size_t)moduleInfo[eef].baseAddr); - - if (fRet) - ExtOut(" retail"); - else - ExtOut(" free"); - } - - ExtOut("\n"); - } - } - } - - if (!InitializeHeapData ()) - ExtOut("GC Heap not initialized, so GC mode is not determined yet.\n"); - else if (IsServerBuild()) - ExtOut("Server mode with %d gc heaps\n", GetGcHeapCount()); - else - ExtOut("Workstation mode\n"); - - if (!GetGcStructuresValid()) - { - ExtOut("In plan phase of garbage collection\n"); - } - - // Print SOS version - VS_FIXEDFILEINFO sosVersion; - if (GetSOSVersion(&sosVersion)) - { - if (sosVersion.dwFileVersionMS != (DWORD)-1) - { - ExtOut("SOS Version: %u.%u.%u.%u", - HIWORD(sosVersion.dwFileVersionMS), - LOWORD(sosVersion.dwFileVersionMS), - HIWORD(sosVersion.dwFileVersionLS), - LOWORD(sosVersion.dwFileVersionLS)); - if (sosVersion.dwFileFlags & VS_FF_DEBUG) - { - ExtOut(" Checked or debug build"); - } - else - { - ExtOut(" retail build"); - } - - ExtOut("\n"); - } - } - return Status; -} -#endif // FEATURE_PAL - -#ifndef FEATURE_PAL -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to print the environment setting for * -* the current process. * -* * -\**********************************************************************/ -DECLARE_API (ProcInfo) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - if (IsDumpFile()) - { - ExtOut("!ProcInfo is not supported on a dump file.\n"); - return Status; - } - -#define INFO_ENV 0x00000001 -#define INFO_TIME 0x00000002 -#define INFO_MEM 0x00000004 -#define INFO_ALL 0xFFFFFFFF - - DWORD fProcInfo = INFO_ALL; - - if (_stricmp (args, "-env") == 0) { - fProcInfo = INFO_ENV; - } - - if (_stricmp (args, "-time") == 0) { - fProcInfo = INFO_TIME; - } - - if (_stricmp (args, "-mem") == 0) { - fProcInfo = INFO_MEM; - } - - if (fProcInfo & INFO_ENV) { - ExtOut("---------------------------------------\n"); - ExtOut("Environment\n"); - ULONG64 pPeb; - g_ExtSystem->GetCurrentProcessPeb(&pPeb); - - static ULONG Offset_ProcessParam = -1; - static ULONG Offset_Environment = -1; - if (Offset_ProcessParam == -1) - { - ULONG TypeId; - ULONG64 NtDllBase; - if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL, - &NtDllBase))) - { - if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB", &TypeId))) - { - if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId, - "ProcessParameters", &Offset_ProcessParam))) - Offset_ProcessParam = -1; - } - if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "_RTL_USER_PROCESS_PARAMETERS", &TypeId))) - { - if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId, - "Environment", &Offset_Environment))) - Offset_Environment = -1; - } - } - } - // We can not get it from PDB. Use the fixed one. - if (Offset_ProcessParam == -1) - Offset_ProcessParam = offsetof (DT_PEB, ProcessParameters); - - if (Offset_Environment == -1) - Offset_Environment = offsetof (DT_RTL_USER_PROCESS_PARAMETERS, Environment); - - - ULONG64 addr = pPeb + Offset_ProcessParam; - DWORD_PTR value; - g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL); - addr = value + Offset_Environment; - g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL); - - static WCHAR buffer[DT_OS_PAGE_SIZE/2]; - ULONG readBytes = DT_OS_PAGE_SIZE; - ULONG64 Page; - if ((g_ExtData->ReadDebuggerData( DEBUG_DATA_MmPageSize, &Page, sizeof(Page), NULL)) == S_OK - && Page > 0) - { - ULONG uPageSize = (ULONG)(ULONG_PTR)Page; - if (readBytes > uPageSize) { - readBytes = uPageSize; - } - } - addr = value; - while (1) { - if (IsInterrupt()) - return Status; - if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &buffer, readBytes, NULL))) - break; - addr += readBytes; - WCHAR *pt = buffer; - WCHAR *end = pt; - while (pt < &buffer[DT_OS_PAGE_SIZE/2]) { - end = _wcschr (pt, L'\0'); - if (end == NULL) { - char format[20]; - sprintf_s (format,_countof (format), "%dS", &buffer[DT_OS_PAGE_SIZE/2] - pt); - ExtOut(format, pt); - break; - } - else if (end == pt) { - break; - } - ExtOut("%S\n", pt); - pt = end + 1; - } - if (end == pt) { - break; - } - } - } - - HANDLE hProcess = INVALID_HANDLE_VALUE; - if (fProcInfo & (INFO_TIME | INFO_MEM)) { - ULONG64 handle; - g_ExtSystem->GetCurrentProcessHandle(&handle); - hProcess = (HANDLE)handle; - } - - if (!IsDumpFile() && fProcInfo & INFO_TIME) { - FILETIME CreationTime; - FILETIME ExitTime; - FILETIME KernelTime; - FILETIME UserTime; - - typedef BOOL (WINAPI *FntGetProcessTimes)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME); - static FntGetProcessTimes pFntGetProcessTimes = (FntGetProcessTimes)-1; - if (pFntGetProcessTimes == (FntGetProcessTimes)-1) { - HINSTANCE hstat = LoadLibrary ("Kernel32.dll"); - if (hstat != 0) - { - pFntGetProcessTimes = (FntGetProcessTimes)GetProcAddress (hstat, "GetProcessTimes"); - FreeLibrary (hstat); - } - else - pFntGetProcessTimes = NULL; - } - - if (pFntGetProcessTimes && pFntGetProcessTimes (hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime)) { - ExtOut("---------------------------------------\n"); - ExtOut("Process Times\n"); - static const char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", - "Oct", "Nov", "Dec"}; - SYSTEMTIME SystemTime; - FILETIME LocalFileTime; - if (FileTimeToLocalFileTime (&CreationTime,&LocalFileTime) - && FileTimeToSystemTime (&LocalFileTime,&SystemTime)) { - ExtOut("Process Started at: %4d %s %2d %d:%d:%d.%02d\n", - SystemTime.wYear, Month[SystemTime.wMonth-1], SystemTime.wDay, - SystemTime.wHour, SystemTime.wMinute, - SystemTime.wSecond, SystemTime.wMilliseconds/10); - } - - DWORD nDay = 0; - DWORD nHour = 0; - DWORD nMin = 0; - DWORD nSec = 0; - DWORD nHundred = 0; - - ULONG64 totalTime; - - totalTime = KernelTime.dwLowDateTime + (((ULONG64)KernelTime.dwHighDateTime) << 32); - nDay = (DWORD)(totalTime/(24*3600*10000000ui64)); - totalTime %= 24*3600*10000000ui64; - nHour = (DWORD)(totalTime/(3600*10000000ui64)); - totalTime %= 3600*10000000ui64; - nMin = (DWORD)(totalTime/(60*10000000)); - totalTime %= 60*10000000; - nSec = (DWORD)(totalTime/10000000); - totalTime %= 10000000; - nHundred = (DWORD)(totalTime/100000); - ExtOut("Kernel CPU time : %d days %02d:%02d:%02d.%02d\n", - nDay, nHour, nMin, nSec, nHundred); - - DWORD sDay = nDay; - DWORD sHour = nHour; - DWORD sMin = nMin; - DWORD sSec = nSec; - DWORD sHundred = nHundred; - - totalTime = UserTime.dwLowDateTime + (((ULONG64)UserTime.dwHighDateTime) << 32); - nDay = (DWORD)(totalTime/(24*3600*10000000ui64)); - totalTime %= 24*3600*10000000ui64; - nHour = (DWORD)(totalTime/(3600*10000000ui64)); - totalTime %= 3600*10000000ui64; - nMin = (DWORD)(totalTime/(60*10000000)); - totalTime %= 60*10000000; - nSec = (DWORD)(totalTime/10000000); - totalTime %= 10000000; - nHundred = (DWORD)(totalTime/100000); - ExtOut("User CPU time : %d days %02d:%02d:%02d.%02d\n", - nDay, nHour, nMin, nSec, nHundred); - - sDay += nDay; - sHour += nHour; - sMin += nMin; - sSec += nSec; - sHundred += nHundred; - if (sHundred >= 100) { - sSec += sHundred/100; - sHundred %= 100; - } - if (sSec >= 60) { - sMin += sSec/60; - sSec %= 60; - } - if (sMin >= 60) { - sHour += sMin/60; - sMin %= 60; - } - if (sHour >= 24) { - sDay += sHour/24; - sHour %= 24; - } - ExtOut("Total CPU time : %d days %02d:%02d:%02d.%02d\n", - sDay, sHour, sMin, sSec, sHundred); - } - } - - if (!IsDumpFile() && fProcInfo & INFO_MEM) { - typedef - NTSTATUS - (NTAPI - *FntNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); - - static FntNtQueryInformationProcess pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)-1; - if (pFntNtQueryInformationProcess == (FntNtQueryInformationProcess)-1) { - HINSTANCE hstat = LoadLibrary ("ntdll.dll"); - if (hstat != 0) - { - pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)GetProcAddress (hstat, "NtQueryInformationProcess"); - FreeLibrary (hstat); - } - else - pFntNtQueryInformationProcess = NULL; - } - VM_COUNTERS memory; - if (pFntNtQueryInformationProcess && - NT_SUCCESS (pFntNtQueryInformationProcess (hProcess,ProcessVmCounters,&memory,sizeof(memory),NULL))) { - ExtOut("---------------------------------------\n"); - ExtOut("Process Memory\n"); - ExtOut("WorkingSetSize: %8d KB PeakWorkingSetSize: %8d KB\n", - memory.WorkingSetSize/1024, memory.PeakWorkingSetSize/1024); - ExtOut("VirtualSize: %8d KB PeakVirtualSize: %8d KB\n", - memory.VirtualSize/1024, memory.PeakVirtualSize/1024); - ExtOut("PagefileUsage: %8d KB PeakPagefileUsage: %8d KB\n", - memory.PagefileUsage/1024, memory.PeakPagefileUsage/1024); - } - - MEMORYSTATUS memstat; - GlobalMemoryStatus (&memstat); - ExtOut("---------------------------------------\n"); - ExtOut("%ld percent of memory is in use.\n\n", - memstat.dwMemoryLoad); - ExtOut("Memory Availability (Numbers in MB)\n\n"); - ExtOut(" %8s %8s\n", "Total", "Avail"); - ExtOut("Physical Memory %8d %8d\n", memstat.dwTotalPhys/1024/1024, memstat.dwAvailPhys/1024/1024); - ExtOut("Page File %8d %8d\n", memstat.dwTotalPageFile/1024/1024, memstat.dwAvailPageFile/1024/1024); - ExtOut("Virtual Memory %8d %8d\n", memstat.dwTotalVirtual/1024/1024, memstat.dwAvailVirtual/1024/1024); - } - - return Status; -} -#endif // FEATURE_PAL - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to find the address of EE data for a * -* metadata token. * -* * -\**********************************************************************/ -DECLARE_API(Token2EE) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - StringHolder DllName; - ULONG64 token = 0; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - - CMDValue arg[] = - { // vptr, type - {&DllName.data, COSTRING}, - {&token, COHEX} - }; - - size_t nArg; - if (!GetCMDOption(args,option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - if (nArg!=2) - { - ExtOut("Usage: !Token2EE module_name mdToken\n"); - ExtOut(" You can pass * for module_name to search all modules.\n"); - return Status; - } - - EnableDMLHolder dmlHolder(dml); - int numModule; - ArrayHolder<DWORD_PTR> moduleList = NULL; - - if (strcmp(DllName.data, "*") == 0) - { - moduleList = ModuleFromName(NULL, &numModule); - } - else - { - moduleList = ModuleFromName(DllName.data, &numModule); - } - - if (moduleList == NULL) - { - ExtOut("Failed to request module list.\n"); - } - else - { - for (int i = 0; i < numModule; i ++) - { - if (IsInterrupt()) - break; - - if (i > 0) - { - ExtOut("--------------------------------------\n"); - } - - DWORD_PTR dwAddr = moduleList[i]; - WCHAR FileName[MAX_LONGPATH]; - FileNameForModule(dwAddr, FileName); - - // We'd like a short form for this output - LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W); - if (pszFilename == NULL) - { - pszFilename = FileName; - } - else - { - pszFilename++; // skip past the last "\" character - } - - DMLOut("Module: %s\n", DMLModule(dwAddr)); - ExtOut("Assembly: %S\n", pszFilename); - - GetInfoFromModule(dwAddr, (ULONG)token); - } - } - - return Status; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function is called to find the address of EE data for a * -* metadata token. * -* * -\**********************************************************************/ -DECLARE_API(Name2EE) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - StringHolder DllName, TypeName; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - - CMDValue arg[] = - { // vptr, type - {&DllName.data, COSTRING}, - {&TypeName.data, COSTRING} - }; - size_t nArg; - - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - - if (nArg == 1) - { - // The input may be in the form <modulename>!<type> - // If so, do some surgery on the input params. - - // There should be only 1 ! character - LPSTR pszSeperator = strchr (DllName.data, '!'); - if (pszSeperator != NULL) - { - if (strchr (pszSeperator + 1, '!') == NULL) - { - size_t capacity_TypeName_data = strlen(pszSeperator + 1) + 1; - TypeName.data = new NOTHROW char[capacity_TypeName_data]; - if (TypeName.data) - { - // get the type name, - strcpy_s (TypeName.data, capacity_TypeName_data, pszSeperator + 1); - // and truncate DllName - *pszSeperator = '\0'; - - // Do some extra validation - if (strlen (DllName.data) >= 1 && - strlen (TypeName.data) > 1) - { - nArg = 2; - } - } - } - } - } - - if (nArg != 2) - { - ExtOut("Usage: " SOSPrefix "name2ee module_name item_name\n"); - ExtOut(" or " SOSPrefix "name2ee module_name!item_name\n"); - ExtOut(" use * for module_name to search all loaded modules\n"); - ExtOut("Examples: " SOSPrefix "name2ee mscorlib.dll System.String.ToString\n"); - ExtOut(" " SOSPrefix "name2ee *!System.String\n"); - return Status; - } - - int numModule; - ArrayHolder<DWORD_PTR> moduleList = NULL; - if (strcmp(DllName.data, "*") == 0) - { - moduleList = ModuleFromName(NULL, &numModule); - } - else - { - moduleList = ModuleFromName(DllName.data, &numModule); - } - - - if (moduleList == NULL) - { - ExtOut("Failed to request module list.\n", DllName.data); - } - else - { - for (int i = 0; i < numModule; i ++) - { - if (IsInterrupt()) - break; - - if (i > 0) - { - ExtOut("--------------------------------------\n"); - } - - DWORD_PTR dwAddr = moduleList[i]; - WCHAR FileName[MAX_LONGPATH]; - FileNameForModule (dwAddr, FileName); - - // We'd like a short form for this output - LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W); - if (pszFilename == NULL) - { - pszFilename = FileName; - } - else - { - pszFilename++; // skip past the last "\" character - } - - DMLOut("Module: %s\n", DMLModule(dwAddr)); - ExtOut("Assembly: %S\n", pszFilename); - GetInfoFromName(dwAddr, TypeName.data); - } - } - - return Status; -} - - -#ifndef FEATURE_PAL -DECLARE_API(PathTo) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - DWORD_PTR root = NULL; - DWORD_PTR target = NULL; - BOOL dml = FALSE; - size_t nArg; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&root, COHEX}, - {&target, COHEX}, - }; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - if (root == 0 || target == 0) - { - ExtOut("Invalid argument %s\n", args); - return Status; - } - - GCRootImpl gcroot; - bool result = gcroot.PrintPathToObject(root, target); - - if (!result) - ExtOut("Did not find a path from %p to %p.\n", SOS_PTR(root), SOS_PTR(target)); - - return Status; -} -#endif - - - -/**********************************************************************\ -* Routine Description: * -* * -* This function finds all roots (on stack or in handles) for a * -* given object. * -* * -\**********************************************************************/ -DECLARE_API(GCRoot) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - BOOL bNoStacks = FALSE; - DWORD_PTR obj = NULL; - BOOL dml = FALSE; - BOOL all = FALSE; - size_t nArg; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-nostacks", &bNoStacks, COBOOL, FALSE}, - {"-all", &all, COBOOL, FALSE}, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - CMDValue arg[] = - - { // vptr, type - {&obj, COHEX} - }; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - if (obj == 0) - { - ExtOut("Invalid argument %s\n", args); - return Status; - } - - EnableDMLHolder dmlHolder(dml); - GCRootImpl gcroot; - int i = gcroot.PrintRootsForObject(obj, all == TRUE, bNoStacks == TRUE); - - if (IsInterrupt()) - ExtOut("Interrupted, data may be incomplete.\n"); - - if (all) - ExtOut("Found %d roots.\n", i); - else - ExtOut("Found %d unique roots (run '" SOSPrefix "gcroot -all' to see all roots).\n", i); - - return Status; -} - -DECLARE_API(GCWhere) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - BOOL dml = FALSE; - BOOL bGetBrick; - BOOL bGetCard; - TADDR taddrObj = 0; - size_t nArg; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-brick", &bGetBrick, COBOOL, FALSE}, - {"-card", &bGetCard, COBOOL, FALSE}, - {"/d", &dml, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&taddrObj, COHEX} - }; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - // Obtain allocation context for each managed thread. - AllocInfo allocInfo; - allocInfo.Init(); - - TADDR_SEGINFO trngSeg = { 0, 0, 0 }; - TADDR_RANGE allocCtx = { 0, 0 }; - int gen = -1; - BOOL bLarge = FALSE; - BOOL bFound = FALSE; - - size_t size = 0; - if (sos::IsObject(taddrObj)) - { - TADDR taddrMT; - BOOL bContainsPointers; - if(FAILED(GetMTOfObject(taddrObj, &taddrMT)) || - !GetSizeEfficient(taddrObj, taddrMT, FALSE, size, bContainsPointers)) - { - ExtWarn("Couldn't get size for object %#p: possible heap corruption.\n", - SOS_PTR(taddrObj)); - } - } - - if (!IsServerBuild()) - { - DacpGcHeapDetails heapDetails; - if (heapDetails.Request(g_sos) != S_OK) - { - ExtOut("Error requesting gc heap details\n"); - return Status; - } - - if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge)) - { - ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated " WIN64_8SPACES " size\n"); - ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n", - SOS_PTR(taddrObj), gen, 0, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size); - bFound = TRUE; - } - } - else - { - DacpGcHeapData gcheap; - if (gcheap.Request(g_sos) != S_OK) - { - ExtOut("Error requesting GC Heap data\n"); - return Status; - } - - DWORD dwAllocSize; - DWORD dwNHeaps = gcheap.HeapCount; - if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize)) - { - ExtOut("Failed to get GCHeaps: integer overflow\n"); - return Status; - } - - CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize); - if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK) - { - ExtOut("Failed to get GCHeaps\n"); - return Status; - } - - for (DWORD n = 0; n < dwNHeaps; n ++) - { - DacpGcHeapDetails heapDetails; - if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK) - { - ExtOut("Error requesting details\n"); - return Status; - } - - if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge)) - { - ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin " WIN64_8SPACES " allocated" WIN64_8SPACES " size\n"); - ExtOut("%p %d %2d %p %p %p 0x%x(%d)\n", - SOS_PTR(taddrObj), gen, n, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size); - bFound = TRUE; - break; - } - } - } - - if (!bFound) - { - ExtOut("Address %#p not found in the managed heap.\n", SOS_PTR(taddrObj)); - } - - return Status; -} - -#ifndef FEATURE_PAL - -DECLARE_API(FindRoots) -{ -#ifndef FEATURE_PAL - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - if (IsDumpFile()) - { - ExtOut("!FindRoots is not supported on a dump file.\n"); - return Status; - } - - LONG_PTR gen = -100; // initialized outside the legal range: [-1, 2] - StringHolder sgen; - TADDR taObj = NULL; - BOOL dml = FALSE; - size_t nArg; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-gen", &sgen.data, COSTRING, TRUE}, - {"/d", &dml, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&taObj, COHEX} - }; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - if (sgen.data != NULL) - { - if (_stricmp(sgen.data, "any") == 0) - { - gen = -1; - } - else - { - gen = GetExpression(sgen.data); - } - } - if ((gen < -1 || gen > 2) && (taObj == 0)) - { - 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"); - return Status; - } - - if (gen >= -1 && gen <= 2) - { - IXCLRDataProcess2* idp2 = NULL; - if (FAILED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2))) - { - ExtOut("Your version of the runtime/DAC do not support this command.\n"); - return Status; - } - - // Request GC_MARK_END notifications from debuggee - GcEvtArgs gea = { GC_MARK_END, { ((gen == -1) ? 7 : (1 << gen)) } }; - idp2->SetGcNotification(gea); - // ... and register the notification handler - g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0); - // the above notification is removed in CNotification::OnGcEvent() - } - else - { - // verify that the last event in the debugger was indeed a CLRN exception - DEBUG_LAST_EVENT_INFO_EXCEPTION dle; - CNotification Notification; - - if (!CheckCLRNotificationEvent(&dle)) - { - ExtOut("The command !FindRoots can only be used after the debugger stopped on a CLRN GC notification.\n"); - ExtOut("At this time !GCRoot should be used instead.\n"); - return Status; - } - // validate argument - if (!g_snapshot.Build()) - { - ExtOut("Unable to build snapshot of the garbage collector state\n"); - return Status; - } - - if (g_snapshot.GetHeap(taObj) == NULL) - { - ExtOut("Address %#p is not in the managed heap.\n", SOS_PTR(taObj)); - return Status; - } - - int ogen = g_snapshot.GetGeneration(taObj); - if (ogen > CNotification::GetCondemnedGen()) - { - DMLOut("Object %s will survive this collection:\n\tgen(%#p) = %d > %d = condemned generation.\n", - DMLObject(taObj), SOS_PTR(taObj), ogen, CNotification::GetCondemnedGen()); - return Status; - } - - GCRootImpl gcroot; - int roots = gcroot.FindRoots(CNotification::GetCondemnedGen(), taObj); - - ExtOut("Found %d roots.\n", roots); - } - - return Status; -#else - return E_NOTIMPL; -#endif -} - -class GCHandleStatsForDomains -{ -public: - GCHandleStatsForDomains() - : m_singleDomainMode(FALSE), m_numDomains(0), m_pStatistics(NULL), m_pDomainPointers(NULL), m_sharedDomainIndex(-1), m_systemDomainIndex(-1) - { - } - - ~GCHandleStatsForDomains() - { - if (m_pStatistics) - { - if (m_singleDomainMode) - delete m_pStatistics; - else - delete [] m_pStatistics; - } - - if (m_pDomainPointers) - delete [] m_pDomainPointers; - } - - BOOL Init(BOOL singleDomainMode) - { - m_singleDomainMode = singleDomainMode; - if (m_singleDomainMode) - { - m_numDomains = 1; - m_pStatistics = new NOTHROW GCHandleStatistics(); - if (m_pStatistics == NULL) - return FALSE; - } - else - { - DacpAppDomainStoreData adsData; - if (adsData.Request(g_sos) != S_OK) - return FALSE; - - LONG numSpecialDomains = (adsData.sharedDomain != NULL) ? 2 : 1; - m_numDomains = adsData.DomainCount + numSpecialDomains; - ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[m_numDomains]; - if (pArray == NULL) - return FALSE; - - int i = 0; - if (adsData.sharedDomain != NULL) - { - pArray[i++] = adsData.sharedDomain; - } - - pArray[i] = adsData.systemDomain; - - m_sharedDomainIndex = i - 1; // The m_sharedDomainIndex is set to -1 if there is no shared domain - m_systemDomainIndex = i; - - if (g_sos->GetAppDomainList(adsData.DomainCount, pArray+numSpecialDomains, NULL) != S_OK) - return FALSE; - - m_pDomainPointers = pArray.Detach(); - m_pStatistics = new NOTHROW GCHandleStatistics[m_numDomains]; - if (m_pStatistics == NULL) - return FALSE; - } - - return TRUE; - } - - GCHandleStatistics *LookupStatistics(CLRDATA_ADDRESS appDomainPtr) const - { - if (m_singleDomainMode) - { - // You can pass NULL appDomainPtr if you are in singleDomainMode - return m_pStatistics; - } - else - { - for (int i=0; i < m_numDomains; i++) - if (m_pDomainPointers[i] == appDomainPtr) - return m_pStatistics + i; - } - - return NULL; - } - - - GCHandleStatistics *GetStatistics(int appDomainIndex) const - { - SOS_Assert(appDomainIndex >= 0); - SOS_Assert(appDomainIndex < m_numDomains); - - return m_singleDomainMode ? m_pStatistics : m_pStatistics + appDomainIndex; - } - - int GetNumDomains() const - { - return m_numDomains; - } - - CLRDATA_ADDRESS GetDomain(int index) const - { - SOS_Assert(index >= 0); - SOS_Assert(index < m_numDomains); - return m_pDomainPointers[index]; - } - - int GetSharedDomainIndex() - { - return m_sharedDomainIndex; - } - - int GetSystemDomainIndex() - { - return m_systemDomainIndex; - } - -private: - BOOL m_singleDomainMode; - int m_numDomains; - GCHandleStatistics *m_pStatistics; - CLRDATA_ADDRESS *m_pDomainPointers; - int m_sharedDomainIndex; - int m_systemDomainIndex; -}; - -class GCHandlesImpl -{ -public: - GCHandlesImpl(PCSTR args) - : mPerDomain(FALSE), mStat(FALSE), mDML(FALSE), mType((int)~0) - { - ArrayHolder<char> type = NULL; - CMDOption option[] = - { - {"-perdomain", &mPerDomain, COBOOL, FALSE}, - {"-stat", &mStat, COBOOL, FALSE}, - {"-type", &type, COSTRING, TRUE}, - {"/d", &mDML, COBOOL, FALSE}, - }; - - if (!GetCMDOption(args,option,_countof(option),NULL,0,NULL)) - sos::Throw<sos::Exception>("Failed to parse command line arguments."); - - if (type != NULL) - if (_stricmp(type, "Pinned") == 0) - mType = HNDTYPE_PINNED; - else if (_stricmp(type, "RefCounted") == 0) - mType = HNDTYPE_REFCOUNTED; - else if (_stricmp(type, "WeakShort") == 0) - mType = HNDTYPE_WEAK_SHORT; - else if (_stricmp(type, "WeakLong") == 0) - mType = HNDTYPE_WEAK_LONG; - else if (_stricmp(type, "Strong") == 0) - mType = HNDTYPE_STRONG; - else if (_stricmp(type, "Variable") == 0) - mType = HNDTYPE_VARIABLE; - else if (_stricmp(type, "AsyncPinned") == 0) - mType = HNDTYPE_ASYNCPINNED; - else if (_stricmp(type, "SizedRef") == 0) - mType = HNDTYPE_SIZEDREF; - else if (_stricmp(type, "Dependent") == 0) - mType = HNDTYPE_DEPENDENT; - else if (_stricmp(type, "WeakWinRT") == 0) - mType = HNDTYPE_WEAK_WINRT; - else - sos::Throw<sos::Exception>("Unknown handle type '%s'.", type.GetPtr()); - } - - void Run() - { - EnableDMLHolder dmlHolder(mDML); - - mOut.ReInit(6, POINTERSIZE_HEX, AlignRight); - mOut.SetWidths(5, POINTERSIZE_HEX, 11, POINTERSIZE_HEX, 8, POINTERSIZE_HEX); - mOut.SetColAlignment(1, AlignLeft); - - if (mHandleStat.Init(!mPerDomain) == FALSE) - sos::Throw<sos::Exception>("Error getting per-appdomain handle information"); - - if (!mStat) - mOut.WriteRow("Handle", "Type", "Object", "Size", "Data", "Type"); - - WalkHandles(); - - for (int i=0; (i < mHandleStat.GetNumDomains()) && !IsInterrupt(); i++) - { - GCHandleStatistics *pStats = mHandleStat.GetStatistics(i); - - if (mPerDomain) - { - Print( "------------------------------------------------------------------------------\n"); - Print("GC Handle Statistics for AppDomain ", AppDomainPtr(mHandleStat.GetDomain(i))); - - if (i == mHandleStat.GetSharedDomainIndex()) - Print(" (Shared Domain)\n"); - else if (i == mHandleStat.GetSystemDomainIndex()) - Print(" (System Domain)\n"); - else - Print("\n"); - } - - if (!mStat) - Print("\n"); - PrintGCStat(&pStats->hs); - - // Don't print handle stats if the user has filtered by type. All handles will be the same - // type, and the total count will be displayed by PrintGCStat. - if (mType == (unsigned int)~0) - { - Print("\n"); - PrintGCHandleStats(pStats); - } - } - } - -private: - void WalkHandles() - { - ToRelease<ISOSHandleEnum> handles; - if (FAILED(g_sos->GetHandleEnum(&handles))) - { - if (IsMiniDumpFile()) - sos::Throw<sos::Exception>("Unable to display GC handles.\nA minidump without full memory may not have this information."); - else - sos::Throw<sos::Exception>("Failed to walk the handle table."); - } - - // GCC can't handle stacks which are too large. -#ifndef FEATURE_PAL - SOSHandleData data[256]; -#else - SOSHandleData data[4]; -#endif - - unsigned int fetched = 0; - HRESULT hr = S_OK; - do - { - if (FAILED(hr = handles->Next(_countof(data), data, &fetched))) - { - ExtOut("Error %x while walking the handle table.\n", hr); - break; - } - - WalkHandles(data, fetched); - } while (_countof(data) == fetched); - } - - void WalkHandles(SOSHandleData data[], unsigned int count) - { - for (unsigned int i = 0; i < count; ++i) - { - sos::CheckInterrupt(); - - if (mType != (unsigned int)~0 && mType != data[i].Type) - continue; - - GCHandleStatistics *pStats = mHandleStat.LookupStatistics(data[i].AppDomain); - TADDR objAddr = 0; - TADDR mtAddr = 0; - size_t size = 0; - const WCHAR *mtName = 0; - const char *type = 0; - - if (FAILED(MOVE(objAddr, data[i].Handle))) - { - objAddr = 0; - mtName = W("<error>"); - } - else - { - sos::Object obj(TO_TADDR(objAddr)); - mtAddr = obj.GetMT(); - if (sos::MethodTable::IsFreeMT(mtAddr)) - { - mtName = W("<free>"); - } - else if (!sos::MethodTable::IsValid(mtAddr)) - { - mtName = W("<error>"); - } - else - { - size = obj.GetSize(); - if (mType == (unsigned int)~0 || mType == data[i].Type) - pStats->hs.Add(obj.GetMT(), (DWORD)size); - } - } - - switch(data[i].Type) - { - case HNDTYPE_PINNED: - type = "Pinned"; - if (pStats) pStats->pinnedHandleCount++; - break; - case HNDTYPE_REFCOUNTED: - type = "RefCounted"; - if (pStats) pStats->refCntHandleCount++; - break; - case HNDTYPE_STRONG: - type = "Strong"; - if (pStats) pStats->strongHandleCount++; - break; - case HNDTYPE_WEAK_SHORT: - type = "WeakShort"; - if (pStats) pStats->weakShortHandleCount++; - break; - case HNDTYPE_WEAK_LONG: - type = "WeakLong"; - if (pStats) pStats->weakLongHandleCount++; - break; - case HNDTYPE_ASYNCPINNED: - type = "AsyncPinned"; - if (pStats) pStats->asyncPinnedHandleCount++; - break; - case HNDTYPE_VARIABLE: - type = "Variable"; - if (pStats) pStats->variableCount++; - break; - case HNDTYPE_SIZEDREF: - type = "SizedRef"; - if (pStats) pStats->sizedRefCount++; - break; - case HNDTYPE_DEPENDENT: - type = "Dependent"; - if (pStats) pStats->dependentCount++; - break; - case HNDTYPE_WEAK_WINRT: - type = "WeakWinRT"; - if (pStats) pStats->weakWinRTHandleCount++; - break; - default: - DebugBreak(); - type = "Unknown"; - pStats->unknownHandleCount++; - break; - } - - if (type && !mStat) - { - sos::MethodTable mt = mtAddr; - if (mtName == 0) - mtName = mt.GetName(); - - if (data[i].Type == HNDTYPE_REFCOUNTED) - mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Decimal(data[i].RefCount), mtName); - else if (data[i].Type == HNDTYPE_DEPENDENT) - mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), ObjectPtr(data[i].Secondary), mtName); - else if (data[i].Type == HNDTYPE_WEAK_WINRT) - mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Pointer(data[i].Secondary), mtName); - else - mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), "", mtName); - } - } - } - - inline void PrintHandleRow(const char *text, int count) - { - if (count) - mOut.WriteRow(text, Decimal(count)); - } - - void PrintGCHandleStats(GCHandleStatistics *pStats) - { - Print("Handles:\n"); - mOut.ReInit(2, 21, AlignLeft, 4); - - PrintHandleRow("Strong Handles:", pStats->strongHandleCount); - PrintHandleRow("Pinned Handles:", pStats->pinnedHandleCount); - PrintHandleRow("Async Pinned Handles:", pStats->asyncPinnedHandleCount); - PrintHandleRow("Ref Count Handles:", pStats->refCntHandleCount); - PrintHandleRow("Weak Long Handles:", pStats->weakLongHandleCount); - PrintHandleRow("Weak Short Handles:", pStats->weakShortHandleCount); - PrintHandleRow("Weak WinRT Handles:", pStats->weakWinRTHandleCount); - PrintHandleRow("Variable Handles:", pStats->variableCount); - PrintHandleRow("SizedRef Handles:", pStats->sizedRefCount); - PrintHandleRow("Dependent Handles:", pStats->dependentCount); - PrintHandleRow("Other Handles:", pStats->unknownHandleCount); - } - -private: - BOOL mPerDomain, mStat, mDML; - unsigned int mType; - TableOutput mOut; - GCHandleStatsForDomains mHandleStat; -}; - -/**********************************************************************\ -* Routine Description: * -* * -* This function dumps GC Handle statistics * -* * -\**********************************************************************/ -DECLARE_API(GCHandles) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - try - { - GCHandlesImpl gchandles(args); - gchandles.Run(); - } - catch(const sos::Exception &e) - { - Print(e.what()); - } - - return Status; -} - -// This is an experimental and undocumented SOS API that attempts to step through code -// stopping once jitted code is reached. It currently has some issues - it can take arbitrarily long -// to reach jitted code and canceling it is terrible. At best it doesn't cancel, at worst it -// kills the debugger. IsInterrupt() doesn't work nearly as nicely as one would hope :/ -#ifndef FEATURE_PAL -DECLARE_API(TraceToCode) -{ - INIT_API_NOEE(); - - static ULONG64 g_clrBaseAddr = 0; - - - while(true) - { - if (IsInterrupt()) - { - ExtOut("Interrupted\n"); - return S_FALSE; - } - - ULONG64 Offset; - g_ExtRegisters->GetInstructionOffset(&Offset); - - DWORD codeType = 0; - ULONG64 base = 0; - CLRDATA_ADDRESS cdaStart = TO_CDADDR(Offset); - DacpMethodDescData MethodDescData; - if(g_ExtSymbols->GetModuleByOffset(Offset, 0, NULL, &base) == S_OK) - { - if(g_clrBaseAddr == 0) - { - g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL, - &g_clrBaseAddr); - } - if(g_clrBaseAddr == base) - { - ExtOut("Compiled code in CLR\n"); - codeType = 4; - } - else - { - ExtOut("Compiled code in module @ 0x%I64x\n", base); - codeType = 8; - } - } - else if (g_sos != NULL || LoadClrDebugDll()==S_OK) - { - CLRDATA_ADDRESS addr; - if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK) - { - WCHAR wszNameBuffer[1024]; // should be large enough - - // get the MethodDesc name - if ((g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK) && - _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0) - { - ExtOut("ILStub\n"); - codeType = 2; - } - else - { - ExtOut("Jitted code\n"); - codeType = 1; - } - } - else - { - ExtOut("Not compiled or jitted, assuming stub\n"); - codeType = 16; - } - } - else - { - // not compiled but CLR isn't loaded... some other code generator? - return E_FAIL; - } - - if(codeType == 1) - { - return S_OK; - } - else - { - Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "thr; .echo wait" ,0); - if (FAILED(Status)) - { - ExtOut("Error tracing instruction\n"); - return Status; - } - } - } - - return Status; - -} -#endif // FEATURE_PAL - -// This is an experimental and undocumented API that sets a debugger pseudo-register based -// on the type of code at the given IP. It can be used in scripts to keep stepping until certain -// kinds of code have been reached. Presumbably its slower than !TraceToCode but at least it -// cancels much better -#ifndef FEATURE_PAL -DECLARE_API(GetCodeTypeFlags) -{ - INIT_API(); - - - char buffer[100+mdNameLen]; - size_t ip; - StringHolder PReg; - - CMDValue arg[] = { - // vptr, type - {&ip, COSIZE_T}, - {&PReg.data, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) - { - return Status; - } - - size_t preg = 1; // by default - if (nArg == 2) - { - preg = GetExpression(PReg.data); - if (preg > 19) - { - ExtOut("Pseudo-register number must be between 0 and 19\n"); - return Status; - } - } - - sprintf_s(buffer,_countof (buffer), - "r$t%d=0", - preg); - Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0); - if (FAILED(Status)) - { - ExtOut("Error initialized register $t%d to zero\n", preg); - return Status; - } - - ULONG64 base = 0; - CLRDATA_ADDRESS cdaStart = TO_CDADDR(ip); - DWORD codeType = 0; - CLRDATA_ADDRESS addr; - if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK) - { - WCHAR wszNameBuffer[1024]; // should be large enough - - // get the MethodDesc name - if (g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK && - _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0) - { - ExtOut("ILStub\n"); - codeType = 2; - } - else - { - ExtOut("Jitted code"); - codeType = 1; - } - } - else if(g_ExtSymbols->GetModuleByOffset (ip, 0, NULL, &base) == S_OK) - { - ULONG64 clrBaseAddr = 0; - if(SUCCEEDED(g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL, &clrBaseAddr)) && base==clrBaseAddr) - { - ExtOut("Compiled code in CLR"); - codeType = 4; - } - else - { - ExtOut("Compiled code in module @ 0x%I64x\n", base); - codeType = 8; - } - } - else - { - ExtOut("Not compiled or jitted, assuming stub\n"); - codeType = 16; - } - - sprintf_s(buffer,_countof (buffer), - "r$t%d=%x", - preg, codeType); - Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0); - if (FAILED(Status)) - { - ExtOut("Error setting register $t%d\n", preg); - return Status; - } - return Status; - -} -#endif // FEATURE_PAL - -DECLARE_API(StopOnException) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - - char buffer[100+mdNameLen]; - - BOOL fDerived = FALSE; - BOOL fCreate1 = FALSE; - BOOL fCreate2 = FALSE; - - CMDOption option[] = { - // name, vptr, type, hasValue - {"-derived", &fDerived, COBOOL, FALSE}, // catch derived exceptions - {"-create", &fCreate1, COBOOL, FALSE}, // create 1st chance handler - {"-create2", &fCreate2, COBOOL, FALSE}, // create 2nd chance handler - }; - - StringHolder TypeName,PReg; - - CMDValue arg[] = { - // vptr, type - {&TypeName.data, COSTRING}, - {&PReg.data, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - if (IsDumpFile()) - { - ExtOut("Live debugging session required\n"); - return Status; - } - if (nArg < 1 || nArg > 2) - { - ExtOut("usage: StopOnException [-derived] [-create | -create2] <type name>\n"); - ExtOut(" [<pseudo-register number for result>]\n"); - ExtOut("ex: StopOnException -create System.OutOfMemoryException 1\n"); - return Status; - } - - size_t preg = 1; // by default - if (nArg == 2) - { - preg = GetExpression(PReg.data); - if (preg > 19) - { - ExtOut("Pseudo-register number must be between 0 and 19\n"); - return Status; - } - } - - sprintf_s(buffer,_countof (buffer), - "r$t%d=0", - preg); - Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0); - if (FAILED(Status)) - { - ExtOut("Error initialized register $t%d to zero\n", preg); - return Status; - } - - if (fCreate1 || fCreate2) - { - sprintf_s(buffer,_countof (buffer), - "sxe %s \"!soe %s %s %d;.if(@$t%d==0) {g} .else {.echo '%s hit'}\" %x", - fCreate1 ? "-c" : "-c2", - fDerived ? "-derived" : "", - TypeName.data, - preg, - preg, - TypeName.data, - EXCEPTION_COMPLUS - ); - - Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0); - if (FAILED(Status)) - { - ExtOut("Error setting breakpoint: %s\n", buffer); - return Status; - } - - ExtOut("Breakpoint set\n"); - return Status; - } - - // Find the last thrown exception on this thread. - // Does it match? If so, set the register. - CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread(); - DacpThreadData Thread; - - if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK)) - { - ExtOut("The current thread is unmanaged\n"); - return Status; - } - - TADDR taLTOH; - if (!SafeReadMemory(Thread.lastThrownObjectHandle, - &taLTOH, - sizeof(taLTOH), NULL)) - { - ExtOut("There is no current managed exception on this thread\n"); - return Status; - } - - if (taLTOH) - { - LPWSTR typeNameWide = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR)); - MultiByteToWideChar(CP_ACP,0,TypeName.data,-1,typeNameWide,mdNameLen); - - TADDR taMT; - if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL)) - { - NameForMT_s (taMT, g_mdName, mdNameLen); - if ((_wcscmp(g_mdName,typeNameWide) == 0) || - (fDerived && IsDerivedFrom(taMT, typeNameWide))) - { - sprintf_s(buffer,_countof (buffer), - "r$t%d=1", - preg); - Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0); - if (FAILED(Status)) - { - ExtOut("Failed to execute the following command: %s\n", buffer); - } - } - } - } - - return Status; -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function finds the size of an object or all roots. * -* * -\**********************************************************************/ -DECLARE_API(ObjSize) -{ -#ifndef FEATURE_PAL - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - BOOL dml = FALSE; - StringHolder str_Object; - - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&str_Object.data, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - TADDR obj = GetExpression(str_Object.data); - - GCRootImpl gcroot; - if (obj == 0) - { - gcroot.ObjSize(); - } - else - { - if(!sos::IsObject(obj)) - { - ExtOut("%p is not a valid object.\n", SOS_PTR(obj)); - return Status; - } - - size_t size = gcroot.ObjSize(obj); - TADDR mt = 0; - MOVE(mt, obj); - sos::MethodTable methodTable = mt; - ExtOut("sizeof(%p) = %d (0x%x) bytes (%S)\n", SOS_PTR(obj), size, size, methodTable.GetName()); - } - return Status; -#else - return E_NOTIMPL; -#endif - -} - -#ifndef FEATURE_PAL -// For FEATURE_PAL, MEMORY_BASIC_INFORMATION64 doesn't exist yet. TODO? -DECLARE_API(GCHandleLeaks) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - ExtOut("-------------------------------------------------------------------------------\n"); - ExtOut("GCHandleLeaks will report any GCHandles that couldn't be found in memory. \n"); - ExtOut("Strong and Pinned GCHandles are reported at this time. You can safely abort the\n"); - ExtOut("memory scan with Control-C or Control-Break. \n"); - ExtOut("-------------------------------------------------------------------------------\n"); - - static DWORD_PTR array[2000]; - UINT i; - BOOL dml = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"/d", &dml, COBOOL, FALSE}, - }; - - if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - - UINT iFinal = FindAllPinnedAndStrong(array,sizeof(array)/sizeof(DWORD_PTR)); - ExtOut("Found %d handles:\n",iFinal); - for (i=1;i<=iFinal;i++) - { - ExtOut("%p\t", SOS_PTR(array[i-1])); - if ((i % 4) == 0) - ExtOut("\n"); - } - - ExtOut("\nSearching memory\n"); - // Now search memory for this: - DWORD_PTR buffer[1024]; - ULONG64 memCur = 0x0; - BOOL bAbort = FALSE; - - //find out memory used by stress log - StressLogMem stressLog; - CLRDATA_ADDRESS StressLogAddress = NULL; - if (LoadClrDebugDll() != S_OK) - { - // Try to find stress log symbols - DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog"); - StressLogAddress = dwAddr; - g_bDacBroken = TRUE; - } - else - { - if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK) - { - ExtOut("Unable to find stress log via DAC\n"); - } - g_bDacBroken = FALSE; - } - - if (stressLog.Init (StressLogAddress, g_ExtData)) - { - ExtOut("Reference found in stress log will be ignored\n"); - } - else - { - ExtOut("Failed to read whole or part of stress log, some references may come from stress log\n"); - } - - - while (!bAbort) - { - NTSTATUS status; - MEMORY_BASIC_INFORMATION64 memInfo; - - status = g_ExtData2->QueryVirtual(UL64_TO_CDA(memCur), &memInfo); - - if( !NT_SUCCESS(status) ) - { - break; - } - - if (memInfo.State == MEM_COMMIT) - { - for (ULONG64 memIter = memCur; memIter < (memCur + memInfo.RegionSize); memIter+=sizeof(buffer)) - { - if (IsInterrupt()) - { - ExtOut("Quitting at %p due to user abort\n", SOS_PTR(memIter)); - bAbort = TRUE; - break; - } - - if ((memIter % 0x10000000)==0x0) - { - ExtOut("Searching %p...\n", SOS_PTR(memIter)); - } - - ULONG size = 0; - HRESULT ret; - ret = g_ExtData->ReadVirtual(UL64_TO_CDA(memIter), buffer, sizeof(buffer), &size); - if (ret == S_OK) - { - for (UINT x=0;x<1024;x++) - { - DWORD_PTR value = buffer[x]; - // We don't care about the low bit. Also, the GCHandle class turns on the - // low bit for pinned handles, so without the statement below, we wouldn't - // notice pinned handles. - value = value & ~1; - for (i=0;i<iFinal;i++) - { - ULONG64 addrInDebugee = (ULONG64)memIter+(x*sizeof(DWORD_PTR)); - if ((array[i] & ~1) == value) - { - if (stressLog.IsInStressLog (addrInDebugee)) - { - ExtOut("Found %p in stress log at location %p, reference not counted\n", SOS_PTR(value), addrInDebugee); - } - else - { - ExtOut("Found %p at location %p\n", SOS_PTR(value), addrInDebugee); - array[i] |= 0x1; - } - } - } - } - } - else - { - if (size > 0) - { - ExtOut("only read %x bytes at %p\n", size, SOS_PTR(memIter)); - } - } - } - } - - memCur += memInfo.RegionSize; - } - - int numNotFound = 0; - for (i=0;i<iFinal;i++) - { - if ((array[i] & 0x1) == 0) - { - numNotFound++; - // ExtOut("WARNING: %p not found\n", SOS_PTR(array[i])); - } - } - - if (numNotFound > 0) - { - ExtOut("------------------------------------------------------------------------------\n"); - ExtOut("Some handles were not found. If the number of not-found handles grows over the\n"); - ExtOut("lifetime of your application, you may have a GCHandle leak. This will cause \n"); - ExtOut("the GC Heap to grow larger as objects are being kept alive, referenced only \n"); - ExtOut("by the orphaned handle. If the number doesn't grow over time, note that there \n"); - ExtOut("may be some noise in this output, as an unmanaged application may be storing \n"); - ExtOut("the handle in a non-standard way, perhaps with some bits flipped. The memory \n"); - ExtOut("scan wouldn't be able to find those. \n"); - ExtOut("------------------------------------------------------------------------------\n"); - - ExtOut("Didn't find %d handles:\n", numNotFound); - int numPrinted=0; - for (i=0;i<iFinal;i++) - { - if ((array[i] & 0x1) == 0) - { - numPrinted++; - ExtOut("%p\t", SOS_PTR(array[i])); - if ((numPrinted % 4) == 0) - ExtOut("\n"); - } - } - ExtOut("\n"); - } - else - { - ExtOut("------------------------------------------------------------------------------\n"); - ExtOut("All handles found"); - if (bAbort) - ExtOut(" even though you aborted.\n"); - else - ExtOut(".\n"); - ExtOut("A leak may still exist because in a general scan of process memory SOS can't \n"); - ExtOut("differentiate between garbage and valid structures, so you may have false \n"); - ExtOut("positives. If you still suspect a leak, use this function over time to \n"); - ExtOut("identify a possible trend. \n"); - ExtOut("------------------------------------------------------------------------------\n"); - } - - return Status; -} -#endif // FEATURE_PAL - -#endif // FEATURE_PAL - -class ClrStackImplWithICorDebug -{ -private: - static HRESULT DereferenceAndUnboxValue(ICorDebugValue * pValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = NULL) - { - HRESULT Status = S_OK; - *ppOutputValue = NULL; - if(pIsNull != NULL) *pIsNull = FALSE; - - ToRelease<ICorDebugReferenceValue> pReferenceValue; - Status = pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue); - if (SUCCEEDED(Status)) - { - BOOL isNull = FALSE; - IfFailRet(pReferenceValue->IsNull(&isNull)); - if(!isNull) - { - ToRelease<ICorDebugValue> pDereferencedValue; - IfFailRet(pReferenceValue->Dereference(&pDereferencedValue)); - return DereferenceAndUnboxValue(pDereferencedValue, ppOutputValue); - } - else - { - if(pIsNull != NULL) *pIsNull = TRUE; - *ppOutputValue = pValue; - (*ppOutputValue)->AddRef(); - return S_OK; - } - } - - ToRelease<ICorDebugBoxValue> pBoxedValue; - Status = pValue->QueryInterface(IID_ICorDebugBoxValue, (LPVOID*) &pBoxedValue); - if (SUCCEEDED(Status)) - { - ToRelease<ICorDebugObjectValue> pUnboxedValue; - IfFailRet(pBoxedValue->GetObject(&pUnboxedValue)); - return DereferenceAndUnboxValue(pUnboxedValue, ppOutputValue); - } - *ppOutputValue = pValue; - (*ppOutputValue)->AddRef(); - return S_OK; - } - - static BOOL ShouldExpandVariable(__in_z WCHAR* varToExpand, __in_z WCHAR* currentExpansion) - { - if(currentExpansion == NULL || varToExpand == NULL) return FALSE; - - size_t varToExpandLen = _wcslen(varToExpand); - size_t currentExpansionLen = _wcslen(currentExpansion); - if(currentExpansionLen > varToExpandLen) return FALSE; - if(currentExpansionLen < varToExpandLen && varToExpand[currentExpansionLen] != L'.') return FALSE; - if(_wcsncmp(currentExpansion, varToExpand, currentExpansionLen) != 0) return FALSE; - - return TRUE; - } - - static BOOL IsEnum(ICorDebugValue * pInputValue) - { - ToRelease<ICorDebugValue> pValue; - if(FAILED(DereferenceAndUnboxValue(pInputValue, &pValue, NULL))) return FALSE; - - WCHAR baseTypeName[mdNameLen]; - ToRelease<ICorDebugValue2> pValue2; - ToRelease<ICorDebugType> pType; - ToRelease<ICorDebugType> pBaseType; - - if(FAILED(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2))) return FALSE; - if(FAILED(pValue2->GetExactType(&pType))) return FALSE; - if(FAILED(pType->GetBase(&pBaseType)) || pBaseType == NULL) return FALSE; - if(FAILED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen))) return FALSE; - - return (_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0); - } - - static HRESULT AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen) - { - bool isFirst = true; - ToRelease<ICorDebugTypeEnum> pTypeEnum; - if(SUCCEEDED(pType->EnumerateTypeParameters(&pTypeEnum))) - { - ULONG numTypes = 0; - ToRelease<ICorDebugType> pCurrentTypeParam; - - while(SUCCEEDED(pTypeEnum->Next(1, &pCurrentTypeParam, &numTypes))) - { - if(numTypes == 0) break; - - if(isFirst) - { - isFirst = false; - wcsncat_s(typeName, typeNameLen, W("<"), typeNameLen); - } - else wcsncat_s(typeName, typeNameLen, W(","), typeNameLen); - - WCHAR typeParamName[mdNameLen]; - typeParamName[0] = L'\0'; - GetTypeOfValue(pCurrentTypeParam, typeParamName, mdNameLen); - wcsncat_s(typeName, typeNameLen, typeParamName, typeNameLen); - } - if(!isFirst) - wcsncat_s(typeName, typeNameLen, W(">"), typeNameLen); - } - - return S_OK; - } - - static HRESULT GetTypeOfValue(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen) - { - HRESULT Status = S_OK; - - CorElementType corElemType; - IfFailRet(pType->GetType(&corElemType)); - - switch (corElemType) - { - //List of unsupported CorElementTypes: - //ELEMENT_TYPE_END = 0x0, - //ELEMENT_TYPE_VAR = 0x13, // a class type variable VAR <U1> - //ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn> - //ELEMENT_TYPE_TYPEDBYREF = 0x16, // TYPEDREF (it takes no args) a typed referece to some other type - //ELEMENT_TYPE_MVAR = 0x1e, // a method type variable MVAR <U1> - //ELEMENT_TYPE_CMOD_REQD = 0x1F, // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef> - //ELEMENT_TYPE_CMOD_OPT = 0x20, // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef> - //ELEMENT_TYPE_INTERNAL = 0x21, // INTERNAL <typehandle> - //ELEMENT_TYPE_MAX = 0x22, // first invalid element type - //ELEMENT_TYPE_MODIFIER = 0x40, - //ELEMENT_TYPE_SENTINEL = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs - //ELEMENT_TYPE_PINNED = 0x05 | ELEMENT_TYPE_MODIFIER, - //ELEMENT_TYPE_R4_HFA = 0x06 | ELEMENT_TYPE_MODIFIER, // used only internally for R4 HFA types - //ELEMENT_TYPE_R8_HFA = 0x07 | ELEMENT_TYPE_MODIFIER, // used only internally for R8 HFA types - default: - swprintf_s(typeName, typeNameLen, W("(Unhandled CorElementType: 0x%x)\0"), corElemType); - break; - - case ELEMENT_TYPE_VALUETYPE: - case ELEMENT_TYPE_CLASS: - { - //Defaults in case we fail... - if(corElemType == ELEMENT_TYPE_VALUETYPE) swprintf_s(typeName, typeNameLen, W("struct\0")); - else swprintf_s(typeName, typeNameLen, W("class\0")); - - mdTypeDef typeDef; - ToRelease<ICorDebugClass> pClass; - if(SUCCEEDED(pType->GetClass(&pClass)) && SUCCEEDED(pClass->GetToken(&typeDef))) - { - ToRelease<ICorDebugModule> pModule; - IfFailRet(pClass->GetModule(&pModule)); - - ToRelease<IUnknown> pMDUnknown; - ToRelease<IMetaDataImport> pMD; - IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); - IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD)); - - if(SUCCEEDED(NameForToken_s(TokenFromRid(typeDef, mdtTypeDef), pMD, g_mdName, mdNameLen, false))) - swprintf_s(typeName, typeNameLen, W("%s\0"), g_mdName); - } - AddGenericArgs(pType, typeName, typeNameLen); - } - break; - case ELEMENT_TYPE_VOID: - swprintf_s(typeName, typeNameLen, W("void\0")); - break; - case ELEMENT_TYPE_BOOLEAN: - swprintf_s(typeName, typeNameLen, W("bool\0")); - break; - case ELEMENT_TYPE_CHAR: - swprintf_s(typeName, typeNameLen, W("char\0")); - break; - case ELEMENT_TYPE_I1: - swprintf_s(typeName, typeNameLen, W("signed byte\0")); - break; - case ELEMENT_TYPE_U1: - swprintf_s(typeName, typeNameLen, W("byte\0")); - break; - case ELEMENT_TYPE_I2: - swprintf_s(typeName, typeNameLen, W("short\0")); - break; - case ELEMENT_TYPE_U2: - swprintf_s(typeName, typeNameLen, W("unsigned short\0")); - break; - case ELEMENT_TYPE_I4: - swprintf_s(typeName, typeNameLen, W("int\0")); - break; - case ELEMENT_TYPE_U4: - swprintf_s(typeName, typeNameLen, W("unsigned int\0")); - break; - case ELEMENT_TYPE_I8: - swprintf_s(typeName, typeNameLen, W("long\0")); - break; - case ELEMENT_TYPE_U8: - swprintf_s(typeName, typeNameLen, W("unsigned long\0")); - break; - case ELEMENT_TYPE_R4: - swprintf_s(typeName, typeNameLen, W("float\0")); - break; - case ELEMENT_TYPE_R8: - swprintf_s(typeName, typeNameLen, W("double\0")); - break; - case ELEMENT_TYPE_OBJECT: - swprintf_s(typeName, typeNameLen, W("object\0")); - break; - case ELEMENT_TYPE_STRING: - swprintf_s(typeName, typeNameLen, W("string\0")); - break; - case ELEMENT_TYPE_I: - swprintf_s(typeName, typeNameLen, W("IntPtr\0")); - break; - case ELEMENT_TYPE_U: - swprintf_s(typeName, typeNameLen, W("UIntPtr\0")); - break; - case ELEMENT_TYPE_SZARRAY: - case ELEMENT_TYPE_ARRAY: - case ELEMENT_TYPE_BYREF: - case ELEMENT_TYPE_PTR: - { - ToRelease<ICorDebugType> pFirstParameter; - if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter))) - GetTypeOfValue(pFirstParameter, typeName, typeNameLen); - else - swprintf_s(typeName, typeNameLen, W("<unknown>\0")); - - switch(corElemType) - { - case ELEMENT_TYPE_SZARRAY: - wcsncat_s(typeName, typeNameLen, W("[]\0"), typeNameLen); - return S_OK; - case ELEMENT_TYPE_ARRAY: - { - ULONG32 rank = 0; - pType->GetRank(&rank); - wcsncat_s(typeName, typeNameLen, W("["), typeNameLen); - for(ULONG32 i = 0; i < rank - 1; i++) - { - // - wcsncat_s(typeName, typeNameLen, W(","), typeNameLen); - } - wcsncat_s(typeName, typeNameLen, W("]\0"), typeNameLen); - } - return S_OK; - case ELEMENT_TYPE_BYREF: - wcsncat_s(typeName, typeNameLen, W("&\0"), typeNameLen); - return S_OK; - case ELEMENT_TYPE_PTR: - wcsncat_s(typeName, typeNameLen, W("*\0"), typeNameLen); - return S_OK; - default: - // note we can never reach here as this is a nested switch - // and corElemType can only be one of the values above - break; - } - } - break; - case ELEMENT_TYPE_FNPTR: - swprintf_s(typeName, typeNameLen, W("*(...)\0")); - break; - case ELEMENT_TYPE_TYPEDBYREF: - swprintf_s(typeName, typeNameLen, W("typedbyref\0")); - break; - } - return S_OK; - } - - static HRESULT GetTypeOfValue(ICorDebugValue * pValue, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen) - { - HRESULT Status = S_OK; - - CorElementType corElemType; - IfFailRet(pValue->GetType(&corElemType)); - - ToRelease<ICorDebugType> pType; - ToRelease<ICorDebugValue2> pValue2; - if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugValue2, (void**) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType))) - return GetTypeOfValue(pType, typeName, typeNameLen); - else - swprintf_s(typeName, typeNameLen, W("<unknown>\0")); - - return S_OK; - } - - static HRESULT PrintEnumValue(ICorDebugValue* pInputValue, BYTE* enumValue) - { - HRESULT Status = S_OK; - - ToRelease<ICorDebugValue> pValue; - IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, NULL)); - - mdTypeDef currentTypeDef; - ToRelease<ICorDebugClass> pClass; - ToRelease<ICorDebugValue2> pValue2; - ToRelease<ICorDebugType> pType; - ToRelease<ICorDebugModule> pModule; - IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)); - IfFailRet(pValue2->GetExactType(&pType)); - IfFailRet(pType->GetClass(&pClass)); - IfFailRet(pClass->GetModule(&pModule)); - IfFailRet(pClass->GetToken(¤tTypeDef)); - - ToRelease<IUnknown> pMDUnknown; - ToRelease<IMetaDataImport> pMD; - IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); - IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD)); - - - //First, we need to figure out the underlying enum type so that we can correctly type cast the raw values of each enum constant - //We get that from the non-static field of the enum variable (I think the field is called __value or something similar) - ULONG numFields = 0; - HCORENUM fEnum = NULL; - mdFieldDef fieldDef; - CorElementType enumUnderlyingType = ELEMENT_TYPE_END; - while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0) - { - DWORD fieldAttr = 0; - PCCOR_SIGNATURE pSignatureBlob = NULL; - ULONG sigBlobLength = 0; - if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, NULL, 0, NULL, &fieldAttr, &pSignatureBlob, &sigBlobLength, NULL, NULL, NULL))) - { - if((fieldAttr & fdStatic) == 0) - { - CorSigUncompressCallingConv(pSignatureBlob); - enumUnderlyingType = CorSigUncompressElementType(pSignatureBlob); - break; - } - } - } - pMD->CloseEnum(fEnum); - - - //Now that we know the underlying enum type, let's decode the enum variable into OR-ed, human readable enum contants - fEnum = NULL; - bool isFirst = true; - ULONG64 remainingValue = *((ULONG64*)enumValue); - while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0) - { - ULONG nameLen = 0; - DWORD fieldAttr = 0; - WCHAR mdName[mdNameLen]; - UVCP_CONSTANT pRawValue = NULL; - ULONG rawValueLength = 0; - if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, &pRawValue, &rawValueLength))) - { - DWORD enumValueRequiredAttributes = fdPublic | fdStatic | fdLiteral | fdHasDefault; - if((fieldAttr & enumValueRequiredAttributes) != enumValueRequiredAttributes) - continue; - - ULONG64 currentConstValue = 0; - switch (enumUnderlyingType) - { - case ELEMENT_TYPE_CHAR: - case ELEMENT_TYPE_I1: - currentConstValue = (ULONG64)(*((CHAR*)pRawValue)); - break; - case ELEMENT_TYPE_U1: - currentConstValue = (ULONG64)(*((BYTE*)pRawValue)); - break; - case ELEMENT_TYPE_I2: - currentConstValue = (ULONG64)(*((SHORT*)pRawValue)); - break; - case ELEMENT_TYPE_U2: - currentConstValue = (ULONG64)(*((USHORT*)pRawValue)); - break; - case ELEMENT_TYPE_I4: - currentConstValue = (ULONG64)(*((INT32*)pRawValue)); - break; - case ELEMENT_TYPE_U4: - currentConstValue = (ULONG64)(*((UINT32*)pRawValue)); - break; - case ELEMENT_TYPE_I8: - currentConstValue = (ULONG64)(*((LONG*)pRawValue)); - break; - case ELEMENT_TYPE_U8: - currentConstValue = (ULONG64)(*((ULONG*)pRawValue)); - break; - case ELEMENT_TYPE_I: - currentConstValue = (ULONG64)(*((int*)pRawValue)); - break; - case ELEMENT_TYPE_U: - case ELEMENT_TYPE_R4: - case ELEMENT_TYPE_R8: - // Technically U and the floating-point ones are options in the CLI, but not in the CLS or C#, so these are NYI - default: - currentConstValue = 0; - } - - if((currentConstValue == remainingValue) || ((currentConstValue != 0) && ((currentConstValue & remainingValue) == currentConstValue))) - { - remainingValue &= ~currentConstValue; - if(isFirst) - { - ExtOut(" = %S", mdName); - isFirst = false; - } - else ExtOut(" | %S", mdName); - } - } - } - pMD->CloseEnum(fEnum); - - return S_OK; - } - - static HRESULT PrintStringValue(ICorDebugValue * pValue) - { - HRESULT Status; - - ToRelease<ICorDebugStringValue> pStringValue; - IfFailRet(pValue->QueryInterface(IID_ICorDebugStringValue, (LPVOID*) &pStringValue)); - - ULONG32 cchValue; - IfFailRet(pStringValue->GetLength(&cchValue)); - cchValue++; // Allocate one more for null terminator - - CQuickString quickString; - quickString.Alloc(cchValue); - - ULONG32 cchValueReturned; - IfFailRet(pStringValue->GetString( - cchValue, - &cchValueReturned, - quickString.String())); - - ExtOut(" = \"%S\"\n", quickString.String()); - - return S_OK; - } - - static HRESULT PrintSzArrayValue(ICorDebugValue * pValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame) - { - HRESULT Status = S_OK; - - ToRelease<ICorDebugArrayValue> pArrayValue; - IfFailRet(pValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue)); - - ULONG32 nRank; - IfFailRet(pArrayValue->GetRank(&nRank)); - if (nRank != 1) - { - return E_UNEXPECTED; - } - - ULONG32 cElements; - IfFailRet(pArrayValue->GetCount(&cElements)); - - if (cElements == 0) ExtOut(" (empty)\n"); - else if (cElements == 1) ExtOut(" (1 element)\n"); - else ExtOut(" (%d elements)\n", cElements); - - if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK; - size_t currentExpansionLen = _wcslen(currentExpansion); - - for (ULONG32 i=0; i < cElements; i++) - { - for(int j = 0; j <= indent; j++) ExtOut(" "); - currentExpansion[currentExpansionLen] = L'\0'; - swprintf_s(currentExpansion, mdNameLen, W("%s.[%d]\0"), currentExpansion, i); - - bool printed = false; - CorElementType corElemType; - ToRelease<ICorDebugType> pFirstParameter; - ToRelease<ICorDebugValue2> pValue2; - ToRelease<ICorDebugType> pType; - if(SUCCEEDED(pArrayValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType))) - { - if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)) && SUCCEEDED(pFirstParameter->GetType(&corElemType))) - { - switch(corElemType) - { - //If the array element is something that we can expand with !clrstack, show information about the type of this element - case ELEMENT_TYPE_VALUETYPE: - case ELEMENT_TYPE_CLASS: - case ELEMENT_TYPE_SZARRAY: - { - WCHAR typeOfElement[mdNameLen]; - GetTypeOfValue(pFirstParameter, typeOfElement, mdNameLen); - DMLOut(" |- %s = %S", DMLManagedVar(currentExpansion, currentFrame, i), typeOfElement); - printed = true; - } - break; - default: - break; - } - } - } - if(!printed) DMLOut(" |- %s", DMLManagedVar(currentExpansion, currentFrame, i)); - - ToRelease<ICorDebugValue> pElementValue; - IfFailRet(pArrayValue->GetElementAtPosition(i, &pElementValue)); - IfFailRet(PrintValue(pElementValue, pILFrame, pMD, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame)); - } - - return S_OK; - } - - static HRESULT PrintValue(ICorDebugValue * pInputValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame) - { - HRESULT Status = S_OK; - - BOOL isNull = TRUE; - ToRelease<ICorDebugValue> pValue; - IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull)); - - if(isNull) - { - ExtOut(" = null\n"); - return S_OK; - } - - ULONG32 cbSize; - IfFailRet(pValue->GetSize(&cbSize)); - ArrayHolder<BYTE> rgbValue = new NOTHROW BYTE[cbSize]; - if (rgbValue == NULL) - { - ReportOOM(); - return E_OUTOFMEMORY; - } - - memset(rgbValue.GetPtr(), 0, cbSize * sizeof(BYTE)); - - CorElementType corElemType; - IfFailRet(pValue->GetType(&corElemType)); - if (corElemType == ELEMENT_TYPE_STRING) - { - return PrintStringValue(pValue); - } - - if (corElemType == ELEMENT_TYPE_SZARRAY) - { - return PrintSzArrayValue(pValue, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame); - } - - ToRelease<ICorDebugGenericValue> pGenericValue; - IfFailRet(pValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue)); - IfFailRet(pGenericValue->GetValue((LPVOID) &(rgbValue[0]))); - - if(IsEnum(pValue)) - { - Status = PrintEnumValue(pValue, rgbValue); - ExtOut("\n"); - return Status; - } - - switch (corElemType) - { - default: - ExtOut(" (Unhandled CorElementType: 0x%x)\n", corElemType); - break; - - case ELEMENT_TYPE_PTR: - ExtOut(" = <pointer>\n"); - break; - - case ELEMENT_TYPE_FNPTR: - { - CORDB_ADDRESS addr = 0; - ToRelease<ICorDebugReferenceValue> pReferenceValue = NULL; - if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue))) - pReferenceValue->GetValue(&addr); - ExtOut(" = <function pointer 0x%x>\n", addr); - } - break; - - case ELEMENT_TYPE_VALUETYPE: - case ELEMENT_TYPE_CLASS: - CORDB_ADDRESS addr; - if(SUCCEEDED(pValue->GetAddress(&addr))) - { - ExtOut(" @ 0x%I64x\n", addr); - } - else - { - ExtOut("\n"); - } - ProcessFields(pValue, NULL, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame); - break; - - case ELEMENT_TYPE_BOOLEAN: - ExtOut(" = %s\n", rgbValue[0] == 0 ? "false" : "true"); - break; - - case ELEMENT_TYPE_CHAR: - ExtOut(" = '%C'\n", *(WCHAR *) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_I1: - ExtOut(" = %d\n", *(char*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_U1: - ExtOut(" = %d\n", *(unsigned char*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_I2: - ExtOut(" = %hd\n", *(short*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_U2: - ExtOut(" = %hu\n", *(unsigned short*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_I: - ExtOut(" = %d\n", *(int*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_U: - ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_I4: - ExtOut(" = %d\n", *(int*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_U4: - ExtOut(" = %u\n", *(unsigned int*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_I8: - ExtOut(" = %I64d\n", *(__int64*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_U8: - ExtOut(" = %I64u\n", *(unsigned __int64*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_R4: - ExtOut(" = %f\n", (double) *(float*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_R8: - ExtOut(" = %f\n", *(double*) &(rgbValue[0])); - break; - - case ELEMENT_TYPE_OBJECT: - ExtOut(" = object\n"); - break; - - // TODO: The following corElementTypes are not yet implemented here. Array - // might be interesting to add, though the others may be of rather limited use: - // ELEMENT_TYPE_ARRAY = 0x14, // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ... - // - // ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn> - } - - return S_OK; - } - - static HRESULT PrintParameters(BOOL bParams, BOOL bLocals, IMetaDataImport * pMD, mdTypeDef typeDef, mdMethodDef methodDef, ICorDebugILFrame * pILFrame, ICorDebugModule * pModule, __in_z WCHAR* varToExpand, int currentFrame) - { - HRESULT Status = S_OK; - - ULONG cParams = 0; - ToRelease<ICorDebugValueEnum> pParamEnum; - IfFailRet(pILFrame->EnumerateArguments(&pParamEnum)); - IfFailRet(pParamEnum->GetCount(&cParams)); - if (cParams > 0 && bParams) - { - DWORD methAttr = 0; - IfFailRet(pMD->GetMethodProps(methodDef, NULL, NULL, 0, NULL, &methAttr, NULL, NULL, NULL, NULL)); - - ExtOut("\nPARAMETERS:\n"); - for (ULONG i=0; i < cParams; i++) - { - ULONG paramNameLen = 0; - mdParamDef paramDef; - WCHAR paramName[mdNameLen] = W("\0"); - - if(i == 0 && (methAttr & mdStatic) == 0) - swprintf_s(paramName, mdNameLen, W("this\0")); - else - { - int idx = ((methAttr & mdStatic) == 0)? i : (i + 1); - if(SUCCEEDED(pMD->GetParamForMethodIndex(methodDef, idx, ¶mDef))) - pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, ¶mNameLen, NULL, NULL, NULL, NULL); - } - if(_wcslen(paramName) == 0) - swprintf_s(paramName, mdNameLen, W("param_%d\0"), i); - - ToRelease<ICorDebugValue> pValue; - ULONG cArgsFetched; - Status = pParamEnum->Next(1, &pValue, &cArgsFetched); - - if (FAILED(Status)) - { - ExtOut(" + (Error 0x%x retrieving parameter '%S')\n", Status, paramName); - continue; - } - - if (Status == S_FALSE) - { - break; - } - - WCHAR typeName[mdNameLen] = W("\0"); - GetTypeOfValue(pValue, typeName, mdNameLen); - DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName)); - - ToRelease<ICorDebugReferenceValue> pRefValue; - if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL) - { - BOOL bIsNull = TRUE; - pRefValue->IsNull(&bIsNull); - if(bIsNull) - { - ExtOut(" = null\n"); - continue; - } - } - - WCHAR currentExpansion[mdNameLen]; - swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName); - if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK) - ExtOut(" + (Error 0x%x printing parameter %d)\n", Status, i); - } - } - else if (cParams == 0 && bParams) - ExtOut("\nPARAMETERS: (none)\n"); - - ULONG cLocals = 0; - ToRelease<ICorDebugValueEnum> pLocalsEnum; - IfFailRet(pILFrame->EnumerateLocalVariables(&pLocalsEnum)); - IfFailRet(pLocalsEnum->GetCount(&cLocals)); - if (cLocals > 0 && bLocals) - { - bool symbolsAvailable = false; - SymbolReader symReader; - if(SUCCEEDED(symReader.LoadSymbols(pMD, pModule))) - symbolsAvailable = true; - ExtOut("\nLOCALS:\n"); - for (ULONG i=0; i < cLocals; i++) - { - ULONG paramNameLen = 0; - WCHAR paramName[mdNameLen] = W("\0"); - - ToRelease<ICorDebugValue> pValue; - if(symbolsAvailable) - { - Status = symReader.GetNamedLocalVariable(pILFrame, i, paramName, mdNameLen, &pValue); - } - else - { - ULONG cArgsFetched; - Status = pLocalsEnum->Next(1, &pValue, &cArgsFetched); - } - if(_wcslen(paramName) == 0) - swprintf_s(paramName, mdNameLen, W("local_%d\0"), i); - - if (FAILED(Status)) - { - ExtOut(" + (Error 0x%x retrieving local variable '%S')\n", Status, paramName); - continue; - } - - if (Status == S_FALSE) - { - break; - } - - WCHAR typeName[mdNameLen] = W("\0"); - GetTypeOfValue(pValue, typeName, mdNameLen); - DMLOut(" + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName)); - - ToRelease<ICorDebugReferenceValue> pRefValue = NULL; - if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL) - { - BOOL bIsNull = TRUE; - pRefValue->IsNull(&bIsNull); - if(bIsNull) - { - ExtOut(" = null\n"); - continue; - } - } - - WCHAR currentExpansion[mdNameLen]; - swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName); - if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK) - ExtOut(" + (Error 0x%x printing local variable %d)\n", Status, i); - } - } - else if (cLocals == 0 && bLocals) - ExtOut("\nLOCALS: (none)\n"); - - if(bParams || bLocals) - ExtOut("\n"); - - return S_OK; - } - - static HRESULT ProcessFields(ICorDebugValue* pInputValue, ICorDebugType* pTypeCast, ICorDebugILFrame * pILFrame, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame) - { - if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK; - size_t currentExpansionLen = _wcslen(currentExpansion); - - HRESULT Status = S_OK; - - BOOL isNull = FALSE; - ToRelease<ICorDebugValue> pValue; - IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull)); - - if(isNull) return S_OK; - - mdTypeDef currentTypeDef; - ToRelease<ICorDebugClass> pClass; - ToRelease<ICorDebugValue2> pValue2; - ToRelease<ICorDebugType> pType; - ToRelease<ICorDebugModule> pModule; - IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)); - if(pTypeCast == NULL) - IfFailRet(pValue2->GetExactType(&pType)); - else - { - pType = pTypeCast; - pType->AddRef(); - } - IfFailRet(pType->GetClass(&pClass)); - IfFailRet(pClass->GetModule(&pModule)); - IfFailRet(pClass->GetToken(¤tTypeDef)); - - ToRelease<IUnknown> pMDUnknown; - ToRelease<IMetaDataImport> pMD; - IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); - IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD)); - - WCHAR baseTypeName[mdNameLen] = W("\0"); - ToRelease<ICorDebugType> pBaseType; - if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen))) - { - if(_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0) - return S_OK; - else if(_wcsncmp(baseTypeName, W("System.Object"), 13) != 0 && _wcsncmp(baseTypeName, W("System.ValueType"), 16) != 0) - { - currentExpansion[currentExpansionLen] = W('\0'); - wcscat_s(currentExpansion, currentExpansionSize, W(".\0")); - wcscat_s(currentExpansion, currentExpansionSize, W("[basetype]")); - for(int i = 0; i < indent; i++) ExtOut(" "); - DMLOut(" |- %S %s\n", baseTypeName, DMLManagedVar(currentExpansion, currentFrame, W("[basetype]"))); - - if(ShouldExpandVariable(varToExpand, currentExpansion)) - ProcessFields(pInputValue, pBaseType, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame); - } - } - - - ULONG numFields = 0; - HCORENUM fEnum = NULL; - mdFieldDef fieldDef; - while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0) - { - ULONG nameLen = 0; - DWORD fieldAttr = 0; - WCHAR mdName[mdNameLen]; - WCHAR typeName[mdNameLen]; - if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, NULL, NULL))) - { - currentExpansion[currentExpansionLen] = W('\0'); - wcscat_s(currentExpansion, currentExpansionSize, W(".\0")); - wcscat_s(currentExpansion, currentExpansionSize, mdName); - - ToRelease<ICorDebugValue> pFieldVal; - if(fieldAttr & fdLiteral) - { - //TODO: Is it worth it?? - //ExtOut(" |- const %S", mdName); - } - else - { - for(int i = 0; i < indent; i++) ExtOut(" "); - - if (fieldAttr & fdStatic) - pType->GetStaticFieldValue(fieldDef, pILFrame, &pFieldVal); - else - { - ToRelease<ICorDebugObjectValue> pObjValue; - if (SUCCEEDED(pValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue))) - pObjValue->GetFieldValue(pClass, fieldDef, &pFieldVal); - } - - if(pFieldVal != NULL) - { - typeName[0] = L'\0'; - GetTypeOfValue(pFieldVal, typeName, mdNameLen); - DMLOut(" |- %S %s", typeName, DMLManagedVar(currentExpansion, currentFrame, mdName)); - PrintValue(pFieldVal, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame); - } - else if(!(fieldAttr & fdLiteral)) - ExtOut(" |- < unknown type > %S\n", mdName); - } - } - } - pMD->CloseEnum(fEnum); - return S_OK; - } - -public: - - // This is the main worker function used if !clrstack is called with "-i" to indicate - // that the public ICorDebug* should be used instead of the private DAC interface. NOTE: - // Currently only bParams is supported. NOTE: This is a work in progress and the - // following would be good to do: - // * More thorough testing with interesting stacks, especially with transitions into - // and out of managed code. - // * Consider interleaving this code back into the main body of !clrstack if it turns - // out that there's a lot of duplication of code between these two functions. - // (Still unclear how things will look once locals is implemented.) - static HRESULT ClrStackFromPublicInterface(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, __in_z WCHAR* varToExpand = NULL, int onlyShowFrame = -1) - { - HRESULT Status; - - IfFailRet(InitCorDebugInterface()); - - ExtOut("\n\n\nDumping managed stack and managed variables using ICorDebug.\n"); - ExtOut("=============================================================================\n"); - - ToRelease<ICorDebugThread> pThread; - ToRelease<ICorDebugThread3> pThread3; - ToRelease<ICorDebugStackWalk> pStackWalk; - ULONG ulThreadID = 0; - g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID); - - IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread)); - IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3)); - IfFailRet(pThread3->CreateStackWalk(&pStackWalk)); - - InternalFrameManager internalFrameManager; - IfFailRet(internalFrameManager.Init(pThread3)); - - #if defined(_AMD64_) || defined(_ARM64_) - ExtOut("%-16s %-16s %s\n", "Child SP", "IP", "Call Site"); - #elif defined(_X86_) || defined(_ARM_) - ExtOut("%-8s %-8s %s\n", "Child SP", "IP", "Call Site"); - #endif - - int currentFrame = -1; - - for (Status = S_OK; ; Status = pStackWalk->Next()) - { - currentFrame++; - - if (Status == CORDBG_S_AT_END_OF_STACK) - { - ExtOut("Stack walk complete.\n"); - break; - } - IfFailRet(Status); - - if (IsInterrupt()) - { - ExtOut("<interrupted>\n"); - break; - } - - CROSS_PLATFORM_CONTEXT context; - ULONG32 cbContextActual; - if ((Status=pStackWalk->GetContext( - DT_CONTEXT_FULL, - sizeof(context), - &cbContextActual, - (BYTE *)&context))!=S_OK) - { - ExtOut("GetFrameContext failed: %lx\n",Status); - break; - } - - // First find the info for the Frame object, if the current frame has an associated clr!Frame. - CLRDATA_ADDRESS sp = GetSP(context); - CLRDATA_ADDRESS ip = GetIP(context); - - ToRelease<ICorDebugFrame> pFrame; - IfFailRet(pStackWalk->GetFrame(&pFrame)); - if (Status == S_FALSE) - { - DMLOut("%p %s [NativeStackFrame]\n", SOS_PTR(sp), DMLIP(ip)); - continue; - } - - // TODO: What about internal frames preceding the above native stack frame? - // Should I just exclude the above native stack frame from the output? - // TODO: Compare caller frame (instead of current frame) against internal frame, - // to deal with issues of current frame's current SP being closer to leaf than - // EE Frames it pushes. By "caller" I mean not just managed caller, but the - // very next non-internal frame dbi would return (native or managed). OR... - // perhaps I should use GetStackRange() instead, to see if the internal frame - // appears leafier than the base-part of the range of the currently iterated - // stack frame? I think I like that better. - _ASSERTE(pFrame != NULL); - IfFailRet(internalFrameManager.PrintPrecedingInternalFrames(pFrame)); - - // Print the stack and instruction pointers. - DMLOut("%p %s ", SOS_PTR(sp), DMLIP(ip)); - - ToRelease<ICorDebugRuntimeUnwindableFrame> pRuntimeUnwindableFrame; - Status = pFrame->QueryInterface(IID_ICorDebugRuntimeUnwindableFrame, (LPVOID *) &pRuntimeUnwindableFrame); - if (SUCCEEDED(Status)) - { - ExtOut("[RuntimeUnwindableFrame]\n"); - continue; - } - - // Print the method/Frame info - - // TODO: IS THE FOLLOWING NECESSARY, OR AM I GUARANTEED THAT ALL INTERNAL FRAMES - // CAN BE FOUND VIA GetActiveInternalFrames? - ToRelease<ICorDebugInternalFrame> pInternalFrame; - Status = pFrame->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame); - if (SUCCEEDED(Status)) - { - // This is a clr!Frame. - LPCWSTR pwszFrameName = W("TODO: Implement GetFrameName"); - ExtOut("[%S: p] ", pwszFrameName); - } - - // Print the frame's associated function info, if it has any. - ToRelease<ICorDebugILFrame> pILFrame; - HRESULT hrILFrame = pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame); - - if (SUCCEEDED(hrILFrame)) - { - ToRelease<ICorDebugFunction> pFunction; - Status = pFrame->GetFunction(&pFunction); - if (FAILED(Status)) - { - // We're on a JITted frame, but there's no Function for it. So it must - // be... - ExtOut("[IL Stub or LCG]\n"); - continue; - } - - ToRelease<ICorDebugClass> pClass; - ToRelease<ICorDebugModule> pModule; - mdMethodDef methodDef; - IfFailRet(pFunction->GetClass(&pClass)); - IfFailRet(pFunction->GetModule(&pModule)); - IfFailRet(pFunction->GetToken(&methodDef)); - - WCHAR wszModuleName[100]; - ULONG32 cchModuleNameActual; - IfFailRet(pModule->GetName(_countof(wszModuleName), &cchModuleNameActual, wszModuleName)); - - ToRelease<IUnknown> pMDUnknown; - ToRelease<IMetaDataImport> pMD; - ToRelease<IMDInternalImport> pMDInternal; - IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); - IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD)); - IfFailRet(GetMDInternalFromImport(pMD, &pMDInternal)); - - mdTypeDef typeDef; - IfFailRet(pClass->GetToken(&typeDef)); - - // Note that we don't need to pretty print the class, as class name is - // already printed from GetMethodName below - - CQuickBytes functionName; - // TODO: WARNING: GetMethodName() appears to include lots of unexercised - // code, as evidenced by some fundamental bugs I found. It should either be - // thoroughly reviewed, or some other more exercised code path to grab the - // name should be used. - // TODO: If we do stay with GetMethodName, it should be updated to print - // generics properly. Today, it does not show generic type parameters, and - // if any arguments have a generic type, those arguments are just shown as - // "__Canon", even when they're value types. - GetMethodName(methodDef, pMD, &functionName); - - DMLOut(DMLManagedVar(W("-a"), currentFrame, (LPWSTR)functionName.Ptr())); - ExtOut(" (%S)\n", wszModuleName); - - if (SUCCEEDED(hrILFrame) && (bParams || bLocals)) - { - if(onlyShowFrame == -1 || (onlyShowFrame >= 0 && currentFrame == onlyShowFrame)) - IfFailRet(PrintParameters(bParams, bLocals, pMD, typeDef, methodDef, pILFrame, pModule, varToExpand, currentFrame)); - } - } - } - ExtOut("=============================================================================\n"); - -#ifdef FEATURE_PAL - // Temporary until we get a process exit notification plumbed from lldb - UninitCorDebugInterface(); -#endif - return S_OK; - } -}; - -WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj) -{ - WString res; - - if (ref.HasRegisterInformation) - { - WCHAR reg[32]; - HRESULT hr = g_sos->GetRegisterName(ref.Register, _countof(reg), reg, NULL); - if (SUCCEEDED(hr)) - res = reg; - else - res = W("<unknown register>"); - - if (ref.Offset) - { - int offset = ref.Offset; - if (offset > 0) - { - res += W("+"); - } - else - { - res += W("-"); - offset = -offset; - } - - res += Hex(offset); - } - - res += W(": "); - } - - if (ref.Address) - res += WString(Pointer(ref.Address)); - - if (printObj) - { - if (ref.Address) - res += W(" -> "); - - res += WString(ObjectPtr(ref.Object)); - } - - if (ref.Flags & SOSRefPinned) - { - res += W(" (pinned)"); - } - - if (ref.Flags & SOSRefInterior) - { - res += W(" (interior)"); - } - - return res; -} - -void PrintRef(const SOSStackRefData &ref, TableOutput &out) -{ - WString res = BuildRegisterOutput(ref); - - if (ref.Object && (ref.Flags & SOSRefInterior) == 0) - { - WCHAR type[128]; - sos::BuildTypeWithExtraInfo(TO_TADDR(ref.Object), _countof(type), type); - - res += WString(W(" - ")) + type; - } - - out.WriteColumn(2, res); -} - - -class ClrStackImpl -{ -public: - static void PrintThread(ULONG osID, BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bFull, BOOL bDisplayRegVals) - { - // Symbols variables - ULONG symlines = 0; // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options - if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines))) - { - symlines &= SYMOPT_LOAD_LINES; - } - - if (symlines == 0) - bSuppressLines = TRUE; - - ToRelease<IXCLRDataStackWalk> pStackWalk; - - HRESULT hr = CreateStackWalk(osID, &pStackWalk); - if (FAILED(hr) || pStackWalk == NULL) - { - ExtOut("Failed to start stack walk: %lx\n", hr); - return; - } - -#ifdef DEBUG_STACK_CONTEXT - PDEBUG_STACK_FRAME currentNativeFrame = NULL; - ULONG numNativeFrames = 0; - if (bFull) - { - hr = GetContextStackTrace(osID, &numNativeFrames); - if (FAILED(hr)) - { - ExtOut("Failed to get native stack frames: %lx\n", hr); - return; - } - currentNativeFrame = &g_Frames[0]; - } -#endif // DEBUG_STACK_CONTEXT - - unsigned int refCount = 0, errCount = 0; - ArrayHolder<SOSStackRefData> pRefs = NULL; - ArrayHolder<SOSStackRefError> pErrs = NULL; - if (bGC && FAILED(GetGCRefs(osID, &pRefs, &refCount, &pErrs, &errCount))) - refCount = 0; - - TableOutput out(3, POINTERSIZE_HEX, AlignRight); - out.WriteRow("Child SP", "IP", "Call Site"); - - do - { - if (IsInterrupt()) - { - ExtOut("<interrupted>\n"); - break; - } - CLRDATA_ADDRESS ip = 0, sp = 0; - hr = GetFrameLocation(pStackWalk, &ip, &sp); - - DacpFrameData FrameData; - HRESULT frameDataResult = FrameData.Request(pStackWalk); - if (SUCCEEDED(frameDataResult) && FrameData.frameAddr) - sp = FrameData.frameAddr; - -#ifdef DEBUG_STACK_CONTEXT - while ((numNativeFrames > 0) && (currentNativeFrame->StackOffset <= sp)) - { - if (currentNativeFrame->StackOffset != sp) - { - PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines); - } - currentNativeFrame++; - numNativeFrames--; - } -#endif // DEBUG_STACK_CONTEXT - - // Print the stack pointer. - out.WriteColumn(0, sp); - - // Print the method/Frame info - if (SUCCEEDED(frameDataResult) && FrameData.frameAddr) - { - // Skip the instruction pointer because it doesn't really mean anything for method frames - out.WriteColumn(1, bFull ? String("") : NativePtr(ip)); - - // This is a clr!Frame. - out.WriteColumn(2, GetFrameFromAddress(TO_TADDR(FrameData.frameAddr), pStackWalk, bFull)); - - // Print out gc references for the Frame. - for (unsigned int i = 0; i < refCount; ++i) - if (pRefs[i].Source == sp) - PrintRef(pRefs[i], out); - - // Print out an error message if we got one. - for (unsigned int i = 0; i < errCount; ++i) - if (pErrs[i].Source == sp) - out.WriteColumn(2, "Failed to enumerate GC references."); - } - else - { - out.WriteColumn(1, InstructionPtr(ip)); - out.WriteColumn(2, MethodNameFromIP(ip, bSuppressLines, bFull, bFull)); - - // Print out gc references. refCount will be zero if bGC is false (or if we - // failed to fetch gc reference information). - for (unsigned int i = 0; i < refCount; ++i) - if (pRefs[i].Source == ip && pRefs[i].StackPointer == sp) - PrintRef(pRefs[i], out); - - // Print out an error message if we got one. - for (unsigned int i = 0; i < errCount; ++i) - if (pErrs[i].Source == sp) - out.WriteColumn(2, "Failed to enumerate GC references."); - - if (bParams || bLocals) - PrintArgsAndLocals(pStackWalk, bParams, bLocals); - } - - if (bDisplayRegVals) - PrintManagedFrameContext(pStackWalk); - - } while (pStackWalk->Next() == S_OK); - -#ifdef DEBUG_STACK_CONTEXT - while (numNativeFrames > 0) - { - PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines); - currentNativeFrame++; - numNativeFrames--; - } -#endif // DEBUG_STACK_CONTEXT - } - - static HRESULT PrintManagedFrameContext(IXCLRDataStackWalk *pStackWalk) - { - CROSS_PLATFORM_CONTEXT context; - HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context); - if (FAILED(hr) || hr == S_FALSE) - { - // GetFrameContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us. - ExtOut("GetFrameContext failed: %lx\n", hr); - return E_FAIL; - } - -#if defined(SOS_TARGET_AMD64) - String outputFormat3 = " %3s=%016x %3s=%016x %3s=%016x\n"; - String outputFormat2 = " %3s=%016x %3s=%016x\n"; - ExtOut(outputFormat3, "rsp", context.Amd64Context.Rsp, "rbp", context.Amd64Context.Rbp, "rip", context.Amd64Context.Rip); - ExtOut(outputFormat3, "rax", context.Amd64Context.Rax, "rbx", context.Amd64Context.Rbx, "rcx", context.Amd64Context.Rcx); - ExtOut(outputFormat3, "rdx", context.Amd64Context.Rdx, "rsi", context.Amd64Context.Rsi, "rdi", context.Amd64Context.Rdi); - ExtOut(outputFormat3, "r8", context.Amd64Context.R8, "r9", context.Amd64Context.R9, "r10", context.Amd64Context.R10); - ExtOut(outputFormat3, "r11", context.Amd64Context.R11, "r12", context.Amd64Context.R12, "r13", context.Amd64Context.R13); - ExtOut(outputFormat2, "r14", context.Amd64Context.R14, "r15", context.Amd64Context.R15); -#elif defined(SOS_TARGET_X86) - String outputFormat3 = " %3s=%08x %3s=%08x %3s=%08x\n"; - String outputFormat2 = " %3s=%08x %3s=%08x\n"; - ExtOut(outputFormat3, "esp", context.X86Context.Esp, "ebp", context.X86Context.Ebp, "eip", context.X86Context.Eip); - ExtOut(outputFormat3, "eax", context.X86Context.Eax, "ebx", context.X86Context.Ebx, "ecx", context.X86Context.Ecx); - ExtOut(outputFormat3, "edx", context.X86Context.Edx, "esi", context.X86Context.Esi, "edi", context.X86Context.Edi); -#elif defined(SOS_TARGET_ARM) - String outputFormat3 = " %3s=%08x %3s=%08x %3s=%08x\n"; - String outputFormat2 = " %s=%08x %s=%08x\n"; - String outputFormat1 = " %s=%08x\n"; - ExtOut(outputFormat3, "r0", context.ArmContext.R0, "r1", context.ArmContext.R1, "r2", context.ArmContext.R2); - ExtOut(outputFormat3, "r3", context.ArmContext.R3, "r4", context.ArmContext.R4, "r5", context.ArmContext.R5); - ExtOut(outputFormat3, "r6", context.ArmContext.R6, "r7", context.ArmContext.R7, "r8", context.ArmContext.R8); - ExtOut(outputFormat3, "r9", context.ArmContext.R9, "r10", context.ArmContext.R10, "r11", context.ArmContext.R11); - ExtOut(outputFormat1, "r12", context.ArmContext.R12); - ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc); - ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr); -#elif defined(SOS_TARGET_ARM64) - String outputXRegFormat3 = " x%d=%016x x%d=%016x x%d=%016x\n"; - String outputXRegFormat1 = " x%d=%016x\n"; - String outputFormat3 = " %s=%016x %s=%016x %s=%016x\n"; - String outputFormat2 = " %s=%08x %s=%08x\n"; - DWORD64 *X = context.Arm64Context.X; - for (int i = 0; i < 9; i++) - { - ExtOut(outputXRegFormat3, i + 0, X[i + 0], i + 1, X[i + 1], i + 2, X[i + 2]); - } - ExtOut(outputXRegFormat1, 28, X[28]); - ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc); - ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr); -#else - ExtOut("Can't display register values for this platform\n"); -#endif - return S_OK; - - } - - static HRESULT GetFrameLocation(IXCLRDataStackWalk *pStackWalk, CLRDATA_ADDRESS *ip, CLRDATA_ADDRESS *sp) - { - CROSS_PLATFORM_CONTEXT context; - HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context); - if (FAILED(hr) || hr == S_FALSE) - { - // GetFrameContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us. - ExtOut("GetFrameContext failed: %lx\n", hr); - return E_FAIL; - } - - // First find the info for the Frame object, if the current frame has an associated clr!Frame. - *ip = GetIP(context); - *sp = GetSP(context); - - if (IsDbgTargetArm()) - *ip = *ip & ~THUMB_CODE; - - return S_OK; - } - - static void PrintNativeStackFrame(TableOutput out, PDEBUG_STACK_FRAME frame, BOOL bSuppressLines) - { - char filename[MAX_LONGPATH + 1]; - char symbol[1024]; - ULONG64 displacement; - - ULONG64 ip = frame->InstructionOffset; - - out.WriteColumn(0, frame->StackOffset); - out.WriteColumn(1, NativePtr(ip)); - - HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, _countof(symbol), NULL, &displacement); - if (SUCCEEDED(hr) && symbol[0] != '\0') - { - String frameOutput; - frameOutput += symbol; - - if (displacement) - { - frameOutput += " + "; - frameOutput += Decimal(displacement); - } - - if (!bSuppressLines) - { - ULONG line; - hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, _countof(filename), NULL, NULL); - if (SUCCEEDED(hr)) - { - frameOutput += " at "; - frameOutput += filename; - frameOutput += ":"; - frameOutput += Decimal(line); - } - } - - out.WriteColumn(2, frameOutput); - } - else - { - out.WriteColumn(2, ""); - } - } - - static void PrintCurrentThread(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals) - { - ULONG id = 0; - ULONG osid = 0; - - g_ExtSystem->GetCurrentThreadSystemId(&osid); - ExtOut("OS Thread Id: 0x%x ", osid); - g_ExtSystem->GetCurrentThreadId(&id); - ExtOut("(%d)\n", id); - - PrintThread(osid, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals); - } - - static void PrintAllThreads(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals) - { - HRESULT Status; - - DacpThreadStoreData ThreadStore; - if ((Status = ThreadStore.Request(g_sos)) != S_OK) - { - ExtErr("Failed to request ThreadStore\n"); - return; - } - - DacpThreadData Thread; - CLRDATA_ADDRESS CurThread = ThreadStore.firstThread; - while (CurThread != 0) - { - if (IsInterrupt()) - break; - - if ((Status = Thread.Request(g_sos, CurThread)) != S_OK) - { - ExtErr("Failed to request thread at %p\n", CurThread); - return; - } - ExtOut("OS Thread Id: 0x%x\n", Thread.osThreadId); - PrintThread(Thread.osThreadId, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals); - CurThread = Thread.nextThread; - } - } - -private: - static HRESULT CreateStackWalk(ULONG osID, IXCLRDataStackWalk **ppStackwalk) - { - HRESULT hr = S_OK; - ToRelease<IXCLRDataTask> pTask; - - if ((hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK) - { - ExtOut("Unable to walk the managed stack. The current thread is likely not a \n"); - ExtOut("managed thread. You can run " SOSThreads " to get a list of managed threads in\n"); - ExtOut("the process\n"); - return hr; - } - - return pTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED | - CLRDATA_SIMPFRAME_MANAGED_METHOD | - CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE | - CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE, - ppStackwalk); - } - - /* Prints the args and locals of for a thread's stack. - * Params: - * pStackWalk - the stack we are printing - * bArgs - whether to print args - * bLocals - whether to print locals - */ - static void PrintArgsAndLocals(IXCLRDataStackWalk *pStackWalk, BOOL bArgs, BOOL bLocals) - { - ToRelease<IXCLRDataFrame> pFrame; - ToRelease<IXCLRDataValue> pVal; - ULONG32 argCount = 0; - ULONG32 localCount = 0; - HRESULT hr = S_OK; - - hr = pStackWalk->GetFrame(&pFrame); - - // Print arguments - if (SUCCEEDED(hr) && bArgs) - hr = pFrame->GetNumArguments(&argCount); - - if (SUCCEEDED(hr) && bArgs) - hr = ShowArgs(argCount, pFrame, pVal); - - // Print locals - if (SUCCEEDED(hr) && bLocals) - hr = pFrame->GetNumLocalVariables(&localCount); - - if (SUCCEEDED(hr) && bLocals) - ShowLocals(localCount, pFrame, pVal); - - ExtOut("\n"); - } - - - - /* Displays the arguments to a function - * Params: - * argy - the number of arguments the function has - * pFramey - the frame we are inspecting - * pVal - a pointer to the CLRDataValue we use to query for info about the args - */ - static HRESULT ShowArgs(ULONG32 argy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal) - { - CLRDATA_ADDRESS addr = 0; - BOOL fPrintedLocation = FALSE; - ULONG64 outVar = 0; - ULONG32 tmp; - HRESULT hr = S_OK; - - ArrayHolder<WCHAR> argName = new NOTHROW WCHAR[mdNameLen]; - if (!argName) - { - ReportOOM(); - return E_FAIL; - } - - for (ULONG32 i=0; i < argy; i++) - { - if (i == 0) - { - ExtOut(" PARAMETERS:\n"); - } - - hr = pFramey->GetArgumentByIndex(i, - &pVal, - mdNameLen, - &tmp, - argName); - - if (FAILED(hr)) - return hr; - - ExtOut(" "); - - if (argName[0] != L'\0') - { - ExtOut("%S ", argName.GetPtr()); - } - - // At times we cannot print the value of a parameter (most - // common case being a non-primitive value type). In these - // cases we need to print the location of the parameter, - // so that we can later examine it (e.g. using !dumpvc) - { - bool result = SUCCEEDED(pVal->GetNumLocations(&tmp)) && tmp == 1; - if (result) - result = SUCCEEDED(pVal->GetLocationByIndex(0, &tmp, &addr)); - - if (result) - { - if (tmp == CLRDATA_VLOC_REGISTER) - { - ExtOut("(<CLR reg>) "); - } - else - { - ExtOut("(0x%p) ", SOS_PTR(CDA_TO_UL64(addr))); - } - fPrintedLocation = TRUE; - } - } - - if (argName[0] != L'\0' || fPrintedLocation) - { - ExtOut("= "); - } - - if (HRESULT_CODE(pVal->GetBytes(0,&tmp,NULL)) == ERROR_BUFFER_OVERFLOW) - { - ArrayHolder<BYTE> pByte = new NOTHROW BYTE[tmp + 1]; - if (pByte == NULL) - { - ReportOOM(); - return E_FAIL; - } - - hr = pVal->GetBytes(tmp, &tmp, pByte); - - if (FAILED(hr)) - { - ExtOut("<unable to retrieve data>\n"); - } - else - { - switch(tmp) - { - case 1: outVar = *((BYTE *)pByte.GetPtr()); break; - case 2: outVar = *((short *)pByte.GetPtr()); break; - case 4: outVar = *((DWORD *)pByte.GetPtr()); break; - case 8: outVar = *((ULONG64 *)pByte.GetPtr()); break; - default: outVar = 0; - } - - if (outVar) - DMLOut("0x%s\n", DMLObject(outVar)); - else - ExtOut("0x%p\n", SOS_PTR(outVar)); - } - - } - else - { - ExtOut("<no data>\n"); - } - - pVal->Release(); - } - - return S_OK; - } - - - /* Prints the locals of a frame. - * Params: - * localy - the number of locals in the frame - * pFramey - the frame we are inspecting - * pVal - a pointer to the CLRDataValue we use to query for info about the args - */ - static HRESULT ShowLocals(ULONG32 localy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal) - { - for (ULONG32 i=0; i < localy; i++) - { - if (i == 0) - ExtOut(" LOCALS:\n"); - - HRESULT hr; - ExtOut(" "); - - // local names don't work in Whidbey. - hr = pFramey->GetLocalVariableByIndex(i, &pVal, mdNameLen, NULL, g_mdName); - if (FAILED(hr)) - { - return hr; - } - - ULONG32 numLocations; - if (SUCCEEDED(pVal->GetNumLocations(&numLocations)) && - numLocations == 1) - { - ULONG32 flags; - CLRDATA_ADDRESS addr; - if (SUCCEEDED(pVal->GetLocationByIndex(0, &flags, &addr))) - { - if (flags == CLRDATA_VLOC_REGISTER) - { - ExtOut("<CLR reg> "); - } - else - { - ExtOut("0x%p ", SOS_PTR(CDA_TO_UL64(addr))); - } - } - - // Can I get a name for the item? - - ExtOut("= "); - } - ULONG32 dwSize = 0; - hr = pVal->GetBytes(0, &dwSize, NULL); - - if (HRESULT_CODE(hr) == ERROR_BUFFER_OVERFLOW) - { - ArrayHolder<BYTE> pByte = new NOTHROW BYTE[dwSize + 1]; - if (pByte == NULL) - { - ReportOOM(); - return E_FAIL; - } - - hr = pVal->GetBytes(dwSize,&dwSize,pByte); - - if (FAILED(hr)) - { - ExtOut("<unable to retrieve data>\n"); - } - else - { - ULONG64 outVar = 0; - switch(dwSize) - { - case 1: outVar = *((BYTE *) pByte.GetPtr()); break; - case 2: outVar = *((short *) pByte.GetPtr()); break; - case 4: outVar = *((DWORD *) pByte.GetPtr()); break; - case 8: outVar = *((ULONG64 *) pByte.GetPtr()); break; - default: outVar = 0; - } - - if (outVar) - DMLOut("0x%s\n", DMLObject(outVar)); - else - ExtOut("0x%p\n", SOS_PTR(outVar)); - } - } - else - { - ExtOut("<no data>\n"); - } - - pVal->Release(); - } - - return S_OK; - } - -}; - -#ifndef FEATURE_PAL - -WatchCmd g_watchCmd; - -// The grand new !Watch command, private to Apollo for now -DECLARE_API(Watch) -{ - INIT_API_NOEE(); - BOOL bExpression = FALSE; - StringHolder addExpression; - StringHolder aExpression; - StringHolder saveName; - StringHolder sName; - StringHolder expression; - StringHolder filterName; - StringHolder renameOldName; - size_t expandIndex = -1; - size_t removeIndex = -1; - BOOL clear = FALSE; - - size_t nArg = 0; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-add", &addExpression.data, COSTRING, TRUE}, - {"-a", &aExpression.data, COSTRING, TRUE}, - {"-save", &saveName.data, COSTRING, TRUE}, - {"-s", &sName.data, COSTRING, TRUE}, - {"-clear", &clear, COBOOL, FALSE}, - {"-c", &clear, COBOOL, FALSE}, - {"-expand", &expandIndex, COSIZE_T, TRUE}, - {"-filter", &filterName.data, COSTRING, TRUE}, - {"-r", &removeIndex, COSIZE_T, TRUE}, - {"-remove", &removeIndex, COSIZE_T, TRUE}, - {"-rename", &renameOldName.data, COSTRING, TRUE}, - }; - - CMDValue arg[] = - { // vptr, type - {&expression.data, COSTRING} - }; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - if(addExpression.data != NULL || aExpression.data != NULL) - { - WCHAR pAddExpression[MAX_EXPRESSION]; - swprintf_s(pAddExpression, MAX_EXPRESSION, W("%S"), addExpression.data != NULL ? addExpression.data : aExpression.data); - Status = g_watchCmd.Add(pAddExpression); - } - else if(removeIndex != -1) - { - if(removeIndex <= 0) - { - ExtOut("Index must be a postive decimal number\n"); - } - else - { - Status = g_watchCmd.Remove((int)removeIndex); - if(Status == S_OK) - ExtOut("Watch expression #%d has been removed\n", removeIndex); - else if(Status == S_FALSE) - ExtOut("There is no watch expression with index %d\n", removeIndex); - else - ExtOut("Unknown failure 0x%x removing watch expression\n", Status); - } - } - else if(saveName.data != NULL || sName.data != NULL) - { - WCHAR pSaveName[MAX_EXPRESSION]; - swprintf_s(pSaveName, MAX_EXPRESSION, W("%S"), saveName.data != NULL ? saveName.data : sName.data); - Status = g_watchCmd.SaveList(pSaveName); - } - else if(clear) - { - g_watchCmd.Clear(); - } - else if(renameOldName.data != NULL) - { - if(nArg != 1) - { - ExtOut("Must provide an old and new name. Usage: !watch -rename <old_name> <new_name>.\n"); - return S_FALSE; - } - WCHAR pOldName[MAX_EXPRESSION]; - swprintf_s(pOldName, MAX_EXPRESSION, W("%S"), renameOldName.data); - WCHAR pNewName[MAX_EXPRESSION]; - swprintf_s(pNewName, MAX_EXPRESSION, W("%S"), expression.data); - g_watchCmd.RenameList(pOldName, pNewName); - } - // print the tree, possibly with filtering and/or expansion - else if(expandIndex != -1 || expression.data == NULL) - { - WCHAR pExpression[MAX_EXPRESSION]; - pExpression[0] = '\0'; - - if(expandIndex != -1) - { - if(expression.data != NULL) - { - swprintf_s(pExpression, MAX_EXPRESSION, W("%S"), expression.data); - } - else - { - ExtOut("No expression was provided. Usage !watch -expand <index> <expression>\n"); - return S_FALSE; - } - } - WCHAR pFilterName[MAX_EXPRESSION]; - pFilterName[0] = '\0'; - - if(filterName.data != NULL) - { - swprintf_s(pFilterName, MAX_EXPRESSION, W("%S"), filterName.data); - } - - g_watchCmd.Print((int)expandIndex, pExpression, pFilterName); - } - else - { - ExtOut("Unrecognized argument: %s\n", expression.data); - } - - return Status; -} - -#endif // FEATURE_PAL - -DECLARE_API(ClrStack) -{ - INIT_API(); - - BOOL bAll = FALSE; - BOOL bParams = FALSE; - BOOL bLocals = FALSE; - BOOL bSuppressLines = FALSE; - BOOL bICorDebug = FALSE; - BOOL bGC = FALSE; - BOOL dml = FALSE; - BOOL bFull = FALSE; - BOOL bDisplayRegVals = FALSE; - BOOL bAllThreads = FALSE; - DWORD frameToDumpVariablesFor = -1; - StringHolder cvariableName; - ArrayHolder<WCHAR> wvariableName = new NOTHROW WCHAR[mdNameLen]; - if (wvariableName == NULL) - { - ReportOOM(); - return E_OUTOFMEMORY; - } - - memset(wvariableName, 0, sizeof(wvariableName)); - - size_t nArg = 0; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-a", &bAll, COBOOL, FALSE}, - {"-all", &bAllThreads, COBOOL, FALSE}, - {"-p", &bParams, COBOOL, FALSE}, - {"-l", &bLocals, COBOOL, FALSE}, - {"-n", &bSuppressLines, COBOOL, FALSE}, - {"-i", &bICorDebug, COBOOL, FALSE}, - {"-gc", &bGC, COBOOL, FALSE}, - {"-f", &bFull, COBOOL, FALSE}, - {"-r", &bDisplayRegVals, COBOOL, FALSE }, -#ifndef FEATURE_PAL - {"/d", &dml, COBOOL, FALSE}, -#endif - }; - CMDValue arg[] = - { // vptr, type - {&cvariableName.data, COSTRING}, - {&frameToDumpVariablesFor, COSIZE_T}, - }; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return Status; - } - - EnableDMLHolder dmlHolder(dml); - if (bAll || bParams || bLocals) - { - // No parameter or local supports for minidump case! - MINIDUMP_NOT_SUPPORTED(); - } - - if (bAll) - { - bParams = bLocals = TRUE; - } - - if (bICorDebug) - { - if(nArg > 0) - { - bool firstParamIsNumber = true; - for(DWORD i = 0; i < strlen(cvariableName.data); i++) - firstParamIsNumber = firstParamIsNumber && isdigit(cvariableName.data[i]); - - if(firstParamIsNumber && nArg == 1) - { - frameToDumpVariablesFor = (DWORD)GetExpression(cvariableName.data); - cvariableName.data[0] = '\0'; - } - } - if(cvariableName.data != NULL && strlen(cvariableName.data) > 0) - swprintf_s(wvariableName, mdNameLen, W("%S\0"), cvariableName.data); - - if(_wcslen(wvariableName) > 0) - bParams = bLocals = TRUE; - - EnableDMLHolder dmlHolder(TRUE); - return ClrStackImplWithICorDebug::ClrStackFromPublicInterface(bParams, bLocals, FALSE, wvariableName, frameToDumpVariablesFor); - } - - if (bAllThreads) { - ClrStackImpl::PrintAllThreads(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals); - } - else { - ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals); - } - - return S_OK; -} - -#ifndef FEATURE_PAL - -BOOL IsMemoryInfoAvailable() -{ - ULONG Class; - ULONG Qualifier; - g_ExtControl->GetDebuggeeType(&Class,&Qualifier); - if (Qualifier == DEBUG_DUMP_SMALL) - { - g_ExtControl->GetDumpFormatFlags(&Qualifier); - if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0) - { - if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY_INFO) == 0) - { - return FALSE; - } - } - } - return TRUE; -} - -DECLARE_API( VMMap ) -{ - INIT_API(); - - if (IsMiniDumpFile() || !IsMemoryInfoAvailable()) - { - ExtOut("!VMMap requires a full memory dump (.dump /ma) or a live process.\n"); - } - else - { - vmmap(); - } - - return Status; -} // DECLARE_API( vmmap ) - -DECLARE_API( SOSFlush ) -{ - INIT_API(); - - g_clrData->Flush(); - - return Status; -} // DECLARE_API( SOSFlush ) - -DECLARE_API( VMStat ) -{ - INIT_API(); - - if (IsMiniDumpFile() || !IsMemoryInfoAvailable()) - { - ExtOut("!VMStat requires a full memory dump (.dump /ma) or a live process.\n"); - } - else - { - vmstat(); - } - - return Status; -} // DECLARE_API( vmmap ) - -/**********************************************************************\ -* Routine Description: * -* * -* This function saves a dll to a file. * -* * -\**********************************************************************/ -DECLARE_API(SaveModule) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - StringHolder Location; - DWORD_PTR moduleAddr = NULL; - BOOL bIsImage; - - CMDValue arg[] = - { // vptr, type - {&moduleAddr, COHEX}, - {&Location.data, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) - { - return Status; - } - if (nArg != 2) - { - ExtOut("Usage: SaveModule <address> <file to save>\n"); - return Status; - } - if (moduleAddr == 0) { - ExtOut ("Invalid arg\n"); - return Status; - } - - char* ptr = Location.data; - - DWORD_PTR dllBase = 0; - ULONG64 base; - if (g_ExtSymbols->GetModuleByOffset(TO_CDADDR(moduleAddr),0,NULL,&base) == S_OK) - { - dllBase = TO_TADDR(base); - } - else if (IsModule(moduleAddr)) - { - DacpModuleData module; - module.Request(g_sos, TO_CDADDR(moduleAddr)); - dllBase = TO_TADDR(module.ilBase); - if (dllBase == 0) - { - ExtOut ("Module does not have base address\n"); - return Status; - } - } - else - { - ExtOut ("%p is not a Module or base address\n", SOS_PTR(moduleAddr)); - return Status; - } - - MEMORY_BASIC_INFORMATION64 mbi; - if (FAILED(g_ExtData2->QueryVirtual(TO_CDADDR(dllBase), &mbi))) - { - ExtOut("Failed to retrieve information about segment %p", SOS_PTR(dllBase)); - return Status; - } - - // module loaded as an image or mapped as a flat file? - bIsImage = (mbi.Type == MEM_IMAGE); - - IMAGE_DOS_HEADER DosHeader; - if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase), &DosHeader, sizeof(DosHeader), NULL) != S_OK) - return S_FALSE; - - IMAGE_NT_HEADERS Header; - if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK) - return S_FALSE; - - DWORD_PTR sectionAddr = dllBase + DosHeader.e_lfanew + offsetof(IMAGE_NT_HEADERS,OptionalHeader) - + Header.FileHeader.SizeOfOptionalHeader; - - IMAGE_SECTION_HEADER section; - struct MemLocation - { - DWORD_PTR VAAddr; - DWORD_PTR VASize; - DWORD_PTR FileAddr; - DWORD_PTR FileSize; - }; - - int nSection = Header.FileHeader.NumberOfSections; - ExtOut("%u sections in file\n",nSection); - MemLocation *memLoc = (MemLocation*)_alloca(nSection*sizeof(MemLocation)); - int indxSec = -1; - int slot; - for (int n = 0; n < nSection; n++) - { - if (g_ExtData->ReadVirtual(TO_CDADDR(sectionAddr), §ion, sizeof(section), NULL) == S_OK) - { - for (slot = 0; slot <= indxSec; slot ++) - if (section.PointerToRawData < memLoc[slot].FileAddr) - break; - - for (int k = indxSec; k >= slot; k --) - memcpy(&memLoc[k+1], &memLoc[k], sizeof(MemLocation)); - - memLoc[slot].VAAddr = section.VirtualAddress; - memLoc[slot].VASize = section.Misc.VirtualSize; - memLoc[slot].FileAddr = section.PointerToRawData; - memLoc[slot].FileSize = section.SizeOfRawData; - ExtOut("section %d - VA=%x, VASize=%x, FileAddr=%x, FileSize=%x\n", - n, memLoc[slot].VAAddr,memLoc[slot]. VASize,memLoc[slot].FileAddr, - memLoc[slot].FileSize); - indxSec ++; - } - else - { - ExtOut("Fail to read PE section info\n"); - return Status; - } - sectionAddr += sizeof(section); - } - - if (ptr[0] == '\0') - { - ExtOut ("File not specified\n"); - return Status; - } - - PCSTR file = ptr; - ptr += strlen(ptr)-1; - while (isspace(*ptr)) - { - *ptr = '\0'; - ptr --; - } - - HANDLE hFile = CreateFileA(file,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL); - if (hFile == INVALID_HANDLE_VALUE) - { - ExtOut ("Fail to create file %s\n", file); - return Status; - } - - ULONG pageSize = OSPageSize(); - char *buffer = (char *)_alloca(pageSize); - DWORD nRead; - DWORD nWrite; - - // NT PE Headers - TADDR dwAddr = dllBase; - TADDR dwEnd = dllBase + Header.OptionalHeader.SizeOfHeaders; - while (dwAddr < dwEnd) - { - nRead = pageSize; - if (dwEnd - dwAddr < nRead) - nRead = (ULONG)(dwEnd - dwAddr); - - if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK) - { - WriteFile(hFile,buffer,nRead,&nWrite,NULL); - } - else - { - ExtOut ("Fail to read memory\n"); - goto end; - } - dwAddr += nRead; - } - - for (slot = 0; slot <= indxSec; slot ++) - { - dwAddr = dllBase + (bIsImage ? memLoc[slot].VAAddr : memLoc[slot].FileAddr); - dwEnd = memLoc[slot].FileSize + dwAddr - 1; - - while (dwAddr <= dwEnd) - { - nRead = pageSize; - if (dwEnd - dwAddr + 1 < pageSize) - nRead = (ULONG)(dwEnd - dwAddr + 1); - - if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK) - { - WriteFile(hFile,buffer,nRead,&nWrite,NULL); - } - else - { - ExtOut ("Fail to read memory\n"); - goto end; - } - dwAddr += pageSize; - } - } -end: - CloseHandle (hFile); - return Status; -} - -#ifdef _DEBUG -DECLARE_API(dbgout) -{ - INIT_API(); - - BOOL bOff = FALSE; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-off", &bOff, COBOOL, FALSE}, - }; - - if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) - { - return Status; - } - - Output::SetDebugOutputEnabled(!bOff); - return Status; -} -DECLARE_API(filthint) -{ - INIT_API(); - - BOOL bOff = FALSE; - DWORD_PTR filter = 0; - - CMDOption option[] = - { // name, vptr, type, hasValue - {"-off", &bOff, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&filter, COHEX} - }; - size_t nArg; - if (!GetCMDOption(args, option, _countof(option), - arg, _countof(arg), &nArg)) - { - return Status; - } - if (bOff) - { - g_filterHint = 0; - return Status; - } - - g_filterHint = filter; - return Status; -} -#endif // _DEBUG - -#endif // FEATURE_PAL - -static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp, - ULONG64 IPAddr, StringOutput& so) -{ -#define DOAPPEND(str) \ - do { \ - if (!so.Append((str))) { \ - return E_OUTOFMEMORY; \ - }} while (0) - - // Should we skip explicit frames? They are characterized by Esp = 0, && Eip = 0 or 1. - // See comment in FormatGeneratedException() for explanation why on non_IA64 Eip is 1, and not 0 - if (!(Flags & SOS_STACKTRACE_SHOWEXPLICITFRAMES) && (Esp == 0) && (IPAddr == 1)) - { - return S_FALSE; - } - - DacpMethodDescData MethodDescData; - if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK) - { - return E_FAIL; - } - - ArrayHolder<WCHAR> wszNameBuffer = new WCHAR[MAX_LONGPATH+1]; - - if (Flags & SOS_STACKTRACE_SHOWADDRESSES) - { - _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("%p %p "), (void*)(size_t) Esp, (void*)(size_t) IPAddr); // _TRUNCATE - DOAPPEND(wszNameBuffer); - } - - DacpModuleData dmd; - BOOL bModuleNameWorked = FALSE; - ULONG64 addrInModule = IPAddr; - if (dmd.Request(g_sos, MethodDescData.ModulePtr) == S_OK) - { - CLRDATA_ADDRESS base = 0; - if (g_sos->GetPEFileBase(dmd.File, &base) == S_OK) - { - if (base) - { - addrInModule = base; - } - } - } - ULONG Index; - ULONG64 base; - if (g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index, &base) == S_OK) - { - ArrayHolder<char> szModuleName = new char[MAX_LONGPATH+1]; - if (g_ExtSymbols->GetModuleNames(Index, base, NULL, 0, NULL, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL) == S_OK) - { - MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, wszNameBuffer, MAX_LONGPATH); - DOAPPEND (wszNameBuffer); - bModuleNameWorked = TRUE; - } - } -#ifdef FEATURE_PAL - else - { - if (g_sos->GetPEFileName(dmd.File, MAX_LONGPATH, wszNameBuffer, NULL) == S_OK) - { - if (wszNameBuffer[0] != W('\0')) - { - WCHAR *pJustName = _wcsrchr(wszNameBuffer, DIRECTORY_SEPARATOR_CHAR_W); - if (pJustName == NULL) - pJustName = wszNameBuffer - 1; - - DOAPPEND(pJustName + 1); - bModuleNameWorked = TRUE; - } - } - } -#endif // FEATURE_PAL - - // Under certain circumstances DacpMethodDescData::GetMethodDescName() - // returns a module qualified method name - HRESULT hr = g_sos->GetMethodDescName(dwStartAddr, MAX_LONGPATH, wszNameBuffer, NULL); - - WCHAR* pwszMethNameBegin = (hr != S_OK ? NULL : _wcschr(wszNameBuffer, L'!')); - if (!bModuleNameWorked && hr == S_OK && pwszMethNameBegin != NULL) - { - // if we weren't able to get the module name, but GetMethodDescName returned - // the module as part of the returned method name, use this data - DOAPPEND(wszNameBuffer); - } - else - { - if (!bModuleNameWorked) - { - DOAPPEND (W("UNKNOWN")); - } - DOAPPEND(W("!")); - if (hr == S_OK) - { - // the module name we retrieved above from debugger will take - // precedence over the name possibly returned by GetMethodDescName() - DOAPPEND(pwszMethNameBegin != NULL ? (pwszMethNameBegin+1) : (WCHAR *)wszNameBuffer); - } - else - { - DOAPPEND(W("UNKNOWN")); - } - } - - ULONG64 Displacement = (IPAddr - MethodDescData.NativeCodeAddr); - if (Displacement) - { - _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("+%#x"), Displacement); // _TRUNCATE - DOAPPEND (wszNameBuffer); - } - - return S_OK; -#undef DOAPPEND -} - -BOOL AppendContext(LPVOID pTransitionContexts, size_t maxCount, size_t *pcurCount, size_t uiSizeOfContext, - CROSS_PLATFORM_CONTEXT *context) -{ - if (pTransitionContexts == NULL || *pcurCount >= maxCount) - { - ++(*pcurCount); - return FALSE; - } - if (uiSizeOfContext == sizeof(StackTrace_SimpleContext)) - { - StackTrace_SimpleContext *pSimple = (StackTrace_SimpleContext *) pTransitionContexts; - g_targetMachine->FillSimpleContext(&pSimple[*pcurCount], context); - } - else if (uiSizeOfContext == g_targetMachine->GetContextSize()) - { - // FillTargetContext ensures we only write uiSizeOfContext bytes in pTransitionContexts - // and not sizeof(CROSS_PLATFORM_CONTEXT) bytes (which would overrun). - g_targetMachine->FillTargetContext(pTransitionContexts, context, (int)(*pcurCount)); - } - else - { - return FALSE; - } - ++(*pcurCount); - return TRUE; -} - -HRESULT CALLBACK ImplementEFNStackTrace( - PDEBUG_CLIENT client, - __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[], - size_t *puiTextLength, - LPVOID pTransitionContexts, - size_t *puiTransitionContextCount, - size_t uiSizeOfContext, - DWORD Flags) -{ - -#define DOAPPEND(str) if (!so.Append((str))) { \ - Status = E_OUTOFMEMORY; \ - goto Exit; \ -} - - HRESULT Status = E_FAIL; - StringOutput so; - size_t transitionContextCount = 0; - - if (puiTextLength == NULL) - { - return E_INVALIDARG; - } - - if (pTransitionContexts) - { - if (puiTransitionContextCount == NULL) - { - return E_INVALIDARG; - } - - // Do error checking on context size - if ((uiSizeOfContext != g_targetMachine->GetContextSize()) && - (uiSizeOfContext != sizeof(StackTrace_SimpleContext))) - { - return E_INVALIDARG; - } - } - - IXCLRDataStackWalk *pStackWalk = NULL; - IXCLRDataTask* Task; - ULONG ThreadId; - - if ((Status = g_ExtSystem->GetCurrentThreadSystemId(&ThreadId)) != S_OK || - (Status = g_clrData->GetTaskByOSThreadID(ThreadId, &Task)) != S_OK) - { - // Not a managed thread. - return SOS_E_NOMANAGEDCODE; - } - - Status = Task->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED | - CLRDATA_SIMPFRAME_MANAGED_METHOD | - CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE | - CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE, - &pStackWalk); - - Task->Release(); - - if (Status != S_OK) - { - if (Status == E_FAIL) - { - return SOS_E_NOMANAGEDCODE; - } - return Status; - } - -#ifdef _TARGET_WIN64_ - ULONG numFrames = 0; - BOOL bInNative = TRUE; - - Status = GetContextStackTrace(ThreadId, &numFrames); - if (FAILED(Status)) - { - goto Exit; - } - - for (ULONG i = 0; i < numFrames; i++) - { - PDEBUG_STACK_FRAME pCur = g_Frames + i; - - CLRDATA_ADDRESS pMD; - if (g_sos->GetMethodDescPtrFromIP(pCur->InstructionOffset, &pMD) == S_OK) - { - if (bInNative || transitionContextCount==0) - { - // We only want to list one transition frame if there are multiple frames. - bInNative = FALSE; - - DOAPPEND (W("(TransitionMU)\n")); - // For each transition, we need to store the context information - if (puiTransitionContextCount) - { - // below we cast the i-th AMD64_CONTEXT to CROSS_PLATFORM_CONTEXT - AppendContext (pTransitionContexts, *puiTransitionContextCount, - &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_FrameContexts[i]))); - } - else - { - transitionContextCount++; - } - } - - Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags, - pCur->StackOffset, pCur->InstructionOffset, so); - if (FAILED(Status)) - { - goto Exit; - } - else if (Status == S_OK) - { - DOAPPEND (W("\n")); - } - // for S_FALSE do not append anything - - } - else - { - if (!bInNative) - { - // We only want to list one transition frame if there are multiple frames. - bInNative = TRUE; - - DOAPPEND (W("(TransitionUM)\n")); - // For each transition, we need to store the context information - if (puiTransitionContextCount) - { - AppendContext (pTransitionContexts, *puiTransitionContextCount, - &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_FrameContexts[i]))); - } - else - { - transitionContextCount++; - } - } - } - } - -Exit: -#else // _TARGET_WIN64_ - -#ifdef _DEBUG - size_t prevLength = 0; - static WCHAR wszNameBuffer[1024]; // should be large enough - wcscpy_s(wszNameBuffer, 1024, W("Frame")); // default value -#endif - - BOOL bInNative = TRUE; - - UINT frameCount = 0; - do - { - DacpFrameData FrameData; - if ((Status = FrameData.Request(pStackWalk)) != S_OK) - { - goto Exit; - } - - CROSS_PLATFORM_CONTEXT context; - if ((Status=pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), - NULL, (BYTE *)&context))!=S_OK) - { - goto Exit; - } - - ExtDbgOut ( " * Ctx[BSI]: %08x %08x %08x ", GetBP(context), GetSP(context), GetIP(context) ); - - CLRDATA_ADDRESS pMD; - if (!FrameData.frameAddr) - { - if (bInNative || transitionContextCount==0) - { - // We only want to list one transition frame if there are multiple frames. - bInNative = FALSE; - - DOAPPEND (W("(TransitionMU)\n")); - // For each transition, we need to store the context information - if (puiTransitionContextCount) - { - AppendContext (pTransitionContexts, *puiTransitionContextCount, - &transitionContextCount, uiSizeOfContext, &context); - } - else - { - transitionContextCount++; - } - } - - // we may have a method, try to get the methoddesc - if (g_sos->GetMethodDescPtrFromIP(GetIP(context), &pMD)==S_OK) - { - Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags, - GetSP(context), GetIP(context), so); - if (FAILED(Status)) - { - goto Exit; - } - else if (Status == S_OK) - { - DOAPPEND (W("\n")); - } - // for S_FALSE do not append anything - } - } - else - { -#ifdef _DEBUG - if (Output::IsDebugOutputEnabled()) - { - DWORD_PTR vtAddr; - MOVE(vtAddr, TO_TADDR(FrameData.frameAddr)); - if (g_sos->GetFrameName(TO_CDADDR(vtAddr), 1024, wszNameBuffer, NULL) == S_OK) - ExtDbgOut("[%ls: %08x] ", wszNameBuffer, FrameData.frameAddr); - else - ExtDbgOut("[Frame: %08x] ", FrameData.frameAddr); - } -#endif - if (!bInNative) - { - // We only want to list one transition frame if there are multiple frames. - bInNative = TRUE; - - DOAPPEND (W("(TransitionUM)\n")); - // For each transition, we need to store the context information - if (puiTransitionContextCount) - { - AppendContext (pTransitionContexts, *puiTransitionContextCount, - &transitionContextCount, uiSizeOfContext, &context); - } - else - { - transitionContextCount++; - } - } - } - -#ifdef _DEBUG - if (so.Length() > prevLength) - { - ExtDbgOut ( "%ls", so.String()+prevLength ); - prevLength = so.Length(); - } - else - ExtDbgOut ( "\n" ); -#endif - - } - while ((frameCount++) < MAX_STACK_FRAMES && pStackWalk->Next()==S_OK); - - Status = S_OK; - -Exit: -#endif // _TARGET_WIN64_ - - if (pStackWalk) - { - pStackWalk->Release(); - pStackWalk = NULL; - } - - // We have finished. Does the user want to copy this data to a buffer? - if (Status == S_OK) - { - if(wszTextOut) - { - // They want at least partial output - wcsncpy_s (wszTextOut, *puiTextLength, so.String(), *puiTextLength-1); // _TRUNCATE - } - else - { - *puiTextLength = _wcslen (so.String()) + 1; - } - - if (puiTransitionContextCount) - { - *puiTransitionContextCount = transitionContextCount; - } - } - - return Status; -} - -#ifdef FEATURE_PAL -#define PAL_TRY_NAKED PAL_CPP_TRY -#define PAL_EXCEPT_NAKED(disp) PAL_CPP_CATCH_ALL -#define PAL_ENDTRY_NAKED PAL_CPP_ENDTRY -#endif - -// TODO: Convert PAL_TRY_NAKED to something that works on the Mac. -HRESULT CALLBACK ImplementEFNStackTraceTry( - PDEBUG_CLIENT client, - __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[], - size_t *puiTextLength, - LPVOID pTransitionContexts, - size_t *puiTransitionContextCount, - size_t uiSizeOfContext, - DWORD Flags) -{ - HRESULT Status = E_FAIL; - - PAL_TRY_NAKED - { - Status = ImplementEFNStackTrace(client, wszTextOut, puiTextLength, - pTransitionContexts, puiTransitionContextCount, - uiSizeOfContext, Flags); - } - PAL_EXCEPT_NAKED (EXCEPTION_EXECUTE_HANDLER) - { - } - PAL_ENDTRY_NAKED - - return Status; -} - -// See sos_stacktrace.h for the contract with the callers regarding the LPVOID arguments. -HRESULT CALLBACK _EFN_StackTrace( - PDEBUG_CLIENT client, - __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[], - size_t *puiTextLength, - __out_bcount_opt(uiSizeOfContext*(*puiTransitionContextCount)) LPVOID pTransitionContexts, - size_t *puiTransitionContextCount, - size_t uiSizeOfContext, - DWORD Flags) -{ - INIT_API(); - - Status = ImplementEFNStackTraceTry(client, wszTextOut, puiTextLength, - pTransitionContexts, puiTransitionContextCount, - uiSizeOfContext, Flags); - - return Status; -} - - -BOOL FormatFromRemoteString(DWORD_PTR strObjPointer, __out_ecount(cchString) PWSTR wszBuffer, ULONG cchString) -{ - BOOL bRet = FALSE; - - wszBuffer[0] = L'\0'; - - DacpObjectData objData; - if (objData.Request(g_sos, TO_CDADDR(strObjPointer))!=S_OK) - { - return bRet; - } - - strobjInfo stInfo; - - if (MOVE(stInfo, strObjPointer) != S_OK) - { - return bRet; - } - - DWORD dwBufLength = 0; - if (!ClrSafeInt<DWORD>::addition(stInfo.m_StringLength, 1, dwBufLength)) - { - ExtOut("<integer overflow>\n"); - return bRet; - } - - LPWSTR pwszBuf = new NOTHROW WCHAR[dwBufLength]; - if (pwszBuf == NULL) - { - return bRet; - } - - if (g_sos->GetObjectStringData(TO_CDADDR(strObjPointer), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK) - { - delete [] pwszBuf; - return bRet; - } - - // String is in format - // <SP><SP><SP>at <function name>(args,...)\n - // ... - // Parse and copy just <function name>(args,...) - - LPWSTR pwszPointer = pwszBuf; - - WCHAR PSZSEP[] = W(" at "); - - UINT Length = 0; - while(1) - { - if (_wcsncmp(pwszPointer, PSZSEP, _countof(PSZSEP)-1) != 0) - { - delete [] pwszBuf; - return bRet; - } - - pwszPointer += _wcslen(PSZSEP); - LPWSTR nextPos = _wcsstr(pwszPointer, PSZSEP); - if (nextPos == NULL) - { - // Done! Note that we are leaving the function before we add the last - // line of stack trace to the output string. This is on purpose because - // this string needs to be merged with a real trace, and the last line - // of the trace will be common to the real trace. - break; - } - WCHAR c = *nextPos; - *nextPos = L'\0'; - - // Buffer is calculated for sprintf below (" %p %p %S\n"); - WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2]; - - // Note that we don't add a newline because we have this embedded in wszLineBuffer - swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %p %p %s"), (void*)(size_t)-1, (void*)(size_t)-1, pwszPointer); - Length += (UINT)_wcslen(wszLineBuffer); - - if (wszBuffer) - { - wcsncat_s(wszBuffer, cchString, wszLineBuffer, _TRUNCATE); - } - - *nextPos = c; - // Move to the next line. - pwszPointer = nextPos; - } - - delete [] pwszBuf; - - // Return TRUE only if the stack string had any information that was successfully parsed. - // (Length > 0) is a good indicator of that. - bRet = (Length > 0); - return bRet; -} - -HRESULT AppendExceptionInfo(CLRDATA_ADDRESS cdaObj, - __out_ecount(cchString) PWSTR wszStackString, - ULONG cchString, - BOOL bNestedCase) // If bNestedCase is TRUE, the last frame of the computed stack is left off -{ - DacpObjectData objData; - if (objData.Request(g_sos, cdaObj) != S_OK) - { - return E_FAIL; - } - - // Make sure it is an exception object, and get the MT of Exception - CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable); - if (exceptionMT == NULL) - { - return E_INVALIDARG; - } - - // First try to get exception object data using ISOSDacInterface2 - DacpExceptionObjectData excData; - BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, cdaObj)); - - int iOffset; - // Is there a _remoteStackTraceString? We'll want to prepend that data. - // We only have string data, so IP/SP info has to be set to -1. - DWORD_PTR strPointer; - if (bGotExcData) - { - strPointer = TO_TADDR(excData.RemoteStackTraceString); - } - else - { - iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_remoteStackTraceString")); - MOVE (strPointer, TO_TADDR(cdaObj) + iOffset); - } - if (strPointer) - { - WCHAR *pwszBuffer = new NOTHROW WCHAR[cchString]; - if (pwszBuffer == NULL) - { - return E_OUTOFMEMORY; - } - - if (FormatFromRemoteString(strPointer, pwszBuffer, cchString)) - { - // Prepend this stuff to the string for the user - wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE); - } - delete[] pwszBuffer; - } - - BOOL bAsync = bGotExcData ? IsAsyncException(excData) - : IsAsyncException(cdaObj, objData.MethodTable); - - DWORD_PTR arrayPtr; - if (bGotExcData) - { - arrayPtr = TO_TADDR(excData.StackTrace); - } - else - { - iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_stackTrace")); - MOVE (arrayPtr, TO_TADDR(cdaObj) + iOffset); - } - - if (arrayPtr) - { - DWORD arrayLen; - MOVE (arrayLen, arrayPtr + sizeof(DWORD_PTR)); - - if (arrayLen) - { -#ifdef _TARGET_WIN64_ - DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD); -#else - DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD); -#endif // _TARGET_WIN64_ - size_t stackTraceSize = 0; - MOVE (stackTraceSize, dataPtr); // data length is stored at the beginning of the array in this case - - DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement)); - dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data - - if (stackTraceSize != 0) - { - size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, bNestedCase); - WCHAR *pwszBuffer = new NOTHROW WCHAR[iLength + 1]; - if (pwszBuffer) - { - FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer, iLength + 1, bAsync, bNestedCase); - wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE); - delete[] pwszBuffer; - } - else - { - return E_OUTOFMEMORY; - } - } - } - } - return S_OK; -} - -HRESULT ImplementEFNGetManagedExcepStack( - CLRDATA_ADDRESS cdaStackObj, - __out_ecount(cchString) PWSTR wszStackString, - ULONG cchString) -{ - HRESULT Status = E_FAIL; - - if (wszStackString == NULL || cchString == 0) - { - return E_INVALIDARG; - } - - CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread(); - DacpThreadData Thread; - BOOL bCanUseThreadContext = TRUE; - - ZeroMemory(&Thread, sizeof(DacpThreadData)); - - if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK)) - { - // The current thread is unmanaged - bCanUseThreadContext = FALSE; - } - - if (cdaStackObj == NULL) - { - if (!bCanUseThreadContext) - { - return E_INVALIDARG; - } - - TADDR taLTOH = NULL; - if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle), - &taLTOH, - sizeof(taLTOH), NULL)) || (taLTOH==NULL)) - { - return Status; - } - else - { - cdaStackObj = TO_CDADDR(taLTOH); - } - } - - // Put the stack trace header on - AddExceptionHeader(wszStackString, cchString); - - // First is there a nested exception? - if (bCanUseThreadContext && Thread.firstNestedException) - { - CLRDATA_ADDRESS obj = 0, next = 0; - CLRDATA_ADDRESS currentNested = Thread.firstNestedException; - do - { - Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next); - - // deal with the inability to read a nested exception gracefully - if (Status != S_OK) - { - break; - } - - Status = AppendExceptionInfo(obj, wszStackString, cchString, TRUE); - currentNested = next; - } - while(currentNested != NULL); - } - - Status = AppendExceptionInfo(cdaStackObj, wszStackString, cchString, FALSE); - - return Status; -} - -// TODO: Enable this when ImplementEFNStackTraceTry is fixed. -// This function, like VerifyDAC, exists for the purpose of testing -// hard-to-get-to SOS APIs. -DECLARE_API(VerifyStackTrace) -{ - INIT_API(); - - BOOL bVerifyManagedExcepStack = FALSE; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-ManagedExcepStack", &bVerifyManagedExcepStack, COBOOL, FALSE}, - }; - - if (!GetCMDOption(args, option, _countof(option), NULL,0,NULL)) - { - return Status; - } - - if (bVerifyManagedExcepStack) - { - CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread(); - DacpThreadData Thread; - - TADDR taExc = NULL; - if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK)) - { - ExtOut("The current thread is unmanaged\n"); - return Status; - } - - TADDR taLTOH = NULL; - if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle), - &taLTOH, - sizeof(taLTOH), NULL)) || (taLTOH == NULL)) - { - ExtOut("There is no current managed exception on this thread\n"); - return Status; - } - else - { - taExc = taLTOH; - } - - const SIZE_T cchStr = 4096; - WCHAR *wszStr = (WCHAR *)alloca(cchStr * sizeof(WCHAR)); - if (ImplementEFNGetManagedExcepStack(TO_CDADDR(taExc), wszStr, cchStr) != S_OK) - { - ExtOut("Error!\n"); - return Status; - } - - ExtOut("_EFN_GetManagedExcepStack(%P, wszStr, sizeof(wszStr)) returned:\n", SOS_PTR(taExc)); - ExtOut("%S\n", wszStr); - - if (ImplementEFNGetManagedExcepStack((ULONG64)NULL, wszStr, cchStr) != S_OK) - { - ExtOut("Error!\n"); - return Status; - } - - ExtOut("_EFN_GetManagedExcepStack(NULL, wszStr, sizeof(wszStr)) returned:\n"); - ExtOut("%S\n", wszStr); - } - else - { - size_t textLength = 0; - size_t contextLength = 0; - Status = ImplementEFNStackTraceTry(client, - NULL, - &textLength, - NULL, - &contextLength, - 0, - 0); - - if (Status != S_OK) - { - ExtOut("Error: %lx\n", Status); - return Status; - } - - ExtOut("Number of characters requested: %d\n", textLength); - WCHAR *wszBuffer = new NOTHROW WCHAR[textLength + 1]; - if (wszBuffer == NULL) - { - ReportOOM(); - return Status; - } - - // For the transition contexts buffer the callers are expected to allocate - // contextLength * sizeof(TARGET_CONTEXT), and not - // contextLength * sizeof(CROSS_PLATFORM_CONTEXT). See sos_stacktrace.h for - // details. - LPBYTE pContexts = new NOTHROW BYTE[contextLength * g_targetMachine->GetContextSize()]; - - if (pContexts == NULL) - { - ReportOOM(); - delete[] wszBuffer; - return Status; - } - - Status = ImplementEFNStackTrace(client, - wszBuffer, - &textLength, - pContexts, - &contextLength, - g_targetMachine->GetContextSize(), - 0); - - if (Status != S_OK) - { - ExtOut("Error: %lx\n", Status); - delete[] wszBuffer; - delete [] pContexts; - return Status; - } - - ExtOut("%S\n", wszBuffer); - - ExtOut("Context information:\n"); - if (IsDbgTargetX86()) - { - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n", - "Ebp", "Esp", "Eip"); - } - else if (IsDbgTargetAmd64()) - { - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n", - "Rbp", "Rsp", "Rip"); - } - else if (IsDbgTargetArm()) - { - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n", - "FP", "SP", "PC"); - } - else - { - ExtOut("Unsupported platform"); - delete [] pContexts; - delete[] wszBuffer; - return S_FALSE; - } - - for (size_t j=0; j < contextLength; j++) - { - CROSS_PLATFORM_CONTEXT *pCtx = (CROSS_PLATFORM_CONTEXT*)(pContexts + j*g_targetMachine->GetContextSize()); - ExtOut("%p %p %p\n", GetBP(*pCtx), GetSP(*pCtx), GetIP(*pCtx)); - } - - delete [] pContexts; - - StackTrace_SimpleContext *pSimple = new NOTHROW StackTrace_SimpleContext[contextLength]; - if (pSimple == NULL) - { - ReportOOM(); - delete[] wszBuffer; - return Status; - } - - Status = ImplementEFNStackTrace(client, - wszBuffer, - &textLength, - pSimple, - &contextLength, - sizeof(StackTrace_SimpleContext), - 0); - - if (Status != S_OK) - { - ExtOut("Error: %lx\n", Status); - delete[] wszBuffer; - delete [] pSimple; - return Status; - } - - ExtOut("Simple Context information:\n"); - if (IsDbgTargetX86()) - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n", - "Ebp", "Esp", "Eip"); - else if (IsDbgTargetAmd64()) - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n", - "Rbp", "Rsp", "Rip"); - else if (IsDbgTargetArm()) - ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n", - "FP", "SP", "PC"); - else - { - ExtOut("Unsupported platform"); - delete[] wszBuffer; - delete [] pSimple; - return S_FALSE; - } - for (size_t j=0; j < contextLength; j++) - { - ExtOut("%p %p %p\n", SOS_PTR(pSimple[j].FrameOffset), - SOS_PTR(pSimple[j].StackOffset), - SOS_PTR(pSimple[j].InstructionOffset)); - } - delete [] pSimple; - delete[] wszBuffer; - } - - return Status; -} - -#ifndef FEATURE_PAL - -// This is an internal-only Apollo extension to de-optimize the code -DECLARE_API(SuppressJitOptimization) -{ - INIT_API_NOEE(); - MINIDUMP_NOT_SUPPORTED(); - - StringHolder onOff; - CMDValue arg[] = - { // vptr, type - {&onOff.data, COSTRING}, - }; - size_t nArg; - if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) - { - return E_FAIL; - } - - if(nArg == 1 && (_stricmp(onOff.data, "On") == 0)) - { - // if CLR is already loaded, try to change the flags now - if(CheckEEDll() == S_OK) - { - SetNGENCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION); - } - - if(!g_fAllowJitOptimization) - ExtOut("JIT optimization is already suppressed\n"); - else - { - g_fAllowJitOptimization = FALSE; - g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0); - ExtOut("JIT optimization will be suppressed\n"); - } - - - } - else if(nArg == 1 && (_stricmp(onOff.data, "Off") == 0)) - { - // if CLR is already loaded, try to change the flags now - if(CheckEEDll() == S_OK) - { - SetNGENCompilerFlags(CORDEBUG_JIT_DEFAULT); - } - - if(g_fAllowJitOptimization) - ExtOut("JIT optimization is already permitted\n"); - else - { - g_fAllowJitOptimization = TRUE; - ExtOut("JIT optimization will be permitted\n"); - } - } - else - { - ExtOut("Usage: !SuppressJitOptimization <on|off>\n"); - } - - return S_OK; -} - -// Uses ICorDebug to set the state of desired NGEN compiler flags. This can suppress pre-jitted optimized -// code -HRESULT SetNGENCompilerFlags(DWORD flags) -{ - HRESULT hr; - - ToRelease<ICorDebugProcess2> proc2; - if(FAILED(hr = InitCorDebugInterface())) - { - ExtOut("SOS: warning, prejitted code optimizations could not be changed. Failed to load ICorDebug HR = 0x%x\n", hr); - } - else if(FAILED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess2), (void**) &proc2))) - { - if(flags != CORDEBUG_JIT_DEFAULT) - { - ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n"); - } - else - { - hr = S_OK; - } - } - else if(FAILED(hr = proc2->SetDesiredNGENCompilerFlags(flags))) - { - // Versions of CLR that don't have SetDesiredNGENCompilerFlags DAC-ized will return E_FAIL. - // This was first supported in the clr_triton branch around 4/1/12, Apollo release - // It will likely be supported in desktop CLR during Dev12 - if(hr == E_FAIL) - { - if(flags != CORDEBUG_JIT_DEFAULT) - { - ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n"); - } - else - { - hr = S_OK; - } - } - else if(hr == CORDBG_E_NGEN_NOT_SUPPORTED) - { - if(flags != CORDEBUG_JIT_DEFAULT) - { - ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support NGEN\n"); - } - else - { - hr = S_OK; - } - } - else if(hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS) - { - DWORD currentFlags = 0; - if(FAILED(hr = proc2->GetDesiredNGENCompilerFlags(¤tFlags))) - { - ExtOut("SOS: warning, prejitted code optimizations could not be changed. GetDesiredNGENCompilerFlags failed hr=0x%x\n", hr); - } - else if(currentFlags != flags) - { - ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. This setting is fixed once CLR starts\n"); - } - else - { - hr = S_OK; - } - } - else - { - ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. SetDesiredNGENCompilerFlags hr = 0x%x\n", hr); - } - } - - return hr; -} - - -// This is an internal-only Apollo extension to save breakpoint/watch state -DECLARE_API(SaveState) -{ - INIT_API_NOEE(); - MINIDUMP_NOT_SUPPORTED(); - - StringHolder filePath; - CMDValue arg[] = - { // vptr, type - {&filePath.data, COSTRING}, - }; - size_t nArg; - if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) - { - return E_FAIL; - } - - if(nArg == 0) - { - ExtOut("Usage: !SaveState <file_path>\n"); - } - - FILE* pFile; - errno_t error = fopen_s(&pFile, filePath.data, "w"); - if(error != 0) - { - ExtOut("Failed to open file %s, error=0x%x\n", filePath.data, error); - return E_FAIL; - } - - g_bpoints.SaveBreakpoints(pFile); - g_watchCmd.SaveListToFile(pFile); - - fclose(pFile); - ExtOut("Session breakpoints and watch expressions saved to %s\n", filePath.data); - return S_OK; -} - -#endif // FEATURE_PAL - -DECLARE_API(StopOnCatch) -{ - INIT_API(); - MINIDUMP_NOT_SUPPORTED(); - - g_stopOnNextCatch = TRUE; - ULONG32 flags = 0; - g_clrData->GetOtherNotificationFlags(&flags); - flags |= CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER; - g_clrData->SetOtherNotificationFlags(flags); - ExtOut("Debuggee will break the next time a managed exception is caught during execution\n"); - return S_OK; -} - -// This is an undocumented SOS extension command intended to help test SOS -// It causes the Dml output to be printed to the console uninterpretted so -// that a test script can read the commands which are hidden in the markup -DECLARE_API(ExposeDML) -{ - Output::SetDMLExposed(true); - return S_OK; -} - -// According to kksharma the Windows debuggers always sign-extend -// arguments when calling externally, therefore StackObjAddr -// conforms to CLRDATA_ADDRESS contract. -HRESULT CALLBACK -_EFN_GetManagedExcepStack( - PDEBUG_CLIENT client, - ULONG64 StackObjAddr, - __out_ecount (cbString) PSTR szStackString, - ULONG cbString - ) -{ - INIT_API(); - - ArrayHolder<WCHAR> tmpStr = new NOTHROW WCHAR[cbString]; - if (tmpStr == NULL) - { - ReportOOM(); - return E_OUTOFMEMORY; - } - - if (FAILED(Status = ImplementEFNGetManagedExcepStack(StackObjAddr, tmpStr, cbString))) - { - return Status; - } - - if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmpStr, -1, szStackString, cbString, NULL, NULL) == 0) - { - return E_FAIL; - } - - return S_OK; -} - -// same as _EFN_GetManagedExcepStack, but returns the stack as a wide string. -HRESULT CALLBACK -_EFN_GetManagedExcepStackW( - PDEBUG_CLIENT client, - ULONG64 StackObjAddr, - __out_ecount(cchString) PWSTR wszStackString, - ULONG cchString - ) -{ - INIT_API(); - - return ImplementEFNGetManagedExcepStack(StackObjAddr, wszStackString, cchString); -} - -// According to kksharma the Windows debuggers always sign-extend -// arguments when calling externally, therefore objAddr -// conforms to CLRDATA_ADDRESS contract. -HRESULT CALLBACK -_EFN_GetManagedObjectName( - PDEBUG_CLIENT client, - ULONG64 objAddr, - __out_ecount (cbName) PSTR szName, - ULONG cbName - ) -{ - INIT_API (); - - if (!sos::IsObject(objAddr, false)) - { - return E_INVALIDARG; - } - - sos::Object obj = TO_TADDR(objAddr); - - if (WideCharToMultiByte(CP_ACP, 0, obj.GetTypeName(), (int) (_wcslen(obj.GetTypeName()) + 1), - szName, cbName, NULL, NULL) == 0) - { - return E_FAIL; - } - return S_OK; -} - -// According to kksharma the Windows debuggers always sign-extend -// arguments when calling externally, therefore objAddr -// conforms to CLRDATA_ADDRESS contract. -HRESULT CALLBACK -_EFN_GetManagedObjectFieldInfo( - PDEBUG_CLIENT client, - ULONG64 objAddr, - __out_ecount (mdNameLen) PSTR szFieldName, - PULONG64 pValue, - PULONG pOffset - ) -{ - INIT_API(); - DacpObjectData objData; - LPWSTR fieldName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR)); - - if (szFieldName == NULL || *szFieldName == '\0' || - objAddr == NULL) - { - return E_FAIL; - } - - if (pOffset == NULL && pValue == NULL) - { - // One of these needs to be valid - return E_FAIL; - } - - if (FAILED(objData.Request(g_sos, objAddr))) - { - return E_FAIL; - } - - MultiByteToWideChar(CP_ACP,0,szFieldName,-1,fieldName,mdNameLen); - - int iOffset = GetObjFieldOffset (objAddr, objData.MethodTable, fieldName); - if (iOffset <= 0) - { - return E_FAIL; - } - - if (pOffset) - { - *pOffset = (ULONG) iOffset; - } - - if (pValue) - { - if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(objAddr + iOffset), pValue, sizeof(ULONG64), NULL))) - { - return E_FAIL; - } - } - - return S_OK; -} - -#ifdef FEATURE_PAL - -#ifdef CREATE_DUMP_SUPPORTED -#include <dumpcommon.h> -#include "datatarget.h" -extern bool CreateDumpForSOS(const char* programPath, const char* dumpPathTemplate, pid_t pid, MINIDUMP_TYPE minidumpType, ICLRDataTarget* dataTarget); -extern bool g_diagnostics; -#endif // CREATE_DUMP_SUPPORTED - -DECLARE_API(CreateDump) -{ - INIT_API(); -#ifdef CREATE_DUMP_SUPPORTED - StringHolder sFileName; - BOOL normal = FALSE; - BOOL withHeap = FALSE; - BOOL triage = FALSE; - BOOL full = FALSE; - BOOL diag = FALSE; - - size_t nArg = 0; - CMDOption option[] = - { // name, vptr, type, hasValue - {"-n", &normal, COBOOL, FALSE}, - {"-h", &withHeap, COBOOL, FALSE}, - {"-t", &triage, COBOOL, FALSE}, - {"-f", &full, COBOOL, FALSE}, - {"-d", &diag, COBOOL, FALSE}, - }; - CMDValue arg[] = - { // vptr, type - {&sFileName.data, COSTRING} - }; - if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) - { - return E_FAIL; - } - MINIDUMP_TYPE minidumpType = MiniDumpWithPrivateReadWriteMemory; - ULONG pid = 0; - g_ExtSystem->GetCurrentProcessId(&pid); - - if (full) - { - minidumpType = MiniDumpWithFullMemory; - } - else if (withHeap) - { - minidumpType = MiniDumpWithPrivateReadWriteMemory; - } - else if (triage) - { - minidumpType = MiniDumpFilterTriage; - } - else if (normal) - { - minidumpType = MiniDumpNormal; - } - g_diagnostics = diag; - - const char* programPath = g_ExtServices->GetCoreClrDirectory(); - const char* dumpPathTemplate = "/tmp/coredump.%d"; - ToRelease<ICLRDataTarget> dataTarget = new DataTarget(); - dataTarget->AddRef(); - - if (sFileName.data != nullptr) - { - dumpPathTemplate = sFileName.data; - } - if (!CreateDumpForSOS(programPath, dumpPathTemplate, pid, minidumpType, dataTarget)) - { - Status = E_FAIL; - } -#else // CREATE_DUMP_SUPPORTED - ExtErr("CreateDump not supported on this platform\n"); -#endif // CREATE_DUMP_SUPPORTED - return Status; -} - -#endif // FEATURE_PAL - -void PrintHelp (__in_z LPCSTR pszCmdName) -{ - static LPSTR pText = NULL; - - if (pText == NULL) { -#ifndef FEATURE_PAL - HGLOBAL hResource = NULL; - HRSRC hResInfo = FindResource (g_hInstance, TEXT ("DOCUMENTATION"), TEXT ("TEXT")); - if (hResInfo) hResource = LoadResource (g_hInstance, hResInfo); - if (hResource) pText = (LPSTR) LockResource (hResource); - if (pText == NULL) - { - ExtOut("Error loading documentation resource\n"); - return; - } -#else - int err = PAL_InitializeDLL(); - if(err != 0) - { - ExtOut("Error initializing PAL\n"); - return; - } - char lpFilename[MAX_LONGPATH + 12]; // + 12 to make enough room for strcat function. - strcpy_s(lpFilename, _countof(lpFilename), g_ExtServices->GetCoreClrDirectory()); - strcat_s(lpFilename, _countof(lpFilename), "sosdocsunix.txt"); - - HANDLE hSosDocFile = CreateFileA(lpFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if (hSosDocFile == INVALID_HANDLE_VALUE) { - ExtOut("Error finding documentation file\n"); - return; - } - - HANDLE hMappedSosDocFile = CreateFileMappingA(hSosDocFile, NULL, PAGE_READONLY, 0, 0, NULL); - CloseHandle(hSosDocFile); - if (hMappedSosDocFile == NULL) { - ExtOut("Error mapping documentation file\n"); - return; - } - - pText = (LPSTR)MapViewOfFile(hMappedSosDocFile, FILE_MAP_READ, 0, 0, 0); - CloseHandle(hMappedSosDocFile); - if (pText == NULL) - { - ExtOut("Error loading documentation file\n"); - return; - } -#endif - } - - // Find our line in the text file - char searchString[MAX_LONGPATH]; - sprintf_s(searchString, _countof(searchString), "COMMAND: %s.", pszCmdName); - - LPSTR pStart = strstr(pText, searchString); - LPSTR pEnd = NULL; - if (!pStart) - { - ExtOut("Documentation for %s not found.\n", pszCmdName); - return; - } - - // Go to the end of this line: - pStart = strchr(pStart, '\n'); - if (!pStart) - { - ExtOut("Expected newline in documentation resource.\n"); - return; - } - - // Bypass the newline that pStart points to and setup pEnd for the loop below. We set - // pEnd to be the old pStart since we add one to it when we call strstr. - pEnd = pStart++; - - // Find the first occurrence of \\ followed by an \r or an \n on a line by itself. - do - { - pEnd = strstr(pEnd+1, "\\\\"); - } while (pEnd && ((pEnd[-1] != '\r' && pEnd[-1] != '\n') || (pEnd[3] != '\r' && pEnd[3] != '\n'))); - - if (pEnd) - { - // We have found a \\ followed by a \r or \n. Do not print out the character pEnd points - // to, as this will be the first \ (this is why we don't add one to the second parameter). - ExtOut("%.*s", pEnd - pStart, pStart); - } - else - { - // If pEnd is false then we have run to the end of the document. However, we did find - // the command to print, so we should simply print to the end of the file. We'll add - // an extra newline here in case the file does not contain one. - ExtOut("%s\n", pStart); - } -} - -/**********************************************************************\ -* Routine Description: * -* * -* This function displays the commands available in strike and the * -* arguments passed into each. -* * -\**********************************************************************/ -DECLARE_API(Help) -{ - // Call extension initialization functions directly, because we don't need the DAC dll to be initialized to get help. - HRESULT Status; - __ExtensionCleanUp __extensionCleanUp; - if ((Status = ExtQuery(client)) != S_OK) return Status; - ControlC = FALSE; - - StringHolder commandName; - CMDValue arg[] = - { - {&commandName.data, COSTRING} - }; - size_t nArg; - if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) - { - return Status; - } - - ExtOut("-------------------------------------------------------------------------------\n"); - - if (nArg == 1) - { - // Convert commandName to lower-case - LPSTR curChar = commandName.data; - while (*curChar != '\0') - { - if ( ((unsigned) *curChar <= 0x7F) && isupper(*curChar)) - { - *curChar = (CHAR) tolower(*curChar); - } - curChar++; - } - - // Strip off leading "!" if the user put that. - curChar = commandName.data; - if (*curChar == '!') - curChar++; - - PrintHelp (curChar); - } - else - { - PrintHelp ("contents"); - } - - return S_OK; -} - -#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_) - -static BOOL -ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size) -{ - ULONG fetched; - HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(address), buffer, size, &fetched); - return SUCCEEDED(hr); -} - -static BOOL -GetStackFrame(CONTEXT* context, ULONG numNativeFrames) -{ - KNONVOLATILE_CONTEXT_POINTERS contextPointers; - memset(&contextPointers, 0, sizeof(contextPointers)); - - ULONG64 baseAddress; - HRESULT hr = g_ExtSymbols->GetModuleByOffset(context->Rip, 0, NULL, &baseAddress); - if (FAILED(hr)) - { - PDEBUG_STACK_FRAME frame = &g_Frames[0]; - for (unsigned int i = 0; i < numNativeFrames; i++, frame++) { - if (frame->InstructionOffset == context->Rip) - { - if ((i + 1) >= numNativeFrames) { - return FALSE; - } - memcpy(context, &(g_FrameContexts[i + 1]), sizeof(*context)); - return TRUE; - } - } - return FALSE; - } - if (!PAL_VirtualUnwindOutOfProc(context, &contextPointers, baseAddress, ReadMemoryAdapter)) - { - return FALSE; - } - return TRUE; -} - -static BOOL -UnwindStackFrames(ULONG32 osThreadId) -{ - ULONG numNativeFrames = 0; - HRESULT hr = GetContextStackTrace(osThreadId, &numNativeFrames); - if (FAILED(hr)) - { - return FALSE; - } - CONTEXT context; - memset(&context, 0, sizeof(context)); - context.ContextFlags = CONTEXT_FULL; - - hr = g_ExtSystem->GetThreadContextById(osThreadId, CONTEXT_FULL, sizeof(context), (PBYTE)&context); - if (FAILED(hr)) - { - return FALSE; - } - TableOutput out(3, POINTERSIZE_HEX, AlignRight); - out.WriteRow("RSP", "RIP", "Call Site"); - - DEBUG_STACK_FRAME nativeFrame; - memset(&nativeFrame, 0, sizeof(nativeFrame)); - - do - { - if (context.Rip == 0) - { - break; - } - nativeFrame.InstructionOffset = context.Rip; - nativeFrame.ReturnOffset = context.Rip; - nativeFrame.FrameOffset = context.Rbp; - nativeFrame.StackOffset = context.Rsp; - ClrStackImpl::PrintNativeStackFrame(out, &nativeFrame, FALSE); - - } while (GetStackFrame(&context, numNativeFrames)); - - return TRUE; -} - -#endif // FEATURE_PAL && _TARGET_AMD64_ |