#include #include #include #include #include #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 }