/**************************************************************************\ * * Copyright (c) 1998-1999 Microsoft Corporation * * Abstract: * * Debugging routines * * Revision History: * * 09/07/1999 agodfrey * Created it. * \**************************************************************************/ #include "global.h" // Optional callback function for reporting a debug event. enum GpAltDebugEventLevel { GpAltDebugFatal, GpAltDebugWarning }; #define GpAltDebugEventFunction 0 //DEBUGEVENTFUNCTION GpAltDebugEventFunction = NULL; #if DBG // GpDebugLevel is used to control the amount/severity of debugging messages // that are actually output. int GpDebugLevel = DBG_TERSE; /**************************************************************************\ * * Function Description: * * Removes the path portion of a pathname * * Arguments: * * [IN] str - pathname to strip * * Return Value: * * A pointer to the filename portion of the pathname * * History: * * 09/07/1999 agodfrey * Moved from Entry\Initialize.cpp * \**************************************************************************/ const char* StripDirPrefix( const char* str ) { const char* p; p = strrchr(str, '\\'); return p ? p+1 : str; } const int maxInputStringSize = 1024; /**************************************************************************\ * * Function Description: * * Outputs to the debugger * * Arguments: * * [IN] format - printf-like format string and variable arguments * * Return Value: * * Zero. This is to conform to NTDLL's definition of DbgPrint. * * Notes: * * There will be no output if a debugger is not connected. * * History: * * 09/07/1999 agodfrey * Moved from Entry\Initialize.cpp * \**************************************************************************/ ULONG _cdecl DbgPrint( char* format, ... ) { va_list arglist; va_start(arglist, format); char buf[maxInputStringSize]; _vsnprintf(buf, maxInputStringSize, format, arglist); buf[maxInputStringSize-1]=0; OutputDebugStringA(buf); va_end(arglist); return 0; } // If we can't allocate memory for the debug string, we'll use this buffer // in desperation. It's not thread-safe. I *did* say 'desperation'. static char desperationBuffer[maxInputStringSize]; /**************************************************************************\ * * Function Description: * * Creates a new string, and sprintf's to it. * * Arguments: * * [IN] format - printf-like format string and variable arguments * * Return Value: * * The probably-newly-allocated string result. * * Notes: * * This function is not intended for general use. It guards against memory * failure by using a global buffer. So, while the caller is responsible * for freeing the memory, the caller must also check for that buffer. * i.e. we only want DbgEmitMessage to call this. * * It's also only mostly thread-safe, because if we run out of memory, * we'll use that global buffer in a non-protected way. * * This is the only solution I could find so that I could move most of the * implementation details out of the header file. The root cause is that * macros don't handle multiple arguments natively, so we have to pass * the printf arguments as a single macro argument (in parentheses). * Which means, the function that consumes those arguments can have no * other arguments. * * History: * * 02/01/2000 agodfrey * Created it. Finally, I've found a way to get debug implementation * details out of the headers. * \**************************************************************************/ char * _cdecl GpParseDebugString( char* format, ... ) { va_list arglist; va_start(arglist, format); // Don't use GpMalloc here so that we can use ASSERT and WARNING in // our memory allocation routines. char *newBuf = static_cast(LocalAlloc(LMEM_FIXED, maxInputStringSize)); if (!newBuf) { newBuf = desperationBuffer; } _vsnprintf(newBuf, maxInputStringSize, format, arglist); // Nuke the last byte, because MSDN isn't clear on what _vsnprintf does // in that case. newBuf[maxInputStringSize-1]=0; va_end(arglist); return newBuf; } /**************************************************************************\ * * Function Description: * * Processes a debug event. Frees the message string. * * Arguments: * * level - The debug level of the event * file - Should be __FILE__ * line - Should be __LINE__ * message - The debug message. * * Notes: * * You don't want to call this directly. That would be error-prone. * Use ASSERT, WARNING, etc. * * In Office, it will raise an 'assert' dialog if necessary. * * Depending on the debug level, an identifying prefix will be output. * * If the debug level is DBG_RIP, will suspend execution (e.g. by * hitting a breakpoint.) * * Note on Office interop: * * Under Office, we'll pass WARNINGs and ASSERTs to a reporting function * they provide. Their function will breakpoint on ASSERTs. * Lesser events will just be sent to the debugger. * * History: * * 02/01/2000 agodfrey * Created it. * \**************************************************************************/ void _cdecl GpLogDebugEvent( int level, char *file, unsigned int line, char *message ) { // We may want to add things to the passed-in message. So we need // a temporary buffer const int maxOutputStringSize = maxInputStringSize + 100; char tempBuffer[maxOutputStringSize+1]; // MSDN's _vsnprintf doc isn't clear on this, so just in case: tempBuffer[maxOutputStringSize] = 0; int altDebugLevel = -1; char *prefix = ""; if (GpDebugLevel <= (level)) { switch (level) { case DBG_WARNING: prefix = "WRN "; if (GpAltDebugEventFunction) { altDebugLevel = GpAltDebugWarning; } break; case DBG_RIP: prefix = "RIP "; if (GpAltDebugEventFunction) { altDebugLevel = GpAltDebugFatal; } break; } // The convention is that we append the trailing \n, not the caller. // Two reasons: // 1) Callers tend to forget it. // 2) More importantly, it encourages the caller to think of each // call as a separate event. This is important in some cases - e.g. // when Office produces a popup for each event. _snprintf( tempBuffer, maxOutputStringSize, "%s%s(%d): %s\n", prefix, StripDirPrefix(file), line, message ); if (altDebugLevel >= 0) { //GpAltDebugEventFunction(altDebugLevel, tempBuffer); } else { OutputDebugStringA(tempBuffer); } } // Free the message buffer if (message != desperationBuffer) { LocalFree(message); } // Force a breakpoint, if it's warranted. if ((GpDebugLevel <= DBG_RIP) && (level == DBG_RIP) && (altDebugLevel < 0)) { DebugBreak(); } } #endif // DBG