xref: /reactos/sdk/lib/ucrt/misc/dbgrpt.cpp (revision ffd69754)
1 /***
2 *dbgrpt.c - Debug CRT Reporting Functions
3 *
4 *       Copyright (c) Microsoft Corporation. All rights reserved.
5 *
6 *Purpose:
7 *
8 *******************************************************************************/
9 #ifndef _DEBUG
10     #error This file is supported only in debug builds
11 #endif
12 
13 #include <corecrt_internal.h>
14 #include <corecrt_internal_traits.h>
15 #include <intrin.h>
16 #include <malloc.h>
17 #include <minmax.h>
18 #include <signal.h>
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 
23 
24 /*---------------------------------------------------------------------------
25  *
26  * Debug Reporting
27  *
28  --------------------------------------------------------------------------*/
29 
30 extern "C" _CRT_REPORT_HOOK _pfnReportHook;
31 extern "C" __crt_report_hook_node<char>* _pReportHookList;
32 extern "C" __crt_report_hook_node<wchar_t>* _pReportHookListW;
33 
get_report_hook_list(char)34 static __crt_report_hook_node<char>*&    __cdecl get_report_hook_list(char)    throw() { return _pReportHookList;  }
get_report_hook_list(wchar_t)35 static __crt_report_hook_node<wchar_t>*& __cdecl get_report_hook_list(wchar_t) throw() { return _pReportHookListW; }
36 
37 static wchar_t const* const report_type_messages[_CRT_ERRCNT] =
38 {
39     L"Warning",
40     L"Error",
41     L"Assertion Failed"
42 };
43 
44 // Enclaves only support MODE_DEBUG for error output
45 #ifndef _UCRT_ENCLAVE_BUILD
46 
get_output_message_format(char)47 static wchar_t const* __cdecl get_output_message_format(char) throw()
48 {
49     return L"Debug %ls!\n\nProgram: %hs%ls%ls%hs%ls%hs%ls%hs%ls%ls%hs%ls\n\n(Press Retry to debug the application)\n";
50 }
51 
get_output_message_format(wchar_t)52 static wchar_t const* __cdecl get_output_message_format(wchar_t) throw()
53 {
54     return L"Debug %ls!\n\nProgram: %ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls\n\n(Press Retry to debug the application)\n";
55 }
56 
57 static wchar_t const* const more_info_string =
58     L"\n\nFor information on how your program can cause an assertion"
59     L"\nfailure, see the Visual C++ documentation on asserts.";
60 
61 _GENERATE_TCHAR_STRING_FUNCTIONS(program_name_unknown_text, "<program name unknown>")
62 
63 #endif /* _UCRT_ENCLAVE_BUILD */
64 
65 /***
66 *_CRT_REPORT_HOOK _CrtSetReportHook2() - configure client report hook in list
67 *
68 *Purpose:
69 *       Install or remove a client report hook from the report list.  Exists
70 *       separately from _CrtSetReportHook because the older function doesn't
71 *       work well in an environment where DLLs that are loaded and unloaded
72 *       dynamically out of LIFO order want to install report hooks.
73 *       This function exists in 2 forms - ANSI & UNICODE
74 *
75 *Entry:
76 *       int mode - _CRT_RPTHOOK_INSTALL or _CRT_RPTHOOK_REMOVE
77 *       _CRT_REPORT_HOOK pfnNewHook - report hook to install/remove/query
78 *
79 *Exit:
80 *       Returns -1 if an error was encountered, with EINVAL or ENOMEM set,
81 *       else returns the reference count of pfnNewHook after the call.
82 *
83 *Exceptions:
84 *       Input parameters are validated. Refer to the validation section of the function.
85 *
86 *******************************************************************************/
87 template <typename Character, typename Hook>
common_set_report_hook(int const mode,Hook const new_hook)88 static int __cdecl common_set_report_hook(
89     int  const mode,
90     Hook const new_hook
91     ) throw()
92 {
93     using node_type = __crt_report_hook_node<Character>;
94 
95     _VALIDATE_RETURN(mode == _CRT_RPTHOOK_INSTALL || mode == _CRT_RPTHOOK_REMOVE, EINVAL, -1);
96     _VALIDATE_RETURN(new_hook != nullptr,                                         EINVAL, -1);
97 
98     return __acrt_lock_and_call(__acrt_debug_lock, [&]
99     {
100         node_type*& hook_list = get_report_hook_list(Character());
101 
102         node_type* p;
103         int ret = 0;
104 
105         // Search for new hook function to see if it's already installed
106         for (p = hook_list; p != nullptr; p = p->next)
107             if (p->hook == new_hook)
108                 break;
109 
110         if (mode == _CRT_RPTHOOK_REMOVE)
111         {
112             // Remove request - free list node if refcount goes to zero
113             if (p != nullptr)
114             {
115                 if ((ret = --p->refcount) == 0)
116                 {
117                     if (p->next)
118                     {
119                         p->next->prev = p->prev;
120                     }
121                     if (p->prev)
122                     {
123                         p->prev->next = p->next;
124                     }
125                     else
126                     {
127                         hook_list = p->next;
128                     }
129 
130                     _free_crt(p);
131                 }
132             }
133             else
134             {
135                 _ASSERTE(("The hook function is not in the list!", 0));
136                 errno = EINVAL;
137                 return -1;
138             }
139         }
140         else
141         {
142             // Insert request
143             if (p != nullptr)
144             {
145                 // Hook function already registered, move to head of list
146                 ret = ++p->refcount;
147                 if (p != hook_list)
148                 {
149                     if (p->next)
150                         p->next->prev = p->prev;
151                     p->prev->next = p->next;
152                     p->prev = nullptr;
153                     p->next = hook_list;
154                     hook_list->prev = p;
155                     hook_list = p;
156                 }
157             }
158             else
159             {
160                 // Hook function not already registered, insert new node
161                 __crt_unique_heap_ptr<node_type> new_node(_calloc_crt_t(node_type, 1));
162                 if (!new_node)
163                 {
164                     ret = -1;
165                     errno = ENOMEM;
166                 }
167                 else
168                 {
169                     new_node.get()->prev = nullptr;
170                     new_node.get()->next = hook_list;
171 
172                     if (hook_list)
173                     {
174                         hook_list->prev = new_node.get();
175                     }
176 
177                     ret = new_node.get()->refcount = 1;
178                     new_node.get()->hook = new_hook;
179                     hook_list = new_node.detach();
180                 }
181             }
182         }
183 
184         return ret;
185     });
186 }
187 
_CrtSetReportHook2(int const mode,_CRT_REPORT_HOOK const new_hook)188 extern "C" int __cdecl _CrtSetReportHook2(
189     int              const mode,
190     _CRT_REPORT_HOOK const new_hook
191     )
192 {
193     return common_set_report_hook<char>(mode, new_hook);
194 }
195 
_CrtSetReportHookW2(int const mode,_CRT_REPORT_HOOKW const new_hook)196 extern "C" int __cdecl _CrtSetReportHookW2(
197     int               const mode,
198     _CRT_REPORT_HOOKW const new_hook
199     )
200 {
201     return common_set_report_hook<wchar_t>(mode, new_hook);
202 }
203 
204 #define MAXLINELEN 64
205 
206 /***
207 *int _CrtDbgReport() - primary reporting function
208 *
209 *Purpose:
210 *       Display a message window with the following format.
211 *
212 *       ================= Microsft Visual C++ Debug Library ================
213 *
214 *       {Warning! | Error! | Assertion Failed!}
215 *
216 *       Program: c:\test\mytest\foo.exe
217 *       [Module: c:\test\mytest\bar.dll]
218 *       [File: c:\test\mytest\bar.c]
219 *       [Line: 69]
220 *
221 *       {<warning or error message> | Expression: <expression>}
222 *
223 *       [For information on how your program can cause an assertion
224 *        failure, see the Visual C++ documentation on asserts]
225 *
226 *       (Press Retry to debug the application)
227 *
228 *       ===================================================================
229 *
230 *Entry:
231 *       int             nRptType    - report type
232 *       void *          returnAddress - return address of caller
233 *       const TCHAR *    szFile      - file name
234 *       int             nLine       - line number
235 *       const TCHAR *    szModule    - module name
236 *       const TCHAR *    szFormat    - format string
237 *       ...                         - var args
238 *
239 *Exit:
240 *       if (MessageBox)
241 *       {
242 *           Abort -> aborts
243 *           Retry -> return TRUE
244 *           Ignore-> return FALSE
245 *       }
246 *       else
247 *           return FALSE
248 *
249 *Exceptions:
250 *       If something goes wrong, we do not assert, but we return -1.
251 *
252 *******************************************************************************/
_CrtDbgReport(int const report_type,char const * const file_name,int const line_number,char const * const module_name,char const * const format,...)253 extern "C" int __cdecl _CrtDbgReport(
254     int         const report_type,
255     char const* const file_name,
256     int         const line_number,
257     char const* const module_name,
258     char const* const format,
259     ...)
260 {
261     va_list arglist;
262     va_start(arglist, format);
263     int const result = _VCrtDbgReportA(report_type, _ReturnAddress(), file_name, line_number, module_name, format, arglist);
264     va_end(arglist);
265     return result;
266 }
267 
_CrtDbgReportW(int const report_type,wchar_t const * const file_name,int const line_number,wchar_t const * const module_name,wchar_t const * const format,...)268 extern "C" int __cdecl _CrtDbgReportW(
269     int            const report_type,
270     wchar_t const* const file_name,
271     int            const line_number,
272     wchar_t const* const module_name,
273     wchar_t const* const format,
274     ...)
275 {
276     va_list arglist;
277     va_start(arglist, format);
278     int const result = _VCrtDbgReportW(report_type, _ReturnAddress(), file_name, line_number, module_name, format, arglist);
279     va_end(arglist);
280     return result;
281 }
282 
283 // Enclaves only support MODE_DEBUG for error output
284 #if !defined _UCRT_ENCLAVE_BUILD
285 
286 /***
287 *int __crtMessageWindow() - report to a message window
288 *
289 *Purpose:
290 *       put report into message window, allow user to choose action to take
291 *
292 *Entry:
293 *       int             nRptType      - report type
294 *       const TCHAR *    szFile        - file name
295 *       const TCHAR *    szLine        - line number
296 *       const TCHAR *    szModule      - module name
297 *       const TCHAR *    szUserMessage - user message
298 *
299 *Exit:
300 *       if (MessageBox)
301 *       {
302 *           Abort -> aborts
303 *           Retry -> return TRUE
304 *           Ignore-> return FALSE
305 *       }
306 *       else
307 *           return FALSE
308 *
309 *Exceptions:
310 *       If something goes wrong, we do not assert, but we simply return -1,
311 *       which will trigger the debugger automatically (the same as the user
312 *       pressing the Retry button).
313 *
314 *******************************************************************************/
315 template <typename Character>
common_message_window(int const report_type,void * const return_address,Character const * const file_name,Character const * const line_number,Character const * const module_name,Character const * const user_message)316 static int __cdecl common_message_window(
317     int              const report_type,
318     void*            const return_address,
319     Character const* const file_name,
320     Character const* const line_number,
321     Character const* const module_name,
322     Character const* const user_message
323     ) throw()
324 {
325     using traits = __crt_char_traits<Character>;
326 
327     if (user_message == nullptr)
328     {
329         return 1;
330     }
331 
332     HMODULE module = nullptr;
333     if (!GetModuleHandleExW(
334             GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
335             static_cast<LPCWSTR>(return_address),
336             &module))
337     {
338         module = nullptr;
339     }
340     #ifdef CRTDLL
341     else if (module == reinterpret_cast<HMODULE>(&__ImageBase))
342     {
343         // If the message was reported from within the CRT DLL, report it as
344         // having come from the EXE instead:
345         module = nullptr;
346     }
347     #endif
348 
349     Character program_name[MAX_PATH + 1]{};
350     if (!traits::get_module_file_name(module, program_name, static_cast<DWORD>(_countof(program_name))))
351     {
352         _ERRCHECK(traits::tcscpy_s(program_name, _countof(program_name), get_program_name_unknown_text(Character())));
353     }
354 
355     // Shorten the program name:
356     size_t const program_name_length = traits::tcslen(program_name);
357     Character*   short_program_name  = program_name;
358     if (program_name_length > MAXLINELEN)
359     {
360         short_program_name += program_name_length - MAXLINELEN;
361         static_assert(MAXLINELEN > 3, "");
362 #pragma warning(push)
363 #pragma warning(disable:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY) // 26015
364         short_program_name[0] = '.';
365         short_program_name[1] = '.';
366         short_program_name[2] = '.';
367 #pragma warning(pop)
368     }
369 
370     // Shorten the module name:
371     size_t    const  module_name_length = module_name ? traits::tcslen(module_name) : 0;
372     Character const* short_module_name  = nullptr;
373     if (module_name && module_name_length > MAXLINELEN)
374     {
375         short_module_name = module_name + module_name_length - MAXLINELEN + 3;
376     }
377 
378     static Character const empty_string[] = { '\0' };
379 
380     wchar_t message_buffer[DBGRPT_MAX_MSG];
381     int const sprintf_result = _snwprintf_s(
382         message_buffer,
383         _countof(message_buffer),
384         _countof(message_buffer) - 1,
385 
386         get_output_message_format(Character()),
387 
388         report_type_messages[report_type],
389         short_program_name,
390         module_name ? L"\nModule: " : L"",
391         short_module_name ? L"..." : L"",
392         short_module_name ? short_module_name : (module_name ? module_name : empty_string),
393         file_name ? L"\nFile: " : L"",
394         file_name ? file_name : empty_string,
395         line_number ? L"\nLine: " : L"",
396         line_number ? line_number : empty_string,
397         user_message[0] ? L"\n\n" : L"",
398         user_message[0] && _CRT_ASSERT == report_type ? L"Expression: " : L"",
399         user_message[0] ? user_message : empty_string,
400         _CRT_ASSERT == report_type ? more_info_string : L"");
401 
402     _ERRCHECK_SPRINTF(sprintf_result);
403 
404     if (sprintf_result < 0)
405     {
406         _ERRCHECK(wcscpy_s(message_buffer, DBGRPT_MAX_MSG, _CRT_WIDE(DBGRPT_TOOLONGMSG)));
407     }
408 
409     int const message_box_result = __acrt_show_wide_message_box(
410         message_buffer,
411         L"Microsoft Visual C++ Runtime Library",
412         MB_TASKMODAL | MB_ICONHAND | MB_ABORTRETRYIGNORE | MB_SETFOREGROUND);
413 
414     switch (message_box_result)
415     {
416     case IDABORT: // Abort:  Terminate execution
417     {
418         // Note that even though we are "aborting," we do not call abort()
419         // because we do not want to invoke Watson (the user has already had an
420         // opportunity to debug the error and chose not to).
421         __crt_signal_handler_t const sigabrt_action = __acrt_get_sigabrt_handler();
422         if (sigabrt_action != SIG_DFL)
423         {
424             raise(SIGABRT);
425         }
426 
427         TerminateProcess(GetCurrentProcess(), 3);
428     }
429 
430     case IDRETRY:
431     {
432         return 1; // Retry:  Debug break
433     }
434 
435     case IDIGNORE:
436     default:
437     {
438         return 0; // Ignore:  Continue execution
439     }
440     }
441 }
442 
__acrt_MessageWindowA(int const report_type,void * const return_address,char const * const file_name,char const * const line_number,char const * const module_name,char const * const user_message)443 extern "C" int __cdecl __acrt_MessageWindowA(
444     int         const report_type,
445     void*       const return_address,
446     char const* const file_name,
447     char const* const line_number,
448     char const* const module_name,
449     char const* const user_message
450     )
451 {
452     return common_message_window(report_type, return_address, file_name, line_number, module_name, user_message);
453 }
454 
__acrt_MessageWindowW(int const report_type,void * const return_address,wchar_t const * const file_name,wchar_t const * const line_number,wchar_t const * const module_name,wchar_t const * const user_message)455 extern "C" int __cdecl __acrt_MessageWindowW(
456     int            const report_type,
457     void*          const return_address,
458     wchar_t const* const file_name,
459     wchar_t const* const line_number,
460     wchar_t const* const module_name,
461     wchar_t const* const user_message
462     )
463 {
464     return common_message_window(report_type, return_address, file_name, line_number, module_name, user_message);
465 }
466 
467 #endif
468