202 lines
8.5 KiB
C++
202 lines
8.5 KiB
C++
//========= Copyright <20> 1996-2008, Valve Corporation, All rights reserved.
|
||
//============//
|
||
//
|
||
// Purpose: Tools for grabbing/dumping the stack at runtime
|
||
//
|
||
// $NoKeywords: $
|
||
//=============================================================================//
|
||
|
||
#ifndef TIER0_STACKTOOLS_H
|
||
#define TIER0_STACKTOOLS_H
|
||
|
||
#ifdef _WIN32
|
||
#pragma once
|
||
#endif
|
||
|
||
#include "tier0/platform.h"
|
||
|
||
#if (defined(PLATFORM_WINDOWS) || defined(PLATFORM_X360)) && \
|
||
!defined(STEAM) && !defined(_CERT) && \
|
||
defined(TCHAR_IS_CHAR) // designed for windows/x360, not built/tested with
|
||
// wide characters, not intended for release builds
|
||
// (but probably wouldn't damage anything)
|
||
#define ENABLE_RUNTIME_STACK_TRANSLATION // uncomment to enable runtime stack
|
||
// translation tools. All of which use
|
||
// on-demand loading of necessary dll's
|
||
// and pdb's
|
||
#endif
|
||
|
||
#if defined(ENABLE_RUNTIME_STACK_TRANSLATION)
|
||
//#define ENABLE_THREAD_PARENT_STACK_TRACING 1 //uncomment to actually enable
|
||
//tracking stack traces from threads and jobs to their parent thread. Must also
|
||
//define THREAD_PARENT_STACK_TRACE_SUPPORTED in threadtools.h
|
||
#if defined(ENABLE_THREAD_PARENT_STACK_TRACING)
|
||
#define THREAD_PARENT_STACK_TRACE_LENGTH 32
|
||
#endif
|
||
#endif
|
||
|
||
PLATFORM_INTERFACE int GetCallStack(void **pReturnAddressesOut, int iArrayCount,
|
||
int iSkipCount);
|
||
|
||
// ONLY WORKS IF THE CRAWLED PORTION OF THE STACK DISABLES FRAME POINTER
|
||
// OMISSION (/Oy-) "vpc /nofpo"
|
||
PLATFORM_INTERFACE int GetCallStack_Fast(void **pReturnAddressesOut,
|
||
int iArrayCount, int iSkipCount);
|
||
|
||
typedef int (*FN_GetCallStack)(void **pReturnAddressesOut, int iArrayCount,
|
||
int iSkipCount);
|
||
|
||
// where we'll find our PDB's for win32.
|
||
PLATFORM_INTERFACE void SetStackTranslationSymbolSearchPath(
|
||
const char *szSemicolonSeparatedList = NULL);
|
||
PLATFORM_INTERFACE void StackToolsNotify_LoadedLibrary(const char *szLibName);
|
||
|
||
// maximum output sample "tier0.dll!TranslateStackInfo -
|
||
// u:\Dev\L4D\src\tier0\stacktools.cpp(162) + 4 bytes"
|
||
enum TranslateStackInfo_StyleFlags_t {
|
||
TSISTYLEFLAG_NONE = 0,
|
||
TSISTYLEFLAG_MODULENAME =
|
||
(1 << 0), // start with module Sample: "tier0.dll!"
|
||
TSISTYLEFLAG_SYMBOLNAME = (1 << 1), // include the symbol name
|
||
// Sample: "TranslateStackInfo"
|
||
TSISTYLEFLAG_FULLPATH =
|
||
(1 << 2), // include full path Sample:
|
||
// "u:\Dev\L4D\src\tier0\stacktools.cpp"
|
||
TSISTYLEFLAG_SHORTPATH = (1 << 3), // only include 2 directories Sample:
|
||
// "\src\tier0\stacktools.cpp"
|
||
TSISTYLEFLAG_LINE = (1 << 4), // file line number Sample:
|
||
// "(162)"
|
||
TSISTYLEFLAG_LINEANDOFFSET =
|
||
(1
|
||
<< 5), // file line + offset Sample: "(162) + 4 bytes"
|
||
TSISTYLEFLAG_LAST = TSISTYLEFLAG_LINEANDOFFSET,
|
||
TSISTYLEFLAG_DEFAULT =
|
||
(TSISTYLEFLAG_MODULENAME | TSISTYLEFLAG_SYMBOLNAME |
|
||
TSISTYLEFLAG_FULLPATH |
|
||
TSISTYLEFLAG_LINEANDOFFSET), // produces sample above
|
||
};
|
||
|
||
// Generates a formatted list of function information, returns number of
|
||
// translated entries On 360 this generates a string that can be decoded by
|
||
// VXConsole in print functions. Optimal path for translation because it's one
|
||
// way. Other paths require multiple transactions.
|
||
PLATFORM_INTERFACE int TranslateStackInfo(
|
||
const void *const *pCallStack, int iCallStackCount, tchar *szOutput,
|
||
int iOutBufferSize, const tchar *szEntrySeparator,
|
||
TranslateStackInfo_StyleFlags_t style = TSISTYLEFLAG_DEFAULT);
|
||
|
||
PLATFORM_INTERFACE void PreloadStackInformation(
|
||
void *const *pAddresses,
|
||
int iAddressCount); // caches data and reduces communication with VXConsole
|
||
// to speed up 360 decoding when using any of the
|
||
// Get***FromAddress() functions. Nop on PC.
|
||
PLATFORM_INTERFACE bool GetFileAndLineFromAddress(
|
||
const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength,
|
||
uint32 &iLineNumberOut, uint32 *pDisplacementOut = NULL);
|
||
PLATFORM_INTERFACE bool GetSymbolNameFromAddress(
|
||
const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength,
|
||
uint64 *pDisplacementOut = NULL);
|
||
PLATFORM_INTERFACE bool GetModuleNameFromAddress(const void *pAddress,
|
||
tchar *pModuleNameOut,
|
||
int iMaxModuleNameLength);
|
||
|
||
class PLATFORM_CLASS
|
||
CCallStackStorage // a helper class to grab a stack trace as close to the
|
||
// leaf code surface as possible, then pass it on to
|
||
// deeper functions intact with less unpredictable
|
||
// inlining pollution
|
||
{
|
||
public:
|
||
CCallStackStorage(FN_GetCallStack GetStackFunction = GetCallStack,
|
||
uint32 iSkipCalls = 0);
|
||
CCallStackStorage(const CCallStackStorage ©From) {
|
||
iValidEntries = copyFrom.iValidEntries;
|
||
memcpy(pStack, copyFrom.pStack,
|
||
sizeof(void *) * copyFrom.iValidEntries);
|
||
}
|
||
|
||
void *pStack[128]; // probably too big, possibly too small for some
|
||
// applications. Don't want to spend the time figuring
|
||
// out how to generalize this without templatizing
|
||
// pollution or mallocs
|
||
uint32 iValidEntries;
|
||
};
|
||
|
||
// Hold onto one of these to denote the top of a functional stack trace. Also
|
||
// allows us to string together threads to their parents
|
||
class PLATFORM_CLASS CStackTop_Base {
|
||
protected:
|
||
#if defined(ENABLE_RUNTIME_STACK_TRANSLATION)
|
||
CStackTop_Base *m_pPrevTop;
|
||
void *m_pStackBase;
|
||
void *m_pReplaceAddress;
|
||
|
||
void *const *m_pParentStackTrace;
|
||
int m_iParentStackTraceLength;
|
||
#endif
|
||
};
|
||
|
||
// makes a copy of the parent stack
|
||
class PLATFORM_CLASS CStackTop_CopyParentStack : public CStackTop_Base {
|
||
public:
|
||
CStackTop_CopyParentStack(void *const *pParentStackTrace,
|
||
int iParentStackTraceLength);
|
||
~CStackTop_CopyParentStack(void);
|
||
};
|
||
|
||
// just references the parent stack. Assuming that you'll keep that memory
|
||
// around as long as you're keeping this Stack Top marker.
|
||
class PLATFORM_CLASS CStackTop_ReferenceParentStack : public CStackTop_Base {
|
||
public:
|
||
CStackTop_ReferenceParentStack(void *const *pParentStackTrace = NULL,
|
||
int iParentStackTraceLength = 0);
|
||
~CStackTop_ReferenceParentStack(void);
|
||
void ReleaseParentStackReferences(
|
||
void); // in case you need to delete the parent stack trace before this
|
||
// class goes out of scope
|
||
};
|
||
|
||
// Encodes data so that every byte's most significant bit is a 1. Ensuring no
|
||
// null terminators. This puts the encoded data in the 128-255 value range.
|
||
// Leaving all standard ascii characters for control. Returns string length (not
|
||
// including the written null terminator as is standard). Or if the buffer is too
|
||
// small. Returns negative of necessary buffer size (including room needed for
|
||
// null terminator)
|
||
PLATFORM_INTERFACE int EncodeBinaryToString(const void *pToEncode,
|
||
int iDataLength, char *pEncodeOut,
|
||
int iEncodeBufferSize);
|
||
|
||
// Decodes a string produced by EncodeBinaryToString(). Safe to decode in place
|
||
// if you don't mind trashing your string, binary byte count always less than
|
||
// string byte count. Returns:
|
||
// >= 0 is the decoded data size
|
||
// INT_MIN (most negative value possible) indicates an improperly formatted
|
||
//string (not our data) all other negative values are the negative of how much
|
||
//dest buffer size is necessary.
|
||
PLATFORM_INTERFACE int DecodeBinaryFromString(const char *pString,
|
||
void *pDestBuffer,
|
||
int iDestBufferSize,
|
||
char **ppParseFinishOut = NULL);
|
||
|
||
// 360<->VXConsole specific communication definitions
|
||
#define XBX_CALLSTACKDECODEPREFIX ":CSDECODE["
|
||
|
||
enum StackTranslation_BinaryHandler_Command_t {
|
||
ST_BHC_LOADEDLIBARY,
|
||
ST_BHC_GETTRANSLATIONINFO,
|
||
};
|
||
|
||
#pragma pack(push)
|
||
#pragma pack(1)
|
||
struct FullStackInfo_t {
|
||
const void *pAddress;
|
||
char szModuleName[24];
|
||
char szFileName[MAX_PATH / 2];
|
||
char szSymbol[64];
|
||
uint32 iLine;
|
||
uint32 iSymbolOffset;
|
||
};
|
||
#pragma pack(pop)
|
||
|
||
#endif //#ifndef TIER0_STACKTOOLS_H
|