/*** *dbgrpt.c - Debug CRT Reporting Functions * * Copyright (c) 1988-1997, Microsoft Corporation. All rights reserved. * *Purpose: * *******************************************************************************/ #ifdef _DEBUG #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #define _CrtInterlockedIncrement InterlockedIncrement #define _CrtInterlockedDecrement InterlockedDecrement #else /* _WIN32 */ #define FALSE 0 #define TRUE 1 #define BYTE char #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include __inline long __cdecl _CrtInterlockedIncrement(long * plVar) { return ++(*plVar); } __inline long __cdecl _CrtInterlockedDecrement(long * plVar) { return --(*plVar); } static void __cdecl _CrtOutputDebugString(char * sz); #endif /* _WIN32 */ /*--------------------------------------------------------------------------- * * Debug Reporting * --------------------------------------------------------------------------*/ static int CrtMessageWindow( int, const char *, const char *, const char *, const char * ); _CRT_REPORT_HOOK _pfnReportHook; _CRTIMP long _crtAssertBusy = -1; int _CrtDbgMode[_CRT_ERRCNT] = { #ifdef _WIN32 _CRTDBG_MODE_DEBUG, _CRTDBG_MODE_WNDW, _CRTDBG_MODE_WNDW #else /* _WIN32 */ _CRTDBG_MODE_DEBUG, _CRTDBG_MODE_DEBUG, _CRTDBG_MODE_DEBUG #endif /* _WIN32 */ }; _HFILE _CrtDbgFile[_CRT_ERRCNT] = { _CRTDBG_INVALID_HFILE, _CRTDBG_INVALID_HFILE, _CRTDBG_INVALID_HFILE }; static const char * _CrtDbgModeMsg[_CRT_ERRCNT] = { "Warning", "Error", "Assertion Failed" }; /*** *void _CrtDebugBreak - call OS-specific debug function * *Purpose: * call OS-specific debug function * *Entry: * *Exit: * *Exceptions: * *******************************************************************************/ #undef _CrtDbgBreak _CRTIMP void _cdecl _CrtDbgBreak( void ) { #ifdef _WIN32 DebugBreak(); #else /* _WIN32 */ Debugger(); #endif /* _WIN32 */ } /*** *int _CrtSetReportMode - 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: * *******************************************************************************/ _CRTIMP int __cdecl _CrtSetReportMode( int nRptType, int fMode ) { int oldMode; if (nRptType < 0 || nRptType >= _CRT_ERRCNT) return -1; if (fMode == _CRTDBG_REPORT_MODE) return _CrtDbgMode[nRptType]; /* verify flags values */ if (fMode & ~(_CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW)) return -1; oldMode = _CrtDbgMode[nRptType]; _CrtDbgMode[nRptType] = fMode; return oldMode; } /*** *int _CrtSetReportFile - 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: * *******************************************************************************/ _CRTIMP _HFILE __cdecl _CrtSetReportFile( int nRptType, _HFILE hFile ) { _HFILE oldFile; if (nRptType < 0 || nRptType >= _CRT_ERRCNT) return _CRTDBG_HFILE_ERROR; if (hFile == _CRTDBG_REPORT_FILE) return _CrtDbgFile[nRptType]; oldFile = _CrtDbgFile[nRptType]; #ifdef _WIN32 if (_CRTDBG_FILE_STDOUT == hFile) _CrtDbgFile[nRptType] = GetStdHandle(STD_OUTPUT_HANDLE); else if (_CRTDBG_FILE_STDERR == hFile) _CrtDbgFile[nRptType] = GetStdHandle(STD_ERROR_HANDLE); #else /* _WIN32 */ if (_CRTDBG_FILE_STDOUT == hFile || _CRTDBG_FILE_STDERR == hFile) return _CRTDBG_HFILE_ERROR; #endif /* _WIN32 */ else _CrtDbgFile[nRptType] = hFile; return oldFile; } /*** *_CRT_REPORT_HOOK _CrtSetReportHook() - set client report hook * *Purpose: * set client report hook * *Entry: * _CRT_REPORT_HOOK pfnNewHook - new report hook * *Exit: * return previous hook * *Exceptions: * *******************************************************************************/ _CRTIMP _CRT_REPORT_HOOK __cdecl _CrtSetReportHook( _CRT_REPORT_HOOK pfnNewHook ) { _CRT_REPORT_HOOK pfnOldHook = _pfnReportHook; _pfnReportHook = pfnNewHook; return pfnOldHook; } #define MAXLINELEN 64 #define MAX_MSG 4096 #define TOOLONGMSG "_CrtDbgReport: String too long or IO Error" /*** *int _CrtDbgReport() - 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] * * { | 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: * *******************************************************************************/ _CRTIMP int __cdecl _CrtDbgReport( 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 >= _CRT_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 (_CRT_ASSERT == nRptType && _CrtInterlockedIncrement(&_crtAssertBusy) > 0) { /* use only 'safe' functions -- must not assert in here! */ #ifdef _WIN32 static int (APIENTRY *pfnwsprintfA)(LPSTR, LPCSTR, ...) = NULL; if (NULL == pfnwsprintfA) { HANDLE hlib = LoadLibrary("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); OutputDebugString(szOutMessage); #else /* _WIN32 */ strcpy(szOutMessage, "Second Chance Assertion Failed: File "); strcat(szOutMessage, szFile); strcat(szOutMessage, ", Line "); numtostring(nLine, &szOutMessage[strlen(szOutMessage)]); strcat(szOutMessage, "\n"); _CrtOutputDebugString(szOutMessage); #endif /* _WIN32 */ _CrtInterlockedDecrement(&_crtAssertBusy); _CrtDbgBreak(); return -1; } if (szFormat && _vsnprintf(szUserMessage, MAX_MSG-max(sizeof(ASSERTINTRO1),sizeof(ASSERTINTRO2)), szFormat, arglist) < 0) strcpy(szUserMessage, TOOLONGMSG); if (_CRT_ASSERT == nRptType) strcpy(szLineMessage, szFormat ? ASSERTINTRO1 : ASSERTINTRO2); strcat(szLineMessage, szUserMessage); if (_CRT_ASSERT == nRptType) { if (_CrtDbgMode[nRptType] & _CRTDBG_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 (_CRT_ASSERT == nRptType) _CrtInterlockedDecrement(&_crtAssertBusy); return retval; } if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE) { if (_CrtDbgFile[nRptType] != _CRTDBG_INVALID_HFILE) { #ifdef _WIN32 DWORD written; WriteFile(_CrtDbgFile[nRptType], szOutMessage, strlen(szOutMessage), &written, NULL); #else /* _WIN32 */ long written = strlen(szOutMessage); FSWrite((short)_CrtDbgFile[nRptType], &written, szOutMessage); #endif /* _WIN32 */ } } if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_DEBUG) { #ifdef _WIN32 OutputDebugString(szOutMessage); #else /* _WIN32 */ _CrtOutputDebugString(szOutMessage); #endif /* _WIN32 */ } if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_WNDW) { char szLine[20]; retval = CrtMessageWindow(nRptType, szFile, nLine ? _itoa(nLine, szLine, 10) : NULL, szModule, szUserMessage); if (_CRT_ASSERT == nRptType) _CrtInterlockedDecrement(&_crtAssertBusy); return retval; } if (_CRT_ASSERT == nRptType) _CrtInterlockedDecrement(&_crtAssertBusy); /* ignore */ return FALSE; } /*** *static int CrtMessageWindow() - 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: * *******************************************************************************/ #ifdef _WIN32 static int CrtMessageWindow( 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]; _ASSERTE(szUserMessage != NULL); /* Shorten program name */ if (!GetModuleFileName(NULL, szExeName, MAX_PATH)) strcpy(szExeName, ""); 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)", _CrtDbgModeMsg[nRptType], szShortProgName, szShortModuleName ? "\nModule: " : "", szShortModuleName ? szShortModuleName : "", szFile ? "\nFile: " : "", szFile ? szFile : "", szLine ? "\nLine: " : "", szLine ? szLine : "", szUserMessage[0] ? "\n\n" : "", szUserMessage[0] && _CRT_ASSERT == nRptType ? "Expression: " : "", szUserMessage[0] ? szUserMessage : "", _CRT_ASSERT == nRptType ? "\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 = __crtMessageBoxA(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; } #elif defined (_M_MPPC) /* Definitions */ #define NIL 0 /* Portable Types */ typedef unsigned char Char; typedef unsigned long Word; QDGlobals qd; static void BlinkButton(int count,Rect *r) { long n; while (count--) { InvertRoundRect(r,10,10); Delay(4,&n); } } static int ButtonClicked(Rect *r) { int state = 0; Point pt; do { GetMouse(&pt); if (PtInRect(pt,r)) { if (0 == state) { state = 1; BlinkButton(1,r); } } else if (1 == state) { state = 0; BlinkButton(1,r); } } while (Button()); if (state) BlinkButton(3,r); return state; } static int CrtMessageWindow( int nRptType, const char * szFile, const char * szLine, const char * szModule, const char * szUserMessage ) { #define LEFTMARGIN 15 #define ALINE 14 char szOutMessage[MAX_MSG]; Rect abort,retry,ignore,r; GrafPtr oldPort; WindowPtr window; EventRecord event; long total, contig; short currHeight = 20; int numLines = 13 + 4 * (_CRT_ASSERT == nRptType) + 2 * (szUserMessage[0] != '\0') + (szModule != NULL) + 2 * (szFile != NULL); /* center a rectangle on the screen, near the top of the screen */ r.top = 50; r.bottom = r.top + numLines * ALINE; r.left = qd.screenBits.bounds.right/2 - 170; r.right = qd.screenBits.bounds.right/2 + 170; /* * NewWindow will hang in insufficient memory conditions. Must * confirm available memory before use. */ contig = 0; PurgeSpace(&total, &contig); if (contig < 1024) return -1; /* create the window and setup the port */ if ((window = NewWindow(NIL,&r,"\p",TRUE,dBoxProc,(WindowPtr)-1,TRUE,0)) == NULL) return -1; GetPort(&oldPort); SetPort(window); InitCursor(); TextFont(systemFont); MoveTo(LEFTMARGIN,currHeight); TextFace(underline); DrawString("\pMicrosoft Visual C++ Debug Library"); TextFace(0); currHeight += 2 * ALINE; _snprintf(szOutMessage, MAX_MSG, "Debug %s!", _CrtDbgModeMsg[nRptType]); _c2pstr(szOutMessage); MoveTo(LEFTMARGIN,currHeight); TextFace(bold); DrawString(szOutMessage); TextFace(0); currHeight += 2 * ALINE; MoveTo(LEFTMARGIN,currHeight); DrawString("\pProgram: "); DrawString(LMGetCurApName()); currHeight += ALINE; if (szModule) { _snprintf(szOutMessage, MAX_MSG, "Module: %s", szModule); _c2pstr(szOutMessage); MoveTo(LEFTMARGIN,currHeight); DrawString(szOutMessage); currHeight += ALINE; } if (szFile) { _snprintf(szOutMessage, MAX_MSG, "File: %s", szFile); _c2pstr(szOutMessage); MoveTo(LEFTMARGIN,currHeight); DrawString(szOutMessage); currHeight += ALINE; } if (szLine) { _snprintf(szOutMessage, MAX_MSG, "Line: %s", szLine); _c2pstr(szOutMessage); MoveTo(LEFTMARGIN,currHeight); DrawString(szOutMessage); currHeight += ALINE; } currHeight += ALINE; if (szUserMessage[0]) { _snprintf(szOutMessage, MAX_MSG, "%s%s", (_CRT_ASSERT == nRptType) ? "Expression: " : "", szUserMessage); r.top = currHeight-10; r.bottom = currHeight - 5; r.left = LEFTMARGIN - 2; r.right = window->portRect.right - LEFTMARGIN - 2; TextFace(condense); TextBox(szOutMessage, strlen(szOutMessage), &r, teJustLeft); TextFace(0); currHeight += 2*ALINE; } currHeight += ALINE; if (_CRT_ASSERT == nRptType) { MoveTo(LEFTMARGIN,currHeight); DrawString("\pFor more information on how your program"); currHeight += ALINE; MoveTo(LEFTMARGIN,currHeight); DrawString("\pcan cause an assertion failure, see the"); currHeight += ALINE; MoveTo(LEFTMARGIN,currHeight); DrawString("\pVisual C++ documentation on asserts."); currHeight += 2*ALINE; MoveTo(LEFTMARGIN,currHeight); DrawString("\p(Press Retry to debug the application)"); currHeight += 2*ALINE; } else { MoveTo(LEFTMARGIN,currHeight); DrawString("\p(Press Retry to debug the application)"); currHeight += 2*ALINE; } /* draw abort button */ abort.top = currHeight; abort.bottom = abort.top + 20; abort.left = LEFTMARGIN; abort.right = abort.left + 75; FrameRoundRect(&abort,10,10); MoveTo((short)(abort.left+20),(short)(abort.bottom-6)); DrawString("\pAbort"); /* draw retry button */ retry.top = currHeight; retry.bottom = retry.top + 20; retry.left = LEFTMARGIN + 110; retry.right = retry.left + 75; FrameRoundRect(&retry,10,10); MoveTo((short)(retry.left+20),(short)(retry.bottom-6)); DrawString("\pRetry"); /* draw ignore button */ ignore.top = currHeight; ignore.bottom = ignore.top + 20; ignore.left = LEFTMARGIN + 110 * 2; ignore.right = ignore.left + 75; FrameRoundRect(&ignore,10,10); MoveTo((short)(ignore.left+17),(short)(ignore.bottom-6)); DrawString("\pIgnore"); /* event loop */ while (TRUE) { if (GetNextEvent(mDownMask+keyDownMask,&event)) { if (event.what == keyDown) { /* keyboard shortcuts */ char c = (char)event.message; /* abort */ if (c == 'a') { BlinkButton(3,&abort); ExitToShell(); } /* retry */ else if (c == 'r') { BlinkButton(3,&retry); Debugger(); break; } /* ignore */ else if (c == 'i') { BlinkButton(3,&ignore); break; } } if (event.what == mouseDown) { GlobalToLocal(&event.where); if (PtInRect(event.where,&abort) && ButtonClicked(&abort)) ExitToShell(); else if (PtInRect(event.where,&retry) && ButtonClicked(&retry)) { Debugger(); break; } else if (PtInRect(event.where,&ignore) && ButtonClicked(&ignore)) break; } } } DisposeWindow(window); SetPort(oldPort); /* ignore */ return 0; } #define WLMD_STR 1 #define LMGetMacJmp() (*(ProcPtr*) 0x120) enum { uppGestaltDebugProcInfo = kCStackBased | STACK_ROUTINE_PARAMETER (1, SIZE_CODE (sizeof (int))) | STACK_ROUTINE_PARAMETER (2, SIZE_CODE (sizeof (StringPtr))) | STACK_ROUTINE_PARAMETER (3, SIZE_CODE (sizeof (char*))) | STACK_ROUTINE_PARAMETER (4, SIZE_CODE (sizeof (char*))) }; static UniversalProcPtr pfnGestaltDebugger; /*** *void _CleanStr - clean string * *Purpose: * Cleans a string of characters that aren't useful. Always removes '\r'. If * the string is going to MacsBug, replaces semicolons with periods, and * removes trailing newlines, since MacsBug always advances to the next line * after a _DebugStr. * *Entry: * StringPtr st - the string to clean * int fMacsBug - whether the string is going to MacsBug * *Exit: * *Exceptions: * *******************************************************************************/ static void __cdecl _CleanStr(StringPtr st, int fMacsBug) { int i; /* change semi-colons to periods (semi-colons hose MacsBug) */ if (fMacsBug) { for (i = 0; i < StrLength(st); i++) { /* skip MB chars */ if (_ismbblead(st[i+1])) { i++; continue; } if (st[i + 1] == ';') st[i + 1] = '.'; } } for (i = StrLength(st); i >= 1; i--) { /* if trail byte, stop processing */ if (_ismbstrail(&st[1], &st[i])) break; /* remove trailing newlines */ if (st[i] == '\n') { if (fMacsBug) st[0]--; } /* remove trailing carriage returns */ else if (st[i] == '\r') { st[0]--; } /* otherwise, stop processing */ else { break; } } } /*** *void _ConfirmVCDebugger * *Purpose: * Determines if a 'WLMD' Gestalt selector is registered. * *Entry: * *Exit: * Current VC++ Debug Output function or NULL. * *Exceptions: * *******************************************************************************/ static UniversalProcPtr __cdecl _ConfirmVCDebugger(void) { /* The VC++ Debug Ouput facilities are registered as "WLMD" */ if (Gestalt('WLMD', (long*) &pfnGestaltDebugger) != noErr) pfnGestaltDebugger = NULL; return pfnGestaltDebugger; } /*** *int _IsMSVCDebugger * *Purpose: * Determines if a 'MSVC' Gestalt selector is registered, or if there * is a low level debugger in the system * * Only call a macsbug-like debugger if it's going to be around to * handle the DebugStr * *Entry: * *Exit: * TRUE: If MSVC debugger registered * FALSE: Otherwise * *Exceptions: * *******************************************************************************/ static int __cdecl _IsLowLevelDebugger(void) { #define mskBeingDebugged 1 #define mskPreviousDebugger 2 unsigned long caps; /* See if VC++ Debugger is running */ if(Gestalt('MSVC', (long*) &caps) == noErr) { if((caps & mskBeingDebugged) || (caps & mskPreviousDebugger)) return 1; else return 0; } /* See if MacsBug is running */ else if (LMGetMacJmp() != NULL) return 1; else return 0; #undef mskBeingDebugged #undef mskPreviousDebugger } /*** *void _CrtOutputDebugString - output Debug String * *Purpose: * Does the actual output of a string to the debugger. * *Entry: * char * sz -- message to output * *Exit: * *Exceptions: * *******************************************************************************/ static void __cdecl _CrtOutputDebugString(char * sz) { Str255 stDebug; long cchTotal; short cchCur; const char* pchCur; EventRecord er; _ConfirmVCDebugger(); OSEventAvail(0, &er); cchTotal = strlen(sz); pchCur = sz; /* output string in edible portions */ while (cchTotal > 0) { cchCur = (short) min(cchTotal, 253); _ASSERTE(cchCur > 0); /* put back orphaned lead byte */ if (_ismbslead(pchCur, pchCur+cchCur-1)) cchCur--; /* convert CString to PascalString */ stDebug[0] = (unsigned char) cchCur; BlockMoveData(pchCur, &stDebug[1], cchCur); /* See if VC++ Debug Output facility is running */ if (pfnGestaltDebugger != NULL) { _CleanStr(stDebug, 0); /* if the Control key is down, we break into MacsBug before sending the string to the Gestalt debugger */ if ((er.modifiers & controlKey) != 0) Debugger(); CallUniversalProc(pfnGestaltDebugger, uppGestaltDebugProcInfo, WLMD_STR, stDebug, NULL, NULL); } /* See if VC++ Debugger or MacsBug is running */ else if (_IsLowLevelDebugger()) { short cchSt; _CleanStr(stDebug, 1); /* _CleanStr may have changed the length of stDebug */ cchSt = StrLength(stDebug); /* if the Control key is down, we always stop in the debugger */ if ((er.modifiers & controlKey) == 0) { stDebug[cchSt + 1] = ';'; stDebug[cchSt + 2] = 'g'; stDebug[0] += 2; } DebugStr(stDebug); } cchTotal -= cchCur; pchCur += cchCur; } } #endif /* defined (_M_MPPC) */ #endif /* _DEBUG */