WindowsXP-SP1/windows/advcore/gdiplus/engine/runtime/debug.cpp
2020-09-30 16:53:49 +02:00

319 lines
7.9 KiB
C++

/**************************************************************************\
*
* 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