538 lines
12 KiB
C++
538 lines
12 KiB
C++
/***
|
|
*DbgRpt.c - Debug Cluster Reporting Functions
|
|
*
|
|
* Copyright (c) 1988-1998, Microsoft Corporation. All rights reserved.
|
|
*
|
|
*Purpose:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include <malloc.h>
|
|
#include <mbstring.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
|
|
#include <windows.h>
|
|
|
|
#define CLRTL_INCLUDE_DEBUG_REPORTING
|
|
#include "ClRtlDbg.h"
|
|
|
|
#define _ClRtlInterlockedIncrement InterlockedIncrement
|
|
#define _ClRtlInterlockedDecrement InterlockedDecrement
|
|
|
|
/*---------------------------------------------------------------------------
|
|
*
|
|
* Debug Reporting
|
|
*
|
|
--------------------------------------------------------------------------*/
|
|
|
|
static int ClRtlMessageWindow(
|
|
int,
|
|
const char *,
|
|
const char *,
|
|
const char *,
|
|
const char *
|
|
);
|
|
|
|
static int __clrtlMessageBoxA(
|
|
LPCSTR lpText,
|
|
LPCSTR lpCaption,
|
|
UINT uType
|
|
);
|
|
|
|
extern "C"
|
|
{
|
|
_CLRTL_REPORT_HOOK _pfnReportHook;
|
|
|
|
long _clrtlAssertBusy = -1;
|
|
|
|
int _ClRtlDbgMode[_CLRTLDBG_ERRCNT] = {
|
|
_CLRTLDBG_MODE_DEBUG,
|
|
_CLRTLDBG_MODE_DEBUG | _CLRTLDBG_MODE_WNDW,
|
|
_CLRTLDBG_MODE_DEBUG | _CLRTLDBG_MODE_WNDW
|
|
};
|
|
|
|
_HFILE _ClRtlDbgFile[_CLRTLDBG_ERRCNT] = {
|
|
_CLRTLDBG_INVALID_HFILE,
|
|
_CLRTLDBG_INVALID_HFILE,
|
|
_CLRTLDBG_INVALID_HFILE
|
|
};
|
|
}
|
|
|
|
static const char * _ClRtlDbgModeMsg[_CLRTLDBG_ERRCNT] = { "Warning", "Error", "Assertion Failed" };
|
|
|
|
/***
|
|
*void _ClRtlDebugBreak - call OS-specific debug function
|
|
*
|
|
*Purpose:
|
|
* call OS-specific debug function
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#undef _ClRtlDbgBreak
|
|
|
|
extern "C" void _cdecl _ClRtlDbgBreak(
|
|
void
|
|
)
|
|
{
|
|
DebugBreak();
|
|
|
|
} //*** _ClRtlDbgBreak()
|
|
|
|
/***
|
|
*int _ClRtlSetReportMode - set the reporting mode for a given report type
|
|
*
|
|
*Purpose:
|
|
* set the reporting mode for a given report type
|
|
*
|
|
*Entry:
|
|
* int nRptType - the report type
|
|
* int fMode - new mode for given report type
|
|
*
|
|
*Exit:
|
|
* previous mode for given report type
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
extern "C" int __cdecl _ClRtlSetReportMode(
|
|
int nRptType,
|
|
int fMode
|
|
)
|
|
{
|
|
int oldMode;
|
|
|
|
if (nRptType < 0 || nRptType >= _CLRTLDBG_ERRCNT)
|
|
return -1;
|
|
|
|
if (fMode == _CLRTLDBG_REPORT_MODE)
|
|
return _ClRtlDbgMode[nRptType];
|
|
|
|
/* verify flags values */
|
|
if (fMode & ~(_CLRTLDBG_MODE_FILE | _CLRTLDBG_MODE_DEBUG | _CLRTLDBG_MODE_WNDW))
|
|
return -1;
|
|
|
|
oldMode = _ClRtlDbgMode[nRptType];
|
|
|
|
_ClRtlDbgMode[nRptType] = fMode;
|
|
|
|
return oldMode;
|
|
|
|
} //*** _ClRtlSetReportMode()
|
|
|
|
/***
|
|
*int _ClRtlSetReportFile - set the reporting file for a given report type
|
|
*
|
|
*Purpose:
|
|
* set the reporting file for a given report type
|
|
*
|
|
*Entry:
|
|
* int nRptType - the report type
|
|
* _HFILE hFile - new file for given report type
|
|
*
|
|
*Exit:
|
|
* previous file for given report type
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
extern "C" _HFILE __cdecl _ClRtlSetReportFile(
|
|
int nRptType,
|
|
_HFILE hFile
|
|
)
|
|
{
|
|
_HFILE oldFile;
|
|
|
|
if (nRptType < 0 || nRptType >= _CLRTLDBG_ERRCNT)
|
|
return _CLRTLDBG_HFILE_ERROR;
|
|
|
|
if (hFile == _CLRTLDBG_REPORT_FILE)
|
|
return _ClRtlDbgFile[nRptType];
|
|
|
|
oldFile = _ClRtlDbgFile[nRptType];
|
|
|
|
if (_CLRTLDBG_FILE_STDOUT == hFile)
|
|
_ClRtlDbgFile[nRptType] = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
else if (_CLRTLDBG_FILE_STDERR == hFile)
|
|
_ClRtlDbgFile[nRptType] = GetStdHandle(STD_ERROR_HANDLE);
|
|
else
|
|
_ClRtlDbgFile[nRptType] = hFile;
|
|
|
|
return oldFile;
|
|
|
|
} //*** _ClRtlSetReportFile()
|
|
|
|
|
|
/***
|
|
*_CLRTL_REPORT_HOOK _ClRtlSetReportHook() - set client report hook
|
|
*
|
|
*Purpose:
|
|
* set client report hook
|
|
*
|
|
*Entry:
|
|
* _CLRTL_REPORT_HOOK pfnNewHook - new report hook
|
|
*
|
|
*Exit:
|
|
* return previous hook
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
extern "C" _CLRTL_REPORT_HOOK __cdecl _ClRtlSetReportHook(
|
|
_CLRTL_REPORT_HOOK pfnNewHook
|
|
)
|
|
{
|
|
_CLRTL_REPORT_HOOK pfnOldHook = _pfnReportHook;
|
|
_pfnReportHook = pfnNewHook;
|
|
return pfnOldHook;
|
|
|
|
} //*** _ClRtlSetReportHook()
|
|
|
|
|
|
#define MAXLINELEN 64
|
|
#define MAX_MSG 4096
|
|
#define TOOLONGMSG "_ClRtlDbgReport: String too long or IO Error"
|
|
|
|
|
|
/***
|
|
*int _ClRtlDbgReport() - primary reporting function
|
|
*
|
|
*Purpose:
|
|
* Display a message window with the following format.
|
|
*
|
|
* ================= Microsft Visual C++ Debug Library ================
|
|
*
|
|
* {Warning! | Error! | Assertion Failed!}
|
|
*
|
|
* Program: c:\test\mytest\foo.exe
|
|
* [Module: c:\test\mytest\bar.dll]
|
|
* [File: c:\test\mytest\bar.c]
|
|
* [Line: 69]
|
|
*
|
|
* {<warning or error message> | Expression: <expression>}
|
|
*
|
|
* [For information on how your program can cause an assertion
|
|
* failure, see the Visual C++ documentation on asserts]
|
|
*
|
|
* (Press Retry to debug the application)
|
|
*
|
|
* ===================================================================
|
|
*
|
|
*Entry:
|
|
* int nRptType - report type
|
|
* const char * szFile - file name
|
|
* int nLine - line number
|
|
* const char * szModule - module name
|
|
* const char * szFormat - format string
|
|
* ... - var args
|
|
*
|
|
*Exit:
|
|
* if (MessageBox)
|
|
* {
|
|
* Abort -> aborts
|
|
* Retry -> return TRUE
|
|
* Ignore-> return FALSE
|
|
* }
|
|
* else
|
|
* return FALSE
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
extern "C" int __cdecl _ClRtlDbgReport(
|
|
int nRptType,
|
|
const char * szFile,
|
|
int nLine,
|
|
const char * szModule,
|
|
const char * szFormat,
|
|
...
|
|
)
|
|
{
|
|
int retval;
|
|
va_list arglist;
|
|
char szLineMessage[MAX_MSG] = {0};
|
|
char szOutMessage[MAX_MSG] = {0};
|
|
char szUserMessage[MAX_MSG] = {0};
|
|
#define ASSERTINTRO1 "Assertion failed: "
|
|
#define ASSERTINTRO2 "Assertion failed!"
|
|
|
|
va_start(arglist, szFormat);
|
|
|
|
if (nRptType < 0 || nRptType >= _CLRTLDBG_ERRCNT)
|
|
return -1;
|
|
|
|
/*
|
|
* handle the (hopefully rare) case of
|
|
*
|
|
* 1) ASSERT while already dealing with an ASSERT
|
|
* or
|
|
* 2) two threads asserting at the same time
|
|
*/
|
|
if (_CLRTLDBG_ASSERT == nRptType && _ClRtlInterlockedIncrement(&_clrtlAssertBusy) > 0)
|
|
{
|
|
/* use only 'safe' functions -- must not assert in here! */
|
|
static int (APIENTRY *pfnwsprintfA)(LPSTR, LPCSTR, ...) = NULL;
|
|
|
|
if (NULL == pfnwsprintfA)
|
|
{
|
|
HINSTANCE hlib = LoadLibraryA("user32.dll");
|
|
|
|
if (NULL == hlib || NULL == (pfnwsprintfA =
|
|
(int (APIENTRY *)(LPSTR, LPCSTR, ...))
|
|
GetProcAddress(hlib, "wsprintfA")))
|
|
return -1;
|
|
}
|
|
|
|
(*pfnwsprintfA)( szOutMessage,
|
|
"Second Chance Assertion Failed: File %s, Line %d\n",
|
|
szFile, nLine);
|
|
|
|
OutputDebugStringA(szOutMessage);
|
|
|
|
_ClRtlInterlockedDecrement(&_clrtlAssertBusy);
|
|
|
|
_ClRtlDbgBreak();
|
|
return -1;
|
|
}
|
|
|
|
if (szFormat && _vsnprintf(szUserMessage,
|
|
MAX_MSG-max(sizeof(ASSERTINTRO1),sizeof(ASSERTINTRO2)),
|
|
szFormat,
|
|
arglist) < 0)
|
|
strcpy(szUserMessage, TOOLONGMSG);
|
|
|
|
if (_CLRTLDBG_ASSERT == nRptType)
|
|
strcpy(szLineMessage, szFormat ? ASSERTINTRO1 : ASSERTINTRO2);
|
|
|
|
strcat(szLineMessage, szUserMessage);
|
|
|
|
if (_CLRTLDBG_ASSERT == nRptType)
|
|
{
|
|
if (_ClRtlDbgMode[nRptType] & _CLRTLDBG_MODE_FILE)
|
|
strcat(szLineMessage, "\r");
|
|
strcat(szLineMessage, "\n");
|
|
}
|
|
|
|
if (szFile)
|
|
{
|
|
if (_snprintf(szOutMessage, MAX_MSG, "%s(%d) : %s",
|
|
szFile, nLine, szLineMessage) < 0)
|
|
strcpy(szOutMessage, TOOLONGMSG);
|
|
}
|
|
else
|
|
strcpy(szOutMessage, szLineMessage);
|
|
|
|
/* user hook may handle report */
|
|
if (_pfnReportHook && (*_pfnReportHook)(nRptType, szOutMessage, &retval))
|
|
{
|
|
if (_CLRTLDBG_ASSERT == nRptType)
|
|
_ClRtlInterlockedDecrement(&_clrtlAssertBusy);
|
|
return retval;
|
|
}
|
|
|
|
if (_ClRtlDbgMode[nRptType] & _CLRTLDBG_MODE_FILE)
|
|
{
|
|
if (_ClRtlDbgFile[nRptType] != _CLRTLDBG_INVALID_HFILE)
|
|
{
|
|
DWORD written;
|
|
WriteFile(_ClRtlDbgFile[nRptType], szOutMessage, strlen(szOutMessage), &written, NULL);
|
|
}
|
|
}
|
|
|
|
if (_ClRtlDbgMode[nRptType] & _CLRTLDBG_MODE_DEBUG)
|
|
{
|
|
OutputDebugStringA(szOutMessage);
|
|
}
|
|
|
|
if (_ClRtlDbgMode[nRptType] & _CLRTLDBG_MODE_WNDW)
|
|
{
|
|
char szLine[20];
|
|
|
|
retval = ClRtlMessageWindow(nRptType, szFile, nLine ? _itoa(nLine, szLine, 10) : NULL, szModule, szUserMessage);
|
|
if (_CLRTLDBG_ASSERT == nRptType)
|
|
_ClRtlInterlockedDecrement(&_clrtlAssertBusy);
|
|
return retval;
|
|
}
|
|
|
|
if (_CLRTLDBG_ASSERT == nRptType)
|
|
_ClRtlInterlockedDecrement(&_clrtlAssertBusy);
|
|
/* ignore */
|
|
return FALSE;
|
|
|
|
} //*** _ClRtlDbgReport()
|
|
|
|
|
|
/***
|
|
*static int ClRtlMessageWindow() - report to a message window
|
|
*
|
|
*Purpose:
|
|
* put report into message window, allow user to choose action to take
|
|
*
|
|
*Entry:
|
|
* int nRptType - report type
|
|
* const char * szFile - file name
|
|
* const char * szLine - line number
|
|
* const char * szModule - module name
|
|
* const char * szUserMessage - user message
|
|
*
|
|
*Exit:
|
|
* if (MessageBox)
|
|
* {
|
|
* Abort -> aborts
|
|
* Retry -> return TRUE
|
|
* Ignore-> return FALSE
|
|
* }
|
|
* else
|
|
* return FALSE
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static int ClRtlMessageWindow(
|
|
int nRptType,
|
|
const char * szFile,
|
|
const char * szLine,
|
|
const char * szModule,
|
|
const char * szUserMessage
|
|
)
|
|
{
|
|
int nCode;
|
|
char *szShortProgName;
|
|
char *szShortModuleName;
|
|
char szExeName[MAX_PATH];
|
|
char szOutMessage[MAX_MSG];
|
|
|
|
_CLRTL_ASSERTE(szUserMessage != NULL);
|
|
|
|
/* Shorten program name */
|
|
if (!GetModuleFileNameA(NULL, szExeName, MAX_PATH))
|
|
strcpy(szExeName, "<program name unknown>");
|
|
|
|
szShortProgName = szExeName;
|
|
|
|
if (strlen(szShortProgName) > MAXLINELEN)
|
|
{
|
|
szShortProgName += strlen(szShortProgName) - MAXLINELEN;
|
|
strncpy(szShortProgName, "...", 3);
|
|
}
|
|
|
|
/* Shorten module name */
|
|
szShortModuleName = (char *) szModule;
|
|
|
|
if (szShortModuleName && strlen(szShortModuleName) > MAXLINELEN)
|
|
{
|
|
szShortModuleName += strlen(szShortModuleName) - MAXLINELEN;
|
|
strncpy(szShortModuleName, "...", 3);
|
|
}
|
|
|
|
if (_snprintf(szOutMessage, MAX_MSG,
|
|
"Debug %s!\n\nProgram: %s%s%s%s%s%s%s%s%s%s%s"
|
|
"\n\n(Press Retry to debug the application)",
|
|
_ClRtlDbgModeMsg[nRptType],
|
|
szShortProgName,
|
|
szShortModuleName ? "\nModule: " : "",
|
|
szShortModuleName ? szShortModuleName : "",
|
|
szFile ? "\nFile: " : "",
|
|
szFile ? szFile : "",
|
|
szLine ? "\nLine: " : "",
|
|
szLine ? szLine : "",
|
|
szUserMessage[0] ? "\n\n" : "",
|
|
szUserMessage[0] && _CLRTLDBG_ASSERT == nRptType ? "Expression: " : "",
|
|
szUserMessage[0] ? szUserMessage : "",
|
|
0 /*_CLRTLDBG_ASSERT == nRptType*/ ? // Don't display this text, it's superfluous
|
|
"\n\nFor information on how your program can cause an assertion"
|
|
"\nfailure, see the Visual C++ documentation on asserts."
|
|
: "") < 0)
|
|
strcpy(szOutMessage, TOOLONGMSG);
|
|
|
|
/* Report the warning/error */
|
|
nCode = __clrtlMessageBoxA(
|
|
szOutMessage,
|
|
"Microsoft Visual C++ Debug Library",
|
|
MB_TASKMODAL|MB_ICONHAND|MB_ABORTRETRYIGNORE|MB_SETFOREGROUND);
|
|
|
|
/* Abort: abort the program */
|
|
if (IDABORT == nCode)
|
|
{
|
|
/* raise abort signal */
|
|
raise(SIGABRT);
|
|
|
|
/* We usually won't get here, but it's possible that
|
|
SIGABRT was ignored. So exit the program anyway. */
|
|
|
|
_exit(3);
|
|
}
|
|
|
|
/* Retry: return 1 to call the debugger */
|
|
if (IDRETRY == nCode)
|
|
return 1;
|
|
|
|
/* Ignore: continue execution */
|
|
return 0;
|
|
|
|
} //*** ClRtlMessageWindow()
|
|
|
|
|
|
/***
|
|
*__clrtlMessageBoxA - call MessageBoxA dynamically.
|
|
*
|
|
*Purpose:
|
|
* Avoid static link with user32.dll. Only load it when actually needed.
|
|
*
|
|
*Entry:
|
|
* see MessageBoxA docs.
|
|
*
|
|
*Exit:
|
|
* see MessageBoxA docs.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
static int __clrtlMessageBoxA(
|
|
LPCSTR lpText,
|
|
LPCSTR lpCaption,
|
|
UINT uType
|
|
)
|
|
{
|
|
static int (APIENTRY *pfnMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT) = NULL;
|
|
static HWND (APIENTRY *pfnGetActiveWindow)(void) = NULL;
|
|
static HWND (APIENTRY *pfnGetLastActivePopup)(HWND) = NULL;
|
|
|
|
HWND hWndParent = NULL;
|
|
|
|
if (NULL == pfnMessageBoxA)
|
|
{
|
|
HINSTANCE hlib = LoadLibraryA("user32.dll");
|
|
|
|
if (NULL == hlib || NULL == (pfnMessageBoxA =
|
|
(int (APIENTRY *)(HWND, LPCSTR, LPCSTR, UINT))
|
|
GetProcAddress(hlib, "MessageBoxA")))
|
|
return 0;
|
|
|
|
pfnGetActiveWindow = (HWND (APIENTRY *)(void))
|
|
GetProcAddress(hlib, "GetActiveWindow");
|
|
|
|
pfnGetLastActivePopup = (HWND (APIENTRY *)(HWND))
|
|
GetProcAddress(hlib, "GetLastActivePopup");
|
|
}
|
|
|
|
if (pfnGetActiveWindow)
|
|
hWndParent = (*pfnGetActiveWindow)();
|
|
|
|
if (hWndParent != NULL && pfnGetLastActivePopup)
|
|
hWndParent = (*pfnGetLastActivePopup)(hWndParent);
|
|
|
|
return (*pfnMessageBoxA)(hWndParent, lpText, lpCaption, uType);
|
|
|
|
} //*** __clrtlMessageBoxA()
|