diff options
Diffstat (limited to 'src/ToolBox/SOS/Strike/util.h')
-rw-r--r-- | src/ToolBox/SOS/Strike/util.h | 3292 |
1 files changed, 3292 insertions, 0 deletions
diff --git a/src/ToolBox/SOS/Strike/util.h b/src/ToolBox/SOS/Strike/util.h new file mode 100644 index 0000000000..f444c9fcb2 --- /dev/null +++ b/src/ToolBox/SOS/Strike/util.h @@ -0,0 +1,3292 @@ +// 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 __util_h__ +#define __util_h__ + +#define LIMITED_METHOD_CONTRACT + +// So we can use the PAL_TRY_NAKED family of macros without dependencies on utilcode. +inline void RestoreSOToleranceState() {} + +#include <cor.h> +#include <corsym.h> +#include <clrdata.h> +#include <palclr.h> +#include <metahost.h> +#include <new> + +#if !defined(FEATURE_PAL) +#include <dia2.h> +#endif + +#ifdef STRIKE +#if defined(_MSC_VER) +#pragma warning(disable:4200) +#pragma warning(default:4200) +#endif +#include "data.h" +#endif //STRIKE + +#include "cordebug.h" +#include "static_assert.h" + +typedef LPCSTR LPCUTF8; +typedef LPSTR LPUTF8; + +DECLARE_HANDLE(OBJECTHANDLE); + +struct IMDInternalImport; + +#if defined(_TARGET_WIN64_) +#define WIN64_8SPACES "" +#define WIN86_8SPACES " " +#define POINTERSIZE "16" +#define POINTERSIZE_HEX 16 +#define POINTERSIZE_BYTES 8 +#define POINTERSIZE_TYPE "I64" +#else +#define WIN64_8SPACES " " +#define WIN86_8SPACES "" +#define POINTERSIZE "8" +#define POINTERSIZE_HEX 8 +#define POINTERSIZE_BYTES 4 +#define POINTERSIZE_TYPE "I32" +#endif + +#if defined(_MSC_VER) +#pragma warning(disable:4510 4512 4610) +#endif + +#ifndef _ASSERTE +#ifdef _DEBUG +#define _ASSERTE(expr) \ + do { if (!(expr) ) { ExtErr("_ASSERTE fired:\n\t%s\n", #expr); if (IsDebuggerPresent()) DebugBreak(); } } while (0) +#else +#define _ASSERTE(x) +#endif +#endif // ASSERTE + +#ifdef _DEBUG +#define ASSERT_CHECK(expr, msg, reason) \ + do { if (!(expr) ) { ExtOut(reason); ExtOut(msg); ExtOut(#expr); DebugBreak(); } } while (0) +#endif + +// PREFIX macros - Begin + +// SOS does not have support for Contracts. Therefore we needed to duplicate +// some of the PREFIX infrastructure from inc\check.h in here. + +// Issue - PREFast_:510 v4.51 does not support __assume(0) +#if (defined(_MSC_VER) && !defined(_PREFAST_)) || defined(_PREFIX_) +#if defined(_AMD64_) +// Empty methods that consist of UNREACHABLE() result in a zero-sized declspec(noreturn) method +// which causes the pdb file to make the next method declspec(noreturn) as well, thus breaking BBT +// Remove when we get a VC compiler that fixes VSW 449170 +# define __UNREACHABLE() DebugBreak(); __assume(0); +#else +# define __UNREACHABLE() __assume(0) +#endif +#else +#define __UNREACHABLE() do { } while(true) +#endif + + +#if defined(_PREFAST_) || defined(_PREFIX_) +#define COMPILER_ASSUME_MSG(_condition, _message) if (!(_condition)) __UNREACHABLE(); +#else + +#if defined(DACCESS_COMPILE) +#define COMPILER_ASSUME_MSG(_condition, _message) do { } while (0) +#else + +#if defined(_DEBUG) +#define COMPILER_ASSUME_MSG(_condition, _message) \ + ASSERT_CHECK(_condition, _message, "Compiler optimization assumption invalid") +#else +#define COMPILER_ASSUME_MSG(_condition, _message) __assume(_condition) +#endif // _DEBUG + +#endif // DACCESS_COMPILE + +#endif // _PREFAST_ || _PREFIX_ + +#define PREFIX_ASSUME(_condition) \ + COMPILER_ASSUME_MSG(_condition, "") + +// PREFIX macros - End + +class MethodTable; + +#define MD_NOT_YET_LOADED ((DWORD_PTR)-1) +/* + * HANDLES + * + * The default type of handle is a strong handle. + * + */ +#define HNDTYPE_DEFAULT HNDTYPE_STRONG +#define HNDTYPE_WEAK_DEFAULT HNDTYPE_WEAK_LONG +#define HNDTYPE_WEAK_SHORT (0) +#define HNDTYPE_WEAK_LONG (1) +#define HNDTYPE_STRONG (2) +#define HNDTYPE_PINNED (3) +#define HNDTYPE_VARIABLE (4) +#define HNDTYPE_REFCOUNTED (5) +#define HNDTYPE_DEPENDENT (6) +#define HNDTYPE_ASYNCPINNED (7) +#define HNDTYPE_SIZEDREF (8) +#define HNDTYPE_WEAK_WINRT (9) + +// Anything above this we consider abnormal and stop processing heap information +const int nMaxHeapSegmentCount = 1000; + +class BaseObject +{ + MethodTable *m_pMethTab; +}; + + +const BYTE gElementTypeInfo[] = { +#define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) s, +#include "cortypeinfo.h" +#undef TYPEINFO +}; + +typedef struct tagLockEntry +{ + tagLockEntry *pNext; // next entry + tagLockEntry *pPrev; // prev entry + DWORD dwULockID; + DWORD dwLLockID; // owning lock + WORD wReaderLevel; // reader nesting level +} LockEntry; + +#define MAX_CLASSNAME_LENGTH 1024 + +enum EEFLAVOR {UNKNOWNEE, MSCOREE, MSCORWKS, MSCOREND}; + +#include "sospriv.h" +extern IXCLRDataProcess *g_clrData; +extern ISOSDacInterface *g_sos; + +#include "dacprivate.h" + +interface ICorDebugProcess; +extern ICorDebugProcess * g_pCorDebugProcess; + +// This class is templated for easy modification. We may need to update the CachedString +// or related classes to use WCHAR instead of char in the future. +template <class T, int count, int size> +class StaticData +{ +public: + StaticData() + { + for (int i = 0; i < count; ++i) + InUse[i] = false; + } + + // Whether the individual data pointers in the cache are in use. + bool InUse[count]; + + // The actual data itself. + T Data[count][size]; + + // The number of arrays in the cache. + static const int Count; + + // The size of each individual array. + static const int Size; +}; + +class CachedString +{ +public: + CachedString(); + CachedString(const CachedString &str); + ~CachedString(); + + const CachedString &operator=(const CachedString &str); + + // Returns the capacity of this string. + size_t GetStrLen() const + { + return mSize; + } + + // Returns a mutable character pointer. Be sure not to write past the + // length of this string. + inline operator char *() + { + return mPtr; + } + + // Returns a const char representation of this string. + inline operator const char *() const + { + return GetPtr(); + } + + // To ensure no AV's, any time a constant pointer is requested, we will + // return an empty string "" if we hit an OOM. This will only happen + // if we hit an OOM and do not check for it before using the string. + // If you request a non-const char pointer out of this class, it may be + // null (see operator char *). + inline const char *GetPtr() const + { + if (!mPtr || IsOOM()) + return ""; + + return mPtr; + } + + // Returns true if we ran out of memory trying to allocate the string + // or the refcount. + bool IsOOM() const + { + return mIndex == -2; + } + + // allocate a string of the specified size. this will Clear() any + // previously allocated string. call IsOOM() to check for failure. + void Allocate(int size); + +private: + // Copies rhs into this string. + void Copy(const CachedString &rhs); + + // Clears this string, releasing any underlying memory. + void Clear(); + + // Creates a new string. + void Create(); + + // Sets an out of memory state. + void SetOOM(); + +private: + char *mPtr; + + // The reference count. This may be null if there is only one copy + // of this string. + mutable unsigned int *mRefCount; + + // mIndex contains the index of the cached pointer we are using, or: + // ~0 - poison value we initialize it to for debugging purposes + // -1 - mPtr points to a pointer we have new'ed + // -2 - We hit an oom trying to allocate either mCount or mPtr + int mIndex; + + // contains the size of current string + int mSize; + +private: + static StaticData<char, 4, 1024> cache; +}; + +// Things in this namespace should not be directly accessed/called outside of +// the output-related functions. +namespace Output +{ + extern unsigned int g_bSuppressOutput; + extern unsigned int g_Indent; + extern unsigned int g_DMLEnable; + extern bool g_bDbgOutput; + extern bool g_bDMLExposed; + + inline bool IsOutputSuppressed() + { return g_bSuppressOutput > 0; } + + inline void ResetIndent() + { g_Indent = 0; } + + inline void SetDebugOutputEnabled(bool enabled) + { g_bDbgOutput = enabled; } + + inline bool IsDebugOutputEnabled() + { return g_bDbgOutput; } + + inline void SetDMLExposed(bool exposed) + { g_bDMLExposed = exposed; } + + inline bool IsDMLExposed() + { return g_bDMLExposed; } + + enum FormatType + { + DML_None, + DML_MethodTable, + DML_MethodDesc, + DML_EEClass, + DML_Module, + DML_IP, + DML_Object, + DML_Domain, + DML_Assembly, + DML_ThreadID, + DML_ValueClass, + DML_DumpHeapMT, + DML_ListNearObj, + DML_ThreadState, + DML_PrintException, + DML_RCWrapper, + DML_CCWrapper, + DML_ManagedVar, + }; + + /**********************************************************************\ + * This function builds a DML string for a ValueClass. If DML is * + * enabled, this function returns a DML string based on the format * + * type. Otherwise this returns a string containing only the hex value * + * of addr. * + * * + * Params: * + * mt - the method table of the ValueClass * + * addr - the address of the ValueClass * + * type - the format type to use to output this object * + * fill - whether or not to pad the hex value with zeros * + * * + \**********************************************************************/ + CachedString BuildVCValue(CLRDATA_ADDRESS mt, CLRDATA_ADDRESS addr, FormatType type, bool fill = true); + + + /**********************************************************************\ + * This function builds a DML string for an object. If DML is enabled, * + * this function returns a DML string based on the format type. * + * Otherwise this returns a string containing only the hex value of * + * addr. * + * * + * Params: * + * addr - the address of the object * + * type - the format type to use to output this object * + * fill - whether or not to pad the hex value with zeros * + * * + \**********************************************************************/ + CachedString BuildHexValue(CLRDATA_ADDRESS addr, FormatType type, bool fill = true); + + /**********************************************************************\ + * This function builds a DML string for an managed variable name. * + * If DML is enabled, this function returns a DML string that will * + * enable the expansion of that managed variable using the !ClrStack * + * command to display the variable's fields, otherwise it will just * + * return the variable's name as a string. + * * + * Params: * + * expansionName - the current variable expansion string * + * frame - the frame that contains the variable of interest * + * simpleName - simple name of the managed variable * + * * + \**********************************************************************/ + CachedString BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, __in_z LPCWSTR simpleName, FormatType type); + CachedString BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, int indexInArray, FormatType type); //used for array indices (simpleName = "[<indexInArray>]") +} + +class NoOutputHolder +{ +public: + NoOutputHolder(BOOL bSuppress = TRUE); + ~NoOutputHolder(); + +private: + BOOL mSuppress; +}; + +class EnableDMLHolder +{ +public: + EnableDMLHolder(BOOL enable); + ~EnableDMLHolder(); + +private: + BOOL mEnable; +}; + +size_t CountHexCharacters(CLRDATA_ADDRESS val); + +// Normal output. +void DMLOut(PCSTR format, ...); /* Prints out DML strings. */ +void IfDMLOut(PCSTR format, ...); /* Prints given DML string ONLY if DML is enabled; prints nothing otherwise. */ +void ExtOut(PCSTR Format, ...); /* Prints out to ExtOut (no DML). */ +void ExtWarn(PCSTR Format, ...); /* Prints out to ExtWarn (no DML). */ +void ExtErr(PCSTR Format, ...); /* Prints out to ExtErr (no DML). */ +void ExtDbgOut(PCSTR Format, ...); /* Prints out to ExtOut in a checked build (no DML). */ +void WhitespaceOut(int count); /* Prints out "count" number of spaces in the output. */ + +// Change indent for ExtOut +inline void IncrementIndent() { Output::g_Indent++; } +inline void DecrementIndent() { if (Output::g_Indent > 0) Output::g_Indent--; } +inline void ExtOutIndent() { WhitespaceOut(Output::g_Indent << 2); } + +// DML Generation Methods +#define DMLListNearObj(addr) Output::BuildHexValue(addr, Output::DML_ListNearObj).GetPtr() +#define DMLDumpHeapMT(addr) Output::BuildHexValue(addr, Output::DML_DumpHeapMT).GetPtr() +#define DMLMethodTable(addr) Output::BuildHexValue(addr, Output::DML_MethodTable).GetPtr() +#define DMLMethodDesc(addr) Output::BuildHexValue(addr, Output::DML_MethodDesc).GetPtr() +#define DMLClass(addr) Output::BuildHexValue(addr, Output::DML_EEClass).GetPtr() +#define DMLModule(addr) Output::BuildHexValue(addr, Output::DML_Module).GetPtr() +#define DMLIP(ip) Output::BuildHexValue(ip, Output::DML_IP).GetPtr() +#define DMLObject(addr) Output::BuildHexValue(addr, Output::DML_Object).GetPtr() +#define DMLDomain(addr) Output::BuildHexValue(addr, Output::DML_Domain).GetPtr() +#define DMLAssembly(addr) Output::BuildHexValue(addr, Output::DML_Assembly).GetPtr() +#define DMLThreadID(id) Output::BuildHexValue(id, Output::DML_ThreadID, false).GetPtr() +#define DMLValueClass(mt, addr) Output::BuildVCValue(mt, addr, Output::DML_ValueClass).GetPtr() +#define DMLRCWrapper(addr) Output::BuildHexValue(addr, Output::DML_RCWrapper).GetPtr() +#define DMLCCWrapper(addr) Output::BuildHexValue(addr, Output::DML_CCWrapper).GetPtr() +#define DMLManagedVar(expansionName,frame,simpleName) Output::BuildManagedVarValue(expansionName, frame, simpleName, Output::DML_ManagedVar).GetPtr() + +bool IsDMLEnabled(); + + +#ifndef SOS_Assert +#define SOS_Assert(x) +#endif + +void ConvertToLower(__out_ecount(len) char *buffer, size_t len); + +extern const char * const DMLFormats[]; +int GetHex(CLRDATA_ADDRESS addr, __out_ecount(len) char *out, size_t len, bool fill); + +// A simple string class for mutable strings. We cannot use STL, so this is a stand in replacement +// for std::string (though it doesn't use the same interface). +template <class T, size_t (__cdecl *LEN)(const T *), errno_t (__cdecl *COPY)(T *, size_t, const T * _Src)> +class BaseString +{ +public: + BaseString() + : mStr(0), mSize(0), mLength(0) + { + const size_t size = 64; + + mStr = new T[size]; + mSize = size; + mStr[0] = 0; + } + + BaseString(const T *str) + : mStr(0), mSize(0), mLength(0) + { + CopyFrom(str, LEN(str)); + } + + BaseString(const BaseString<T, LEN, COPY> &rhs) + : mStr(0), mSize(0), mLength(0) + { + *this = rhs; + } + + ~BaseString() + { + Clear(); + } + + const BaseString<T, LEN, COPY> &operator=(const BaseString<T, LEN, COPY> &rhs) + { + Clear(); + CopyFrom(rhs.mStr, rhs.mLength); + return *this; + } + + const BaseString<T, LEN, COPY> &operator=(const T *str) + { + Clear(); + CopyFrom(str, LEN(str)); + return *this; + } + + const BaseString<T, LEN, COPY> &operator +=(const T *str) + { + size_t len = LEN(str); + CopyFrom(str, len); + return *this; + } + + const BaseString<T, LEN, COPY> &operator +=(const BaseString<T, LEN, COPY> &str) + { + CopyFrom(str.mStr, str.mLength); + return *this; + } + + BaseString<T, LEN, COPY> operator+(const T *str) const + { + return BaseString<T, LEN, COPY>(mStr, mLength, str, LEN(str)); + } + + BaseString<T, LEN, COPY> operator+(const BaseString<T, LEN, COPY> &str) const + { + return BaseString<T, LEN, COPY>(mStr, mLength, str.mStr, str.mLength); + } + + operator const T *() const + { + return mStr; + } + + const T *c_str() const + { + return mStr; + } + + size_t GetLength() const + { + return mLength; + } + +private: + BaseString(const T * str1, size_t len1, const T * str2, size_t len2) + : mStr(0), mSize(0), mLength(0) + { + const size_t size = len1 + len2 + 1 + ((len1 + len2) >> 1); + mStr = new T[size]; + mSize = size; + + CopyFrom(str1, len1); + CopyFrom(str2, len2); + } + + void Clear() + { + mLength = 0; + mSize = 0; + if (mStr) + { + delete [] mStr; + mStr = 0; + } + } + + void CopyFrom(const T *str, size_t len) + { + if (mLength + len + 1 >= mSize) + Resize(mLength + len + 1); + + COPY(mStr+mLength, mSize-mLength, str); + mLength += len; + } + + void Resize(size_t size) + { + /* We always resize at least one half bigger than we need. When CopyFrom requests a resize + * it asks for the exact size that's needed to concatenate strings. However in practice + * it's common to add multiple strings together in a row, e.g.: + * String foo = "One " + "Two " + "Three " + "Four " + "\n"; + * Ensuring the size of the string is bigger than we need, and that the minimum size is 64, + * we will cut down on a lot of needless resizes at the cost of a few bytes wasted in some + * cases. + */ + size += size >> 1; + if (size < 64) + size = 64; + + T *newStr = new T[size]; + + if (mStr) + { + COPY(newStr, size, mStr); + delete [] mStr; + } + else + { + newStr[0] = 0; + } + + mStr = newStr; + mSize = size; + } +private: + T *mStr; + size_t mSize, mLength; +}; + +typedef BaseString<char, strlen, strcpy_s> String; +typedef BaseString<WCHAR, _wcslen, wcscpy_s> WString; + + +template<class T> +void Flatten(__out_ecount(len) T *data, unsigned int len) +{ + for (unsigned int i = 0; i < len; ++i) + if (data[i] < 32 || (data[i] > 126 && data[i] <= 255)) + data[i] = '.'; + data[len] = 0; +} + +void Flatten(__out_ecount(len) char *data, unsigned int len); + +/* Formats for the Format class. We support the following formats: + * Pointer - Same as %p. + * Hex - Same as %x (same as %p, but does not output preceding zeros. + * PrefixHex - Same as %x, but prepends 0x. + * Decimal - Same as %d. + * Strings and wide strings don't use this. + */ +class Formats +{ +public: + enum Format + { + Default, + Pointer, + Hex, + PrefixHex, + Decimal, + }; +}; + +enum Alignment +{ + AlignLeft, + AlignRight +}; + +namespace Output +{ + /* Defines how a value will be printed. This class understands how to format + * and print values according to the format and DML settings provided. + * The raw templated class handles the pointer/integer case. Support for + * character arrays and wide character arrays are handled by template + * specializations. + * + * Note that this class is not used directly. Instead use the typedefs and + * macros which define the type of data you are outputing (for example ObjectPtr, + * MethodTablePtr, etc). + */ + template <class T> + class Format + { + public: + Format(T value) + : mValue(value), mFormat(Formats::Default), mDml(Output::DML_None) + { + } + + Format(T value, Formats::Format format, Output::FormatType dmlType) + : mValue(value), mFormat(format), mDml(dmlType) + { + } + + Format(const Format<T> &rhs) + : mValue(rhs.mValue), mFormat(rhs.mFormat), mDml(rhs.mDml) + { + } + + /* Prints out the value according to the Format and DML settings provided. + */ + void Output() const + { + if (IsDMLEnabled() && mDml != Output::DML_None) + { + const int len = GetDMLWidth(mDml); + char *buffer = (char*)alloca(len); + + BuildDML(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml); + DMLOut(buffer); + } + else + { + if (mFormat == Formats::Default || mFormat == Formats::Pointer) + { + ExtOut("%p", SOS_PTR(mValue)); + } + else + { + const char *format = NULL; + if (mFormat == Formats::PrefixHex) + { + format = "0x%x"; + } + else if (mFormat == Formats::Hex) + { + format = "%x"; + } + else if (mFormat == Formats::Decimal) + { + format = "%d"; + } + + ExtOut(format, (__int32)mValue); + } + + } + } + + /* Prints out the value based on a specified width and alignment. + * Params: + * align - Whether the output should be left or right justified. + * width - The output width to fill. + * Note: + * This function guarantees that exactly width will be printed out (so if width is 24, + * exactly 24 characters will be printed), even if the output wouldn't normally fit + * in the space provided. This function makes no guarantees as to what part of the + * data will be printed in the case that width isn't wide enough. + */ + void OutputColumn(Alignment align, int width) const + { + bool leftAlign = align == AlignLeft; + if (IsDMLEnabled() && mDml != Output::DML_None) + { + const int len = GetDMLColWidth(mDml, width); + char *buffer = (char*)alloca(len); + + BuildDMLCol(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml, leftAlign, width); + DMLOut(buffer); + } + else + { + int precision = GetPrecision(); + if (mFormat == Formats::Default || mFormat == Formats::Pointer) + { + if (precision > width) + precision = width; + + ExtOut(leftAlign ? "%-*.*p" : "%*.*p", width, precision, SOS_PTR(mValue)); + } + else + { + const char *format = NULL; + if (mFormat == Formats::PrefixHex) + { + format = leftAlign ? "0x%-*.*x" : "0x%*.*x"; + width -= 2; + } + else if (mFormat == Formats::Hex) + { + format = leftAlign ? "%-*.*x" : "%*.*x"; + } + else if (mFormat == Formats::Decimal) + { + format = leftAlign ? "%-*.*d" : "%*.*d"; + } + + if (precision > width) + precision = width; + + ExtOut(format, width, precision, (__int32)mValue); + } + } + } + + /* Converts this object into a Wide char string. This allows you to write the following code: + * WString foo = L"bar " + ObjectPtr(obj); + * Where ObjectPtr is a subclass/typedef of this Format class. + */ + operator WString() const + { + String str = *this; + const char *cstr = (const char *)str; + + int len = MultiByteToWideChar(CP_ACP, 0, cstr, -1, NULL, 0); + WCHAR *buffer = (WCHAR *)alloca(len*sizeof(WCHAR)); + + MultiByteToWideChar(CP_ACP, 0, cstr, -1, buffer, len); + + return WString(buffer); + } + + /* Converts this object into a String object. This allows you to write the following code: + * String foo = "bar " + ObjectPtr(obj); + * Where ObjectPtr is a subclass/typedef of this Format class. + */ + operator String() const + { + if (IsDMLEnabled() && mDml != Output::DML_None) + { + const int len = GetDMLColWidth(mDml, 0); + char *buffer = (char*)alloca(len); + + BuildDMLCol(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml, false, 0); + return buffer; + } + else + { + char buffer[64]; + if (mFormat == Formats::Default || mFormat == Formats::Pointer) + { + sprintf_s(buffer, _countof(buffer), "%p", (int *)(SIZE_T)mValue); + ConvertToLower(buffer, _countof(buffer)); + } + else + { + const char *format = NULL; + if (mFormat == Formats::PrefixHex) + format = "0x%x"; + else if (mFormat == Formats::Hex) + format = "%x"; + else if (mFormat == Formats::Decimal) + format = "%d"; + + sprintf_s(buffer, _countof(buffer), format, (__int32)mValue); + ConvertToLower(buffer, _countof(buffer)); + } + + return buffer; + } + } + + private: + int GetPrecision() const + { + if (mFormat == Formats::Hex || mFormat == Formats::PrefixHex) + { + ULONGLONG val = mValue; + int count = 0; + while (val) + { + val >>= 4; + count++; + } + + if (count == 0) + count = 1; + + return count; + } + + else if (mFormat == Formats::Decimal) + { + T val = mValue; + int count = (val > 0) ? 0 : 1; + while (val) + { + val /= 10; + count++; + } + + return count; + } + + // mFormat == Formats::Pointer + return sizeof(int*)*2; + } + + static inline void BuildDML(__out_ecount(len) char *result, int len, CLRDATA_ADDRESS value, Formats::Format format, Output::FormatType dmlType) + { + BuildDMLCol(result, len, value, format, dmlType, true, 0); + } + + static int GetDMLWidth(Output::FormatType dmlType) + { + return GetDMLColWidth(dmlType, 0); + } + + static void BuildDMLCol(__out_ecount(len) char *result, int len, CLRDATA_ADDRESS value, Formats::Format format, Output::FormatType dmlType, bool leftAlign, int width) + { + char hex[64]; + int count = GetHex(value, hex, _countof(hex), format != Formats::Hex); + int i = 0; + + if (!leftAlign) + { + for (; i < width - count; ++i) + result[i] = ' '; + + result[i] = 0; + } + + int written = sprintf_s(result+i, len - i, DMLFormats[dmlType], hex, hex); + + SOS_Assert(written != -1); + if (written != -1) + { + for (i = i + written; i < width; ++i) + result[i] = ' '; + + result[i] = 0; + } + } + + static int GetDMLColWidth(Output::FormatType dmlType, int width) + { + return 1 + 4*sizeof(int*) + (int)strlen(DMLFormats[dmlType]) + width; + } + + private: + T mValue; + Formats::Format mFormat; + Output::FormatType mDml; + }; + + /* Format class used for strings. + */ + template <> + class Format<const char *> + { + public: + Format(const char *value) + : mValue(value) + { + } + + Format(const Format<const char *> &rhs) + : mValue(rhs.mValue) + { + } + + void Output() const + { + if (IsDMLEnabled()) + DMLOut("%s", mValue); + else + ExtOut("%s", mValue); + } + + void OutputColumn(Alignment align, int width) const + { + int precision = (int)strlen(mValue); + + if (precision > width) + precision = width; + + const char *format = align == AlignLeft ? "%-*.*s" : "%*.*s"; + + if (IsDMLEnabled()) + DMLOut(format, width, precision, mValue); + else + ExtOut(format, width, precision, mValue); + } + + private: + const char *mValue; + }; + + /* Format class for wide char strings. + */ + template <> + class Format<const WCHAR *> + { + public: + Format(const WCHAR *value) + : mValue(value) + { + } + + Format(const Format<const WCHAR *> &rhs) + : mValue(rhs.mValue) + { + } + + void Output() const + { + if (IsDMLEnabled()) + DMLOut("%S", mValue); + else + ExtOut("%S", mValue); + } + + void OutputColumn(Alignment align, int width) const + { + int precision = (int)_wcslen(mValue); + if (precision > width) + precision = width; + + const char *format = align == AlignLeft ? "%-*.*S" : "%*.*S"; + + if (IsDMLEnabled()) + DMLOut(format, width, precision, mValue); + else + ExtOut(format, width, precision, mValue); + } + + private: + const WCHAR *mValue; + }; + + + template <class T> + void InternalPrint(const T &t) + { + Format<T>(t).Output(); + } + + template <class T> + void InternalPrint(const Format<T> &t) + { + t.Output(); + } + + inline void InternalPrint(const char t[]) + { + Format<const char *>(t).Output(); + } +} + +#define DefineFormatClass(name, format, dml) \ + template <class T> \ + Output::Format<T> name(T value) \ + { return Output::Format<T>(value, format, dml); } + +DefineFormatClass(EEClassPtr, Formats::Pointer, Output::DML_EEClass); +DefineFormatClass(ObjectPtr, Formats::Pointer, Output::DML_Object); +DefineFormatClass(ExceptionPtr, Formats::Pointer, Output::DML_PrintException); +DefineFormatClass(ModulePtr, Formats::Pointer, Output::DML_Module); +DefineFormatClass(MethodDescPtr, Formats::Pointer, Output::DML_MethodDesc); +DefineFormatClass(AppDomainPtr, Formats::Pointer, Output::DML_Domain); +DefineFormatClass(ThreadState, Formats::Hex, Output::DML_ThreadState); +DefineFormatClass(ThreadID, Formats::Hex, Output::DML_ThreadID); +DefineFormatClass(RCWrapper, Formats::Pointer, Output::DML_RCWrapper); +DefineFormatClass(CCWrapper, Formats::Pointer, Output::DML_CCWrapper); +DefineFormatClass(InstructionPtr, Formats::Pointer, Output::DML_IP); +DefineFormatClass(NativePtr, Formats::Pointer, Output::DML_None); + +DefineFormatClass(Decimal, Formats::Decimal, Output::DML_None); +DefineFormatClass(Pointer, Formats::Pointer, Output::DML_None); +DefineFormatClass(PrefixHex, Formats::PrefixHex, Output::DML_None); +DefineFormatClass(Hex, Formats::Hex, Output::DML_None); + +#undef DefineFormatClass + +template <class T0> +void Print(const T0 &val0) +{ + Output::InternalPrint(val0); +} + +template <class T0, class T1> +void Print(const T0 &val0, const T1 &val1) +{ + Output::InternalPrint(val0); + Output::InternalPrint(val1); +} + +template <class T0> +void PrintLn(const T0 &val0) +{ + Output::InternalPrint(val0); + ExtOut("\n"); +} + +template <class T0, class T1> +void PrintLn(const T0 &val0, const T1 &val1) +{ + Output::InternalPrint(val0); + Output::InternalPrint(val1); + ExtOut("\n"); +} + +template <class T0, class T1, class T2> +void PrintLn(const T0 &val0, const T1 &val1, const T2 &val2) +{ + Output::InternalPrint(val0); + Output::InternalPrint(val1); + Output::InternalPrint(val2); + ExtOut("\n"); +} + + +/* This class handles the formatting for output which is in a table format. To use this class you define + * how the table is formatted by setting the number of columns in the table, the default column width, + * the default column alignment, the indentation (whitespace) for the table, and the amount of padding + * (whitespace) between each column. Once this has been setup, you output rows at a time or individual + * columns to build the output instead of manually tabbing out space. + * Also note that this class was built to work with the Format class. When outputing data, use the + * predefined output types to specify the format (such as ObjectPtr, MethodDescPtr, Decimal, etc). This + * tells the TableOutput class how to display the data, and where applicable, it automatically generates + * the appropriate DML output. See the DefineFormatClass macro. + */ +class TableOutput +{ +public: + + TableOutput() + : mColumns(0), mDefaultWidth(0), mIndent(0), mPadding(0), mCurrCol(0), mDefaultAlign(AlignLeft), + mWidths(0), mAlignments(0) + { + } + /* Constructor. + * Params: + * numColumns - the number of columns the table has + * defaultColumnWidth - the default width of each column + * alignmentDefault - whether columns are by default left aligned or right aligned + * indent - the amount of whitespace to prefix at the start of the row (in characters) + * padding - the amount of whitespace to place between each column (in characters) + */ + TableOutput(int numColumns, int defaultColumnWidth, Alignment alignmentDefault = AlignLeft, int indent = 0, int padding = 1) + : mColumns(numColumns), mDefaultWidth(defaultColumnWidth), mIndent(indent), mPadding(padding), mCurrCol(0), mDefaultAlign(alignmentDefault), + mWidths(0), mAlignments(0) + { + } + + ~TableOutput() + { + Clear(); + } + + /* See the documentation for the constructor. + */ + void ReInit(int numColumns, int defaultColumnWidth, Alignment alignmentDefault = AlignLeft, int indent = 0, int padding = 1); + + /* Sets the amount of whitespace to prefix at the start of the row (in characters). + */ + void SetIndent(int indent) + { + SOS_Assert(indent >= 0); + + mIndent = indent; + } + + /* Sets the exact widths for the the given columns. + * Params: + * columns - the number of columns you are providing the width for, starting at the first column + * ... - an int32 for each column (given by the number of columns in the first parameter). + * Example: + * If you have 5 columns in the table, you can set their widths like so: + * tableOutput.SetWidths(5, 2, 3, 5, 7, 13); + * Note: + * It's fine to pass a value for "columns" less than the number of columns in the table. This + * is useful when you set the default column width to be correct for most of the table, and need + * to make a minor adjustment to a few. + */ + void SetWidths(int columns, ...); + + /* Individually sets a column to the given width. + * Params: + * col - the column to set, 0 indexed + * width - the width of the column (note this must be non-negative) + */ + void SetColWidth(int col, int width); + + /* Individually sets the column alignment. + * Params: + * col - the column to set, 0 indexed + * align - the new alignment (left or right) for the column + */ + void SetColAlignment(int col, Alignment align); + + + /* The WriteRow family of functions allows you to write an entire row of the table at once. + * The common use case for the TableOutput class is to individually output each column after + * calculating what the value should contain. However, this would be tedious if you already + * knew the contents of the entire row which usually happenes when you are printing out the + * header for the table. To use this, simply pass each column as an individual parameter, + * for example: + * tableOutput.WriteRow("First Column", "Second Column", Decimal(3), PrefixHex(4), "Fifth Column"); + */ + template <class T0, class T1> + void WriteRow(T0 t0, T1 t1) + { + WriteColumn(0, t0); + WriteColumn(1, t1); + } + + template <class T0, class T1, class T2> + void WriteRow(T0 t0, T1 t1, T2 t2) + { + WriteColumn(0, t0); + WriteColumn(1, t1); + WriteColumn(2, t2); + } + + + template <class T0, class T1, class T2, class T3> + void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3) + { + WriteColumn(0, t0); + WriteColumn(1, t1); + WriteColumn(2, t2); + WriteColumn(3, t3); + } + + + template <class T0, class T1, class T2, class T3, class T4> + void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4) + { + WriteColumn(0, t0); + WriteColumn(1, t1); + WriteColumn(2, t2); + WriteColumn(3, t3); + WriteColumn(4, t4); + } + + template <class T0, class T1, class T2, class T3, class T4, class T5> + void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) + { + WriteColumn(0, t0); + WriteColumn(1, t1); + WriteColumn(2, t2); + WriteColumn(3, t3); + WriteColumn(4, t4); + WriteColumn(5, t5); + } + + template <class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9> + void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) + { + WriteColumn(0, t0); + WriteColumn(1, t1); + WriteColumn(2, t2); + WriteColumn(3, t3); + WriteColumn(4, t4); + WriteColumn(5, t5); + WriteColumn(6, t6); + WriteColumn(7, t7); + WriteColumn(8, t8); + WriteColumn(9, t9); + } + + /* The WriteColumn family of functions is used to output individual columns in the table. + * The intent is that the bulk of the table will be generated in a loop like so: + * while (condition) { + * int value1 = CalculateFirstColumn(); + * table.WriteColumn(0, value1); + * + * String value2 = CalculateSecondColumn(); + * table.WriteColumn(1, value2); + * } + * Params: + * col - the column to write, 0 indexed + * t - the value to write + * Note: + * You should generally use the specific instances of the Format class to generate output. + * For example, use the "Decimal", "Pointer", "ObjectPtr", etc. When passing data to this + * function. This tells the Table class how to display the value. + */ + template <class T> + void WriteColumn(int col, const Output::Format<T> &t) + { + SOS_Assert(col >= 0); + SOS_Assert(col < mColumns); + + if (col != mCurrCol) + OutputBlankColumns(col); + + if (col == 0) + OutputIndent(); + + bool lastCol = col == mColumns - 1; + + if (!lastCol) + t.OutputColumn(GetColAlign(col), GetColumnWidth(col)); + else + t.Output(); + + ExtOut(lastCol ? "\n" : GetWhitespace(mPadding)); + + if (lastCol) + mCurrCol = 0; + else + mCurrCol = col+1; + } + + template <class T> + void WriteColumn(int col, T t) + { + WriteColumn(col, Output::Format<T>(t)); + } + + void WriteColumn(int col, const String &str) + { + WriteColumn(col, Output::Format<const char *>(str)); + } + + void WriteColumn(int col, const WString &str) + { + WriteColumn(col, Output::Format<const WCHAR *>(str)); + } + + void WriteColumn(int col, __in_z WCHAR *str) + { + WriteColumn(col, Output::Format<const WCHAR *>(str)); + } + + void WriteColumn(int col, const WCHAR *str) + { + WriteColumn(col, Output::Format<const WCHAR *>(str)); + } + + inline void WriteColumn(int col, __in_z char *str) + { + WriteColumn(col, Output::Format<const char *>(str)); + } + + /* Writes a column using a printf style format. You cannot use the Format class with + * this function to specify how the output should look, use printf style formatting + * with the appropriate parameters instead. + */ + void WriteColumnFormat(int col, const char *fmt, ...) + { + SOS_Assert(strstr(fmt, "%S") == NULL); + + char result[128]; + + va_list list; + va_start(list, fmt); + vsprintf_s(result, _countof(result), fmt, list); + va_end(list); + + WriteColumn(col, result); + } + + void WriteColumnFormat(int col, const WCHAR *fmt, ...) + { + WCHAR result[128]; + + va_list list; + va_start(list, fmt); + vswprintf_s(result, _countof(result), fmt, list); + va_end(list); + + WriteColumn(col, result); + } + + /* This function is a shortcut for writing the next column. (That is, the one after the + * one you just wrote.) + */ + template <class T> + void WriteColumn(T t) + { + WriteColumn(mCurrCol, t); + } + +private: + void Clear(); + void AllocWidths(); + int GetColumnWidth(int col); + Alignment GetColAlign(int col); + const char *GetWhitespace(int amount); + void OutputBlankColumns(int col); + void OutputIndent(); + +private: + int mColumns, mDefaultWidth, mIndent, mPadding, mCurrCol; + Alignment mDefaultAlign; + int *mWidths; + Alignment *mAlignments; +}; + +HRESULT GetMethodDefinitionsFromName(DWORD_PTR ModulePtr, IXCLRDataModule* mod, const char* name, IXCLRDataMethodDefinition **ppMethodDefinitions, int numMethods, int *numMethodsNeeded); +HRESULT GetMethodDescsFromName(DWORD_PTR ModulePtr, IXCLRDataModule* mod, const char* name, DWORD_PTR **pOut, int *numMethodDescs); + +HRESULT FileNameForModule (DacpModuleData *pModule, __out_ecount (MAX_LONGPATH) WCHAR *fileName); +HRESULT FileNameForModule (DWORD_PTR pModuleAddr, __out_ecount (MAX_LONGPATH) WCHAR *fileName); +void IP2MethodDesc (DWORD_PTR IP, DWORD_PTR &methodDesc, JITTypes &jitType, + DWORD_PTR &gcinfoAddr); +const char *ElementTypeName (unsigned type); +void DisplayFields (CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodTableFieldData *pMTFD, + DWORD_PTR dwStartAddr = 0, BOOL bFirst=TRUE, BOOL bValueClass=FALSE); +int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE); +int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE); + +BOOL IsValidToken(DWORD_PTR ModuleAddr, mdTypeDef mb); +void NameForToken_s(DacpModuleData *pModule, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName, + bool bClassName=true); +void NameForToken_s(DWORD_PTR ModuleAddr, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName, + bool bClassName=true); +HRESULT NameForToken_s(mdTypeDef mb, IMetaDataImport *pImport, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName, + bool bClassName); +HRESULT NameForTokenNew_s(mdTypeDef mb, IMDInternalImport *pImport, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName, + bool bClassName); + +void vmmap(); +void vmstat(); + +#ifndef FEATURE_PAL +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Support for managed stack tracing +// + +DWORD_PTR GetDebuggerJitInfo(DWORD_PTR md); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#endif // FEATURE_PAL + +template <typename SCALAR> +inline +int bitidx(SCALAR bitflag) +{ + for (int idx = 0; idx < static_cast<int>(sizeof(bitflag))*8; ++idx) + { + if (bitflag & (1 << idx)) + { + _ASSERTE((bitflag & (~(1 << idx))) == 0); + return idx; + } + } + return -1; +} + +HRESULT +DllsName( + ULONG_PTR addrContaining, + __out_ecount (MAX_LONGPATH) WCHAR *dllName + ); + +inline +BOOL IsElementValueType (CorElementType cet) +{ + return (cet >= ELEMENT_TYPE_BOOLEAN && cet <= ELEMENT_TYPE_R8) + || cet == ELEMENT_TYPE_VALUETYPE || cet == ELEMENT_TYPE_I || cet == ELEMENT_TYPE_U; +} + + +#define safemove(dst, src) \ +SafeReadMemory (TO_TADDR(src), &(dst), sizeof(dst), NULL) + +extern "C" PDEBUG_DATA_SPACES g_ExtData; + +template <class T> +class ArrayHolder +{ +public: + ArrayHolder(T *ptr) + : mPtr(ptr) + { + } + + ~ArrayHolder() + { + Clear(); + } + + ArrayHolder(const ArrayHolder &rhs) + { + mPtr = const_cast<ArrayHolder *>(&rhs)->Detach(); + } + + ArrayHolder &operator=(T *ptr) + { + Clear(); + mPtr = ptr; + return *this; + } + + const T &operator[](int i) const + { + return mPtr[i]; + } + + T &operator[](int i) + { + return mPtr[i]; + } + + operator const T *() const + { + return mPtr; + } + + operator T *() + { + return mPtr; + } + + T **operator&() + { + return &mPtr; + } + + T *GetPtr() + { + return mPtr; + } + + T *Detach() + { + T *ret = mPtr; + mPtr = NULL; + return ret; + } + +private: + void Clear() + { + if (mPtr) + { + delete [] mPtr; + mPtr = NULL; + } + } + +private: + T *mPtr; +}; + + + +// This class acts a smart pointer which calls the Release method on any object +// you place in it when the ToRelease class falls out of scope. You may use it +// just like you would a standard pointer to a COM object (including if (foo), +// if (!foo), if (foo == 0), etc) except for two caveats: +// 1. This class never calls AddRef and it always calls Release when it +// goes out of scope. +// 2. You should never use & to try to get a pointer to a pointer unless +// you call Release first, or you will leak whatever this object contains +// prior to updating its internal pointer. +template<class T> +class ToRelease +{ +public: + ToRelease() + : m_ptr(NULL) + {} + + ToRelease(T* ptr) + : m_ptr(ptr) + {} + + ~ToRelease() + { + Release(); + } + + void operator=(T *ptr) + { + Release(); + + m_ptr = ptr; + } + + T* operator->() + { + return m_ptr; + } + + operator T*() + { + return m_ptr; + } + + T** operator&() + { + return &m_ptr; + } + + T* GetPtr() const + { + return m_ptr; + } + + T* Detach() + { + T* pT = m_ptr; + m_ptr = NULL; + return pT; + } + + void Release() + { + if (m_ptr != NULL) + { + m_ptr->Release(); + m_ptr = NULL; + } + } + +private: + T* m_ptr; +}; + +struct ModuleInfo +{ + ULONG64 baseAddr; + ULONG64 size; + BOOL hasPdb; +}; +extern ModuleInfo moduleInfo[]; + +BOOL InitializeHeapData(); +BOOL IsServerBuild (); +UINT GetMaxGeneration(); +UINT GetGcHeapCount(); +BOOL GetGcStructuresValid(); + +ULONG GetILSize(DWORD_PTR ilAddr); // REturns 0 if error occurs +HRESULT DecodeILFromAddress(IMetaDataImport *pImport, TADDR ilAddr); +void DecodeIL(IMetaDataImport *pImport, BYTE *buffer, ULONG bufSize); +void DecodeDynamicIL(BYTE *data, ULONG Size, DacpObjectData& tokenArray); + +BOOL IsRetailBuild (size_t base); +EEFLAVOR GetEEFlavor (); +HRESULT InitCorDebugInterface(); +VOID UninitCorDebugInterface(); +#ifndef FEATURE_PAL +BOOL GetEEVersion(VS_FIXEDFILEINFO *pFileInfo); +BOOL GetSOSVersion(VS_FIXEDFILEINFO *pFileInfo); +#endif + +BOOL IsDumpFile (); + +// IsMiniDumpFile will return true if 1) we are in +// a small format minidump, and g_InMinidumpSafeMode is true. +extern BOOL g_InMinidumpSafeMode; + +BOOL IsMiniDumpFile(); +void ReportOOM(); + +BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead); +#if !defined(_TARGET_WIN64_) && !defined(_ARM64_) +// on 64-bit platforms TADDR and CLRDATA_ADDRESS are identical +inline BOOL SafeReadMemory (CLRDATA_ADDRESS offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead) +{ return SafeReadMemory(TO_TADDR(offset), lpBuffer, cb, lpcbBytesRead); } +#endif + +BOOL NameForMD_s (DWORD_PTR pMD, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName); +BOOL NameForMT_s (DWORD_PTR MTAddr, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName); + +WCHAR *CreateMethodTableName(TADDR mt, TADDR cmt = NULL); + +void isRetAddr(DWORD_PTR retAddr, DWORD_PTR* whereCalled); +DWORD_PTR GetValueFromExpression (___in __in_z const char *const str); + +enum ModuleHeapType +{ + ModuleHeapType_ThunkHeap, + ModuleHeapType_LookupTableHeap +}; + +HRESULT PrintDomainHeapInfo(const char *name, CLRDATA_ADDRESS adPtr, DWORD_PTR *size, DWORD_PTR *wasted = 0); +DWORD_PTR PrintModuleHeapInfo(DWORD_PTR *moduleList, int count, ModuleHeapType type, DWORD_PTR *wasted = 0); +void PrintHeapSize(DWORD_PTR total, DWORD_PTR wasted); +void DomainInfo(DacpAppDomainData *pDomain); +void AssemblyInfo(DacpAssemblyData *pAssembly); +DWORD_PTR LoaderHeapInfo(CLRDATA_ADDRESS pLoaderHeapAddr, DWORD_PTR *wasted = 0); +DWORD_PTR JitHeapInfo(); +DWORD_PTR VSDHeapInfo(CLRDATA_ADDRESS appDomain, DWORD_PTR *wasted = 0); + +DWORD GetNumComponents(TADDR obj); + +struct GenUsageStat +{ + size_t allocd; + size_t freed; + size_t unrooted; +}; + +struct HeapUsageStat +{ + GenUsageStat genUsage[4]; // gen0, 1, 2, LOH +}; + +extern DacpUsefulGlobalsData g_special_usefulGlobals; +BOOL GCHeapUsageStats(const DacpGcHeapDetails& heap, BOOL bIncUnreachable, HeapUsageStat *hpUsage); + +class HeapStat +{ +protected: + struct Node + { + DWORD_PTR data; + DWORD count; + size_t totalSize; + Node* left; + Node* right; + Node () + : data(0), count(0), totalSize(0), left(NULL), right(NULL) + { + } + }; + BOOL bHasStrings; + Node *head; + BOOL fLinear; +public: + HeapStat () + : bHasStrings(FALSE), head(NULL), fLinear(FALSE) + {} + ~HeapStat() + { + Delete(); + } + // TODO: Change the aSize argument to size_t when we start supporting + // TODO: object sizes above 4GB + void Add (DWORD_PTR aData, DWORD aSize); + void Sort (); + void Print (const char* label = NULL); + void Delete (); + void HasStrings(BOOL abHasStrings) + { + bHasStrings = abHasStrings; + } +private: + int CompareData(DWORD_PTR n1, DWORD_PTR n2); + void SortAdd (Node *&root, Node *entry); + void LinearAdd (Node *&root, Node *entry); + void ReverseLeftMost (Node *root); + void Linearize(); +}; + +class CGCDesc; + +// The information MethodTableCache returns. +struct MethodTableInfo +{ + bool IsInitialized() { return BaseSize != 0; } + + DWORD BaseSize; // Caching BaseSize and ComponentSize for a MethodTable + DWORD ComponentSize; // here has HUGE perf benefits in heap traversals. + BOOL bContainsPointers; + DWORD_PTR* GCInfoBuffer; // Start of memory of GC info + CGCDesc* GCInfo; // Just past GC info (which is how it is stored) + bool ArrayOfVC; +}; + +class MethodTableCache +{ +protected: + + struct Node + { + DWORD_PTR data; // This is the key (the method table pointer) + MethodTableInfo info; // The info associated with this MethodTable + Node* left; + Node* right; + Node (DWORD_PTR aData) : data(aData), left(NULL), right(NULL) + { + info.BaseSize = 0; + info.ComponentSize = 0; + info.bContainsPointers = false; + info.GCInfo = NULL; + info.ArrayOfVC = false; + info.GCInfoBuffer = NULL; + } + }; + Node *head; +public: + MethodTableCache () + : head(NULL) + {} + ~MethodTableCache() { Clear(); } + + // Always succeeds, if it is not present it adds an empty Info struct and returns that + // Thus you must call 'IsInitialized' on the returned value before using it + MethodTableInfo* Lookup(DWORD_PTR aData); + + void Clear (); +private: + int CompareData(DWORD_PTR n1, DWORD_PTR n2); + void ReverseLeftMost (Node *root); +}; + +extern MethodTableCache g_special_mtCache; + +struct DumpArrayFlags +{ + DWORD_PTR startIndex; + DWORD_PTR Length; + BOOL bDetail; + LPSTR strObject; + BOOL bNoFieldsForElement; + + DumpArrayFlags () + : startIndex(0), Length((DWORD_PTR)-1), bDetail(FALSE), strObject (0), bNoFieldsForElement(FALSE) + {} + ~DumpArrayFlags () + { + if (strObject) + delete [] strObject; + } +}; //DumpArrayFlags + + + +// ----------------------------------------------------------------------- + +#define BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX 0x08000000 +#define BIT_SBLK_FINALIZER_RUN 0x40000000 +#define BIT_SBLK_SPIN_LOCK 0x10000000 +#define SBLK_MASK_LOCK_THREADID 0x000003FF // special value of 0 + 1023 thread ids +#define SBLK_MASK_LOCK_RECLEVEL 0x0000FC00 // 64 recursion levels +#define SBLK_APPDOMAIN_SHIFT 16 // shift right this much to get appdomain index +#define SBLK_MASK_APPDOMAININDEX 0x000007FF // 2048 appdomain indices +#define SBLK_RECLEVEL_SHIFT 10 // shift right this much to get recursion level +#define BIT_SBLK_IS_HASHCODE 0x04000000 +#define MASK_HASHCODE ((1<<HASHCODE_BITS)-1) +#define SYNCBLOCKINDEX_BITS 26 +#define MASK_SYNCBLOCKINDEX ((1<<SYNCBLOCKINDEX_BITS)-1) + +HRESULT GetMTOfObject(TADDR obj, TADDR *mt); + +struct needed_alloc_context +{ + BYTE* alloc_ptr; // starting point for next allocation + BYTE* alloc_limit; // ending point for allocation region/quantum +}; + +struct AllocInfo +{ + needed_alloc_context *array; + int num; // number of allocation contexts in array + + AllocInfo() + : array(NULL) + , num(0) + {} + void Init() + { + extern void GetAllocContextPtrs(AllocInfo *pallocInfo); + GetAllocContextPtrs(this); + } + ~AllocInfo() + { + if (array != NULL) + delete[] array; + } +}; + +struct GCHandleStatistics +{ + HeapStat hs; + + DWORD strongHandleCount; + DWORD pinnedHandleCount; + DWORD asyncPinnedHandleCount; + DWORD refCntHandleCount; + DWORD weakLongHandleCount; + DWORD weakShortHandleCount; + DWORD variableCount; + DWORD sizedRefCount; + DWORD dependentCount; + DWORD weakWinRTHandleCount; + DWORD unknownHandleCount; + GCHandleStatistics() + : strongHandleCount(0), pinnedHandleCount(0), asyncPinnedHandleCount(0), refCntHandleCount(0), + weakLongHandleCount(0), weakShortHandleCount(0), variableCount(0), sizedRefCount(0), + dependentCount(0), weakWinRTHandleCount(0), unknownHandleCount(0) + {} + ~GCHandleStatistics() + { + hs.Delete(); + } +}; + +struct SegmentLookup +{ + DacpHeapSegmentData *m_segments; + int m_iSegmentsSize; + int m_iSegmentCount; + + SegmentLookup(); + ~SegmentLookup(); + + void Clear(); + BOOL AddSegment(DacpHeapSegmentData *pData); + CLRDATA_ADDRESS GetHeap(CLRDATA_ADDRESS object, BOOL& bFound); +}; + +class GCHeapSnapshot +{ +private: + BOOL m_isBuilt; + DacpGcHeapDetails *m_heapDetails; + DacpGcHeapData m_gcheap; + SegmentLookup m_segments; + + BOOL AddSegments(DacpGcHeapDetails& details); +public: + GCHeapSnapshot(); + + BOOL Build(); + void Clear(); + BOOL IsBuilt() { return m_isBuilt; } + + DacpGcHeapData *GetHeapData() { return &m_gcheap; } + + int GetHeapCount() { return m_gcheap.HeapCount; } + + DacpGcHeapDetails *GetHeap(CLRDATA_ADDRESS objectPointer); + int GetGeneration(CLRDATA_ADDRESS objectPointer); + + +}; +extern GCHeapSnapshot g_snapshot; + +BOOL IsSameModuleName (const char *str1, const char *str2); +BOOL IsModule (DWORD_PTR moduleAddr); +BOOL IsMethodDesc (DWORD_PTR value); +BOOL IsMethodTable (DWORD_PTR value); +BOOL IsStringObject (size_t obj); +BOOL IsObjectArray (DWORD_PTR objPointer); +BOOL IsObjectArray (DacpObjectData *pData); + +/* Returns a list of all modules in the process. + * Params: + * name - The name of the module you would like. If mName is NULL the all modules are returned. + * numModules - The number of modules in the array returned. + * Returns: + * An array of modules whose length is *numModules, NULL if an error occurred. Note that if this + * function succeeds but finds no modules matching the name given, this function returns a valid + * array, but *numModules will equal 0. + * Note: + * You must clean up the return value of this array by calling delete [] on it, or using the + * ArrayHolder class. + */ +DWORD_PTR *ModuleFromName(__in_opt LPSTR name, int *numModules); +void GetInfoFromName(DWORD_PTR ModuleAddr, const char* name); +void GetInfoFromModule (DWORD_PTR ModuleAddr, ULONG token, DWORD_PTR *ret=NULL); + + +typedef void (*VISITGCHEAPFUNC)(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token); +BOOL GCHeapsTraverse(VISITGCHEAPFUNC pFunc, LPVOID token, BOOL verify=true); + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct strobjInfo +{ + size_t methodTable; + DWORD m_StringLength; +}; + +// Just to make figuring out which fill pointer element matches a generation +// a bit less confusing. This gen_segment function is ported from gc.cpp. +inline unsigned int gen_segment (int gen) +{ + return (DAC_NUMBERGENERATIONS - gen - 1); +} + +inline CLRDATA_ADDRESS SegQueue(DacpGcHeapDetails& heapDetails, int seg) +{ + return heapDetails.finalization_fill_pointers[seg - 1]; +} + +inline CLRDATA_ADDRESS SegQueueLimit(DacpGcHeapDetails& heapDetails, int seg) +{ + return heapDetails.finalization_fill_pointers[seg]; +} + +#define FinalizerListSeg (DAC_NUMBERGENERATIONS+1) +#define CriticalFinalizerListSeg (DAC_NUMBERGENERATIONS) + +void GatherOneHeapFinalization(DacpGcHeapDetails& heapDetails, HeapStat *stat, BOOL bAllReady, BOOL bShort); + +CLRDATA_ADDRESS GetAppDomainForMT(CLRDATA_ADDRESS mtPtr); +CLRDATA_ADDRESS GetAppDomain(CLRDATA_ADDRESS objPtr); +void GCHeapInfo(const DacpGcHeapDetails &heap, DWORD_PTR &total_size); +BOOL GCObjInHeap(TADDR taddrObj, const DacpGcHeapDetails &heap, + TADDR_SEGINFO& trngSeg, int& gen, TADDR_RANGE& allocCtx, BOOL &bLarge); + +BOOL VerifyObject(const DacpGcHeapDetails &heap, const DacpHeapSegmentData &seg, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize, + BOOL bVerifyMember); +BOOL VerifyObject(const DacpGcHeapDetails &heap, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize, + BOOL bVerifyMember); + +BOOL IsMTForFreeObj(DWORD_PTR pMT); +void DumpStackObjectsHelper (TADDR StackTop, TADDR StackBottom, BOOL verifyFields); + + +enum ARGTYPE {COBOOL,COSIZE_T,COHEX,COSTRING}; +struct CMDOption +{ + const char* name; + void *vptr; + ARGTYPE type; + BOOL hasValue; + BOOL hasSeen; +}; +struct CMDValue +{ + void *vptr; + ARGTYPE type; +}; +BOOL GetCMDOption(const char *string, CMDOption *option, size_t nOption, + CMDValue *arg, size_t maxArg, size_t *nArg); + +void DumpMDInfo(DWORD_PTR dwStartAddr, CLRDATA_ADDRESS dwRequestedIP = 0, BOOL fStackTraceFormat = FALSE); +void DumpMDInfoFromMethodDescData(DacpMethodDescData * pMethodDescData, BOOL fStackTraceFormat); +void GetDomainList(DWORD_PTR *&domainList, int &numDomain); +HRESULT GetThreadList(DWORD_PTR **threadList, int *numThread); +CLRDATA_ADDRESS GetCurrentManagedThread(); // returns current managed thread if any +void GetAllocContextPtrs(AllocInfo *pallocInfo); + +void ReloadSymbolWithLineInfo(); + +size_t FunctionType (size_t EIP); + +size_t Align (size_t nbytes); +// Aligns large objects +size_t AlignLarge (size_t nbytes); + +ULONG OSPageSize (); +size_t NextOSPageAddress (size_t addr); + +// This version of objectsize reduces the lookup of methodtables in the DAC. +// It uses g_special_mtCache for it's work. +BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj, + DWORD_PTR dwAddrMethTable, BOOL bLarge, size_t& s, BOOL& bContainsPointers); + +// ObjSize now uses the methodtable cache for its work too. +size_t ObjectSize (DWORD_PTR obj, BOOL fIsLargeObject=FALSE); +size_t ObjectSize(DWORD_PTR obj, DWORD_PTR mt, BOOL fIsValueClass, BOOL fIsLargeObject=FALSE); + +void CharArrayContent(TADDR pos, ULONG num, bool widechar); +void StringObjectContent (size_t obj, BOOL fLiteral=FALSE, const int length=-1); // length=-1: dump everything in the string object. + +UINT FindAllPinnedAndStrong (DWORD_PTR handlearray[],UINT arraySize); +void PrintNotReachableInRange(TADDR rngStart, TADDR rngEnd, BOOL bExcludeReadyForFinalization, + HeapStat* stat, BOOL bShort); + +const char *EHTypeName(EHClauseType et); + +template<typename T> +inline const LPCSTR GetTransparency(const T &t) +{ + if (!t.bHasCriticalTransparentInfo) + { + return "Not calculated"; + } + else if (t.bIsCritical && !t.bIsTreatAsSafe) + { + return "Critical"; + } + else if (t.bIsCritical) + { + return "Safe critical"; + } + else + { + return "Transparent"; + } +} + +struct StringHolder +{ + LPSTR data; + StringHolder() : data(NULL) { } + ~StringHolder() { if(data) delete [] data; } +}; + + +ULONG DebuggeeType(); + +inline BOOL IsKernelDebugger () +{ + return DebuggeeType() == DEBUG_CLASS_KERNEL; +} + +void ResetGlobals(void); +HRESULT LoadClrDebugDll(void); +extern "C" void UnloadClrDebugDll(void); + +extern IMetaDataImport* MDImportForModule (DacpModuleData *pModule); +extern IMetaDataImport* MDImportForModule (DWORD_PTR pModule); + +//***************************************************************************** +// +// **** CQuickBytes +// This helper class is useful for cases where 90% of the time you allocate 512 +// or less bytes for a data structure. This class contains a 512 byte buffer. +// Alloc() will return a pointer to this buffer if your allocation is small +// enough, otherwise it asks the heap for a larger buffer which is freed for +// you. No mutex locking is required for the small allocation case, making the +// code run faster, less heap fragmentation, etc... Each instance will allocate +// 520 bytes, so use accordinly. +// +//***************************************************************************** +template <DWORD SIZE, DWORD INCREMENT> +class CQuickBytesBase +{ +public: + CQuickBytesBase() : + pbBuff(0), + iSize(0), + cbTotal(SIZE) + { } + + void Destroy() + { + if (pbBuff) + { + delete[] (BYTE*)pbBuff; + pbBuff = 0; + } + } + + void *Alloc(SIZE_T iItems) + { + iSize = iItems; + if (iItems <= SIZE) + { + cbTotal = SIZE; + return (&rgData[0]); + } + else + { + if (pbBuff) + delete[] (BYTE*)pbBuff; + pbBuff = new BYTE[iItems]; + cbTotal = pbBuff ? iItems : 0; + return (pbBuff); + } + } + + // This is for conformity to the CQuickBytesBase that is defined by the runtime so + // that we can use it inside of some GC code that SOS seems to include as well. + // + // The plain vanilla "Alloc" version on this CQuickBytesBase doesn't throw either, + // so we'll just forward the call. + void *AllocNoThrow(SIZE_T iItems) + { + return Alloc(iItems); + } + + HRESULT ReSize(SIZE_T iItems) + { + void *pbBuffNew; + if (iItems <= cbTotal) + { + iSize = iItems; + return NOERROR; + } + + pbBuffNew = new BYTE[iItems + INCREMENT]; + if (!pbBuffNew) + return E_OUTOFMEMORY; + if (pbBuff) + { + memcpy(pbBuffNew, pbBuff, cbTotal); + delete[] (BYTE*)pbBuff; + } + else + { + _ASSERTE(cbTotal == SIZE); + memcpy(pbBuffNew, rgData, SIZE); + } + cbTotal = iItems + INCREMENT; + iSize = iItems; + pbBuff = pbBuffNew; + return NOERROR; + + } + + operator PVOID() + { return ((pbBuff) ? pbBuff : &rgData[0]); } + + void *Ptr() + { return ((pbBuff) ? pbBuff : &rgData[0]); } + + SIZE_T Size() + { return (iSize); } + + SIZE_T MaxSize() + { return (cbTotal); } + + void *pbBuff; + SIZE_T iSize; // number of bytes used + SIZE_T cbTotal; // total bytes allocated in the buffer + // use UINT64 to enforce the alignment of the memory + UINT64 rgData[(SIZE+sizeof(UINT64)-1)/sizeof(UINT64)]; +}; + +#define CQUICKBYTES_BASE_SIZE 512 +#define CQUICKBYTES_INCREMENTAL_SIZE 128 + +class CQuickBytesNoDtor : public CQuickBytesBase<CQUICKBYTES_BASE_SIZE, CQUICKBYTES_INCREMENTAL_SIZE> +{ +}; + +class CQuickBytes : public CQuickBytesNoDtor +{ +public: + CQuickBytes() { } + + ~CQuickBytes() + { + Destroy(); + } +}; + +template <DWORD CQUICKBYTES_BASE_SPECIFY_SIZE> +class CQuickBytesNoDtorSpecifySize : public CQuickBytesBase<CQUICKBYTES_BASE_SPECIFY_SIZE, CQUICKBYTES_INCREMENTAL_SIZE> +{ +}; + +template <DWORD CQUICKBYTES_BASE_SPECIFY_SIZE> +class CQuickBytesSpecifySize : public CQuickBytesNoDtorSpecifySize<CQUICKBYTES_BASE_SPECIFY_SIZE> +{ +public: + CQuickBytesSpecifySize() { } + + ~CQuickBytesSpecifySize() + { + CQuickBytesNoDtorSpecifySize<CQUICKBYTES_BASE_SPECIFY_SIZE>::Destroy(); + } +}; + + +#define STRING_SIZE 10 +class CQuickString : public CQuickBytesBase<STRING_SIZE, STRING_SIZE> +{ +public: + CQuickString() { } + + ~CQuickString() + { + Destroy(); + } + + void *Alloc(SIZE_T iItems) + { + return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::Alloc(iItems*sizeof(WCHAR)); + } + + HRESULT ReSize(SIZE_T iItems) + { + return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::ReSize(iItems * sizeof(WCHAR)); + } + + SIZE_T Size() + { + return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::Size() / sizeof(WCHAR); + } + + SIZE_T MaxSize() + { + return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::MaxSize() / sizeof(WCHAR); + } + + WCHAR* String() + { + return (WCHAR*) Ptr(); + } + +}; + +enum GetSignatureStringResults +{ + GSS_SUCCESS, + GSS_ERROR, + GSS_INSUFFICIENT_DATA, +}; + +GetSignatureStringResults GetMethodSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString); +GetSignatureStringResults GetSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString); +void GetMethodName(mdMethodDef methodDef, IMetaDataImport * pImport, CQuickBytes *fullName); + +#ifndef _TARGET_WIN64_ +#define itoa_s_ptr _itoa_s +#define itow_s_ptr _itow_s +#define itoa_ptr _itoa +#define itow_ptr _itow +#else +#define itoa_s_ptr _i64toa_s +#define itow_s_ptr _i64tow_s +#define itoa_ptr _i64toa +#define itow_ptr _i64tow +#endif + +#ifdef FEATURE_PAL +extern "C" +int _itoa_s( int inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); +extern "C" +int _ui64toa_s( unsigned __int64 inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); +#endif // FEATURE_PAL + +struct MemRange +{ + MemRange (ULONG64 s = NULL, size_t l = 0, MemRange * n = NULL) + : start(s), len (l), next (n) + {} + + bool InRange (ULONG64 addr) + { + return addr >= start && addr < start + len; + } + + ULONG64 start; + size_t len; + MemRange * next; +}; //struct MemRange + +#ifndef FEATURE_PAL + +class StressLogMem +{ +private: + // use a linked list for now, could be optimazied later + MemRange * list; + + void AddRange (ULONG64 s, size_t l) + { + list = new MemRange (s, l, list); + } + +public: + StressLogMem () : list (NULL) + {} + ~StressLogMem (); + bool Init (ULONG64 stressLogAddr, IDebugDataSpaces* memCallBack); + bool IsInStressLog (ULONG64 addr); +}; //class StressLogMem + +// An adapter class that DIA consumes so that it can read PE data from the an image +// This implementation gets the backing data from the image loaded in debuggee memory +// that has been layed out identical to the disk format (ie not seperated by section) +class PEOffsetMemoryReader : IDiaReadExeAtOffsetCallback +{ +public: + PEOffsetMemoryReader(TADDR moduleBaseAddress); + + // IUnknown implementation + HRESULT __stdcall QueryInterface(REFIID riid, VOID** ppInterface); + ULONG __stdcall AddRef(); + ULONG __stdcall Release(); + + // IDiaReadExeAtOffsetCallback implementation + HRESULT __stdcall ReadExecutableAt(DWORDLONG fileOffset, DWORD cbData, DWORD* pcbData, BYTE data[]); + +private: + TADDR m_moduleBaseAddress; + volatile ULONG m_refCount; +}; + +// An adapter class that DIA consumes so that it can read PE data from the an image +// This implementation gets the backing data from the image loaded in debuggee memory +// that has been layed out in LoadLibrary format +class PERvaMemoryReader : IDiaReadExeAtRVACallback +{ +public: + PERvaMemoryReader(TADDR moduleBaseAddress); + + // IUnknown implementation + HRESULT __stdcall QueryInterface(REFIID riid, VOID** ppInterface); + ULONG __stdcall AddRef(); + ULONG __stdcall Release(); + + // IDiaReadExeAtOffsetCallback implementation + HRESULT __stdcall ReadExecutableAtRVA(DWORD relativeVirtualAddress, DWORD cbData, DWORD* pcbData, BYTE data[]); + +private: + TADDR m_moduleBaseAddress; + volatile ULONG m_refCount; +}; + +#endif // !FEATURE_PAL + +static const char *SymbolReaderDllName = "SOS.NETCore"; +static const char *SymbolReaderClassName = "SOS.SymbolReader"; + +typedef int (*ReadMemoryDelegate)(ULONG64, char *, int); +typedef ULONG64 (*LoadSymbolsForModuleDelegate)(const char*, BOOL, ULONG64, int, ULONG64, int, ReadMemoryDelegate); +typedef void (*DisposeDelegate)(ULONG64); +typedef BOOL (*ResolveSequencePointDelegate)(ULONG64, const char*, unsigned int, unsigned int*, unsigned int*); +typedef BOOL (*GetLocalVariableName)(ULONG64, int, int, BSTR*); +typedef BOOL (*GetLineByILOffsetDelegate)(ULONG64, mdMethodDef, ULONG64, ULONG *, BSTR*); + +class SymbolReader +{ +private: +#ifndef FEATURE_PAL + ISymUnmanagedReader* m_pSymReader; +#endif + ULONG64 m_symbolReaderHandle; + + static LoadSymbolsForModuleDelegate loadSymbolsForModuleDelegate; + static DisposeDelegate disposeDelegate; + static ResolveSequencePointDelegate resolveSequencePointDelegate; + static GetLocalVariableName getLocalVariableNameDelegate; + static GetLineByILOffsetDelegate getLineByILOffsetDelegate; + static HRESULT PrepareSymbolReader(); + + HRESULT GetNamedLocalVariable(___in ISymUnmanagedScope* pScope, ___in ICorDebugILFrame* pILFrame, ___in mdMethodDef methodToken, ___in ULONG localIndex, + __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue); + HRESULT LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in ULONG64 peAddress, __in_z WCHAR* pModuleName, ___in BOOL isFileLayout); + HRESULT LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout, ___in ULONG64 peAddress, ___in ULONG64 peSize, + ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize); + +public: + SymbolReader() + { +#ifndef FEATURE_PAL + m_pSymReader = NULL; +#endif + m_symbolReaderHandle = 0; + } + + ~SymbolReader() + { +#ifndef FEATURE_PAL + if(m_pSymReader != NULL) + { + m_pSymReader->Release(); + m_pSymReader = NULL; + } +#endif + if (m_symbolReaderHandle != 0) + { + disposeDelegate(m_symbolReaderHandle); + m_symbolReaderHandle = 0; + } + } + + HRESULT LoadSymbols(___in IMetaDataImport* pMD, ___in ICorDebugModule* pModule); + HRESULT LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataModule* pModule); + HRESULT GetLineByILOffset(___in mdMethodDef MethodToken, ___in ULONG64 IlOffset, ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, ___in ULONG cchFileName); + HRESULT GetNamedLocalVariable(___in ICorDebugFrame * pFrame, ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue); + HRESULT ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___in TADDR mod, ___out mdMethodDef* ___out pToken, ___out ULONG32* pIlOffset); +}; + +HRESULT +GetLineByOffset( + ___in ULONG64 IP, + ___out ULONG *pLinenum, + __out_ecount(cchFileName) WCHAR* pwszFileName, + ___in ULONG cchFileName); + +/// X86 Context +#define X86_SIZE_OF_80387_REGISTERS 80 +#define X86_MAXIMUM_SUPPORTED_EXTENSION 512 + +typedef struct { + DWORD ControlWord; + DWORD StatusWord; + DWORD TagWord; + DWORD ErrorOffset; + DWORD ErrorSelector; + DWORD DataOffset; + DWORD DataSelector; + BYTE RegisterArea[X86_SIZE_OF_80387_REGISTERS]; + DWORD Cr0NpxState; +} X86_FLOATING_SAVE_AREA; + +typedef struct { + + DWORD ContextFlags; + DWORD Dr0; + DWORD Dr1; + DWORD Dr2; + DWORD Dr3; + DWORD Dr6; + DWORD Dr7; + + X86_FLOATING_SAVE_AREA FloatSave; + + DWORD SegGs; + DWORD SegFs; + DWORD SegEs; + DWORD SegDs; + + DWORD Edi; + DWORD Esi; + DWORD Ebx; + DWORD Edx; + DWORD Ecx; + DWORD Eax; + + DWORD Ebp; + DWORD Eip; + DWORD SegCs; + DWORD EFlags; + DWORD Esp; + DWORD SegSs; + + BYTE ExtendedRegisters[X86_MAXIMUM_SUPPORTED_EXTENSION]; + +} X86_CONTEXT; + +typedef struct { + ULONGLONG Low; + LONGLONG High; +} M128A_XPLAT; + + +/// AMD64 Context +typedef struct { + WORD ControlWord; + WORD StatusWord; + BYTE TagWord; + BYTE Reserved1; + WORD ErrorOpcode; + DWORD ErrorOffset; + WORD ErrorSelector; + WORD Reserved2; + DWORD DataOffset; + WORD DataSelector; + WORD Reserved3; + DWORD MxCsr; + DWORD MxCsr_Mask; + M128A_XPLAT FloatRegisters[8]; + +#if defined(_WIN64) + M128A_XPLAT XmmRegisters[16]; + BYTE Reserved4[96]; +#else + M128A_XPLAT XmmRegisters[8]; + BYTE Reserved4[220]; + + DWORD Cr0NpxState; +#endif + +} AMD64_XMM_SAVE_AREA32; + +typedef struct { + + DWORD64 P1Home; + DWORD64 P2Home; + DWORD64 P3Home; + DWORD64 P4Home; + DWORD64 P5Home; + DWORD64 P6Home; + + DWORD ContextFlags; + DWORD MxCsr; + + WORD SegCs; + WORD SegDs; + WORD SegEs; + WORD SegFs; + WORD SegGs; + WORD SegSs; + DWORD EFlags; + + DWORD64 Dr0; + DWORD64 Dr1; + DWORD64 Dr2; + DWORD64 Dr3; + DWORD64 Dr6; + DWORD64 Dr7; + + DWORD64 Rax; + DWORD64 Rcx; + DWORD64 Rdx; + DWORD64 Rbx; + DWORD64 Rsp; + DWORD64 Rbp; + DWORD64 Rsi; + DWORD64 Rdi; + DWORD64 R8; + DWORD64 R9; + DWORD64 R10; + DWORD64 R11; + DWORD64 R12; + DWORD64 R13; + DWORD64 R14; + DWORD64 R15; + + DWORD64 Rip; + + union { + AMD64_XMM_SAVE_AREA32 FltSave; + struct { + M128A_XPLAT Header[2]; + M128A_XPLAT Legacy[8]; + M128A_XPLAT Xmm0; + M128A_XPLAT Xmm1; + M128A_XPLAT Xmm2; + M128A_XPLAT Xmm3; + M128A_XPLAT Xmm4; + M128A_XPLAT Xmm5; + M128A_XPLAT Xmm6; + M128A_XPLAT Xmm7; + M128A_XPLAT Xmm8; + M128A_XPLAT Xmm9; + M128A_XPLAT Xmm10; + M128A_XPLAT Xmm11; + M128A_XPLAT Xmm12; + M128A_XPLAT Xmm13; + M128A_XPLAT Xmm14; + M128A_XPLAT Xmm15; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + M128A_XPLAT VectorRegister[26]; + DWORD64 VectorControl; + + DWORD64 DebugControl; + DWORD64 LastBranchToRip; + DWORD64 LastBranchFromRip; + DWORD64 LastExceptionToRip; + DWORD64 LastExceptionFromRip; + +} AMD64_CONTEXT; + +typedef struct{ + __int64 LowPart; + __int64 HighPart; +} FLOAT128_XPLAT; + + +/// ARM Context +#define ARM_MAX_BREAKPOINTS_CONST 8 +#define ARM_MAX_WATCHPOINTS_CONST 4 +typedef struct { + + DWORD ContextFlags; + + DWORD R0; + DWORD R1; + DWORD R2; + DWORD R3; + DWORD R4; + DWORD R5; + DWORD R6; + DWORD R7; + DWORD R8; + DWORD R9; + DWORD R10; + DWORD R11; + DWORD R12; + + DWORD Sp; + DWORD Lr; + DWORD Pc; + DWORD Cpsr; + + DWORD Fpscr; + union { + M128A_XPLAT Q[16]; + ULONGLONG D[32]; + DWORD S[32]; + } DUMMYUNIONNAME; + + DWORD Bvr[ARM_MAX_BREAKPOINTS_CONST]; + DWORD Bcr[ARM_MAX_BREAKPOINTS_CONST]; + DWORD Wvr[ARM_MAX_WATCHPOINTS_CONST]; + DWORD Wcr[ARM_MAX_WATCHPOINTS_CONST]; + +} ARM_CONTEXT; + +// On ARM this mask is or'ed with the address of code to get an instruction pointer +#ifndef THUMB_CODE +#define THUMB_CODE 1 +#endif + +///ARM64 Context +#define ARM64_MAX_BREAKPOINTS 8 +#define ARM64_MAX_WATCHPOINTS 2 +typedef struct { + + DWORD ContextFlags; + DWORD Cpsr; // NZVF + DAIF + CurrentEL + SPSel + union { + struct { + DWORD64 X0; + DWORD64 X1; + DWORD64 X2; + DWORD64 X3; + DWORD64 X4; + DWORD64 X5; + DWORD64 X6; + DWORD64 X7; + DWORD64 X8; + DWORD64 X9; + DWORD64 X10; + DWORD64 X11; + DWORD64 X12; + DWORD64 X13; + DWORD64 X14; + DWORD64 X15; + DWORD64 X16; + DWORD64 X17; + DWORD64 X18; + DWORD64 X19; + DWORD64 X20; + DWORD64 X21; + DWORD64 X22; + DWORD64 X23; + DWORD64 X24; + DWORD64 X25; + DWORD64 X26; + DWORD64 X27; + DWORD64 X28; + }; + + DWORD64 X[29]; + }; + + DWORD64 Fp; + DWORD64 Lr; + DWORD64 Sp; + DWORD64 Pc; + + + M128A_XPLAT V[32]; + DWORD Fpcr; + DWORD Fpsr; + + DWORD Bcr[ARM64_MAX_BREAKPOINTS]; + DWORD64 Bvr[ARM64_MAX_BREAKPOINTS]; + DWORD Wcr[ARM64_MAX_WATCHPOINTS]; + DWORD64 Wvr[ARM64_MAX_WATCHPOINTS]; + +} ARM64_CONTEXT; + +typedef struct _CROSS_PLATFORM_CONTEXT { + + _CROSS_PLATFORM_CONTEXT() {} + + union { + X86_CONTEXT X86Context; + AMD64_CONTEXT Amd64Context; + ARM_CONTEXT ArmContext; + ARM64_CONTEXT Arm64Context; + }; + +} CROSS_PLATFORM_CONTEXT, *PCROSS_PLATFORM_CONTEXT; + + + +WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj = true); +WString MethodNameFromIP(CLRDATA_ADDRESS methodDesc, BOOL bSuppressLines = FALSE, BOOL bAssemblyName = FALSE, BOOL bDisplacement = FALSE); +HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount); +WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackwalk = NULL, BOOL bAssemblyName = FALSE); + +/* This cache is used to read data from the target process if the reads are known + * to be sequential. + */ +class LinearReadCache +{ +public: + LinearReadCache(ULONG pageSize = 0x10000); + ~LinearReadCache(); + + /* Reads an address out of the target process, caching the page of memory read. + * Params: + * addr - The address to read out of the target process. + * t - A pointer to the data to stuff it in. We will read sizeof(T) data + * from the process and write it into the location t points to. This + * parameter must be non-null. + * Returns: + * True if the read succeeded. False if it did not, usually as a result + * of the memory simply not being present in the target process. + * Note: + * The state of *t is undefined if this function returns false. We may + * have written partial data to it if we return false, so you must + * absolutely NOT use it if Read returns false. + */ + template <class T> + bool Read(TADDR addr, T *t, bool update = true) + { + _ASSERTE(t); + + // Unfortunately the ctor can fail the alloc for the byte array. In this case + // we'll just fall back to non-cached reads. + if (mPage == NULL) + return MisalignedRead(addr, t); + + // Is addr on the current page? If not read the page of memory addr is on. + // If this fails, we will fall back to a raw read out of the process (which + // is what MisalignedRead does). + if ((addr < mCurrPageStart) || (addr - mCurrPageStart > mCurrPageSize)) + if (!update || !MoveToPage(addr)) + return MisalignedRead(addr, t); + + // If MoveToPage succeeds, we MUST be on the right page. + _ASSERTE(addr >= mCurrPageStart); + + // However, the amount of data requested may fall off of the page. In that case, + // fall back to MisalignedRead. + TADDR offset = addr - mCurrPageStart; + if (offset + sizeof(T) > mCurrPageSize) + return MisalignedRead(addr, t); + + // If we reach here we know we are on the right page of memory in the cache, and + // that the read won't fall off of the end of the page. +#ifdef _DEBUG + mReads++; +#endif + + *t = *reinterpret_cast<T*>(mPage+offset); + return true; + } + + void EnsureRangeInCache(TADDR start, unsigned int size) + { + if (mCurrPageStart == start) + { + if (size <= mCurrPageSize) + return; + + // Total bytes to read, don't overflow buffer. + unsigned int total = size + mCurrPageSize; + if (total + mCurrPageSize > mPageSize) + total = mPageSize-mCurrPageSize; + + // Read into the middle of the buffer, update current page size. + ULONG read = 0; + HRESULT hr = g_ExtData->ReadVirtual(mCurrPageStart+mCurrPageSize, mPage+mCurrPageSize, total, &read); + mCurrPageSize += read; + + if (hr != S_OK) + { + mCurrPageStart = 0; + mCurrPageSize = 0; + } + } + else + { + MoveToPage(start, size); + } + } + + void ClearStats() + { +#ifdef _DEBUG + mMisses = 0; + mReads = 0; + mMisaligned = 0; +#endif + } + + void PrintStats(const char *func) + { +#ifdef _DEBUG + char buffer[1024]; + sprintf_s(buffer, _countof(buffer), "Cache (%s): %d reads (%2.1f%% hits), %d misses (%2.1f%%), %d misaligned (%2.1f%%).\n", + func, mReads, 100*(mReads-mMisses)/(float)(mReads+mMisaligned), mMisses, + 100*mMisses/(float)(mReads+mMisaligned), mMisaligned, 100*mMisaligned/(float)(mReads+mMisaligned)); + OutputDebugStringA(buffer); +#endif + } + +private: + /* Sets the cache to the page specified by addr, or false if we could not move to + * that page. + */ + bool MoveToPage(TADDR addr, unsigned int size = 0x18); + + /* Attempts to read from the target process if the data is possibly hanging off + * the end of a page. + */ + template<class T> + inline bool MisalignedRead(TADDR addr, T *t) + { + ULONG fetched = 0; + HRESULT hr = g_ExtData->ReadVirtual(addr, (BYTE*)t, sizeof(T), &fetched); + + if (FAILED(hr) || fetched != sizeof(T)) + return false; + + mMisaligned++; + return true; + } + +private: + TADDR mCurrPageStart; + ULONG mPageSize, mCurrPageSize; + BYTE *mPage; + + int mMisses, mReads, mMisaligned; +}; + + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// Methods for creating a database out of the gc heap and it's roots in xml format or CLRProfiler format +// + +#include <unordered_map> +#include <unordered_set> +#include <list> + +class TypeTree; +enum { FORMAT_XML=0, FORMAT_CLRPROFILER=1 }; +enum { TYPE_START=0,TYPE_TYPES=1,TYPE_ROOTS=2,TYPE_OBJECTS=3,TYPE_HIGHEST=4}; +class HeapTraverser +{ +private: + TypeTree *m_pTypeTree; + size_t m_curNID; + FILE *m_file; + int m_format; // from the enum above + size_t m_objVisited; // for UI updates + bool m_verify; + LinearReadCache mCache; + + std::unordered_map<TADDR, std::list<TADDR>> mDependentHandleMap; + +public: + HeapTraverser(bool verify); + ~HeapTraverser(); + + FILE *getFile() { return m_file; } + + BOOL Initialize(); + BOOL CreateReport (FILE *fp, int format); + +private: + // First all types are added to a tree + void insert(size_t mTable); + size_t getID(size_t mTable); + + // Functions for writing to the output file. + void PrintType(size_t ID,LPCWSTR name); + + void PrintObjectHead(size_t objAddr,size_t typeID,size_t Size); + void PrintObjectMember(size_t memberValue, bool dependentHandle); + void PrintObjectTail(); + + void PrintRootHead(); + void PrintRoot(LPCWSTR kind,size_t Value); + void PrintRootTail(); + + void PrintSection(int Type,BOOL bOpening); + + // Root and object member helper functions + void FindGCRootOnStacks(); + void PrintRefs(size_t obj, size_t methodTable, size_t size); + + // Callback functions used during traversals + static void GatherTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable, LPVOID token); + static void PrintHeap(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable, LPVOID token); + static void PrintOutTree(size_t methodTable, size_t ID, LPVOID token); + void TraceHandles(); +}; + + +class GCRootImpl +{ +private: + struct MTInfo + { + TADDR MethodTable; + WCHAR *TypeName; + + TADDR *Buffer; + CGCDesc *GCDesc; + + bool ArrayOfVC; + bool ContainsPointers; + size_t BaseSize; + size_t ComponentSize; + + const WCHAR *GetTypeName() + { + if (!TypeName) + TypeName = CreateMethodTableName(MethodTable); + + if (!TypeName) + return W("<error>"); + + return TypeName; + } + + MTInfo() + : MethodTable(0), TypeName(0), Buffer(0), GCDesc(0), + ArrayOfVC(false), ContainsPointers(false), BaseSize(0), ComponentSize(0) + { + } + + ~MTInfo() + { + if (Buffer) + delete [] Buffer; + + if (TypeName) + delete [] TypeName; + } + }; + + struct RootNode + { + RootNode *Next; + RootNode *Prev; + TADDR Object; + MTInfo *MTInfo; + + bool FilledRefs; + bool FromDependentHandle; + RootNode *GCRefs; + + + const WCHAR *GetTypeName() + { + if (!MTInfo) + return W("<unknown>"); + + return MTInfo->GetTypeName(); + } + + RootNode() + : Next(0), Prev(0) + { + Clear(); + } + + void Clear() + { + if (Next && Next->Prev == this) + Next->Prev = NULL; + + if (Prev && Prev->Next == this) + Prev->Next = NULL; + + Next = 0; + Prev = 0; + Object = 0; + MTInfo = 0; + FilledRefs = false; + FromDependentHandle = false; + GCRefs = 0; + } + + void Remove(RootNode *&list) + { + RootNode *curr_next = Next; + + // We've already considered this object, remove it. + if (Prev == NULL) + { + // If we've filtered out the head, update it. + list = curr_next; + + if (curr_next) + curr_next->Prev = NULL; + } + else + { + // Otherwise remove the current item from the list + Prev->Next = curr_next; + + if (curr_next) + curr_next->Prev = Prev; + } + } + }; + +public: + static void GetDependentHandleMap(std::unordered_map<TADDR, std::list<TADDR>> &map); + +public: + // Finds all objects which root "target" and prints the path from the root + // to "target". If all is true, all possible paths to the object are printed. + // If all is false, only completely unique paths will be printed. + int PrintRootsForObject(TADDR obj, bool all, bool noStacks); + + // Finds a path from root to target if it exists and prints it out. Returns + // true if it found a path, false otherwise. + bool PrintPathToObject(TADDR root, TADDR target); + + // Calculates the size of the closure of objects kept alive by root. + size_t ObjSize(TADDR root); + + // Walks each root, printing out the total amount of memory held alive by it. + void ObjSize(); + + // Returns the set of all live objects in the process. + const std::unordered_set<TADDR> &GetLiveObjects(bool excludeFQ = false); + + // See !FindRoots. + int FindRoots(int gen, TADDR target); + +private: + // typedefs + typedef void (*ReportCallback)(TADDR root, RootNode *path, bool printHeader); + + // Book keeping and debug. + void ClearAll(); + void ClearNodes(); + void ClearSizeData(); + + // Printing roots + int PrintRootsOnHandleTable(int gen = -1); + int PrintRootsOnAllThreads(); + int PrintRootsOnThread(DWORD osThreadId); + int PrintRootsOnFQ(bool notReadyForFinalization = false); + int PrintRootsInOlderGen(); + int PrintRootsInRange(LinearReadCache &cache, TADDR start, TADDR stop, ReportCallback func, bool printHeader); + + // Calculate gc root + RootNode *FilterRoots(RootNode *&list); + RootNode *FindPathToTarget(TADDR root); + RootNode *GetGCRefs(RootNode *path, RootNode *node); + + void InitDependentHandleMap(); + + //Reporting: + void ReportOneHandlePath(const SOSHandleData &handle, RootNode *node, bool printHeader); + void ReportOnePath(DWORD thread, const SOSStackRefData &stackRef, RootNode *node, bool printThread, bool printFrame); + static void ReportOneFQEntry(TADDR root, RootNode *path, bool printHeader); + static void ReportOlderGenEntry(TADDR root, RootNode *path, bool printHeader); + void ReportSizeInfo(const SOSHandleData &handle, TADDR obj); + void ReportSizeInfo(DWORD thread, const SOSStackRefData &ref, TADDR obj); + + // Data reads: + TADDR ReadPointer(TADDR location); + TADDR ReadPointerCached(TADDR location); + + // Object/MT data: + MTInfo *GetMTInfo(TADDR mt); + DWORD GetComponents(TADDR obj, TADDR mt); + size_t GetSizeOfObject(TADDR obj, MTInfo *info); + + // RootNode management: + RootNode *NewNode(TADDR obj = 0, MTInfo *mtinfo = 0, bool fromDependent = false); + void DeleteNode(RootNode *node); + +private: + + bool mAll, // Print all roots or just unique roots? + mSize; // Print rooting information or total size info? + + std::list<RootNode*> mCleanupList; // A list of RootNode's we've newed up. This is only used to delete all of them later. + std::list<RootNode*> mRootNewList; // A list of unused RootNodes that are free to use instead of having to "new" up more. + + std::unordered_map<TADDR, MTInfo*> mMTs; // The MethodTable cache which maps from MT -> MethodTable data (size, gcdesc, string typename) + std::unordered_map<TADDR, RootNode*> mTargets; // The objects that we are searching for. + std::unordered_set<TADDR> mConsidered; // A hashtable of objects we've already visited. + std::unordered_map<TADDR, size_t> mSizes; // A mapping from object address to total size of data the object roots. + + std::unordered_map<TADDR, std::list<TADDR>> mDependentHandleMap; + + LinearReadCache mCache; // A linear cache which stops us from having to read from the target process more than 1-2 times per object. +}; + +// +// Helper class used for type-safe bitflags +// T - the enum type specifying the individual bit flags +// U - the underlying/storage type +// Requirement: +// sizeof(T) <= sizeof(U) +// +template <typename T, typename U> +struct Flags +{ + typedef T UnderlyingType; + typedef U BitFlagEnumType; + + static_assert_no_msg(sizeof(BitFlagEnumType) <= sizeof(UnderlyingType)); + + Flags(UnderlyingType v) + : m_val(v) + { } + + Flags(BitFlagEnumType v) + : m_val(v) + { } + + Flags(const Flags& other) + : m_val(other.m_val) + { } + + Flags& operator = (const Flags& other) + { m_val = other.m_val; return *this; } + + Flags operator | (Flags other) const + { return Flags<T, U>(m_val | other._val); } + + void operator |= (Flags other) + { m_val |= other.m_val; } + + Flags operator & (Flags other) const + { return Flags<T, U>(m_val & other.m_val); } + + void operator &= (Flags other) + { m_val &= other.m_val; } + + Flags operator ^ (Flags other) const + { return Flags<T, U>(m_val ^ other._val); } + + void operator ^= (Flags other) + { m_val ^= other.m_val; } + + BOOL operator == (Flags other) const + { return m_val == other.m_val; } + + BOOL operator != (Flags other) const + { return m_val != other.m_val; } + + +private: + UnderlyingType m_val; +}; + +#ifndef FEATURE_PAL + +// Flags defining activation policy for COM objects +enum CIOptionsBits +{ + cciLatestFx = 0x01, // look in the most recent .NETFx installation + cciMatchFx = 0x02, // NYI: Look in the .NETFx installation matching the debuggee's runtime + cciAnyFx = 0x04, // look in any .NETFx installation + cciFxMask = 0x0f, + cciDbiColocated = 0x10, // NYI: Look next to the already loaded DBI module + cciDacColocated = 0x20, // Look next to the already loaded DAC module + cciDbgPath = 0x40, // Look in all folders in the debuggers symbols and binary path +}; + +typedef Flags<DWORD, CIOptionsBits> CIOptions; + +/**********************************************************************\ +* Routine Description: * +* * +* CreateInstanceCustom() provides a way to activate a COM object w/o * +* triggering the FeatureOnDemand dialog. In order to do this we * +* must avoid using the CoCreateInstance() API, which, on a machine * +* with v4+ installed and w/o v2, would trigger this. * +* CreateInstanceCustom() activates the requested COM object according * +* to the specified passed in CIOptions, in the following order * +* (skipping the steps not enabled in the CIOptions flags passed in): * +* 1. Attempt to activate the COM object using a framework install: * +* a. If the debugger machine has a V4+ shell shim use the shim * +* to activate the object * +* b. Otherwise simply call CoCreateInstance * +* 2. If unsuccessful attempt to activate looking for the dllName in * +* the same folder as the DAC was loaded from * +* 3. If unsuccessful attempt to activate the COM object looking in * +* every path specified in the debugger's .exepath and .sympath * +\**********************************************************************/ +HRESULT CreateInstanceCustom( + REFCLSID clsid, + REFIID iid, + LPCWSTR dllName, + CIOptions cciOptions, + void** ppItf); + + +//------------------------------------------------------------------------ +// A typesafe version of GetProcAddress +//------------------------------------------------------------------------ +template <typename T> +BOOL +GetProcAddressT( + ___in PCSTR FunctionName, + __in_opt PCWSTR DllName, + __inout T* OutFunctionPointer, + __inout HMODULE* InOutDllHandle + ) +{ + _ASSERTE(InOutDllHandle != NULL); + _ASSERTE(OutFunctionPointer != NULL); + + T FunctionPointer = NULL; + HMODULE DllHandle = *InOutDllHandle; + if (DllHandle == NULL) + { + DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + if (DllHandle != NULL) + *InOutDllHandle = DllHandle; + } + if (DllHandle != NULL) + { + FunctionPointer = (T) GetProcAddress(DllHandle, FunctionName); + } + *OutFunctionPointer = FunctionPointer; + return FunctionPointer != NULL; +} + + +#endif // FEATURE_PAL + +struct ImageInfo +{ + ULONG64 modBase; +}; + +// Helper class used in ClrStackFromPublicInterface() to keep track of explicit EE Frames +// (i.e., "internal frames") on the stack. Call Init() with the appropriate +// ICorDebugThread3, and this class will initialize itself with the set of internal +// frames. You can then call PrintPrecedingInternalFrames during your stack walk to +// have this class output any internal frames that "precede" (i.e., that are closer to +// the leaf than) the specified ICorDebugFrame. +class InternalFrameManager +{ +private: + // TODO: Verify constructor AND destructor is called for each array element + // TODO: Comment about hard-coding 1000 + ToRelease<ICorDebugInternalFrame2> m_rgpInternalFrame2[1000]; + ULONG32 m_cInternalFramesActual; + ULONG32 m_iInternalFrameCur; + +public: + InternalFrameManager(); + HRESULT Init(ICorDebugThread3 * pThread3); + HRESULT PrintPrecedingInternalFrames(ICorDebugFrame * pFrame); + +private: + HRESULT PrintCurrentInternalFrame(); +}; + +#endif // __util_h__ |