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.
318 lines
7.9 KiB
318 lines
7.9 KiB
/**************************************************************************\
|
|
*
|
|
* Copyright (c) 1998-1999 Microsoft Corporation
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Debugging routines
|
|
*
|
|
* Revision History:
|
|
*
|
|
* 09/07/1999 agodfrey
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hpp"
|
|
|
|
namespace Globals
|
|
{
|
|
DebugEventProc UserDebugEventProc = 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<char *>(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.
|
|
*
|
|
* 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 the "debug event" callback:
|
|
*
|
|
* We optionally pass WARNINGs and ASSERTs to a reporting function
|
|
* provided by the user, instead of to the debugger.
|
|
* (e.g. their function may pop up a dialog or throw an exception).
|
|
* Lesser events will still be sent to the debugger.
|
|
*
|
|
* History:
|
|
*
|
|
* 02/01/2000 agodfrey
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID _cdecl
|
|
GpLogDebugEvent(
|
|
INT level,
|
|
CHAR *file,
|
|
UINT 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 callbackEventType = -1;
|
|
|
|
CHAR *prefix = "";
|
|
|
|
if (GpDebugLevel <= (level))
|
|
{
|
|
switch (level)
|
|
{
|
|
case DBG_WARNING:
|
|
prefix = "WRN ";
|
|
if (Globals::UserDebugEventProc)
|
|
{
|
|
callbackEventType = DebugEventLevelWarning;
|
|
}
|
|
break;
|
|
|
|
case DBG_RIP:
|
|
prefix = "RIP ";
|
|
if (Globals::UserDebugEventProc)
|
|
{
|
|
callbackEventType = DebugEventLevelFatal;
|
|
}
|
|
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 the user's "debug event" callback produces a popup for
|
|
// each event.
|
|
|
|
_snprintf(
|
|
tempBuffer,
|
|
maxOutputStringSize,
|
|
"%s%s(%d): %s\n",
|
|
prefix,
|
|
StripDirPrefix(file),
|
|
line,
|
|
message
|
|
);
|
|
|
|
if (callbackEventType >= 0)
|
|
{
|
|
// Wrap the following call in an exception handler in case the
|
|
// initial component who set the debug event proc uninitializes
|
|
// on another thread, resulting in UserDebugEventProc being set
|
|
// to NULL.
|
|
|
|
__try
|
|
{
|
|
Globals::UserDebugEventProc((DebugEventLevel) callbackEventType, tempBuffer);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
OutputDebugStringA(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) && (callbackEventType < 0))
|
|
{
|
|
DebugBreak();
|
|
}
|
|
}
|
|
|
|
#endif // DBG
|