summaryrefslogtreecommitdiff
path: root/src/ToolBox/SOS/Strike/exts.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/ToolBox/SOS/Strike/exts.h')
-rw-r--r--src/ToolBox/SOS/Strike/exts.h513
1 files changed, 513 insertions, 0 deletions
diff --git a/src/ToolBox/SOS/Strike/exts.h b/src/ToolBox/SOS/Strike/exts.h
new file mode 100644
index 0000000000..36b5230c37
--- /dev/null
+++ b/src/ToolBox/SOS/Strike/exts.h
@@ -0,0 +1,513 @@
+// 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.
+
+// ==++==
+//
+
+//
+// ==--==
+#ifndef __exts_h__
+#define __exts_h__
+
+#define KDEXT_64BIT
+
+#include <windows.h>
+#include <winternl.h>
+
+#if defined(_MSC_VER)
+#pragma warning(disable:4245) // signed/unsigned mismatch
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+#pragma warning(disable:4127) // conditional expression is constant
+#pragma warning(disable:4430) // missing type specifier: C++ doesn't support default-int
+#endif
+#include "strike.h"
+#include <wdbgexts.h>
+#include <dbgeng.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// wdbgexts.h defines StackTrace which interferes with other parts of the
+// system that use the StackTrace identifier
+#ifdef StackTrace
+#undef StackTrace
+#endif
+
+#include "platformspecific.h"
+
+// We need to define the target address type. This has to be used in the
+// functions that read directly from the debuggee address space, vs. using
+// the DAC to read the DAC-ized data structures.
+#include "daccess.h"
+
+#include "gcinfo.h"
+
+// Convert between CLRDATA_ADDRESS and TADDR.
+#define TO_TADDR(cdaddr) ((TADDR)(cdaddr))
+#define TO_CDADDR(taddr) ((CLRDATA_ADDRESS)(LONG_PTR)(taddr))
+
+// We also need a "correction" macro: there are a number of places in the DAC
+// where instead of using the CLRDATA_ADDRESS sign-extension convention
+// we 0-extend (most notably DacpGcHeapDetails)
+#define NEED_DAC_CLRDATA_ADDRESS_CORRECTION 1
+#if NEED_DAC_CLRDATA_ADDRESS_CORRECTION == 1
+ // the macro below "corrects" a CDADDR to always represent the
+ // sign-extended equivalent ULONG64 value of the original TADDR
+ #define UL64_TO_CDA(ul64) (TO_CDADDR(TO_TADDR(ul64)))
+#else
+ #define UL64_TO_CDA(ul64) (ul64)
+#endif // NEED_DAC_CLRDATA_ADDRESS_CORRECTION 1
+
+// The macro below removes the sign extension, returning the
+// equivalent ULONG64 value to the original TADDR. Useful when
+// printing CDA values.
+#define CDA_TO_UL64(cda) ((ULONG64)(TO_TADDR(cda)))
+
+typedef struct _TADDR_RANGE
+{
+ TADDR start;
+ TADDR end;
+} TADDR_RANGE;
+
+typedef struct _TADDR_SEGINFO
+{
+ TADDR segAddr;
+ TADDR start;
+ TADDR end;
+} TADDR_SEGINFO;
+
+#include "util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Cleanup tasks to be executed when the extension is unloaded
+class OnUnloadTask
+{
+public:
+ FORCEINLINE static void Register(void (*fn)())
+ {
+ // append a new unload task to the head of the list
+ OnUnloadTask *pNew = new OnUnloadTask(fn);
+ pNew->pNext = s_pUnloadTaskList;
+ s_pUnloadTaskList = pNew;
+ }
+
+ static void Run()
+ {
+ // walk the list of UnloadTasks and execute each in turn
+ OnUnloadTask* pCur = s_pUnloadTaskList;
+ while (pCur != NULL)
+ {
+ OnUnloadTask* pNext = pCur->pNext;
+ pCur->OnUnloadFn();
+ delete pCur;
+ pCur = pNext;
+ }
+ s_pUnloadTaskList = NULL;
+ }
+
+private:
+ OnUnloadTask(void(*fn)())
+ : OnUnloadFn(fn)
+ , pNext(NULL)
+ { }
+
+private:
+ void (*OnUnloadFn)();
+ OnUnloadTask* pNext;
+
+ static OnUnloadTask *s_pUnloadTaskList;
+};
+
+#ifndef MINIDUMP
+
+#define EXIT_API ExtRelease
+
+// Safe release and NULL.
+#define EXT_RELEASE(Unk) \
+ ((Unk) != NULL ? ((Unk)->Release(), (Unk) = NULL) : NULL)
+
+extern PDEBUG_CONTROL2 g_ExtControl;
+extern PDEBUG_DATA_SPACES g_ExtData;
+extern PDEBUG_SYMBOLS g_ExtSymbols;
+extern PDEBUG_SYSTEM_OBJECTS g_ExtSystem;
+extern PDEBUG_REGISTERS g_ExtRegisters;
+
+#ifndef FEATURE_PAL
+
+// Global variables initialized by query.
+extern PDEBUG_CLIENT g_ExtClient;
+extern PDEBUG_DATA_SPACES2 g_ExtData2;
+extern PDEBUG_SYMBOLS2 g_ExtSymbols2;
+extern PDEBUG_ADVANCED3 g_ExtAdvanced3;
+
+#else // FEATURE_PAL
+
+extern ILLDBServices* g_ExtServices;
+
+#endif // FEATURE_PAL
+
+HRESULT
+ExtQuery(PDEBUG_CLIENT client);
+
+HRESULT
+ArchQuery(void);
+
+void
+ExtRelease(void);
+
+#ifdef _DEBUG
+extern DWORD_PTR g_filterHint;
+#endif
+
+extern BOOL ControlC;
+
+inline BOOL IsInterrupt()
+{
+ if (!ControlC && g_ExtControl->GetInterrupt() == S_OK)
+ {
+ ExtOut("Command cancelled at the user's request.\n");
+ ControlC = TRUE;
+ }
+
+ return ControlC;
+}
+
+//
+// undef the wdbgexts
+//
+#undef DECLARE_API
+
+#define DECLARE_API(extension) \
+CPPMOD HRESULT CALLBACK extension(PDEBUG_CLIENT client, PCSTR args)
+
+class __ExtensionCleanUp
+{
+public:
+ __ExtensionCleanUp(){}
+ ~__ExtensionCleanUp(){ExtRelease();}
+};
+
+inline void EENotLoadedMessage(HRESULT Status)
+{
+ ExtOut("Failed to find runtime DLL (%s), 0x%08x\n", MAKEDLLNAME_A("coreclr"), Status);
+ ExtOut("Extension commands need it in order to have something to do.\n");
+}
+
+inline void DACMessage(HRESULT Status)
+{
+ ExtOut("Failed to load data access DLL, 0x%08x\n", Status);
+#ifndef FEATURE_PAL
+ ExtOut("Verify that 1) you have a recent build of the debugger (6.2.14 or newer)\n");
+ ExtOut(" 2) the file mscordacwks.dll that matches your version of coreclr.dll is \n");
+ ExtOut(" in the version directory or on the symbol path\n");
+ ExtOut(" 3) or, if you are debugging a dump file, verify that the file \n");
+ ExtOut(" mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.\n");
+ ExtOut(" 4) you are debugging on supported cross platform architecture as \n");
+ ExtOut(" the dump file. For example, an ARM dump file must be debugged\n");
+ ExtOut(" on an X86 or an ARM machine; an AMD64 dump file must be\n");
+ ExtOut(" debugged on an AMD64 machine.\n");
+ ExtOut("\n");
+ ExtOut("You can also run the debugger command .cordll to control the debugger's\n");
+ ExtOut("load of mscordacwks.dll. .cordll -ve -u -l will do a verbose reload.\n");
+ ExtOut("If that succeeds, the SOS command should work on retry.\n");
+ ExtOut("\n");
+ ExtOut("If you are debugging a minidump, you need to make sure that your executable\n");
+ ExtOut("path is pointing to coreclr.dll as well.\n");
+#else // FEATURE_PAL
+ if (Status == CORDBG_E_MISSING_DEBUGGER_EXPORTS)
+ {
+ ExtOut("You can run the debugger command 'setclrpath' to control the load of %s.\n", MAKEDLLNAME_A("mscordaccore"));
+ ExtOut("If that succeeds, the SOS command should work on retry.\n");
+ }
+ else
+ {
+ ExtOut("Can not load or initialize %s. The target runtime may not be initialized.\n", MAKEDLLNAME_A("mscordaccore"));
+ }
+#endif // FEATURE_PAL
+}
+
+HRESULT CheckEEDll();
+
+#define INIT_API_NOEE() \
+ HRESULT Status; \
+ __ExtensionCleanUp __extensionCleanUp; \
+ if ((Status = ExtQuery(client)) != S_OK) return Status; \
+ if ((Status = ArchQuery()) != S_OK) return Status; \
+ ControlC = FALSE; \
+ g_bDacBroken = TRUE; \
+ g_clrData = NULL; \
+ g_sos = NULL;
+
+#define INIT_API_EE() \
+ if ((Status = CheckEEDll()) != S_OK) \
+ { \
+ EENotLoadedMessage(Status); \
+ return Status; \
+ }
+
+#define INIT_API_NODAC() \
+ INIT_API_NOEE() \
+ INIT_API_EE()
+
+#define INIT_API_DAC() \
+ if ((Status = LoadClrDebugDll()) != S_OK) \
+ { \
+ DACMessage(Status); \
+ return Status; \
+ } \
+ g_bDacBroken = FALSE; \
+ /* If LoadClrDebugDll() succeeded make sure we release g_clrData. */ \
+ /* We may reconsider caching g_clrData in the future */ \
+ ToRelease<IXCLRDataProcess> spIDP(g_clrData); \
+ ToRelease<ISOSDacInterface> spISD(g_sos); \
+ ResetGlobals();
+
+#define INIT_API() \
+ INIT_API_NODAC() \
+ INIT_API_DAC()
+
+// Attempt to initialize DAC and SOS globals, but do not "return" on failure.
+// Instead, mark the failure to initialize the DAC by setting g_bDacBroken to TRUE.
+// This should be used from extension commands that should work OK even when no
+// runtime is loaded in the debuggee, e.g. DumpLog, DumpStack. These extensions
+// and functions they call should test g_bDacBroken before calling any DAC enabled
+// feature.
+#define INIT_API_NO_RET_ON_FAILURE() \
+ INIT_API_NOEE() \
+ if ((Status = CheckEEDll()) != S_OK) \
+ { \
+ ExtOut("Failed to find runtime DLL (%s), 0x%08x\n", MAKEDLLNAME_A("coreclr"), Status); \
+ ExtOut("Some functionality may be impaired\n"); \
+ } \
+ else if ((Status = LoadClrDebugDll()) != S_OK) \
+ { \
+ ExtOut("Failed to load data access DLL (%s), 0x%08x\n", MAKEDLLNAME_A("mscordaccore"), Status); \
+ ExtOut("Some functionality may be impaired\n"); \
+ } \
+ else \
+ { \
+ g_bDacBroken = FALSE; \
+ ResetGlobals(); \
+ } \
+ /* If LoadClrDebugDll() succeeded make sure we release g_clrData. */ \
+ /* We may reconsider caching g_clrData in the future */ \
+ ToRelease<ISOSDacInterface> spISD(g_sos); \
+ ToRelease<IXCLRDataProcess> spIDP(g_clrData);
+
+extern BOOL g_bDacBroken;
+
+#define PAGE_ALIGN64(Va) ((ULONG64)((Va) & ~((ULONG64) ((LONG64) (LONG) PageSize - 1))))
+
+extern ULONG PageSize;
+
+
+//-----------------------------------------------------------------------------------------
+//
+// Target platform abstraction
+//
+//-----------------------------------------------------------------------------------------
+
+// some needed forward declarations
+struct StackTrace_SimpleContext;
+struct GCEncodingInfo;
+struct SOSEHInfo;
+class GCDump;
+
+///
+/// IMachine interface
+///
+/// Note:
+/// The methods accepting target address args take them as size_t==DWORD_PTR==TADDR,
+/// which means this can only provide cross platform support for same-word size
+/// architectures (only ARM on x86 currently). Since this is not exposed outside SOS
+/// and since the some-word-size limitation exists across EE/DAC/SOS this is not an
+/// actual limitation.
+///
+
+class IMachine
+{
+public:
+ // Returns the IMAGE_FILE_MACHINE_*** constant corresponding to the target machine
+ virtual ULONG GetPlatform() const = 0;
+ // Returns the size of the CONTEXT for the target machine
+ virtual ULONG GetContextSize() const = 0;
+
+ // Disassembles a managed method specified by the IPBegin-IPEnd range
+ virtual void Unassembly(
+ TADDR IPBegin,
+ TADDR IPEnd,
+ TADDR IPAskedFor,
+ TADDR GCStressCodeCopy,
+ GCEncodingInfo *pGCEncodingInfo,
+ SOSEHInfo *pEHInfo,
+ BOOL bSuppressLines,
+ BOOL bDisplayOffsets) const = 0;
+
+ // Validates whether retAddr represents a return address by unassembling backwards.
+ // If the instruction before retAddr represents a target-specific call instruction
+ // it attempts to identify the target of the call. If successful it sets *whereCalled
+ // to the call target, otherwise it sets it to 0xffffffff.
+ virtual void IsReturnAddress(
+ TADDR retAddr,
+ TADDR* whereCalled) const = 0;
+
+ // If, while unwinding the stack, "PC" represents a known return address in
+ // KiUserExceptionDispatcher, "stack" is used to retrieve an exception context record
+ // in "cxr", and an exception record in "exr"
+ virtual BOOL GetExceptionContext (
+ TADDR stack,
+ TADDR PC,
+ TADDR *cxrAddr,
+ CROSS_PLATFORM_CONTEXT * cxr,
+ TADDR *exrAddr,
+ PEXCEPTION_RECORD exr) const = 0;
+
+ // Retrieves stack pointer, frame pointer, and instruction pointer from the target context
+ virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
+ virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
+ virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
+
+ // Fills dest's data fields from a target specific context
+ virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const = 0;
+ // Fills a target specific context, destCtx, from the idx-th location in a target specific
+ // array of contexts that start at srcCtx
+ virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const = 0;
+
+ // Retrieve some target specific output strings
+ virtual LPCSTR GetDumpStackHeading() const = 0;
+ virtual LPCSTR GetDumpStackObjectsHeading() const = 0;
+ virtual LPCSTR GetSPName() const = 0;
+ // Retrieves the non-volatile registers reported to the GC
+ virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const = 0;
+
+ typedef void (*printfFtn)(const char* fmt, ...);
+ // Dumps the GCInfo
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const = 0;
+
+protected:
+ IMachine() {}
+ virtual ~IMachine() {}
+
+private:
+ IMachine(const IMachine& machine); // undefined
+ IMachine & operator=(const IMachine&); // undefined
+}; // class IMachine
+
+
+extern IMachine* g_targetMachine;
+
+
+inline BOOL IsDbgTargetX86() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_I386; }
+inline BOOL IsDbgTargetAmd64() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_AMD64; }
+inline BOOL IsDbgTargetArm() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_ARMNT; }
+inline BOOL IsDbgTargetWin64() { return IsDbgTargetAmd64(); }
+
+/* Returns the instruction pointer for the given CONTEXT. We need this and its family of
+ * functions because certain headers are inconsistantly included on the various platforms,
+ * meaning that we cannot use GetIP and GetSP as defined by CLR.
+ */
+inline CLRDATA_ADDRESS GetIP(const CROSS_PLATFORM_CONTEXT& context)
+{
+ return TO_CDADDR(g_targetMachine->GetIP(context));
+}
+
+/* Returns the stack pointer for the given CONTEXT.
+ */
+inline CLRDATA_ADDRESS GetSP(const CROSS_PLATFORM_CONTEXT& context)
+{
+ return TO_CDADDR(g_targetMachine->GetSP(context));
+}
+
+/* Returns the base/frame pointer for the given CONTEXT.
+ */
+inline CLRDATA_ADDRESS GetBP(const CROSS_PLATFORM_CONTEXT& context)
+{
+ return TO_CDADDR(g_targetMachine->GetBP(context));
+}
+
+
+//-----------------------------------------------------------------------------------------
+//
+// api declaration macros & api access macros
+//
+//-----------------------------------------------------------------------------------------
+
+#ifndef FEATURE_PAL
+
+extern WINDBG_EXTENSION_APIS ExtensionApis;
+#define GetExpression (ExtensionApis.lpGetExpressionRoutine)
+
+extern ULONG TargetMachine;
+extern ULONG g_TargetClass;
+extern ULONG g_VDbgEng;
+
+#else // FEATURE_PAL
+
+#define GetExpression(exp) g_ExtServices->GetExpression(exp)
+
+#endif // FEATURE_PAL
+
+#define CACHE_SIZE DT_OS_PAGE_SIZE
+
+struct ReadVirtualCache
+{
+ BYTE m_cache[CACHE_SIZE];
+ TADDR m_startCache;
+ BOOL m_cacheValid;
+ ULONG m_cacheSize;
+
+ ReadVirtualCache() { Clear(); }
+ HRESULT Read(TADDR Offset, PVOID Buffer, ULONG BufferSize, PULONG lpcbBytesRead);
+ void Clear() { m_cacheValid = FALSE; m_cacheSize = CACHE_SIZE; }
+};
+
+extern ReadVirtualCache *rvCache;
+
+#define MOVE(dst, src) rvCache->Read(TO_TADDR(src), &(dst), sizeof(dst), NULL)
+#define MOVEBLOCK(dst, src, size) rvCache->Read(TO_TADDR(src), &(dst), size, NULL)
+
+#define moveN(dst, src) \
+{ \
+ HRESULT ret = MOVE(dst, src); \
+ if (FAILED(ret)) return ret; \
+}
+
+#define moveBlockN(dst, src, size) \
+{ \
+ HRESULT ret = MOVEBLOCK(dst, src, size); \
+ if (FAILED(ret)) return ret; \
+}
+
+// move cross-process: reads memory from the debuggee into
+// debugger address space and returns in case of error
+#define move_xp(dst, src) \
+{ \
+ HRESULT ret = MOVE(dst, src); \
+ if (FAILED(ret)) return; \
+}
+
+#define moveBlock(dst, src, size) \
+{ \
+ HRESULT ret = MOVEBLOCK(dst, src, size); \
+ if (FAILED(ret)) return; \
+}
+
+#ifdef __cplusplus
+#define CPPMOD extern "C"
+#else
+#define CPPMOD
+#endif
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __exts_h__
+