Reactos
at master 692 lines 20 kB view raw
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 24extern "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 33extern "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 42extern "C" { 43 44_CRT_REPORT_HOOK _pfnReportHook; 45 46__crt_report_hook_node<char> *_pReportHookList; 47__crt_report_hook_node<wchar_t> *_pReportHookListW; 48 49long _crtAssertBusy = -1; 50 51// Enclaves only support MODE_DEBUG for error output 52#ifdef _UCRT_ENCLAVE_BUILD 53 54int 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 70int _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*******************************************************************************/ 101int __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. 241int __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. 461int __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"