1 /*** 2 *dbgrptt.c - Debug CRT Reporting Functions 3 * 4 * Copyright (c) Microsoft Corporation. All rights reserved. 5 * 6 *Purpose: 7 * 8 *******************************************************************************/ 9 10 #ifndef _DEBUG 11 #error This file is supported only in debug builds 12 #define _DEBUG // For design-time support, when editing/viewing CRT sources 13 #endif 14 15 #include <corecrt_internal.h> 16 #include <errno.h> 17 #include <malloc.h> 18 #include <minmax.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 22 23 24 extern "C" int __cdecl __acrt_MessageWindowA( 25 int report_type, 26 void* return_address, 27 char const* file_name, 28 char const* line_number, 29 char const* module_name, 30 char const* user_message 31 ); 32 33 extern "C" int __cdecl __acrt_MessageWindowW( 34 int report_type, 35 void* return_address, 36 wchar_t const* file_name, 37 wchar_t const* line_number, 38 wchar_t const* module_name, 39 wchar_t const* user_message 40 ); 41 42 extern "C" { 43 44 _CRT_REPORT_HOOK _pfnReportHook; 45 46 __crt_report_hook_node<char> *_pReportHookList; 47 __crt_report_hook_node<wchar_t> *_pReportHookListW; 48 49 long _crtAssertBusy = -1; 50 51 // Enclaves only support MODE_DEBUG for error output 52 #ifdef _UCRT_ENCLAVE_BUILD 53 54 int const _CrtDbgMode[_CRT_ERRCNT] 55 { 56 _CRTDBG_MODE_DEBUG, 57 _CRTDBG_MODE_DEBUG, 58 _CRTDBG_MODE_DEBUG 59 }; 60 61 _HFILE const _CrtDbgFile[_CRT_ERRCNT] 62 { 63 _CRTDBG_INVALID_HFILE, 64 _CRTDBG_INVALID_HFILE, 65 _CRTDBG_INVALID_HFILE 66 }; 67 68 #else 69 70 int _CrtDbgMode[_CRT_ERRCNT] 71 { 72 _CRTDBG_MODE_DEBUG, 73 _CRTDBG_MODE_WNDW, 74 _CRTDBG_MODE_WNDW 75 }; 76 77 _HFILE _CrtDbgFile[_CRT_ERRCNT] 78 { 79 _CRTDBG_INVALID_HFILE, 80 _CRTDBG_INVALID_HFILE, 81 _CRTDBG_INVALID_HFILE 82 }; 83 84 /*** 85 *int _CrtSetReportMode - set the reporting mode for a given report type 86 * 87 *Purpose: 88 * set the reporting mode for a given report type 89 * 90 *Entry: 91 * int nRptType - the report type 92 * int fMode - new mode for given report type 93 * 94 *Exit: 95 * previous mode for given report type 96 * 97 *Exceptions: 98 * Input parameters are validated. Refer to the validation section of the function. 99 * 100 *******************************************************************************/ 101 int __cdecl _CrtSetReportMode( 102 int nRptType, 103 int fMode 104 ) 105 { 106 int oldMode; 107 108 /* validation section */ 109 _VALIDATE_RETURN(nRptType >= 0 && nRptType < _CRT_ERRCNT, EINVAL, -1); 110 _VALIDATE_RETURN( 111 fMode == _CRTDBG_REPORT_MODE || 112 (fMode & ~(_CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW)) == 0, 113 EINVAL, 114 -1); 115 116 if (fMode == _CRTDBG_REPORT_MODE) 117 return _CrtDbgMode[nRptType]; 118 119 oldMode = _CrtDbgMode[nRptType]; 120 121 _CrtDbgMode[nRptType] = fMode; 122 123 return oldMode; 124 } 125 126 /*** 127 *int _CrtSetReportFile - set the reporting file for a given report type 128 * 129 *Purpose: 130 * set the reporting file for a given report type 131 * 132 *Entry: 133 * int nRptType - the report type 134 * _HFILE hFile - new file for given report type 135 * 136 *Exit: 137 * previous file for given report type 138 * 139 *Exceptions: 140 * Input parameters are validated. Refer to the validation section of the function. 141 * 142 *******************************************************************************/ 143 _HFILE __cdecl _CrtSetReportFile( 144 int nRptType, 145 _HFILE hFile 146 ) 147 { 148 _HFILE oldFile; 149 150 /* validation section */ 151 _VALIDATE_RETURN(nRptType >= 0 && nRptType < _CRT_ERRCNT, EINVAL, _CRTDBG_HFILE_ERROR); 152 153 if (hFile == _CRTDBG_REPORT_FILE) 154 return _CrtDbgFile[nRptType]; 155 156 oldFile = _CrtDbgFile[nRptType]; 157 158 if (_CRTDBG_FILE_STDOUT == hFile) 159 _CrtDbgFile[nRptType] = GetStdHandle(STD_OUTPUT_HANDLE); 160 else if (_CRTDBG_FILE_STDERR == hFile) 161 _CrtDbgFile[nRptType] = GetStdHandle(STD_ERROR_HANDLE); 162 else 163 _CrtDbgFile[nRptType] = hFile; 164 165 return oldFile; 166 } 167 168 #endif /* _UCRT_ENCLAVE_BUILD */ 169 170 /*** 171 *_CRT_REPORT_HOOK _CrtSetReportHook() - set client report hook 172 * 173 *Purpose: 174 * set client report hook. This function is provided only in ANSI 175 * for backward compatibility. No Unicode Version of this function exists 176 * 177 *Entry: 178 * _CRT_REPORT_HOOK pfnNewHook - new report hook 179 * 180 *Exit: 181 * return previous hook 182 * 183 *Exceptions: 184 * 185 *******************************************************************************/ 186 187 _CRT_REPORT_HOOK __cdecl _CrtSetReportHook(_CRT_REPORT_HOOK pfnNewHook) 188 { 189 _CRT_REPORT_HOOK pfnOldHook = _pfnReportHook; 190 _pfnReportHook = pfnNewHook; 191 return pfnOldHook; 192 } 193 194 /*** 195 *_CRT_REPORT_HOOK _CrtGetReportHook() - get client report hook 196 * 197 *Purpose: 198 * get client report hook. 199 * 200 *Entry: 201 * 202 *Exit: 203 * return current hook 204 * 205 *Exceptions: 206 * 207 *******************************************************************************/ 208 209 _CRT_REPORT_HOOK __cdecl _CrtGetReportHook(void) 210 { 211 return _pfnReportHook; 212 } 213 214 #define ASSERTINTRO1 "Assertion failed: " 215 #define ASSERTINTRO2 "Assertion failed!" 216 217 /*** 218 *int _VCrtDbgReportA() - _CrtDbgReport calls into this function 219 * 220 *Purpose: 221 * See remarks for _CrtDbgReport. 222 * 223 *Entry: 224 * int nRptType - report type 225 * char const* szFile - file name 226 * int nLine - line number 227 * char const* szModule - module name 228 * char const* szFormat - format string 229 * va_list arglist - var args arglist 230 * 231 *Exit: 232 * See remarks for _CrtDbgReport 233 * 234 *Exceptions: 235 * 236 *******************************************************************************/ 237 238 #pragma warning(push) 239 #pragma warning(disable:6262) 240 // prefast (6262): This func uses lots of stack because we want to tolerate very large reports, and we can't use malloc here. 241 int __cdecl _VCrtDbgReportA( 242 int nRptType, 243 void * returnAddress, 244 char const* szFile, 245 int nLine, 246 char const* szModule, 247 char const* szFormat, 248 va_list arglist 249 ) 250 { 251 int retval=0; 252 int handled=FALSE; 253 254 char szLineMessage[DBGRPT_MAX_MSG]{0}; 255 char szOutMessage [DBGRPT_MAX_MSG]{0}; 256 wchar_t szOutMessage2[DBGRPT_MAX_MSG]{0}; 257 char szUserMessage[DBGRPT_MAX_MSG]{0}; 258 259 if (nRptType < 0 || nRptType >= _CRT_ERRCNT) 260 return -1; 261 262 /* 263 * handle the (hopefully rare) case of 264 * 265 * 1) ASSERT while already dealing with an ASSERT 266 * or 267 * 2) two threads asserting at the same time 268 */ 269 270 __try 271 { 272 if (_CRT_ASSERT == nRptType && _InterlockedIncrement(&_crtAssertBusy) > 0) 273 { 274 /* use only 'safe' functions -- must not assert in here! */ 275 276 _ERRCHECK(_itoa_s(nLine, szLineMessage, DBGRPT_MAX_MSG, 10)); 277 278 __acrt_OutputDebugStringA("Second Chance Assertion Failed: File "); 279 __acrt_OutputDebugStringA(szFile ? szFile : "<file unknown>"); 280 __acrt_OutputDebugStringA(", Line "); 281 __acrt_OutputDebugStringA(szLineMessage); 282 __acrt_OutputDebugStringA("\n"); 283 284 _CrtDbgBreak(); 285 retval=-1; 286 __leave; 287 } 288 289 // Leave space for ASSERTINTRO1 and "\r\n" 290 if (szFormat) 291 { 292 int szlen = 0; 293 _ERRCHECK_SPRINTF(szlen = _vsnprintf_s(szUserMessage, DBGRPT_MAX_MSG, 294 DBGRPT_MAX_MSG - 2- max(sizeof(ASSERTINTRO1),sizeof(ASSERTINTRO2)), 295 szFormat, arglist)); 296 if (szlen < 0) 297 { 298 _ERRCHECK(strcpy_s(szUserMessage, DBGRPT_MAX_MSG, DBGRPT_TOOLONGMSG)); 299 } 300 } 301 302 if (_CRT_ASSERT == nRptType) 303 { 304 _ERRCHECK(strcpy_s(szLineMessage, DBGRPT_MAX_MSG, szFormat ? ASSERTINTRO1 : ASSERTINTRO2)); 305 } 306 307 _ERRCHECK(strcat_s(szLineMessage, DBGRPT_MAX_MSG, szUserMessage)); 308 309 if (_CRT_ASSERT == nRptType) 310 { 311 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE) 312 { 313 _ERRCHECK(strcat_s(szLineMessage, DBGRPT_MAX_MSG, "\r")); 314 } 315 316 _ERRCHECK(strcat_s(szLineMessage, DBGRPT_MAX_MSG, "\n")); 317 } 318 319 if (szFile) 320 { 321 int szlen = 0; 322 _ERRCHECK_SPRINTF(szlen = _snprintf_s(szOutMessage, DBGRPT_MAX_MSG, DBGRPT_MAX_MSG - 1, "%s(%d) : %s", 323 szFile, nLine, szLineMessage)); 324 if (szlen < 0) 325 { 326 _ERRCHECK(strcpy_s(szOutMessage, DBGRPT_MAX_MSG, DBGRPT_TOOLONGMSG)); 327 } 328 } 329 else 330 { 331 _ERRCHECK(strcpy_s(szOutMessage, DBGRPT_MAX_MSG, szLineMessage)); 332 } 333 334 { 335 size_t ret = 0; 336 errno_t e = 0; 337 _ERRCHECK_EINVAL_ERANGE(e = mbstowcs_s(&ret, szOutMessage2, DBGRPT_MAX_MSG, szOutMessage, _TRUNCATE)); 338 if(e != 0) 339 { 340 _ERRCHECK(wcscpy_s(szOutMessage2, DBGRPT_MAX_MSG, _CRT_WIDE(DBGRPT_INVALIDMSG))); 341 } 342 } 343 344 /* User hook may handle report. 345 We have to check the ANSI Hook2 List & then the UNICODE Hook2 List. 346 Then we have check any ANSI individual Hook set through 347 SetReportHook */ 348 349 if (_pReportHookList || _pReportHookListW) 350 { 351 __crt_report_hook_node<char> *pnode=nullptr; 352 __crt_report_hook_node<wchar_t> *pnodeW=nullptr; 353 354 __acrt_lock(__acrt_debug_lock); 355 __try 356 { 357 for (pnode = _pReportHookList; pnode; pnode = pnode->next) 358 { 359 int hook_retval=0; 360 if (pnode->hook(nRptType, szOutMessage, &hook_retval)) 361 { 362 handled=TRUE; 363 retval=hook_retval; 364 __leave; 365 } 366 } 367 368 for (pnodeW = _pReportHookListW; pnodeW; pnodeW = pnodeW->next) 369 { 370 int hook_retval=0; 371 if (pnodeW->hook(nRptType, szOutMessage2, &hook_retval)) 372 { 373 handled=TRUE; 374 retval=hook_retval; 375 __leave; 376 } 377 } 378 } 379 __finally 380 { 381 __acrt_unlock(__acrt_debug_lock); 382 } 383 __endtry 384 } 385 386 if (handled) 387 __leave; 388 389 if (_pfnReportHook) 390 { 391 int hook_retval=0; 392 if (_pfnReportHook(nRptType, szOutMessage, &hook_retval)) 393 { 394 retval = hook_retval; 395 __leave; 396 } 397 } 398 399 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE) 400 { 401 if (_CrtDbgFile[nRptType] != _CRTDBG_INVALID_HFILE) 402 { 403 DWORD bytes_written = 0; 404 WriteFile(_CrtDbgFile[nRptType], szOutMessage, static_cast<DWORD>(strlen(szOutMessage)), &bytes_written, nullptr); 405 } 406 } 407 408 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_DEBUG) 409 { 410 __acrt_OutputDebugStringA(szOutMessage); 411 } 412 413 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_WNDW) 414 { 415 szLineMessage[0] = 0; 416 if (nLine) 417 { 418 _ERRCHECK(_itoa_s(nLine, szLineMessage, DBGRPT_MAX_MSG, 10)); 419 } 420 421 retval = __acrt_MessageWindowA(nRptType, returnAddress, szFile, (nLine ? szLineMessage : nullptr), szModule, szUserMessage); 422 } 423 } 424 __finally 425 { 426 if (_CRT_ASSERT == nRptType) 427 { 428 _InterlockedDecrement(&_crtAssertBusy); 429 } 430 } 431 __endtry 432 433 return retval; 434 } 435 #pragma warning(pop) 436 437 /*** 438 *int _VCrtDbgReportW() - _CrtDbgReportW calls into this function 439 * 440 *Purpose: 441 * See remarks for _CrtDbgReport. 442 * 443 *Entry: 444 * int nRptType - report type 445 * wchar_t const* szFile - file name 446 * int nLine - line number 447 * wchar_t const* szModule - module name 448 * wchar_t const* szFormat - format string 449 * va_list arglist - var args arglist 450 * 451 *Exit: 452 * See remarks for _CrtDbgReport 453 * 454 *Exceptions: 455 * 456 *******************************************************************************/ 457 458 #pragma warning(push) 459 #pragma warning(disable:6262) 460 // prefast(6262): This func uses lots of stack because we want to tolerate very large reports, and we can't use malloc here. 461 int __cdecl _VCrtDbgReportW 462 ( 463 int nRptType, 464 void * returnAddress, 465 wchar_t const* szFile, 466 int nLine, 467 wchar_t const* szModule, 468 wchar_t const* szFormat, 469 va_list arglist 470 ) 471 { 472 int retval=0; 473 int handled=FALSE; 474 wchar_t szLineMessage[DBGRPT_MAX_MSG] = {0}; 475 wchar_t szOutMessage[DBGRPT_MAX_MSG] = {0}; 476 char szOutMessage2[DBGRPT_MAX_MSG] = {0}; 477 wchar_t szUserMessage[DBGRPT_MAX_MSG] = {0}; 478 479 if (nRptType < 0 || nRptType >= _CRT_ERRCNT) 480 return -1; 481 482 /* 483 * handle the (hopefully rare) case of 484 * 485 * 1) ASSERT while already dealing with an ASSERT 486 * or 487 * 2) two threads asserting at the same time 488 */ 489 490 __try 491 { 492 if (_CRT_ASSERT == nRptType && _InterlockedIncrement(&_crtAssertBusy) > 0) 493 { 494 /* use only 'safe' functions -- must not assert in here! */ 495 496 _ERRCHECK(_itow_s(nLine, szLineMessage, DBGRPT_MAX_MSG, 10)); 497 498 OutputDebugStringW(L"Second Chance Assertion Failed: File "); 499 OutputDebugStringW(szFile ? szFile : L"<file unknown>"); 500 OutputDebugStringW(L", Line "); 501 OutputDebugStringW(szLineMessage); 502 OutputDebugStringW(L"\n"); 503 504 _CrtDbgBreak(); 505 retval = -1; 506 __leave; 507 } 508 509 if (szFormat) 510 { 511 // Leave space for ASSERTINTRO{1,2} and "\r\n" 512 size_t const max_assert_intro_count = __max(_countof(ASSERTINTRO1), _countof(ASSERTINTRO2)); 513 size_t const max_user_message_count = _countof(szUserMessage) - 2 - max_assert_intro_count; 514 515 // Force use of the legacy stdio wide character format specifiers 516 // mode for source compatibility. If we ever revisit support for 517 // the standard format specifiers, we'll need to revisit this as 518 // well. 519 int szlen = 0; 520 _ERRCHECK_SPRINTF(szlen = __stdio_common_vsnwprintf_s( 521 _CRT_INTERNAL_PRINTF_LEGACY_WIDE_SPECIFIERS, 522 szUserMessage, 523 _countof(szUserMessage), 524 max_user_message_count, 525 szFormat, 526 nullptr, 527 arglist)); 528 if (szlen < 0) 529 { 530 _ERRCHECK(wcscpy_s(szUserMessage, DBGRPT_MAX_MSG, _CRT_WIDE(DBGRPT_TOOLONGMSG))); 531 } 532 } 533 534 if (_CRT_ASSERT == nRptType) 535 _ERRCHECK(wcscpy_s(szLineMessage, DBGRPT_MAX_MSG, szFormat ? _CRT_WIDE(ASSERTINTRO1) : _CRT_WIDE(ASSERTINTRO2))); 536 537 _ERRCHECK(wcscat_s(szLineMessage, DBGRPT_MAX_MSG, szUserMessage)); 538 539 if (_CRT_ASSERT == nRptType) 540 { 541 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE) 542 _ERRCHECK(wcscat_s(szLineMessage, DBGRPT_MAX_MSG, L"\r")); 543 { 544 _ERRCHECK(wcscat_s(szLineMessage, DBGRPT_MAX_MSG, L"\n")); 545 } 546 } 547 548 if (szFile) 549 { 550 int szlen = 0; 551 _ERRCHECK_SPRINTF(szlen = _snwprintf_s(szOutMessage, DBGRPT_MAX_MSG, DBGRPT_MAX_MSG, L"%ls(%d) : %ls", 552 szFile, nLine, szLineMessage)); 553 if (szlen < 0) 554 _ERRCHECK(wcscpy_s(szOutMessage, DBGRPT_MAX_MSG, _CRT_WIDE(DBGRPT_TOOLONGMSG))); 555 } 556 else 557 { 558 _ERRCHECK(wcscpy_s(szOutMessage, DBGRPT_MAX_MSG, szLineMessage)); 559 } 560 561 /* scope */ 562 { 563 errno_t e = _ERRCHECK_EINVAL_ERANGE(wcstombs_s(nullptr, szOutMessage2, DBGRPT_MAX_MSG, szOutMessage, _TRUNCATE)); 564 if(e != 0) 565 _ERRCHECK(strcpy_s(szOutMessage2, DBGRPT_MAX_MSG, DBGRPT_INVALIDMSG)); 566 } 567 568 /* User hook may handle report. 569 We have to check the ANSI Hook2 List & then the UNICODE Hook2 List. 570 Then we have check any ANSI individual Hook set through 571 SetReportHook */ 572 573 if (_pReportHookList || _pReportHookListW) 574 { 575 __crt_report_hook_node<char> *pnode=nullptr; 576 __crt_report_hook_node<wchar_t> *pnodeW=nullptr; 577 578 __acrt_lock(__acrt_debug_lock); 579 __try 580 { 581 for (pnode = _pReportHookList; pnode; pnode = pnode->next) 582 { 583 int hook_retval=0; 584 if (pnode->hook(nRptType, szOutMessage2, &hook_retval)) 585 { 586 retval=hook_retval; 587 handled=TRUE; 588 __leave; 589 } 590 } 591 592 for (pnodeW = _pReportHookListW; pnodeW; pnodeW = pnodeW->next) 593 { 594 int hook_retval=0; 595 if (pnodeW->hook(nRptType, szOutMessage, &hook_retval)) 596 { 597 retval=hook_retval; 598 handled=TRUE; 599 __leave; 600 } 601 } 602 } 603 __finally 604 { 605 __acrt_unlock(__acrt_debug_lock); 606 } 607 __endtry 608 } 609 610 if (handled) 611 __leave; 612 613 if(_pfnReportHook) 614 { 615 int hook_retval=0; 616 if (_pfnReportHook(nRptType, szOutMessage2, &hook_retval)) 617 { 618 retval = hook_retval; 619 __leave; 620 } 621 } 622 623 if ((_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE) && _CrtDbgFile[nRptType] != _CRTDBG_INVALID_HFILE) 624 { 625 /* Use WriteConsole for Consoles, WriteFile otherwise */ 626 switch (GetFileType(_CrtDbgFile[nRptType])) 627 { 628 case FILE_TYPE_CHAR: 629 { 630 DWORD characters_written = 0; 631 if (WriteConsoleW(_CrtDbgFile[nRptType], szOutMessage, static_cast<DWORD>(wcslen(szOutMessage)), &characters_written, nullptr)) 632 break; 633 634 /* If WriteConsole fails & LastError is ERROR_INVALID_VALUE, then the console is redirected */ 635 if (GetLastError() != ERROR_INVALID_HANDLE) 636 break; 637 } 638 default: 639 { 640 char szaOutMessage[DBGRPT_MAX_MSG]; 641 size_t ret = 0; 642 errno_t e = _ERRCHECK_EINVAL_ERANGE(wcstombs_s(&ret, szaOutMessage, DBGRPT_MAX_MSG, szOutMessage, _TRUNCATE)); 643 644 if (e != 0 && e != STRUNCATE) 645 { 646 DWORD bytes_written = 0; 647 WriteFile(_CrtDbgFile[nRptType], szOutMessage, static_cast<DWORD>(wcslen(szOutMessage)) * 2, &bytes_written, nullptr); 648 } 649 else 650 { 651 /* ret counts for the null terminator as well */ 652 if (ret > 0) 653 { 654 --ret; 655 } 656 657 DWORD bytes_written = 0; 658 WriteFile(_CrtDbgFile[nRptType], szaOutMessage, static_cast<DWORD>(ret), &bytes_written, nullptr); 659 } 660 } 661 } 662 } 663 664 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_DEBUG) 665 { 666 ::OutputDebugStringW(szOutMessage); 667 } 668 669 if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_WNDW) 670 { 671 szLineMessage[0] = 0; 672 if (nLine) 673 { 674 _ERRCHECK(_itow_s(nLine, szLineMessage, DBGRPT_MAX_MSG, 10)); 675 } 676 retval = __acrt_MessageWindowW(nRptType, returnAddress, szFile, (nLine ? szLineMessage : nullptr), szModule, szUserMessage); 677 } 678 } 679 __finally 680 { 681 if (_CRT_ASSERT == nRptType) 682 { 683 _InterlockedDecrement(&_crtAssertBusy); 684 } 685 } 686 __endtry 687 688 return retval; 689 } 690 #pragma warning(pop) 691 692 } // extern "C" 693