windows-nt/Source/XPSP1/NT/multimedia/directx/dplay/dnet/common/ministack.h
2020-09-26 16:20:57 +08:00

425 lines
13 KiB
C++

/*==========================================================================
*
* Copyright (C) 2001 Microsoft Corporation. All Rights Reserved.
*
* File: MiniStack.h
* Content: Reduced-overhead call stack tracking class
*
* History:
* Date By Reason
* ==== == ======
* 03/27/01 RichGr Derived from CallStack.h
***************************************************************************/
#ifndef __MINISTACK_H__
#define __MINISTACK_H__
#define _IMAGEHLP_SOURCE_
#include <Imagehlp.h>
#include <string.h>
//**********************************************************************
// Constant definitions
//**********************************************************************
#define MINISTACK_DEPTH 5 // Increasing this beyond 5 definitely slows things down.
#define SYM_CACHE_SIZE 100 // 100 seems big enough. Old entries are automatically replaced.
//**********************************************************************
// Macro definitions
//**********************************************************************
//**********************************************************************
// Structure definitions
//**********************************************************************
//**********************************************************************
// Variable definitions
//**********************************************************************
//**********************************************************************
// Function prototypes
//**********************************************************************
//
// prototypes for ImageHlp.dll functions we get from LoadLibrary().
//
typedef DWORD (__stdcall * PIMAGEHELP_SYMGETOPTIONS)( void );
typedef DWORD (__stdcall * PIMAGEHELP_SYMSETOPTIONS)( DWORD SymOptions );
typedef BOOL (__stdcall * PIMAGEHELP_SYMINITIALIZE)( HANDLE hProcess, PSTR pUserSearchPath, BOOL fInvadeProcess );
typedef BOOL (__stdcall * PIMAGEHELP_SYMGETSYMFROMADDR)( HANDLE hProcess, DWORD dwAddress, PDWORD pdwDisplacement, PIMAGEHLP_SYMBOL pSymbol );
typedef BOOL (__stdcall * PIMAGEHELP_SYMGETSYMFROMADDR64)( HANDLE hProcess, DWORD_PTR dwAddress, PDWORD_PTR pdwDisplacement, PIMAGEHLP_SYMBOL64 pSymbol );
//**********************************************************************
// Class definitions
//**********************************************************************
class CMiniStack
{
public:
// Member functions
CMiniStack();
~CMiniStack();
void GetCallStackString(char *const pOutputString);
private:
// Member functions
void Initialize(char *const pOutputString);
void NoteCurrentCallStack(PVOID pCallStack[]);
// Member variables
HANDLE m_hPID;
BOOL m_bNotInited;
volatile LONG m_nGuardInit;
HINSTANCE m_hImageHelp;
PIMAGEHELP_SYMGETOPTIONS m_pSymGetOptions;
PIMAGEHELP_SYMSETOPTIONS m_pSymSetOptions;
PIMAGEHELP_SYMINITIALIZE m_pSymInitialize;
#ifndef _WIN64
PIMAGEHELP_SYMGETSYMFROMADDR m_pSymGetSymFromAddr;
#else
PIMAGEHELP_SYMGETSYMFROMADDR64 m_pSymGetSymFromAddr;
#endif // _WIN64
volatile LONG m_nGuardSymTable;
int m_nNextCacheSlot;
BOOL m_bCacheIsFull;
struct {
PVOID pAddress;
char szName[64];
int nReadCount;
} m_SymTable[SYM_CACHE_SIZE];
};
//**********************************************************************
// Per-Process instantiations
//**********************************************************************
#ifdef PER_PROCESS_INSTANTIATIONS // Define this in just one source file.
CMiniStack g_MiniStack;
#else
extern CMiniStack g_MiniStack;
#endif
#ifdef PER_PROCESS_INSTANTIATIONS // Define this in just one source file.
//**********************************************************************
// Class member function definitions
//**********************************************************************
// These should be moved to a new file MiniStack.cpp for DX9.
// ------------------------------
// CMiniStack::CMiniStack - constructor
//
// Entry: Nothing
//
// Exit: Nothing
// ------------------------------
CMiniStack::CMiniStack()
{
m_bNotInited = TRUE;
m_nGuardInit = 0;
m_hPID = GetCurrentProcess();
m_pSymGetOptions = NULL;
m_pSymSetOptions = NULL;
m_pSymInitialize = NULL;
m_pSymGetSymFromAddr = NULL;
m_hImageHelp = NULL;
memset(&m_SymTable, 0, sizeof m_SymTable);
m_nNextCacheSlot = 0;
m_bCacheIsFull = FALSE;
return;
}
// ------------------------------
// CMiniStack::Initialize - load ImageHlp.dll and get proc addresses.
//
// Entry: Nothing
//
// Exit: Nothing
// ------------------------------
void CMiniStack::Initialize(char *const pOutputString)
{
// Attempt to load ImageHelp.dll
if ( (m_hImageHelp = LoadLibrary( "ImageHlp.dll" )) == NULL)
goto FailedImageHelpLoad;
m_pSymGetOptions = (PIMAGEHELP_SYMGETOPTIONS)GetProcAddress( m_hImageHelp, "SymGetOptions" );
m_pSymSetOptions = (PIMAGEHELP_SYMSETOPTIONS)GetProcAddress( m_hImageHelp, "SymSetOptions" );
m_pSymInitialize = (PIMAGEHELP_SYMINITIALIZE)GetProcAddress( m_hImageHelp, "SymInitialize" );
#ifndef _WIN64
m_pSymGetSymFromAddr = (PIMAGEHELP_SYMGETSYMFROMADDR)GetProcAddress( m_hImageHelp, "SymGetSymFromAddr" );
#else // _WIN64
m_pSymGetSymFromAddr = (PIMAGEHELP_SYMGETSYMFROMADDR64)GetProcAddress( m_hImageHelp, "SymGetSymFromAddr64" );
#endif // _WIN64
if ( m_pSymGetOptions == NULL
|| m_pSymSetOptions == NULL
|| m_pSymInitialize == NULL
|| m_pSymGetSymFromAddr == NULL )
{
goto FailedImageHelpLoad;
}
m_pSymSetOptions( SYMOPT_DEFERRED_LOADS | m_pSymGetOptions() );
if ( m_pSymInitialize( m_hPID, NULL, TRUE ) == FALSE
|| m_pSymInitialize( m_hPID, NULL, FALSE ) == FALSE )
{
goto FailedImageHelpLoad;
}
m_bNotInited = FALSE;
Exit:
return;
FailedImageHelpLoad:
strcpy(pOutputString, "*** ImageHlp.dll could not be loaded ***\r\n");
if (m_hImageHelp)
{
FreeLibrary(m_hImageHelp);
m_hImageHelp = NULL;
}
goto Exit;
}
//**********************************************************************
// ------------------------------
// CMiniStack::CMiniStack - destructor
//
// Entry: Nothing
//
// Exit: Nothing
// ------------------------------
CMiniStack::~CMiniStack()
{
if (m_hImageHelp)
{
FreeLibrary(m_hImageHelp);
m_hImageHelp = NULL;
}
return;
}
//**********************************************************************
// ------------------------------
// CMiniStack::NoteCurrentCallStack - get a call stack
//
// Entry: Nothing
//
// Exit: Nothing
// ------------------------------
void CMiniStack::NoteCurrentCallStack(PVOID pCallStack[])
{
PVOID *ppCallersEBP;
PVOID pStackTop;
PVOID pStackBottom;
int i;
ppCallersEBP = NULL;
pStackTop = pStackBottom = NULL;
#ifdef _X86_
_asm
{
mov eax,dword ptr fs:[4]
mov pStackTop, eax
mov eax,dword ptr fs:[8]
mov pStackBottom, eax
mov eax,[ebp]
mov ppCallersEBP,eax
}
__try
{
// This code can generate exception if it steps back too far...
// Skip the 1st. stack item because it's always PerfEnterCriticalSection()
// and we don't need to report it.
for (i = -1; i < MINISTACK_DEPTH; i++)
{
if ( ppCallersEBP < pStackBottom || ppCallersEBP >= pStackTop )
break;
if (i >= 0)
pCallStack[i] = ppCallersEBP[1];
ppCallersEBP = (PVOID*)*ppCallersEBP; // get callers callers ebp
}
}
__except( 1 ) // went too far back on the stack, rest of array is filled with zeros
{
// Benign access violation creating return address stack.
}
#endif // _X86_
return;
}
//**********************************************************************
// ------------------------------
// CMiniStack::GetCallStack - return pointer to call stack string
//
// Entry: Pointer to destination string
//
// Exit: Nothing
// ------------------------------
void CMiniStack::GetCallStackString( char *const pOutputString )
{
int i, j;
PVOID pCallStack[MINISTACK_DEPTH] = {0};
char ImageBuffer[sizeof IMAGEHLP_SYMBOL + 64] = {0};
DWORD_PTR dwFunctionDisplacement = 0;
BOOL b1stStringDone = FALSE;
char szMsg[50] = {0};
#ifndef _WIN64
IMAGEHLP_SYMBOL *const pImageHelpSymbol = (IMAGEHLP_SYMBOL*)ImageBuffer;
#else // _WIN64
IMAGEHLP_SYMBOL64 *const pImageHelpSymbol = (IMAGEHLP_SYMBOL64*)ImageBuffer;
#endif // _WIN64
strcpy(pOutputString, "STACK: ");
if (m_bNotInited)
{
// Make sure we are the only thread attempting to call Initialize() at one time.
if (InterlockedIncrement(&m_nGuardInit) == 1)
Initialize(pOutputString);
InterlockedDecrement(&m_nGuardInit);
// If we skipped the Initialize() step or the Initialize() failed, we can just exit.
if (m_bNotInited)
return;
}
NoteCurrentCallStack(pCallStack);
pImageHelpSymbol->SizeOfStruct = sizeof( *pImageHelpSymbol );
pImageHelpSymbol->Flags = 0;
pImageHelpSymbol->MaxNameLength = sizeof ImageBuffer - sizeof *pImageHelpSymbol - sizeof TCHAR;
// Loop thru the call stack addresses and pick up the corresponding function names.
for (i = 0; i < MINISTACK_DEPTH && pCallStack[i] != NULL; i++)
{
PVOID pAddr;
char *psz;
pAddr = pCallStack[i];
psz = NULL;
// Check to see if the address is in our Symbol table cache.
// For a full array of 100, this only takes an average of 5 usecs on a P550.
for (j = 0; m_SymTable[j].pAddress != NULL && j < SYM_CACHE_SIZE; j++)
{
if (pAddr == m_SymTable[j].pAddress)
{
psz = &m_SymTable[j].szName[0];
m_SymTable[j].nReadCount++; // Don't worry about wraps.
break;
}
}
// It's not in the cache, so get the name from the symbol file(using ImageHlp.dll).
if (psz == NULL)
{
pImageHelpSymbol->Address = (DWORD_PTR)pAddr;
if ( m_pSymGetSymFromAddr( m_hPID, (DWORD_PTR)pCallStack[i], &dwFunctionDisplacement, pImageHelpSymbol) != FALSE )
{
psz = pImageHelpSymbol->Name;
// We've taken a lot of time to extract the name. Now save it in our Symbol table cache.
// Make sure we are the only thread attempting to update the cache at one time.
// If it doesn't get updated by a particular thread, it doesn't matter - it'll get updated
// some other time.
if (InterlockedIncrement(&m_nGuardSymTable) == 1)
{
int nLastSlotFilled;
// Fill slot.
m_SymTable[m_nNextCacheSlot].pAddress = pAddr;
m_SymTable[m_nNextCacheSlot].nReadCount = 0;
// Some symbol names are invalid, so omit them.
if ( !_stricmp(pImageHelpSymbol->Name, "GetModuleHandleA"))
{
wsprintf(szMsg, "0x%p (no symbols)", pAddr);
strcpy(m_SymTable[m_nNextCacheSlot].szName, szMsg);
psz = m_SymTable[m_nNextCacheSlot].szName;
}
else
strcpy(m_SymTable[m_nNextCacheSlot].szName, pImageHelpSymbol->Name);
nLastSlotFilled = m_nNextCacheSlot;
// Select the next slot
if ( !m_bCacheIsFull)
{
// This will work fine until we fill the cache table,
// then we have to change our strategy and look for the
// Least-Used slot.
m_nNextCacheSlot++;
if (m_nNextCacheSlot >= SYM_CACHE_SIZE)
m_bCacheIsFull = TRUE;
}
// The cache is full, so we'll find the Least-Used slot and select that.
if (m_bCacheIsFull)
{
int nLowestReadCount = 0x7fffffff;
int k = 0;
for (j = 0; j < SYM_CACHE_SIZE; j++)
{
if (j != nLastSlotFilled && m_SymTable[j].nReadCount < nLowestReadCount)
{
nLowestReadCount = m_SymTable[j].nReadCount;
k = j;
if (nLowestReadCount == 0)
break;
}
}
m_nNextCacheSlot = k;
}
}
InterlockedDecrement(&m_nGuardSymTable);
}
}
if (psz)
{
if (b1stStringDone)
strcat(pOutputString, ", ");
strcat(pOutputString, psz);
b1stStringDone = TRUE;
}
}
return;
}
#endif //#ifdef PER_PROCESS_INSTANTIATIONS // Define this in just one source file.
#endif // __MINISTACK_H__