|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1993 - 1993.
//
// File: debug.cxx
//
// Contents: Shell debugging functionality
//
//----------------------------------------------------------------------------
/*
* DEBUG.CXX * * Developer's API to the Debug Module */
#include <headers.h>
#include "debug.h"
#include "dalibc.h"
#ifdef _DEBUG
// Globals
HINSTANCE g_hinstMain = NULL; HWND g_hwndMain = NULL;
ULONG g_cInitCount = 0; BOOL g_fInit = FALSE; BOOL g_fOutputToConsole = FALSE;
CRITICAL_SECTION g_csTrace; CRITICAL_SECTION g_csResDlg;
// TAGS and stuff
/*
* Number of TAG's registered so far. * */ TAG tagMac;
/*
* Mapping from TAG's to information about them. Entries * 0...tagMac-1 are valid. */ TGRC mptagtgrc[tagMax];
TAG tagCom1 = tagNull; TAG tagError = tagNull; TAG tagWarn = tagNull; TAG tagAssertPop = tagNull; TAG tagTestFailures = tagNull; TAG tagRRETURN = tagNull; TAG tagLeaks = tagNull; TAG tagMagic = tagNull; TAG tagIWatch = tagNull; TAG tagIWatch2 = tagNull; TAG tagReadMapFile = tagNull; TAG tagLeakFilter = tagNull; TAG tagHookMemory = tagNull; TAG tagHookBreak = tagNull; TAG tagCheckAlways = tagNull; TAG tagCheckCRT = tagNull; TAG tagDelayFree = tagNull;
/*
* Handle for debug output file. This file is opened during init, * and output is sent to it when enabled. */ HANDLE hfileDebugOutput = NULL;
/*
* static variables to prevent infinite recursion when calling * SpitPchToDisk */ static BOOL fInSpitPchToDisk = FALSE;
static CHAR szNewline[] = "\r\n"; static CHAR szBackslash[] = "\\";
static CHAR szStateFileExt[] = ".tag"; static CHAR szDbgOutFileExt[] = ".log"; static CHAR szStateFileName[] = "capone.dbg"; static CHAR szDbgOutFileName[] = "capone.log";
/*
* Global temporary buffer for handling TraceTag output. Since * this code is non-reentrant and not recursive, a single buffer * for all Demilayr callers will work ok. */ CHAR rgchTraceTagBuffer[1024] = { 0 };
void DeinitDebug(void); const LPTSTR GetHResultName(HRESULT r); void DebugOutput( CHAR * sz ); VOID SpitPchToDisk(CHAR * pch, UINT cch, HANDLE hfile); VOID SpitSzToDisk( CHAR * sz, HANDLE hfile); TAG TagRegisterSomething( TGTY tgty, CHAR * szOwner, CHAR * szDescrip, BOOL fEnabled = FALSE); BOOL EnableTag(TAG tag, BOOL fEnable);
// F u n c t i o n s
// for some reason GetModuleFileNameA(NULL, rgch, sizeof(rgch));
// seems to return a different length (one including the terminating null?)
// when run under NT and Purify. So I made the dot detector non-fixed!
int findDot(char *string) { int value = -1; // default to return err
int index = 0; // start at the beggining
while(string[++index]) if(string[index]=='.') { value = index; break; }
return(value); }
/*
* InitDebug * * Purpose: * Called to initialize the Debug Module. Sets up any debug * structures. This routine DOES NOT restore the state of the * Debug Module, since TAGs can't be registered until after * this routine exit. The routine RestoreDefaultDebugState() * should be called to restore the state of all TAGs after * all TAGs have been registered. * * Parameters: * hinstance Pointer to application instance * phwnd Pointer to main application window * * Returns: * error code */ void InitDebug(HINSTANCE hinst, HWND hwnd) { static struct { TAG * ptag; TGTY tgty; LPSTR pszClass; LPSTR pszDescr; BOOL fEnabled; } g_ataginfo[] = { &tagCom1, tgtyOther, "!Debug", "Enable Disk for debug output", TRUE, &tagAssertPop, tgtyOther, "!Debug", "Popups on asserts", TRUE, &tagReadMapFile, tgtyOther, "!Debug", "Read MAP file for stack traces", TRUE, &tagLeaks, tgtyOther, "!Memory", "Memory Leaks", FALSE, &tagMagic, tgtyOther, "!Memory", "Module/MAP file parsing", FALSE, &tagError, tgtyTrace, "!Trace", "Errors", TRUE, &tagWarn, tgtyTrace, "!Trace", "Warnings", FALSE, &tagTestFailures, tgtyTrace, "!Trace", "THR, IGNORE_HR", TRUE, &tagRRETURN, tgtyTrace, "!Trace", "RRETURN", FALSE, &tagIWatch, tgtyTrace, "!Watch", "Interface watch", FALSE, &tagIWatch2, tgtyOther, "!Watch", "Interface watch (create wrap, no trace)", FALSE, &tagLeakFilter, tgtyOther, "!Memory", "Filter out known leaks", FALSE, &tagHookMemory, tgtyOther, "!Memory", "Watch unexp sysmem allocs", FALSE, &tagHookBreak, tgtyOther, "!Memory", "Break on simulated failure", FALSE, &tagCheckAlways, tgtyOther, "!Memory", "Check Mem on every alloc/free", FALSE, &tagCheckCRT, tgtyOther, "!Memory", "Include CRT types in leak detection", FALSE, &tagDelayFree, tgtyOther, "!Memory", "Keep freed blocks in heap list", FALSE, };
TGRC * ptgrc; CHAR rgch[MAX_PATH]; int i;
g_cInitCount++;
if (g_fInit) return;
g_fInit = TRUE;
g_hinstMain = hinst; g_hwndMain = hwnd;
// don't want windows to put up message box on INT 24H errors.
SetErrorMode(0x0001);
InitializeCriticalSection(&g_csTrace); InitializeCriticalSection(&g_csResDlg);
// Initialize simulated failures
SetSimFailCounts(0, 1);
// Initialize TAG array
tagMac = tagMin;
// enable tagNull at end of RestoreDefaultDebugState
ptgrc = mptagtgrc + tagNull; ptgrc->tgty = tgtyNull; ptgrc->fEnabled = FALSE; ptgrc->ulBitFlags = TGRC_DEFAULT_FLAGS; ptgrc->szOwner = "dgreene"; ptgrc->szDescrip = "NULL";
// Open debug output file
if (g_hinstMain) { #ifndef _MAC
UINT cch = (UINT) GetModuleFileNameA(g_hinstMain, rgch, sizeof(rgch)); int dotLoc = findDot(rgch); Assert(dotLoc!=-1); strcpy(&rgch[dotLoc], szDbgOutFileExt); #else
TCHAR achAppLoc[MAX_PATH]; DWORD dwRet; short iRet;
dwRet = GetModuleFileName(g_hinstMain, achAppLoc, ARRAY_SIZE(achAppLoc)); Assert (dwRet != 0);
iRet = GetFileTitle(achAppLoc,rgch,sizeof(rgch)); Assert(iRet == 0);
strcat (rgch, szDbgOutFileExt); #endif
} else strcpy(rgch, szDbgOutFileName);
hfileDebugOutput = CreateFileA(rgch, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); if (hfileDebugOutput != INVALID_HANDLE_VALUE) { char rgch2[100];
rgch2[(sizeof(rgch2)/sizeof(rgch2[0])) - 1] = 0; _snprintf(rgch2, (sizeof(rgch2)/sizeof(rgch2[0])) - 1, "logging hinst %p to %s\r\n", g_hinstMain, rgch); SpitSzToDisk(rgch2, hfileDebugOutput); Assert(hfileDebugOutput); }
for (i = 0; i < ARRAY_SIZE(g_ataginfo); i++) { *g_ataginfo[i].ptag = TagRegisterSomething( g_ataginfo[i].tgty, g_ataginfo[i].pszClass, g_ataginfo[i].pszDescr, g_ataginfo[i].fEnabled); }
fInSpitPchToDisk = FALSE; }
/*
* DeinitDebug * * Undoes InitDebug(). */ void DeinitDebug(void) { TAG tag; TGRC * ptgrc;
g_cInitCount--;
if (g_cInitCount) return;
// Close the debug output file
if (hfileDebugOutput) { CHAR rgch[100];
rgch[(sizeof(rgch)/sizeof(rgch[0])) - 1] = 0; _snprintf(rgch, (sizeof(rgch)/sizeof(rgch[0])) - 1, "Done logging for hinst %d\r\n", (ULONG_PTR)g_hinstMain); SpitSzToDisk(rgch, hfileDebugOutput); CloseHandle(hfileDebugOutput); hfileDebugOutput = NULL; }
// Free the tag strings if not already done
for (tag = tagMin, ptgrc = mptagtgrc + tag; tag < tagMac; tag++, ptgrc++) { if (ptgrc->TestFlag(TGRC_FLAG_VALID)) { LocalFree(ptgrc->szOwner); ptgrc->szOwner = NULL; LocalFree(ptgrc->szDescrip); ptgrc->szDescrip = NULL; } }
// Set flags to FALSE. Need to separate from loop above so that
// final memory leak trace tag can work.
for (tag=tagMin, ptgrc = mptagtgrc + tag; tag < tagMac; tag++, ptgrc++) { if (ptgrc->TestFlag(TGRC_FLAG_VALID)) { ptgrc->fEnabled = FALSE; ptgrc->ClearFlag(TGRC_FLAG_VALID); } }
DeleteCriticalSection(&g_csTrace); DeleteCriticalSection(&g_csResDlg); }
//+---------------------------------------------------------------------------
//
// Function: SendDebugOutputToConsole
//
// Synopsis: If called, causes all debug output to go the the console as
// well as the debugger.
//
//----------------------------------------------------------------------------
void SendDebugOutputToConsole(void) { g_fOutputToConsole = TRUE; }
/*
* FReadDebugState * * Purpose: * Read the debug state information file whose name is given by the * string szDebugFile. Set up the tag records accordingly. * * Parameters: * szDebugFile Name of debug file to read * * Returns: * TRUE if file was successfully read; FALSE otherwise. * */
BOOL FReadDebugState( CHAR * szDebugFile ) { HANDLE hfile = NULL; TGRC tgrc; TGRC * ptgrc; TAG tag; INT cchOwner; CHAR rgchOwner[MAX_PATH]; INT cchDescrip; CHAR rgchDescrip[MAX_PATH]; BOOL fReturn = FALSE; DWORD cRead;
hfile = CreateFileA(szDebugFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); if (hfile != INVALID_HANDLE_VALUE) { for (;;) { if (!ReadFile(hfile, &tgrc, sizeof(TGRC), &cRead, NULL)) break;
if (cRead == 0) break;
if (!ReadFile(hfile, &cchOwner, sizeof(UINT), &cRead, NULL)) goto ErrorReturn; Assert(cchOwner <= sizeof(rgchOwner)); if (!ReadFile(hfile, rgchOwner, cchOwner, &cRead, NULL)) goto ErrorReturn;
if (!ReadFile(hfile, &cchDescrip, sizeof(UINT), &cRead, NULL)) goto ErrorReturn; Assert(cchDescrip <= sizeof(rgchDescrip)); if (!ReadFile(hfile, rgchDescrip, cchDescrip, &cRead, NULL)) goto ErrorReturn;
ptgrc = mptagtgrc + tagMin; for (tag = tagMin; tag < tagMac; tag++) { if (ptgrc->TestFlag(TGRC_FLAG_VALID) && !strcmp(rgchOwner, ptgrc->szOwner) && !strcmp(rgchDescrip, ptgrc->szDescrip)) { ptgrc->fEnabled = tgrc.fEnabled; Assert(tgrc.TestFlag(TGRC_FLAG_VALID)); ptgrc->ulBitFlags = tgrc.ulBitFlags; break; }
ptgrc++; } }
CloseHandle(hfile); fReturn = TRUE; }
goto Exit;
ErrorReturn: if (hfile) CloseHandle(hfile);
Exit: return fReturn; }
/*
* FWriteDebugState * * Purpose: * Writes the current state of the Debug Module to the file * name given. The saved state can be restored later by calling * FReadDebugState. * * Parameters: * szDebugFile Name of the file to create and write the debug * state to. * * Returns: * TRUE if file was successfully written; FALSE otherwise. */ BOOL FWriteDebugState( CHAR * szDebugFile ) { HANDLE hfile = NULL; TAG tag; UINT cch; TGRC * ptgrc; BOOL fReturn = FALSE; DWORD cWrite;
hfile = CreateFileA(szDebugFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); if (hfile != INVALID_HANDLE_VALUE) { for (tag = tagMin; tag < tagMac; tag++) { ptgrc = mptagtgrc + tag;
if (!ptgrc->TestFlag(TGRC_FLAG_VALID)) continue;
Assert(ptgrc->szOwner); Assert(ptgrc->szDescrip);
if (!WriteFile(hfile, ptgrc, sizeof(TGRC), &cWrite, NULL)) goto ErrorReturn;
// SZ fields will be overwritten when read back
cch = strlen(ptgrc->szOwner) + 1; if (!WriteFile(hfile, &cch, sizeof(UINT), &cWrite, NULL)) goto ErrorReturn; if (!WriteFile(hfile, ptgrc->szOwner, cch, &cWrite, NULL)) goto ErrorReturn;
cch = strlen(ptgrc->szDescrip) + 1; if (!WriteFile(hfile, &cch, sizeof(UINT), &cWrite, NULL)) goto ErrorReturn; if (!WriteFile(hfile, ptgrc->szDescrip, cch, &cWrite, NULL)) goto ErrorReturn; }
CloseHandle(hfile); fReturn = TRUE; }
goto Exit;
ErrorReturn: if (hfile) CloseHandle(hfile); DeleteFileA(szDebugFile);
Exit: return fReturn; }
//+------------------------------------------------------------------------
//
// Function: SaveDefaultDebugState
//
// Synopsis: Saves the debug state of the executing program to a file
// of the same name, substituting the ".tag" suffix.
//
// Arguments: [void]
//
//-------------------------------------------------------------------------
void SaveDefaultDebugState( void ) { CHAR rgch[MAX_PATH] = "";
if (g_hinstMain) { #ifndef _MAC
UINT cch = (UINT) GetModuleFileNameA(g_hinstMain, rgch, sizeof(rgch)); int dotLoc = findDot(rgch); Assert(dotLoc!=-1); strcpy(&rgch[dotLoc], szStateFileExt); #else
TCHAR achAppLoc[MAX_PATH]; DWORD dwRet; short iRet;
dwRet = GetModuleFileNameA(g_hinstMain, achAppLoc, ARRAY_SIZE(achAppLoc)); Assert (dwRet != 0);
iRet = GetFileTitle(achAppLoc,rgch,sizeof(rgch)); Assert(iRet == 0);
strcat (rgch, szStateFileExt); #endif
} else { strcat(rgch, szStateFileName); } FWriteDebugState(rgch); }
//+------------------------------------------------------------------------
//
// Function: RestoreDefaultDebugState
//
// Synopsis: Restores the debug state for the executing program from
// the state file of the same name, substituting the ".tag"
// suffix.
//
// Arguments: [void]
//
//-------------------------------------------------------------------------
void RestoreDefaultDebugState( void ) { CHAR rgch[MAX_PATH] = "";
if (!g_fInit) { DebugOutput("RestoreDefaultDebugState: Debug library not initialized\n"); return; }
if (g_hinstMain) { #ifndef _MAC
UINT cch = (UINT) GetModuleFileNameA(g_hinstMain, rgch, sizeof(rgch)); int dotLoc = findDot(rgch); Assert(dotLoc!=-1); strcpy(&rgch[dotLoc], szStateFileExt); #else
TCHAR achAppLoc[MAX_PATH]; DWORD dwRet; short iRet;
dwRet = GetModuleFileName(g_hinstMain, achAppLoc, ARRAY_SIZE(achAppLoc)); Assert (dwRet != 0);
iRet = GetFileTitle(achAppLoc,rgch,sizeof(rgch)); Assert(iRet == 0);
strcat (rgch, szStateFileExt); #endif
} else { strcat(rgch, szStateFileName); } FReadDebugState(rgch);
mptagtgrc[tagNull].fEnabled = TRUE; }
/*
* IsTagEnabled * * Purpose: * Returns a boolean value indicating whether the given TAG * has been enabled or disabled by the user. * * Parameters: * tag The TAG to check * * Returns: * TRUE if the TAG has been enabled. * FALSE if the TAG has been disabled. */
BOOL IsTagEnabled(TAG tag) { return mptagtgrc[tag].TestFlag(TGRC_FLAG_VALID) && mptagtgrc[tag].fEnabled; }
/*
* EnableTag * * Purpose: * Sets or resets the TAG value given. Allows code to enable or * disable TAG'd assertions and trace switches. * * Parameters: * tag The TAG to enable or disable * fEnable TRUE if TAG should be enabled, FALSE if it should * be disabled. * Returns: * old state of tag (TRUE if tag was enabled, otherwise FALSE) * */
BOOL EnableTag( TAG tag, BOOL fEnable ) { BOOL fOld;
Assert(mptagtgrc[tag].TestFlag(TGRC_FLAG_VALID)); fOld = mptagtgrc[tag].fEnabled; mptagtgrc[tag].fEnabled = fEnable; return fOld; }
/*
* SpitPchToDisk * * Purpose: * Writes the given string to the (previously opened) debug module * disk file. Does NOT write newline-return; caller should embed it * in string. * * Parameters: * pch Pointer to an array of characters. * cch Number of characters to spit. * pfile file to which to write, or NULL to use * debug output file. */
void SpitPchToDisk( CHAR * pch, UINT cch, HANDLE hfile ) { DWORD cWrite;
if (fInSpitPchToDisk) // already inside this function
return; // aVOID recursion
if (hfile && pch && cch) {
fInSpitPchToDisk = TRUE;
WriteFile(hfile, pch, cch, &cWrite, NULL);
fInSpitPchToDisk = FALSE; } }
/*
* SpitSzToDisk * * Purpose: * Writes the given string to the (previously opened) debug module * disk file. Does NOT write newline-return; caller should embed it * in string. * * Parameters: * sz String to spit. * pfile file to which to write, or NULL to use * debug output file. * * Because this function calls fflush(), we're assuming for the * sake of reasonable performance that only debug functions making * output to disk are calling this function. We can't put this in * SpitPchToDisk because calls that function, and any * enabled trace tag would degrade performance. */
VOID SpitSzToDisk( CHAR * sz, HANDLE hfile ) { if (hfile && sz) { SpitPchToDisk(sz, strlen(sz), hfile); } }
/*
* TagRegisterSomething * * Purpose: * Does actual work of allocating TAG, and initializing TGRC. * The owner and description strings are duplicated from the * arguments passed in. * * Parameters: * tgty Tag type to register. * szOwner Owner. * szDescrip Description. * * Returns: * New TAG, or tagNull if none is available. */
TAG TagRegisterSomething( TGTY tgty, CHAR * szOwner, CHAR * szDescrip, BOOL fEnabled) { TAG tag; TAG tagNew = tagNull; TGRC * ptgrc; CHAR * szOwnerDup = NULL; CHAR * szDescripDup = NULL; UINT cb;
for (tag = tagMin, ptgrc = mptagtgrc + tag; tag < tagMac; tag++, ptgrc++) { if (ptgrc->TestFlag(TGRC_FLAG_VALID)) { if(!(strcmp(szOwner, ptgrc->szOwner) || strcmp(szDescrip, ptgrc->szDescrip))) { return tag; } } else if (tagNew == tagNull) tagNew= tag; }
// Make duplicate copies.
Assert(szOwner); Assert(szDescrip); cb = strlen(szOwner) + 1;
// we use LocalAlloc here instead of new so
// we don't interfere with leak reporting because of the
// dependency between the debug library and the
// leak reporting code (i.e., don't touch this --Erik)
szOwnerDup = (LPSTR) LocalAlloc(LMEM_FIXED, cb); if (szOwnerDup == NULL) { goto Error; }
strcpy(szOwnerDup, szOwner);
cb = strlen(szDescrip) + 1; szDescripDup = (LPSTR) LocalAlloc(LMEM_FIXED, cb); if (szDescripDup == NULL) { goto Error; }
strcpy(szDescripDup, szDescrip);
if (tagNew == tagNull) { if (tagMac >= tagMax) { #ifdef NEVER
AssertSz(FALSE, "Too many tags registered already!"); #endif
Assert(FALSE); return tagNull; }
tag = tagMac++; } else tag = tagNew;
ptgrc = mptagtgrc + tag;
ptgrc->fEnabled = fEnabled; ptgrc->ulBitFlags = TGRC_DEFAULT_FLAGS; ptgrc->tgty = tgty; ptgrc->szOwner = szOwnerDup; ptgrc->szDescrip = szDescripDup;
return tag;
Error: LocalFree(szOwnerDup); LocalFree(szDescripDup); return tagNull; }
/*
* DeregisterTag * * Purpose: * Deregisters tag, removing it from tag table. * * Parameters: * tag Tag to deregister. */
void DeregisterTag(TAG tag) { // don't allow deregistering the tagNull entry
// but exit gracefully
if (!tag) return;
Assert(tag < tagMac); Assert(mptagtgrc[tag].TestFlag(TGRC_FLAG_VALID));
mptagtgrc[tag].fEnabled = FALSE; mptagtgrc[tag].ClearFlag(TGRC_FLAG_VALID); LocalFree(mptagtgrc[tag].szOwner); mptagtgrc[tag].szOwner = NULL; LocalFree(mptagtgrc[tag].szDescrip); mptagtgrc[tag].szDescrip = NULL; }
/*
* TagRegisterTrace * * Purpose: * Registers a class of trace points, and returns an identifying * TAG for that class. * * Parameters: * szOwner The email name of the developer writing the code * that registers the class. * szDescrip A short description of the class of trace points. * For instance: "All calls to PvAlloc() and HvFree()" * * Returns: * TAG identifying class of trace points, to be used in calls to * the trace routines. */
TAG TagRegisterTrace( CHAR * szOwner, CHAR * szDescrip, BOOL fEnabled ) { if (!g_fInit) { DebugOutput("TagRegisterTrace: Debug library not initialized\n"); return tagNull; }
return TagRegisterSomething(tgtyTrace, szOwner, szDescrip, fEnabled); }
TAG TagRegisterOther( CHAR * szOwner, CHAR * szDescrip, BOOL fEnabled ) { if (!g_fInit) { OutputDebugStringA("TagRegisterOther: Debug library not initialized"); return tagNull; }
return TagRegisterSomething(tgtyOther, szOwner, szDescrip, fEnabled); }
TAG TagError( void ) { return tagError; }
TAG TagWarning( void ) { return tagWarn; }
TAG TagLeakFilter( void ) { return tagLeakFilter; }
TAG TagHookMemory(void) { return tagHookMemory; }
TAG TagHookBreak(void) { return tagHookBreak; }
TAG TagLeaks(void) { return tagLeaks; }
TAG TagCheckAlways(void) { return tagCheckAlways; }
TAG TagCheckCRT(void) { return tagCheckCRT; }
TAG TagDelayFree(void) { return tagDelayFree; }
/*
* Purpose: * Clears the debug screen */
void ClearDebugScreen( void ) { #ifndef _MAC
TraceTag((tagNull, "\x1B[2J")); #endif
}
/*
* DebugOutput * * Purpose: * Writes the given string out the debug port. * Does NOT write newline-return; caller should embed it in string. * * Parameters: * sz String to spit. */
void DebugOutput( CHAR * sz ) { #ifdef NEVER
HANDLE hfile;
hfile = CreateFileA("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hfile != INVALID_HANDLE_VALUE) { DWORD lcbWritten; WriteFile(hfile, sz, (DWORD) strlen(sz), &lcbWritten, NULL); CloseHandle(hfile); } #endif // NEVER
OutputDebugStringA(sz); }
/*
* TaggedTrace * * Purpose: * Uses the given format string and parameters to render a * string into a buffer. The rendered string is sent to the * destination indicated by the given tag, or sent to the bit * bucket if the tag is disabled. * * Arguments: * tag Identifies the tag group * szFmt Format string for _snprintf (qqv) */
BOOL __cdecl TaggedTrace(TAG tag, CHAR * szFmt, ...) { BOOL f;
va_list valMarker;
va_start(valMarker, szFmt); f = TaggedTraceListEx(tag, 0, szFmt, valMarker); va_end(valMarker);
return f; }
BOOL __cdecl TaggedTraceEx(TAG tag, USHORT usFlags, CHAR * szFmt, ...) { BOOL f;
va_list valMarker;
va_start(valMarker, szFmt); f = TaggedTraceListEx(tag, usFlags, szFmt, valMarker); va_end(valMarker);
return f; }
BOOL __cdecl TaggedTraceListEx(TAG tag, USHORT usFlags, CHAR * szFmt, va_list valMarker) { static CHAR szFmtOwner[] = "DA %s (%lx): "; static CHAR szFmtHR[] = "<%ls (0x%lx)>"; static CHAR szHRID[] = "%hr"; TGRC * ptgrc; int cch;
if (!g_fInit) { DebugOutput("TaggedTrace: Debug library not initialized\n"); return FALSE; }
if (tag == tagNull) ptgrc = mptagtgrc + tagCom1; else ptgrc = mptagtgrc + tag;
if (!ptgrc->fEnabled) return FALSE;
EnterCriticalSection(&g_csTrace);
Assert(ptgrc->TestFlag(TGRC_FLAG_VALID));
if (!(usFlags & TAG_NONAME)) { cch = _snprintf( rgchTraceTagBuffer, ARRAY_SIZE(rgchTraceTagBuffer), szFmtOwner, ptgrc->szOwner, GetCurrentThreadId()); } else { cch = 0; }
hrvsnprintf( rgchTraceTagBuffer + cch, ARRAY_SIZE(rgchTraceTagBuffer) - cch, szFmt, valMarker);
if (ptgrc->TestFlag(TGRC_FLAG_DISK)) { SpitSzToDisk(rgchTraceTagBuffer, hfileDebugOutput); SpitSzToDisk(szNewline, hfileDebugOutput); }
if ((usFlags & TAG_USECONSOLE) || g_fOutputToConsole) printf(rgchTraceTagBuffer);
if (!(usFlags & TAG_USECONSOLE)) DebugOutput(rgchTraceTagBuffer);
if (!(usFlags & TAG_NONEWLINE)) { if ((usFlags & TAG_USECONSOLE) || g_fOutputToConsole) printf(szNewline); if (!(usFlags & TAG_USECONSOLE)) DebugOutput(szNewline); }
LeaveCriticalSection(&g_csTrace);
if (ptgrc->TestFlag(TGRC_FLAG_BREAK)) { return MessageBoxA( NULL, ptgrc->szDescrip, "Trace Tag Break, OK=Ignore, Cancel=Int3", MB_SETFOREGROUND | MB_TASKMODAL | MB_ICONEXCLAMATION | MB_OKCANCEL) == IDCANCEL; }
return FALSE; }
#ifdef NEVER
void TaggedTraceCallers(TAG tag, int iStart, int cTotal) { DWORD adwEip[32]; int i; int c; int ib; LPSTR pstr;
if (!IsTagEnabled(tag)) return;
if (cTotal > ARRAY_SIZE(adwEip)) cTotal = ARRAY_SIZE(adwEip);
c = GetStackBacktrace(iStart + 1, cTotal, adwEip); for (i = 0; i < c; i++) { MapAddressToFunctionOffset((LPBYTE) adwEip[i], &pstr, &ib); TaggedTraceEx(tag, TAG_NONAME, " %08x %s + 0x%x", adwEip[i], pstr, ib); } } #endif // NEVER
//+---------------------------------------------------------------
//
// Function: GetHResultName
//
// Synopsis: Returns a printable string for the given hresult
//
// Arguments: [scode] -- The status code to report.
//
// Notes: This function disappears in retail builds.
//
//----------------------------------------------------------------
const LPTSTR GetHResultName(HRESULT r) { LPTSTR lpstr;
#define CASE_SCODE(sc) \
case sc: lpstr = _T(#sc); break;
switch (r) { /* SCODE's defined in SCODE.H */ CASE_SCODE(S_OK) CASE_SCODE(S_FALSE) CASE_SCODE(OLE_S_USEREG) CASE_SCODE(OLE_S_STATIC) CASE_SCODE(OLE_S_MAC_CLIPFORMAT) CASE_SCODE(DRAGDROP_S_DROP) CASE_SCODE(DRAGDROP_S_USEDEFAULTCURSORS) CASE_SCODE(DRAGDROP_S_CANCEL) CASE_SCODE(DATA_S_SAMEFORMATETC) CASE_SCODE(VIEW_S_ALREADY_FROZEN) CASE_SCODE(CACHE_S_FORMATETC_NOTSUPPORTED) CASE_SCODE(CACHE_S_SAMECACHE) CASE_SCODE(CACHE_S_SOMECACHES_NOTUPDATED) CASE_SCODE(OLEOBJ_S_INVALIDVERB) CASE_SCODE(OLEOBJ_S_CANNOT_DOVERB_NOW) CASE_SCODE(OLEOBJ_S_INVALIDHWND) CASE_SCODE(INPLACE_S_TRUNCATED) CASE_SCODE(CONVERT10_S_NO_PRESENTATION) CASE_SCODE(MK_S_REDUCED_TO_SELF) CASE_SCODE(MK_S_ME) CASE_SCODE(MK_S_HIM) CASE_SCODE(MK_S_US) CASE_SCODE(MK_S_MONIKERALREADYREGISTERED) CASE_SCODE(STG_S_CONVERTED)
CASE_SCODE(E_UNEXPECTED) CASE_SCODE(E_NOTIMPL) CASE_SCODE(E_OUTOFMEMORY) CASE_SCODE(E_INVALIDARG) CASE_SCODE(E_NOINTERFACE) CASE_SCODE(E_POINTER) CASE_SCODE(E_HANDLE) CASE_SCODE(E_ABORT) CASE_SCODE(E_FAIL) CASE_SCODE(E_ACCESSDENIED)
/* SCODE's defined in DVOBJ.H */ CASE_SCODE(DATA_E_FORMATETC) // same as DATA_E_FORMATETC CASE_SCODE(DV_E_FORMATETC)
CASE_SCODE(VIEW_E_DRAW) // same as VIEW_E_DRAW CASE_SCODE(E_DRAW)
CASE_SCODE(CACHE_E_NOCACHE_UPDATED)
/* SCODE's defined in OLE2.H */ CASE_SCODE(OLE_E_OLEVERB) CASE_SCODE(OLE_E_ADVF) CASE_SCODE(OLE_E_ENUM_NOMORE) CASE_SCODE(OLE_E_ADVISENOTSUPPORTED) CASE_SCODE(OLE_E_NOCONNECTION) CASE_SCODE(OLE_E_NOTRUNNING) CASE_SCODE(OLE_E_NOCACHE) CASE_SCODE(OLE_E_BLANK) CASE_SCODE(OLE_E_CLASSDIFF) CASE_SCODE(OLE_E_CANT_GETMONIKER) CASE_SCODE(OLE_E_CANT_BINDTOSOURCE) CASE_SCODE(OLE_E_STATIC) CASE_SCODE(OLE_E_PROMPTSAVECANCELLED) CASE_SCODE(OLE_E_INVALIDRECT) CASE_SCODE(OLE_E_WRONGCOMPOBJ) CASE_SCODE(OLE_E_INVALIDHWND) CASE_SCODE(DV_E_DVTARGETDEVICE) CASE_SCODE(DV_E_STGMEDIUM) CASE_SCODE(DV_E_STATDATA) CASE_SCODE(DV_E_LINDEX) CASE_SCODE(DV_E_TYMED) CASE_SCODE(DV_E_CLIPFORMAT) CASE_SCODE(DV_E_DVASPECT) CASE_SCODE(DV_E_DVTARGETDEVICE_SIZE) CASE_SCODE(DV_E_NOIVIEWOBJECT) CASE_SCODE(CONVERT10_E_OLESTREAM_GET) CASE_SCODE(CONVERT10_E_OLESTREAM_PUT) CASE_SCODE(CONVERT10_E_OLESTREAM_FMT) CASE_SCODE(CONVERT10_E_OLESTREAM_BITMAP_TO_DIB) CASE_SCODE(CONVERT10_E_STG_FMT) CASE_SCODE(CONVERT10_E_STG_NO_STD_STREAM) CASE_SCODE(CONVERT10_E_STG_DIB_TO_BITMAP) CASE_SCODE(CLIPBRD_E_CANT_OPEN) CASE_SCODE(CLIPBRD_E_CANT_EMPTY) CASE_SCODE(CLIPBRD_E_CANT_SET) CASE_SCODE(CLIPBRD_E_BAD_DATA) CASE_SCODE(CLIPBRD_E_CANT_CLOSE) CASE_SCODE(DRAGDROP_E_NOTREGISTERED) CASE_SCODE(DRAGDROP_E_ALREADYREGISTERED) CASE_SCODE(DRAGDROP_E_INVALIDHWND) CASE_SCODE(OLEOBJ_E_NOVERBS) CASE_SCODE(INPLACE_E_NOTUNDOABLE) CASE_SCODE(INPLACE_E_NOTOOLSPACE)
/* SCODE's defined in STORAGE.H */ CASE_SCODE(STG_E_INVALIDFUNCTION) CASE_SCODE(STG_E_FILENOTFOUND) CASE_SCODE(STG_E_PATHNOTFOUND) CASE_SCODE(STG_E_TOOMANYOPENFILES) CASE_SCODE(STG_E_ACCESSDENIED) CASE_SCODE(STG_E_INVALIDHANDLE) CASE_SCODE(STG_E_INSUFFICIENTMEMORY) CASE_SCODE(STG_E_INVALIDPOINTER) CASE_SCODE(STG_E_NOMOREFILES) CASE_SCODE(STG_E_DISKISWRITEPROTECTED) CASE_SCODE(STG_E_SEEKERROR) CASE_SCODE(STG_E_WRITEFAULT) CASE_SCODE(STG_E_READFAULT) CASE_SCODE(STG_E_LOCKVIOLATION) CASE_SCODE(STG_E_FILEALREADYEXISTS) CASE_SCODE(STG_E_INVALIDPARAMETER) CASE_SCODE(STG_E_MEDIUMFULL) CASE_SCODE(STG_E_ABNORMALAPIEXIT) CASE_SCODE(STG_E_INVALIDHEADER) CASE_SCODE(STG_E_INVALIDNAME) CASE_SCODE(STG_E_UNKNOWN) CASE_SCODE(STG_E_UNIMPLEMENTEDFUNCTION) CASE_SCODE(STG_E_INVALIDFLAG) CASE_SCODE(STG_E_INUSE) CASE_SCODE(STG_E_NOTCURRENT) CASE_SCODE(STG_E_REVERTED) CASE_SCODE(STG_E_CANTSAVE) CASE_SCODE(STG_E_OLDFORMAT) CASE_SCODE(STG_E_OLDDLL) CASE_SCODE(STG_E_SHAREREQUIRED)
/* SCODE's defined in COMPOBJ.H */ CASE_SCODE(CO_E_NOTINITIALIZED) CASE_SCODE(CO_E_ALREADYINITIALIZED) CASE_SCODE(CO_E_CANTDETERMINECLASS) CASE_SCODE(CO_E_CLASSSTRING) CASE_SCODE(CO_E_IIDSTRING) CASE_SCODE(CO_E_APPNOTFOUND) CASE_SCODE(CO_E_APPSINGLEUSE) CASE_SCODE(CO_E_ERRORINAPP) CASE_SCODE(CO_E_DLLNOTFOUND) CASE_SCODE(CO_E_ERRORINDLL) CASE_SCODE(CO_E_WRONGOSFORAPP) CASE_SCODE(CO_E_OBJNOTREG) CASE_SCODE(CO_E_OBJISREG) CASE_SCODE(CO_E_OBJNOTCONNECTED) CASE_SCODE(CO_E_APPDIDNTREG) CASE_SCODE(CLASS_E_NOAGGREGATION) CASE_SCODE(CLASS_E_CLASSNOTAVAILABLE) CASE_SCODE(REGDB_E_READREGDB) CASE_SCODE(REGDB_E_WRITEREGDB) CASE_SCODE(REGDB_E_KEYMISSING) CASE_SCODE(REGDB_E_INVALIDVALUE) CASE_SCODE(REGDB_E_CLASSNOTREG) CASE_SCODE(REGDB_E_IIDNOTREG) CASE_SCODE(RPC_E_CALL_REJECTED) CASE_SCODE(RPC_E_CALL_CANCELED) CASE_SCODE(RPC_E_CANTPOST_INSENDCALL) CASE_SCODE(RPC_E_CANTCALLOUT_INASYNCCALL) CASE_SCODE(RPC_E_CANTCALLOUT_INEXTERNALCALL) CASE_SCODE(RPC_E_CONNECTION_TERMINATED) #if defined(NO_NTOLEBUGS)
CASE_SCODE(RPC_E_SERVER_DIED) #endif // NO_NTOLEBUGS
CASE_SCODE(RPC_E_CLIENT_DIED) CASE_SCODE(RPC_E_INVALID_DATAPACKET) CASE_SCODE(RPC_E_CANTTRANSMIT_CALL) CASE_SCODE(RPC_E_CLIENT_CANTMARSHAL_DATA) CASE_SCODE(RPC_E_CLIENT_CANTUNMARSHAL_DATA) CASE_SCODE(RPC_E_SERVER_CANTMARSHAL_DATA) CASE_SCODE(RPC_E_SERVER_CANTUNMARSHAL_DATA) CASE_SCODE(RPC_E_INVALID_DATA) CASE_SCODE(RPC_E_INVALID_PARAMETER) CASE_SCODE(RPC_E_UNEXPECTED)
/* SCODE's defined in MONIKER.H */ CASE_SCODE(MK_E_CONNECTMANUALLY) CASE_SCODE(MK_E_EXCEEDEDDEADLINE) CASE_SCODE(MK_E_NEEDGENERIC) CASE_SCODE(MK_E_UNAVAILABLE) CASE_SCODE(MK_E_SYNTAX) CASE_SCODE(MK_E_NOOBJECT) CASE_SCODE(MK_E_INVALIDEXTENSION) CASE_SCODE(MK_E_INTERMEDIATEINTERFACENOTSUPPORTED) CASE_SCODE(MK_E_NOTBINDABLE) CASE_SCODE(MK_E_NOTBOUND) CASE_SCODE(MK_E_CANTOPENFILE) CASE_SCODE(MK_E_MUSTBOTHERUSER) CASE_SCODE(MK_E_NOINVERSE) CASE_SCODE(MK_E_NOSTORAGE) #if defined(NO_NTOLEBUGS)
CASE_SCODE(MK_S_MONIKERALREADYREGISTERED) #endif //NO_NTOLEBUGS
// Forms error codes
// CASE_SCODE(FORMS_E_NOPAGESSPECIFIED)
// CASE_SCODE(FORMS_E_NOPAGESINTERSECT)
// Dispatch error codes
CASE_SCODE(DISP_E_MEMBERNOTFOUND) CASE_SCODE(DISP_E_PARAMNOTFOUND) CASE_SCODE(DISP_E_BADPARAMCOUNT) CASE_SCODE(DISP_E_BADINDEX) CASE_SCODE(DISP_E_UNKNOWNINTERFACE) CASE_SCODE(DISP_E_NONAMEDARGS) CASE_SCODE(DISP_E_EXCEPTION) CASE_SCODE(DISP_E_TYPEMISMATCH) CASE_SCODE(DISP_E_UNKNOWNNAME)
// Typinfo error codes
CASE_SCODE(TYPE_E_REGISTRYACCESS) CASE_SCODE(TYPE_E_LIBNOTREGISTERED) CASE_SCODE(TYPE_E_UNDEFINEDTYPE) CASE_SCODE(TYPE_E_WRONGTYPEKIND) CASE_SCODE(TYPE_E_ELEMENTNOTFOUND) CASE_SCODE(TYPE_E_INVALIDID) CASE_SCODE(TYPE_E_CANTLOADLIBRARY)
default: lpstr = _T("UNKNOWN SCODE"); }
#undef CASE_SCODE
return lpstr; }
//+---------------------------------------------------------------------------
//
// Function: hrvsnprintf
//
// Synopsis: Prints a string to a buffer, interpreting %hr as a
// format string for an HRESULT.
//
// Arguments: [achBuf] -- The buffer to print into.
// [cchBuf] -- The size of the buffer.
// [pstrFmt] -- The format string.
// [valMarker] -- List of arguments to format string.
//
// Returns: Number of characters printed to the buffer not including
// the terminating NULL. In case of buffer overflow, returns
// -1.
//
// Modifies: [achBuf]
//
//----------------------------------------------------------------------------
int hrvsnprintf(char * achBuf, int cchBuf, const char * pstrFmt, va_list valMarker) { static char achFmtHR[] = "<%ls (0x%lx)>"; static char achHRID[] = "%hr"; char * buf = NULL;
int cch; int cchTotal; const char * lpstr; const char * lpstrLast; int cFormat; HRESULT hrVA;
//
// Scan for %hr tokens. If found, print the corresponding
// hresult into the buffer.
//
// Need to copy a const string since we plan to modify it below
buf = (char *) malloc ((lstrlen(pstrFmt) + 1) * sizeof(char)); lstrcpy(buf,pstrFmt); cch = 0; cchTotal = 0; cFormat = 0; lpstrLast = buf; lpstr = buf; while (*lpstr) { if (*lpstr != '%') { lpstr++; } else if (lpstr[1] == '%') { lpstr += 2; } else if (StrCmpNA(lpstr, achHRID, ARRAY_SIZE(achHRID) - 1)) { cFormat++; lpstr++; } else { //
// Print format string up to the hresult.
//
* (char *) lpstr = 0; cch = _vsnprintf( achBuf + cchTotal, cchBuf - cchTotal, lpstrLast, valMarker); * (char *) lpstr = '%'; if (cch == -1) break;
cchTotal += cch;
//
// Advance valMarker for each printed format.
//
while (cFormat-- > 0) { //
// BUGBUG (adams): Won't work for floats, as their stack size
// is not four bytes.
//
va_arg(valMarker, void *); }
//
// Print hresult into buffer.
//
hrVA = va_arg(valMarker, HRESULT); cch = _snprintf( achBuf + cchTotal, cchBuf - cchTotal, achFmtHR, GetHResultName(hrVA), hrVA); if (cch == -1) break;
cchTotal += cch; lpstr += ARRAY_SIZE(achHRID) - 1; lpstrLast = lpstr; } }
if (cch != -1) { cch = _vsnprintf( achBuf + cchTotal, cchBuf - cchTotal, lpstrLast, valMarker); }
free (buf); return (cch == -1) ? -1 : cchTotal + cch; }
#endif
|