Source code of Windows XP (NT5)
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.

675 lines
19 KiB

  1. /***
  2. *dbgrpt.c - Debug CRT Reporting Functions
  3. *
  4. * Copyright (c) 1988-2001, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. *
  8. *Revision History:
  9. * 08-16-94 CFW Module created.
  10. * 11-28-94 CFW Change _SetCrtxxx to _CrtSetxxx.
  11. * 12-08-94 CFW Use non-win32 names.
  12. * 01-05-94 CFW Add report hook.
  13. * 01-11-94 CFW Report uses _snprintf, all unsigned chars.
  14. * 01-20-94 CFW Change unsigned chars to chars.
  15. * 01-24-94 CFW Name cleanup.
  16. * 02-09-95 CFW PMac work, _CrtDbgReport now returns 1 for debug,
  17. * -1 for error.
  18. * 02-15-95 CFW Make all CRT message boxes look alike.
  19. * 02-24-95 CFW Use __crtMessageBoxA.
  20. * 02-27-95 CFW Move GetActiveWindow/GetLastrActivePopup into
  21. * __crtMessageBoxA, add _CrtDbgBreak.
  22. * 02-28-95 CFW Fix PMac reporting.
  23. * 03-21-95 CFW Add _CRT_ASSERT report type, improve assert windows.
  24. * 04-19-95 CFW Avoid double asserts.
  25. * 04-25-95 CFW Add _CRTIMP to all exported functions.
  26. * 04-30-95 CFW "JIT" message removed.
  27. * 05-10-95 CFW Change Interlockedxxx to _CrtInterlockedxxx.
  28. * 05-24-95 CFW Change report hook scheme, make _crtAssertBusy available.
  29. * 06-06-95 CFW Remove _MB_SERVICE_NOTIFICATION.
  30. * 06-08-95 CFW Macos header changes cause warning.
  31. * 06-08-95 CFW Add return value parameter to report hook.
  32. * 06-27-95 CFW Add win32s support for debug libs.
  33. * 07-07-95 CFW Simplify default report mode scheme.
  34. * 07-19-95 CFW Use WLM debug string scheme for PMac.
  35. * 08-01-95 JWM PMac file output fixed.
  36. * 01-08-96 JWM File output now in text mode.
  37. * 04-22-96 JWM MAX_MSG increased from 512 to 4096.
  38. * 04-29-96 JWM _crtAssertBusy no longer being decremented prematurely.
  39. * 01-05-99 GJF Changes for 64-bit size_t.
  40. * 05-17-99 PML Remove all Macintosh support.
  41. * 03-21-01 PML Add _CrtSetReportHook2 (vs7#124998)
  42. * 03-28-01 PML Protect against GetModuleFileName overflow (vs7#231284)
  43. *
  44. *******************************************************************************/
  45. #ifdef _DEBUG
  46. #include <internal.h>
  47. #include <mtdll.h>
  48. #include <malloc.h>
  49. #include <mbstring.h>
  50. #include <stdarg.h>
  51. #include <stdlib.h>
  52. #include <stdio.h>
  53. #include <dbgint.h>
  54. #include <signal.h>
  55. #include <string.h>
  56. #include <awint.h>
  57. #include <windows.h>
  58. #include <errno.h>
  59. #define _CrtInterlockedIncrement InterlockedIncrement
  60. #define _CrtInterlockedDecrement InterlockedDecrement
  61. /*---------------------------------------------------------------------------
  62. *
  63. * Debug Reporting
  64. *
  65. --------------------------------------------------------------------------*/
  66. static int CrtMessageWindow(
  67. int,
  68. const char *,
  69. const char *,
  70. const char *,
  71. const char *
  72. );
  73. _CRT_REPORT_HOOK _pfnReportHook;
  74. typedef struct ReportHookNode {
  75. struct ReportHookNode *prev;
  76. struct ReportHookNode *next;
  77. unsigned refcount;
  78. _CRT_REPORT_HOOK pfnHookFunc;
  79. } ReportHookNode;
  80. ReportHookNode *_pReportHookList;
  81. _CRTIMP long _crtAssertBusy = -1;
  82. int _CrtDbgMode[_CRT_ERRCNT] = {
  83. _CRTDBG_MODE_DEBUG,
  84. _CRTDBG_MODE_WNDW,
  85. _CRTDBG_MODE_WNDW
  86. };
  87. _HFILE _CrtDbgFile[_CRT_ERRCNT] = { _CRTDBG_INVALID_HFILE,
  88. _CRTDBG_INVALID_HFILE,
  89. _CRTDBG_INVALID_HFILE
  90. };
  91. static const char * _CrtDbgModeMsg[_CRT_ERRCNT] = { "Warning",
  92. "Error",
  93. "Assertion Failed"
  94. };
  95. /***
  96. *void _CrtDebugBreak - call OS-specific debug function
  97. *
  98. *Purpose:
  99. * call OS-specific debug function
  100. *
  101. *Entry:
  102. *
  103. *Exit:
  104. *
  105. *Exceptions:
  106. *
  107. *******************************************************************************/
  108. #undef _CrtDbgBreak
  109. _CRTIMP void _cdecl _CrtDbgBreak(
  110. void
  111. )
  112. {
  113. DebugBreak();
  114. }
  115. /***
  116. *int _CrtSetReportMode - set the reporting mode for a given report type
  117. *
  118. *Purpose:
  119. * set the reporting mode for a given report type
  120. *
  121. *Entry:
  122. * int nRptType - the report type
  123. * int fMode - new mode for given report type
  124. *
  125. *Exit:
  126. * previous mode for given report type
  127. *
  128. *Exceptions:
  129. *
  130. *******************************************************************************/
  131. _CRTIMP int __cdecl _CrtSetReportMode(
  132. int nRptType,
  133. int fMode
  134. )
  135. {
  136. int oldMode;
  137. if (nRptType < 0 || nRptType >= _CRT_ERRCNT)
  138. return -1;
  139. if (fMode == _CRTDBG_REPORT_MODE)
  140. return _CrtDbgMode[nRptType];
  141. /* verify flags values */
  142. if (fMode & ~(_CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW))
  143. return -1;
  144. oldMode = _CrtDbgMode[nRptType];
  145. _CrtDbgMode[nRptType] = fMode;
  146. return oldMode;
  147. }
  148. /***
  149. *int _CrtSetReportFile - set the reporting file for a given report type
  150. *
  151. *Purpose:
  152. * set the reporting file for a given report type
  153. *
  154. *Entry:
  155. * int nRptType - the report type
  156. * _HFILE hFile - new file for given report type
  157. *
  158. *Exit:
  159. * previous file for given report type
  160. *
  161. *Exceptions:
  162. *
  163. *******************************************************************************/
  164. _CRTIMP _HFILE __cdecl _CrtSetReportFile(
  165. int nRptType,
  166. _HFILE hFile
  167. )
  168. {
  169. _HFILE oldFile;
  170. if (nRptType < 0 || nRptType >= _CRT_ERRCNT)
  171. return _CRTDBG_HFILE_ERROR;
  172. if (hFile == _CRTDBG_REPORT_FILE)
  173. return _CrtDbgFile[nRptType];
  174. oldFile = _CrtDbgFile[nRptType];
  175. if (_CRTDBG_FILE_STDOUT == hFile)
  176. _CrtDbgFile[nRptType] = GetStdHandle(STD_OUTPUT_HANDLE);
  177. else if (_CRTDBG_FILE_STDERR == hFile)
  178. _CrtDbgFile[nRptType] = GetStdHandle(STD_ERROR_HANDLE);
  179. else
  180. _CrtDbgFile[nRptType] = hFile;
  181. return oldFile;
  182. }
  183. /***
  184. *_CRT_REPORT_HOOK _CrtSetReportHook() - set client report hook
  185. *
  186. *Purpose:
  187. * set client report hook
  188. *
  189. *Entry:
  190. * _CRT_REPORT_HOOK pfnNewHook - new report hook
  191. *
  192. *Exit:
  193. * return previous hook
  194. *
  195. *Exceptions:
  196. *
  197. *******************************************************************************/
  198. _CRTIMP _CRT_REPORT_HOOK __cdecl _CrtSetReportHook(
  199. _CRT_REPORT_HOOK pfnNewHook
  200. )
  201. {
  202. _CRT_REPORT_HOOK pfnOldHook = _pfnReportHook;
  203. _pfnReportHook = pfnNewHook;
  204. return pfnOldHook;
  205. }
  206. /***
  207. *_CRT_REPORT_HOOK _CrtSetReportHook2() - configure client report hook in list
  208. *
  209. *Purpose:
  210. * Install or remove a client report hook from the report list. Exists
  211. * separately from _CrtSetReportHook because the older function doesn't
  212. * work well in an environment where DLLs that are loaded and unloaded
  213. * dynamically out of LIFO order want to install report hooks.
  214. *
  215. *Entry:
  216. * int mode - _CRT_RPTHOOK_INSTALL or _CRT_RPTHOOK_REMOVE
  217. * _CRT_REPORT_HOOK pfnNewHook - report hook to install/remove/query
  218. *
  219. *Exit:
  220. * Returns -1 if an error was encountered, with EINVAL or ENOMEM set,
  221. * else returns the reference count of pfnNewHook after the call.
  222. *
  223. *Exceptions:
  224. *
  225. *******************************************************************************/
  226. _CRTIMP int __cdecl _CrtSetReportHook2(
  227. int mode,
  228. _CRT_REPORT_HOOK pfnNewHook
  229. )
  230. {
  231. ReportHookNode *p;
  232. int ret;
  233. /* Handle invalid parameters */
  234. if ((mode != _CRT_RPTHOOK_INSTALL && mode != _CRT_RPTHOOK_REMOVE) ||
  235. pfnNewHook == NULL)
  236. {
  237. errno = EINVAL;
  238. return -1;
  239. }
  240. #ifdef _MT
  241. if (!_mtinitlocknum(_DEBUG_LOCK))
  242. return -1;
  243. _mlock(_DEBUG_LOCK);
  244. __try
  245. {
  246. #endif
  247. /* Search for new hook function to see if it's already installed */
  248. for (p = _pReportHookList; p != NULL; p = p->next)
  249. if (p->pfnHookFunc == pfnNewHook)
  250. break;
  251. if (mode == _CRT_RPTHOOK_REMOVE)
  252. {
  253. /* Remove request - free list node if refcount goes to zero */
  254. if (p != NULL)
  255. {
  256. if ((ret = --p->refcount) == 0)
  257. {
  258. if (p->next)
  259. p->next->prev = p->prev;
  260. if (p->prev)
  261. p->prev->next = p->next;
  262. else
  263. _pReportHookList = p->next;
  264. _free_crt(p);
  265. }
  266. }
  267. else
  268. {
  269. ret = -1;
  270. errno = EINVAL;
  271. }
  272. }
  273. else
  274. {
  275. /* Insert request */
  276. if (p != NULL)
  277. {
  278. /* Hook function already registered, move to head of list */
  279. ret = ++p->refcount;
  280. if (p != _pReportHookList)
  281. {
  282. if (p->next)
  283. p->next->prev = p->prev;
  284. p->prev->next = p->next;
  285. p->prev = NULL;
  286. p->next = _pReportHookList;
  287. _pReportHookList->prev = p;
  288. _pReportHookList = p;
  289. }
  290. }
  291. else
  292. {
  293. /* Hook function not already registered, insert new node */
  294. p = (ReportHookNode *)_malloc_crt(sizeof(ReportHookNode));
  295. if (p == NULL)
  296. {
  297. ret = -1;
  298. errno = ENOMEM;
  299. }
  300. else
  301. {
  302. p->prev = NULL;
  303. p->next = _pReportHookList;
  304. if (_pReportHookList)
  305. _pReportHookList->prev = p;
  306. ret = p->refcount = 1;
  307. p->pfnHookFunc = pfnNewHook;
  308. _pReportHookList = p;
  309. }
  310. }
  311. }
  312. #ifdef _MT
  313. }
  314. __finally {
  315. _munlock(_DEBUG_LOCK);
  316. }
  317. #endif
  318. return ret;
  319. }
  320. #define MAXLINELEN 64
  321. #define MAX_MSG 4096
  322. #define TOOLONGMSG "_CrtDbgReport: String too long or IO Error"
  323. /***
  324. *int _CrtDbgReport() - primary reporting function
  325. *
  326. *Purpose:
  327. * Display a message window with the following format.
  328. *
  329. * ================= Microsft Visual C++ Debug Library ================
  330. *
  331. * {Warning! | Error! | Assertion Failed!}
  332. *
  333. * Program: c:\test\mytest\foo.exe
  334. * [Module: c:\test\mytest\bar.dll]
  335. * [File: c:\test\mytest\bar.c]
  336. * [Line: 69]
  337. *
  338. * {<warning or error message> | Expression: <expression>}
  339. *
  340. * [For information on how your program can cause an assertion
  341. * failure, see the Visual C++ documentation on asserts]
  342. *
  343. * (Press Retry to debug the application)
  344. *
  345. * ===================================================================
  346. *
  347. *Entry:
  348. * int nRptType - report type
  349. * const char * szFile - file name
  350. * int nLine - line number
  351. * const char * szModule - module name
  352. * const char * szFormat - format string
  353. * ... - var args
  354. *
  355. *Exit:
  356. * if (MessageBox)
  357. * {
  358. * Abort -> aborts
  359. * Retry -> return TRUE
  360. * Ignore-> return FALSE
  361. * }
  362. * else
  363. * return FALSE
  364. *
  365. *Exceptions:
  366. *
  367. *******************************************************************************/
  368. _CRTIMP int __cdecl _CrtDbgReport(
  369. int nRptType,
  370. const char * szFile,
  371. int nLine,
  372. const char * szModule,
  373. const char * szFormat,
  374. ...
  375. )
  376. {
  377. int retval;
  378. va_list arglist;
  379. char szLineMessage[MAX_MSG] = {0};
  380. char szOutMessage[MAX_MSG] = {0};
  381. char szUserMessage[MAX_MSG] = {0};
  382. #define ASSERTINTRO1 "Assertion failed: "
  383. #define ASSERTINTRO2 "Assertion failed!"
  384. va_start(arglist, szFormat);
  385. if (nRptType < 0 || nRptType >= _CRT_ERRCNT)
  386. return -1;
  387. /*
  388. * handle the (hopefully rare) case of
  389. *
  390. * 1) ASSERT while already dealing with an ASSERT
  391. * or
  392. * 2) two threads asserting at the same time
  393. */
  394. if (_CRT_ASSERT == nRptType && _CrtInterlockedIncrement(&_crtAssertBusy) > 0)
  395. {
  396. /* use only 'safe' functions -- must not assert in here! */
  397. static int (APIENTRY *pfnwsprintfA)(LPSTR, LPCSTR, ...) = NULL;
  398. if (NULL == pfnwsprintfA)
  399. {
  400. HANDLE hlib = LoadLibrary("user32.dll");
  401. if (NULL == hlib || NULL == (pfnwsprintfA =
  402. (int (APIENTRY *)(LPSTR, LPCSTR, ...))
  403. GetProcAddress(hlib, "wsprintfA")))
  404. return -1;
  405. }
  406. (*pfnwsprintfA)( szOutMessage,
  407. "Second Chance Assertion Failed: File %s, Line %d\n",
  408. szFile, nLine);
  409. OutputDebugString(szOutMessage);
  410. _CrtInterlockedDecrement(&_crtAssertBusy);
  411. _CrtDbgBreak();
  412. return -1;
  413. }
  414. if (szFormat && _vsnprintf(szUserMessage,
  415. MAX_MSG-max(sizeof(ASSERTINTRO1),sizeof(ASSERTINTRO2)),
  416. szFormat,
  417. arglist) < 0)
  418. strcpy(szUserMessage, TOOLONGMSG);
  419. if (_CRT_ASSERT == nRptType)
  420. strcpy(szLineMessage, szFormat ? ASSERTINTRO1 : ASSERTINTRO2);
  421. strcat(szLineMessage, szUserMessage);
  422. if (_CRT_ASSERT == nRptType)
  423. {
  424. if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE)
  425. strcat(szLineMessage, "\r");
  426. strcat(szLineMessage, "\n");
  427. }
  428. if (szFile)
  429. {
  430. if (_snprintf(szOutMessage, MAX_MSG, "%s(%d) : %s",
  431. szFile, nLine, szLineMessage) < 0)
  432. strcpy(szOutMessage, TOOLONGMSG);
  433. }
  434. else
  435. strcpy(szOutMessage, szLineMessage);
  436. /* User hook may handle report. Check Hook2 list first */
  437. if (_pReportHookList)
  438. {
  439. ReportHookNode *pnode;
  440. #ifdef _MT
  441. _mlock(_DEBUG_LOCK);
  442. __try
  443. {
  444. #endif
  445. for (pnode = _pReportHookList; pnode; pnode = pnode->next)
  446. {
  447. if ((*pnode->pfnHookFunc)(nRptType, szOutMessage, &retval))
  448. {
  449. if (_CRT_ASSERT == nRptType)
  450. _CrtInterlockedDecrement(&_crtAssertBusy);
  451. return retval;
  452. }
  453. }
  454. #ifdef _MT
  455. }
  456. __finally {
  457. _munlock(_DEBUG_LOCK);
  458. }
  459. #endif
  460. }
  461. if (_pfnReportHook)
  462. {
  463. if ((*_pfnReportHook)(nRptType, szOutMessage, &retval))
  464. {
  465. if (_CRT_ASSERT == nRptType)
  466. _CrtInterlockedDecrement(&_crtAssertBusy);
  467. return retval;
  468. }
  469. }
  470. if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE)
  471. {
  472. if (_CrtDbgFile[nRptType] != _CRTDBG_INVALID_HFILE)
  473. {
  474. DWORD written;
  475. WriteFile(_CrtDbgFile[nRptType], szOutMessage, (unsigned long)strlen(szOutMessage), &written, NULL);
  476. }
  477. }
  478. if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_DEBUG)
  479. {
  480. OutputDebugString(szOutMessage);
  481. }
  482. if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_WNDW)
  483. {
  484. char szLine[20];
  485. retval = CrtMessageWindow(nRptType, szFile, nLine ? _itoa(nLine, szLine, 10) : NULL, szModule, szUserMessage);
  486. if (_CRT_ASSERT == nRptType)
  487. _CrtInterlockedDecrement(&_crtAssertBusy);
  488. return retval;
  489. }
  490. if (_CRT_ASSERT == nRptType)
  491. _CrtInterlockedDecrement(&_crtAssertBusy);
  492. /* ignore */
  493. return FALSE;
  494. }
  495. /***
  496. *static int CrtMessageWindow() - report to a message window
  497. *
  498. *Purpose:
  499. * put report into message window, allow user to choose action to take
  500. *
  501. *Entry:
  502. * int nRptType - report type
  503. * const char * szFile - file name
  504. * const char * szLine - line number
  505. * const char * szModule - module name
  506. * const char * szUserMessage - user message
  507. *
  508. *Exit:
  509. * if (MessageBox)
  510. * {
  511. * Abort -> aborts
  512. * Retry -> return TRUE
  513. * Ignore-> return FALSE
  514. * }
  515. * else
  516. * return FALSE
  517. *
  518. *Exceptions:
  519. *
  520. *******************************************************************************/
  521. static int CrtMessageWindow(
  522. int nRptType,
  523. const char * szFile,
  524. const char * szLine,
  525. const char * szModule,
  526. const char * szUserMessage
  527. )
  528. {
  529. int nCode;
  530. char *szShortProgName;
  531. char *szShortModuleName;
  532. char szExeName[MAX_PATH + 1];
  533. char szOutMessage[MAX_MSG];
  534. _ASSERTE(szUserMessage != NULL);
  535. /* Shorten program name */
  536. szExeName[MAX_PATH] = '\0';
  537. if (!GetModuleFileName(NULL, szExeName, MAX_PATH))
  538. strcpy(szExeName, "<program name unknown>");
  539. szShortProgName = szExeName;
  540. if (strlen(szShortProgName) > MAXLINELEN)
  541. {
  542. szShortProgName += strlen(szShortProgName) - MAXLINELEN;
  543. strncpy(szShortProgName, "...", 3);
  544. }
  545. /* Shorten module name */
  546. szShortModuleName = (char *) szModule;
  547. if (szShortModuleName && strlen(szShortModuleName) > MAXLINELEN)
  548. {
  549. szShortModuleName += strlen(szShortModuleName) - MAXLINELEN;
  550. strncpy(szShortModuleName, "...", 3);
  551. }
  552. if (_snprintf(szOutMessage, MAX_MSG,
  553. "Debug %s!\n\nProgram: %s%s%s%s%s%s%s%s%s%s%s"
  554. "\n\n(Press Retry to debug the application)",
  555. _CrtDbgModeMsg[nRptType],
  556. szShortProgName,
  557. szShortModuleName ? "\nModule: " : "",
  558. szShortModuleName ? szShortModuleName : "",
  559. szFile ? "\nFile: " : "",
  560. szFile ? szFile : "",
  561. szLine ? "\nLine: " : "",
  562. szLine ? szLine : "",
  563. szUserMessage[0] ? "\n\n" : "",
  564. szUserMessage[0] && _CRT_ASSERT == nRptType ? "Expression: " : "",
  565. szUserMessage[0] ? szUserMessage : "",
  566. _CRT_ASSERT == nRptType ?
  567. "\n\nFor information on how your program can cause an assertion"
  568. "\nfailure, see the Visual C++ documentation on asserts."
  569. : "") < 0)
  570. strcpy(szOutMessage, TOOLONGMSG);
  571. /* Report the warning/error */
  572. nCode = __crtMessageBoxA(szOutMessage,
  573. "Microsoft Visual C++ Debug Library",
  574. MB_TASKMODAL|MB_ICONHAND|MB_ABORTRETRYIGNORE|MB_SETFOREGROUND);
  575. /* Abort: abort the program */
  576. if (IDABORT == nCode)
  577. {
  578. /* raise abort signal */
  579. raise(SIGABRT);
  580. /* We usually won't get here, but it's possible that
  581. SIGABRT was ignored. So exit the program anyway. */
  582. _exit(3);
  583. }
  584. /* Retry: return 1 to call the debugger */
  585. if (IDRETRY == nCode)
  586. return 1;
  587. /* Ignore: continue execution */
  588. return 0;
  589. }
  590. #endif /* _DEBUG */