summaryrefslogtreecommitdiff
path: root/src/ToolBox/SOS/Strike/util.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/ToolBox/SOS/Strike/util.h')
-rw-r--r--src/ToolBox/SOS/Strike/util.h3292
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__