You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
464 lines
12 KiB
464 lines
12 KiB
|
|
#include <windows.h>
|
|
|
|
#include <debug.h>
|
|
#include <crtdbg.h>
|
|
#include <tstr.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
|
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
|
|
|
|
|
|
#define TRACE_HRESULT 0x01
|
|
#define TRACE_Win32 0x02
|
|
|
|
|
|
void OutputDebugStringDBWIN( LPCTSTR lpOutputString, ...);
|
|
|
|
void WriteFilename( TSTR & msg, LPCTSTR pFile );
|
|
|
|
void StackTrace( TSTR & str );
|
|
|
|
|
|
LPCTSTR g_pLevelStrs [ ] =
|
|
{
|
|
TEXT("DBG"),
|
|
TEXT("INF"),
|
|
TEXT("WRN"),
|
|
TEXT("ERR"),
|
|
TEXT("PRM"),
|
|
TEXT("PRW"),
|
|
TEXT("IOP"),
|
|
TEXT("ASD"),
|
|
TEXT("ASR"),
|
|
TEXT("CAL"),
|
|
TEXT("RET"),
|
|
TEXT("???"),
|
|
};
|
|
|
|
|
|
static
|
|
void InternalTrace( LPCTSTR pFile, ULONG uLineNo, DWORD dwLevel, DWORD dwFlags, const void * pThis, HRESULT hr, LPCTSTR pStr )
|
|
{
|
|
if( dwLevel >= ARRAYSIZE( g_pLevelStrs ) )
|
|
dwLevel = ARRAYSIZE( g_pLevelStrs ) - 1; // "???" unknown entry
|
|
|
|
// Basic message stuff - pid, tid... (also pass this and use object ptr?)
|
|
// TODO - allow naming of threads?
|
|
|
|
DWORD pid = GetCurrentProcessId();
|
|
DWORD tid = GetCurrentThreadId();
|
|
|
|
// Module:file:line pid:tid str
|
|
TSTR msg(512);
|
|
msg << g_pLevelStrs[ dwLevel ] << TEXT(" ");
|
|
|
|
if( ! pFile )
|
|
WriteFilename( msg, TEXT("[missing file]") );
|
|
else
|
|
WriteFilename( msg, pFile );
|
|
|
|
msg << TEXT(":")
|
|
<< uLineNo << TEXT(" ")
|
|
<< WriteHex( pid ) << TEXT(":")
|
|
<< WriteHex( tid ) << TEXT(" ");
|
|
|
|
if( pThis )
|
|
{
|
|
msg << TEXT("this=") << WriteHex( pThis, 8 ) << TEXT(" ");
|
|
}
|
|
|
|
if( dwFlags & TRACE_HRESULT )
|
|
{
|
|
msg << WriteError( hr ) << TEXT(" ");
|
|
}
|
|
|
|
if( dwFlags & TRACE_Win32 )
|
|
{
|
|
msg << WriteError( GetLastError() ) << TEXT(" ");
|
|
}
|
|
|
|
if( ! pStr )
|
|
msg << TEXT("[missing string]") << TEXT("\r\n");
|
|
else
|
|
msg << pStr << TEXT("\r\n");
|
|
|
|
// For the moment, just send to DBWIN...
|
|
// OutputDebugString( msg );
|
|
OutputDebugStringDBWIN( msg );
|
|
|
|
#ifdef DEBUG
|
|
if( dwLevel == _TRACE_ASSERT_D || dwLevel == _TRACE_ERR )
|
|
{
|
|
_ASSERT(0);
|
|
// DebugBreak();
|
|
}
|
|
#endif // DEBUG
|
|
}
|
|
|
|
|
|
void _Trace( LPCTSTR pFile, ULONG uLineNo, DWORD dwLevel, const void * pThis, LPCTSTR pStr )
|
|
{
|
|
InternalTrace( pFile, uLineNo, dwLevel, 0, pThis, 0, pStr );
|
|
}
|
|
|
|
void _TraceHR( LPCTSTR pFile, ULONG uLineNo, DWORD dwLevel, const void * pThis, HRESULT hr, LPCTSTR pStr )
|
|
{
|
|
InternalTrace( pFile, uLineNo, dwLevel, TRACE_HRESULT, pThis, hr, pStr );
|
|
}
|
|
|
|
void _TraceW32( LPCTSTR pFile, ULONG uLineNo, DWORD dwLevel, const void * pThis, LPCTSTR pStr )
|
|
{
|
|
InternalTrace( pFile, uLineNo, dwLevel, TRACE_Win32, pThis, 0, pStr );
|
|
}
|
|
|
|
|
|
|
|
// Add just the 'filename' part of the full path, minus base and extention.
|
|
// So for "g:\dev\vss\msaa\common\file.cpp", write "file".
|
|
// The start of this string is that last found ':', '\', or start of string if those are not present.
|
|
// The end of this string is the last '.' found after the start position, otherwise the end of the string.
|
|
void WriteFilename( TSTR & str, LPCTSTR pPath )
|
|
{
|
|
LPCTSTR pScan = pPath;
|
|
LPCTSTR pStart = pPath;
|
|
LPCTSTR pEnd = NULL;
|
|
|
|
// Scan till we hit the end, or a '.'...
|
|
while( *pScan != '\0' )
|
|
{
|
|
if( *pScan == '.' )
|
|
{
|
|
pEnd = pScan;
|
|
pScan++;
|
|
}
|
|
if( *pScan == '\\' || *pScan == ':' )
|
|
{
|
|
pScan++;
|
|
pStart = pScan;
|
|
pEnd = NULL;
|
|
}
|
|
else
|
|
{
|
|
pScan++;
|
|
}
|
|
}
|
|
|
|
if( pEnd == NULL )
|
|
pEnd = pScan;
|
|
|
|
str.append( pStart, pEnd - pStart );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void OutputDebugStringDBWIN( LPCTSTR lpOutputString, ... )
|
|
{
|
|
// create the output buffer
|
|
TCHAR achBuffer[500];
|
|
va_list args;
|
|
va_start(args, lpOutputString);
|
|
wvsprintf(achBuffer, lpOutputString, args);
|
|
va_end(args);
|
|
|
|
|
|
// make sure DBWIN is open and waiting
|
|
HANDLE heventDBWIN = OpenEvent(EVENT_MODIFY_STATE, FALSE, TEXT("DBWIN_BUFFER_READY"));
|
|
if( !heventDBWIN )
|
|
{
|
|
//MessageBox(NULL, TEXT("DBWIN_BUFFER_READY nonexistent"), NULL, MB_OK);
|
|
return;
|
|
}
|
|
|
|
// get a handle to the data synch object
|
|
HANDLE heventData = OpenEvent(EVENT_MODIFY_STATE, FALSE, TEXT("DBWIN_DATA_READY"));
|
|
if ( !heventData )
|
|
{
|
|
// MessageBox(NULL, TEXT("DBWIN_DATA_READY nonexistent"), NULL, MB_OK);
|
|
CloseHandle(heventDBWIN);
|
|
return;
|
|
}
|
|
|
|
HANDLE hSharedFile = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, 4096, TEXT("DBWIN_BUFFER"));
|
|
if (!hSharedFile)
|
|
{
|
|
//MessageBox(NULL, TEXT("DebugTrace: Unable to create file mapping object DBWIN_BUFFER"), TEXT("Error"), MB_OK);
|
|
CloseHandle(heventDBWIN);
|
|
CloseHandle(heventData);
|
|
return;
|
|
}
|
|
|
|
LPSTR lpszSharedMem = (LPSTR)MapViewOfFile(hSharedFile, FILE_MAP_WRITE, 0, 0, 512);
|
|
if (!lpszSharedMem)
|
|
{
|
|
//MessageBox(NULL, "DebugTrace: Unable to map shared memory", "Error", MB_OK);
|
|
CloseHandle(heventDBWIN);
|
|
CloseHandle(heventData);
|
|
return;
|
|
}
|
|
|
|
// wait for buffer event
|
|
WaitForSingleObject(heventDBWIN, INFINITE);
|
|
|
|
// write it to the shared memory
|
|
*((LPDWORD)lpszSharedMem) = GetCurrentProcessId();
|
|
#ifdef UNICODE
|
|
CHAR szBuf[500];
|
|
wcstombs(szBuf, achBuffer, sizeof( szBuf ) );
|
|
sprintf(lpszSharedMem + sizeof(DWORD), "%s", szBuf);
|
|
#else
|
|
sprintf(lpszSharedMem + sizeof(DWORD), "%s", achBuffer);
|
|
#endif
|
|
|
|
// signal data ready event
|
|
SetEvent(heventData);
|
|
|
|
// clean up handles
|
|
CloseHandle(hSharedFile);
|
|
CloseHandle(heventData);
|
|
CloseHandle(heventDBWIN);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Prototype stack trace code...
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct _IMAGEHLP_SYMBOL {
|
|
DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL)
|
|
DWORD Address; // virtual address including dll base address
|
|
DWORD Size; // estimated size of symbol, can be zero
|
|
DWORD Flags; // info about the symbols, see the SYMF defines
|
|
DWORD MaxNameLength; // maximum size of symbol name in 'Name'
|
|
CHAR Name[1]; // symbol name (null terminated string)
|
|
} IMAGEHLP_SYMBOL, *PIMAGEHLP_SYMBOL;
|
|
|
|
typedef enum {
|
|
AddrMode1616,
|
|
AddrMode1632,
|
|
AddrModeReal,
|
|
AddrModeFlat
|
|
} ADDRESS_MODE;
|
|
|
|
typedef struct _tagADDRESS64 {
|
|
DWORD64 Offset;
|
|
WORD Segment;
|
|
ADDRESS_MODE Mode;
|
|
} ADDRESS64, *LPADDRESS64;
|
|
|
|
typedef struct _tagADDRESS {
|
|
DWORD Offset;
|
|
WORD Segment;
|
|
ADDRESS_MODE Mode;
|
|
} ADDRESS, *LPADDRESS;
|
|
|
|
typedef struct _KDHELP {
|
|
DWORD Thread;
|
|
DWORD ThCallbackStack;
|
|
DWORD NextCallback;
|
|
DWORD FramePointer;
|
|
DWORD KiCallUserMode;
|
|
DWORD KeUserCallbackDispatcher;
|
|
DWORD SystemRangeStart;
|
|
DWORD ThCallbackBStore;
|
|
DWORD Reserved[8];
|
|
} KDHELP, *PKDHELP;
|
|
|
|
typedef struct _tagSTACKFRAME {
|
|
ADDRESS AddrPC; // program counter
|
|
ADDRESS AddrReturn; // return address
|
|
ADDRESS AddrFrame; // frame pointer
|
|
ADDRESS AddrStack; // stack pointer
|
|
PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
|
|
DWORD Params[4]; // possible arguments to the function
|
|
BOOL Far; // WOW far call
|
|
BOOL Virtual; // is this a virtual frame?
|
|
DWORD Reserved[3];
|
|
KDHELP KdHelp;
|
|
ADDRESS AddrBStore; // backing store pointer
|
|
} STACKFRAME, *LPSTACKFRAME;
|
|
|
|
typedef
|
|
BOOL
|
|
(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE)(
|
|
HANDLE hProcess,
|
|
LPCVOID lpBaseAddress,
|
|
PVOID lpBuffer,
|
|
DWORD nSize,
|
|
PDWORD lpNumberOfBytesRead
|
|
);
|
|
|
|
typedef
|
|
PVOID
|
|
(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE)(
|
|
HANDLE hProcess,
|
|
DWORD AddrBase
|
|
);
|
|
|
|
typedef
|
|
DWORD
|
|
(__stdcall *PGET_MODULE_BASE_ROUTINE)(
|
|
HANDLE hProcess,
|
|
DWORD Address
|
|
);
|
|
|
|
typedef
|
|
DWORD
|
|
(__stdcall *PTRANSLATE_ADDRESS_ROUTINE)(
|
|
HANDLE hProcess,
|
|
HANDLE hThread,
|
|
LPADDRESS lpaddr
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef BOOL (WINAPI * PFN_SymInitialize)( HANDLE, LPSTR, BOOL );
|
|
typedef BOOL (WINAPI * PFN_StackWalk)( DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID,
|
|
PREAD_PROCESS_MEMORY_ROUTINE,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE,
|
|
PGET_MODULE_BASE_ROUTINE,
|
|
PTRANSLATE_ADDRESS_ROUTINE );
|
|
typedef LPVOID (WINAPI * PFN_SymFunctionTableAccess)( HANDLE, DWORD );
|
|
typedef DWORD (WINAPI * PFN_SymGetModuleBase)( HANDLE, DWORD );
|
|
typedef BOOL (WINAPI * PFN_SymGetSymFromAddr)( HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL );
|
|
typedef BOOL (WINAPI * PFN_SymCleanup)( HANDLE hProcess );
|
|
|
|
|
|
PFN_SymInitialize pfnSymInitialize;
|
|
PFN_StackWalk pfnStackWalk;
|
|
PFN_SymFunctionTableAccess pfnSymFunctionTableAccess;
|
|
PFN_SymGetModuleBase pfnSymGetModuleBase;
|
|
PFN_SymGetSymFromAddr pfnSymGetSymFromAddr;
|
|
PFN_SymCleanup pfnSymCleanup;
|
|
|
|
|
|
#ifdef _ALPHA_
|
|
#define CH_MACHINE IMAGE_FILE_MACHINE_ALPHA
|
|
#else
|
|
#define CH_MACHINE IMAGE_FILE_MACHINE_I386
|
|
#endif
|
|
|
|
|
|
#define MAX_SYM_LEN 128
|
|
|
|
void StackTrace1( EXCEPTION_POINTERS *exp, TSTR & str );
|
|
|
|
#define MY_DBG_EXCEPTION 3
|
|
|
|
void StackTrace( TSTR & str )
|
|
{
|
|
__try {
|
|
// raise an exception to get the exception record to start the stack walk
|
|
RaiseException(MY_DBG_EXCEPTION, 0, 0, NULL);
|
|
}
|
|
__except( StackTrace1( GetExceptionInformation(), str ), EXCEPTION_CONTINUE_EXECUTION ) {
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void StackTrace1( EXCEPTION_POINTERS *exp, TSTR & str )
|
|
{
|
|
#ifdef DBG
|
|
|
|
// don't load debug libraries in retail builds
|
|
|
|
CONTEXT * context = exp->ContextRecord;
|
|
|
|
|
|
HMODULE hModule = LoadLibrary( TEXT( "dbghelp" ) );
|
|
pfnSymInitialize = (PFN_SymInitialize) GetProcAddress( hModule, "SymInitialize" );
|
|
pfnStackWalk = (PFN_StackWalk) GetProcAddress( hModule, "StackWalk" );
|
|
pfnSymFunctionTableAccess = (PFN_SymFunctionTableAccess) GetProcAddress( hModule, "SymFunctionTableAccess" );
|
|
pfnSymGetModuleBase = (PFN_SymGetModuleBase) GetProcAddress( hModule, "SymGetModuleBase" );
|
|
pfnSymGetSymFromAddr = (PFN_SymGetSymFromAddr) GetProcAddress( hModule, "SymGetSymFromAddr" );
|
|
pfnSymCleanup = (PFN_SymCleanup) GetProcAddress( hModule, "SymCleanup" );
|
|
|
|
|
|
|
|
HANDLE hProcess = GetCurrentProcess();
|
|
HANDLE hThread = GetCurrentThread();
|
|
|
|
pfnSymInitialize( hProcess, NULL, TRUE );
|
|
|
|
IMAGEHLP_SYMBOL * psym = (IMAGEHLP_SYMBOL *) new char[ sizeof(IMAGEHLP_SYMBOL) + MAX_SYM_LEN ];
|
|
|
|
STACKFRAME frame;
|
|
memset( &frame, 0, sizeof( frame ) );
|
|
|
|
#if defined (_M_IX86)
|
|
// Initialize the STACKFRAME structure for the first call. This is only
|
|
// necessary for Intel CPUs, and isn't mentioned in the documentation.
|
|
frame.AddrPC.Offset = context->Eip;
|
|
frame.AddrPC.Mode = AddrModeFlat;
|
|
frame.AddrFrame.Offset = context->Ebp;
|
|
frame.AddrFrame.Mode = AddrModeFlat;
|
|
frame.AddrStack.Offset = context->Esp;
|
|
frame.AddrStack.Mode = AddrModeFlat;
|
|
#endif // _M_IX86
|
|
|
|
for( ; ; )
|
|
{
|
|
BOOL bSWRet = pfnStackWalk( CH_MACHINE,
|
|
hProcess,
|
|
hThread,
|
|
& frame,
|
|
NULL, // CONTEXT - NULL for i386
|
|
NULL, // Use ReadProcessMemory
|
|
pfnSymFunctionTableAccess,
|
|
pfnSymGetModuleBase,
|
|
NULL );
|
|
if( ! bSWRet )
|
|
{
|
|
break;
|
|
}
|
|
/*
|
|
frame.AddrPC
|
|
frame.AddrReturn
|
|
frame.AddrFrame
|
|
frame.AddrStack
|
|
frame.Params[ 4 ]
|
|
*/
|
|
psym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
|
|
psym->MaxNameLength = MAX_SYM_LEN;
|
|
|
|
DWORD dwDisplacement;
|
|
if( pfnSymGetSymFromAddr( hProcess, frame.AddrPC.Offset, & dwDisplacement, psym ) )
|
|
{
|
|
}
|
|
else
|
|
{
|
|
}
|
|
|
|
}
|
|
|
|
delete [] psym;
|
|
|
|
pfnSymCleanup( hProcess );
|
|
#endif
|
|
}
|