ReactOS 0.4.16-dev-732-g2d1144a
assert.cpp
Go to the documentation of this file.
1/***
2*assert.c - Display a message and abort
3*
4* Copyright (c) Microsoft Corporation. All rights reserved.
5*
6*Purpose:
7*
8*******************************************************************************/
9
10#include <corecrt_internal.h>
12#include <limits.h>
13#include <signal.h>
14#include <stdio.h>
15#include <string.h>
16
17#undef NDEBUG
18#define _ASSERT_OK
19#include <assert.h>
20
21// Assertion string components:
22#define MAXLINELEN 64 /* max length for line in message box */
23#define ASSERTBUFSZ (MAXLINELEN * 9) /* 9 lines in message box */
24
25// Format of stderr for assertions:
26//
27// Assertion failed: <expression>, file c:\test\mytest\bar.c, line 69
28//
29
30
31
32_GENERATE_TCHAR_STRING_FUNCTIONS(assert_format, "Assertion failed: %Ts, file %Ts, line %d\n")
33
34// Enclaves only support assertions sent to the debugger.
35// This mode could also be enabled for normal apps as well.
36
37#ifdef _UCRT_ENCLAVE_BUILD
38
39template <typename Character>
40__declspec(noreturn) static void __cdecl common_assert_to_debug(
41 Character const* const expression,
42 Character const* const file_name,
43 unsigned const line_number
44 ) throw()
45{
46 using traits = __crt_char_traits<Character>;
47
48 Character assert_buffer[ASSERTBUFSZ];
49 if (traits::sntprintf_s(assert_buffer, _countof(assert_buffer), _countof(assert_buffer), get_assert_format(Character()), expression, file_name, line_number) < 0)
50 {
51 abort();
52 }
53 traits::output_debug_string(assert_buffer);
54 abort();
55}
56
57template <typename Character>
58static void __cdecl common_assert(
59 Character const* const expression,
60 Character const* const file_name,
61 unsigned const line_number,
62 void* const
63 ) throw()
64{
65 common_assert_to_debug(expression, file_name, line_number);
66}
67
68#else /* ^^^ _UCRT_ENCLAVE_BUILD ^^^ // vvv !_UCRT_ENCLAVE_BUILD vvv */
69
70// Format of MessageBox for assertions:
71//
72// ================= Microsft Visual C++ Debug Library ================
73//
74// Assertion Failed!
75//
76// Program: c:\test\mytest\foo.exe
77// File: c:\test\mytest\bar.c
78// Line: 69
79//
80// Expression: <expression>
81//
82// For information on how your program can cause an assertion
83// failure, see the Visual C++ documentation on asserts
84//
85// (Press Retry to debug the application - JIT must be enabled)
86//
87// ===================================================================
88
89
90
91_GENERATE_TCHAR_STRING_FUNCTIONS(banner_text, "Microsoft Visual C++ Runtime Library")
92
93_GENERATE_TCHAR_STRING_FUNCTIONS(box_intro, "Assertion failed!")
94_GENERATE_TCHAR_STRING_FUNCTIONS(program_intro, "Program: ")
98_GENERATE_TCHAR_STRING_FUNCTIONS(info_intro, "For information on how your program can cause an assertion\nfailure, see the Visual C++ documentation on asserts")
99_GENERATE_TCHAR_STRING_FUNCTIONS(help_intro, "(Press Retry to debug the application - JIT must be enabled)")
100
101_GENERATE_TCHAR_STRING_FUNCTIONS(dot_dot_dot, "...")
103_GENERATE_TCHAR_STRING_FUNCTIONS(double_newline, "\n\n")
104
105_GENERATE_TCHAR_STRING_FUNCTIONS(program_name_unknown_text, "<program name unknown>")
106
107/***
108*_assert() - Display a message and abort
109*
110*Purpose:
111* The assert macro calls this routine if the assert expression is
112* true. By placing the assert code in a subroutine instead of within
113* the body of the macro, programs that call assert multiple times will
114* save space.
115*
116*Entry:
117*
118*Exit:
119*
120*Exceptions:
121*
122*******************************************************************************/
123static void __cdecl common_assert_to_stderr_direct(char const*, char const*, unsigned) throw()
124{
125 // No action for narrow strings
126}
127
129 wchar_t const* const expression,
130 wchar_t const* const file_name,
131 unsigned const line_number
132 ) throw()
133{
134 HANDLE const stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
135#pragma warning(suppress:__WARNING_REDUNDANT_POINTER_TEST) // 28922
136 if (stderr_handle == INVALID_HANDLE_VALUE || stderr_handle == nullptr)
137 {
138 return;
139 }
140
141 if (GetFileType(stderr_handle) != FILE_TYPE_CHAR)
142 {
143 return;
144 }
145
146 wchar_t assert_buffer[ASSERTBUFSZ];
147#pragma warning(suppress:__WARNING_BANNED_API_USAGE) // 28719
148 if (swprintf(assert_buffer, _countof(assert_buffer), get_assert_format(wchar_t()), expression, file_name, line_number) < 0)
149 {
150 return;
151 }
152
153 DWORD const assert_buffer_length = static_cast<DWORD>(wcslen(assert_buffer));
154 DWORD characters_written = 0;
155 if (WriteConsoleW(stderr_handle, assert_buffer, assert_buffer_length, &characters_written, nullptr) == 0)
156 {
157 return;
158 }
159
160 abort();
161}
162
163template <typename Character>
164__declspec(noreturn) static void __cdecl common_assert_to_stderr(
165 Character const* const expression,
166 Character const* const file_name,
167 unsigned const line_number
169{
170 using traits = __crt_char_traits<Character>;
171
172 // Try to write directly to the console. This is only supported for wide
173 // character strings. If we have a narrow character string or the write
174 // fails, we fall back to call through stdio.
176
177 // If stderr does not yet have a buffer, set it to use single character
178 // buffering to avoid dynamic allocation of a stream buffer:
179 if (!__crt_stdio_stream(stderr).has_any_buffer())
180 {
181 setvbuf(stderr, nullptr, _IONBF, 0);
182 }
183
184 traits::ftprintf(stderr, get_assert_format(Character()), expression, file_name, line_number);
185 fflush(stderr);
186 abort();
187}
188
189template <typename Character>
191 Character* const assert_buffer,
192 size_t const assert_buffer_count,
193 Character const* const expression,
194 Character const* const file_name,
195 unsigned const line_number,
196 void* const return_address
197 ) throw()
198{
199 using traits = __crt_char_traits<Character>;
200
201 // Line 1: Box introduction line:
202 _ERRCHECK(traits::tcscpy_s(assert_buffer, assert_buffer_count, get_box_intro(Character())));
203 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_double_newline(Character())));
204
205 // Line 2: Program line:
206 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_program_intro(Character())));
207
208 Character program_name[_MAX_PATH + 1]{};
209
210 HMODULE asserting_module = nullptr;
212 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
213 static_cast<wchar_t const*>(return_address),
214 &asserting_module))
215 {
216 asserting_module = nullptr;
217 }
218
219 #ifdef CRTDLL
220 // If the assert came from within the CRT DLL, report it as having come
221 // from the EXE instead:
222 if (asserting_module == reinterpret_cast<HMODULE>(&__ImageBase))
223 {
224 asserting_module = nullptr;
225 }
226 #endif
227
228 if (!traits::get_module_file_name(asserting_module, program_name, static_cast<DWORD>(_countof(program_name))))
229 {
230 _ERRCHECK(traits::tcscpy_s(program_name, _countof(program_name), get_program_name_unknown_text(Character())));
231 }
232
233 Character* pchProg = program_name;
234 if (program_intro_count + traits::tcslen(program_name) + newline_length > MAXLINELEN)
235 {
236 pchProg += (program_intro_count + traits::tcslen(program_name) + newline_length) - MAXLINELEN;
237 // Only replace first (sizeof(Character) * dot_dot_dot_length) bytes to ellipsis:
239 pchProg,
240 sizeof(Character) * ((MAX_PATH + 1) - (pchProg - program_name)),
241 get_dot_dot_dot(Character()),
242 sizeof(Character) * dot_dot_dot_length
243 ));
244 }
245
246 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, pchProg));
247 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_newline(Character())));
248
249 // Line 3: File line
250 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_file_intro(Character())));
251
252 if (file_intro_count + traits::tcslen(file_name) + newline_length > MAXLINELEN)
253 {
254 size_t const ffn = MAXLINELEN - file_intro_count - newline_length;
255
256 size_t p = 0;
257 size_t len = 0;
258 Character const* pch = file_name;
259 for (len = traits::tcslen(file_name), p = 1;
260 pch[len - p] != '\\' && pch[len - p] != '/' && p < len;
261 p++)
262 {
263 }
264
265 // Trim the path and file name so that they fit, using up to 2/3 of
266 // the maximum number of characters for the path and the remaining
267 // 1/3 for the file name:
268 if ((ffn - ffn / 3) < (len - p) && ffn / 3 > p)
269 {
270 // The path is too long. Use the first part of the path and the
271 // full file name:
272 _ERRCHECK(traits::tcsncat_s(assert_buffer, assert_buffer_count, pch, ffn - dot_dot_dot_length - p));
273 _ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, get_dot_dot_dot(Character())));
274 _ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, pch + len - p));
275 }
276 else if (ffn - ffn / 3 > len - p)
277 {
278 // The file name is too long. Use the full path and the first
279 // and last part of the file name, with a ... in between:
280 p = p / 2;
281 _ERRCHECK(traits::tcsncat_s(assert_buffer, assert_buffer_count, pch, ffn - dot_dot_dot_length - p));
282 _ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, get_dot_dot_dot(Character())));
283 _ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, pch + len - p));
284 }
285 else
286 {
287 // Both are too long. Use the first part of the path and the
288 // first and last part of the file name, with ...s in between:
289 _ERRCHECK(traits::tcsncat_s(assert_buffer, assert_buffer_count, pch, ffn - ffn / 3 - dot_dot_dot_length));
290 _ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, get_dot_dot_dot(Character())));
291 _ERRCHECK(traits::tcsncat_s(assert_buffer, assert_buffer_count, pch + len - p, ffn / 6 - 1));
292 _ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, get_dot_dot_dot(Character())));
293 _ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, pch + len - (ffn / 3 - ffn / 6 - 2)));
294 }
295 }
296 else
297 {
298 // Plenty of room on the line; just append the full path and file name:
299 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, file_name));
300 }
301
302 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_newline(Character())));
303
304 // Line 4: Line Number line:
305 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_line_intro(Character())));
306 _ERRCHECK(traits::itot_s(
308 assert_buffer + traits::tcslen(assert_buffer),
309 assert_buffer_count - traits::tcslen(assert_buffer),
310 10));
311 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_double_newline(Character())));
312
313 // Line 5: Message line:
314 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_expression_intro(Character())));
315
316 size_t const characters_used =
317 traits::tcslen(assert_buffer) +
318 2 * double_newline_length +
319 info_intro_length +
320 help_intro_count;
321
322 if (characters_used + traits::tcslen(expression) > assert_buffer_count)
323 {
324 size_t const characters_to_write = assert_buffer_count - (characters_used + dot_dot_dot_length);
325 _ERRCHECK(traits::tcsncat_s(
326 assert_buffer,
327 assert_buffer_count,
328 expression,
329 characters_to_write));
330 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_dot_dot_dot(Character())));
331 }
332 else
333 {
334 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, expression));
335 }
336
337 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_double_newline(Character())));
338
339 // Info line:
340 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_info_intro(Character())));
341 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_double_newline(Character())));
342
343 // Help line:
344 _ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_help_intro(Character())));
345}
346
347
348
349template <typename Character>
351 Character const* const expression,
352 Character const* const file_name,
353 unsigned const line_number,
354 void* const return_address
355 ) throw()
356{
357 using traits = __crt_char_traits<Character>;
358
359 Character assert_buffer[ASSERTBUFSZ]{};
361 assert_buffer,
362 _countof(assert_buffer),
363 expression,
364 file_name,
366 return_address);
367
368 int const action = traits::show_message_box(
369 assert_buffer,
370 get_banner_text(Character()),
372
373 switch (action)
374 {
375 case IDABORT: // Abort the program:
376 {
377 raise(SIGABRT);
378
379 // We won't usually get here, but it's possible that a user-registered
380 // abort handler returns, so exit the program immediately. Note that
381 // even though we are "aborting," we do not call abort() because we do
382 // not want to invoke Watson (the user has already had an opportunity
383 // to debug the error and chose not to).
384 _exit(3);
385 }
386 case IDRETRY: // Break into the debugger then return control to caller
387 {
388 __debugbreak();
389 return;
390 }
391 case IDIGNORE: // Return control to caller
392 {
393 return;
394 }
395 default: // This should not happen; treat as fatal error:
396 {
397 abort();
398 }
399 }
400}
401
402template <typename Character>
404 Character const* const expression,
405 Character const* const file_name,
406 unsigned const line_number,
407 void* const return_address
408 ) throw()
409{
410 using traits = __crt_char_traits<Character>;
411
412 int const current_error_mode = _set_error_mode(_REPORT_ERRMODE);
413 if (current_error_mode == _OUT_TO_STDERR)
414 {
415 return common_assert_to_stderr(expression, file_name, line_number);
416 }
417
418 if (current_error_mode == _OUT_TO_DEFAULT && _query_app_type() == _crt_console_app)
419 {
420 return common_assert_to_stderr(expression, file_name, line_number);
421 }
422
423 return common_assert_to_message_box(expression, file_name, line_number, return_address);
424}
425
426#endif /* _UCRT_ENCLAVE_BUILD */
427
428extern "C" void __cdecl _assert(
429 char const* const expression,
430 char const* const file_name,
431 unsigned const line_number
432 ) throw()
433{
434 return common_assert(expression, file_name, line_number, _ReturnAddress());
435}
436
437extern "C" void __cdecl _wassert(
438 wchar_t const* const expression,
439 wchar_t const* const file_name,
440 unsigned const line_number
441 )
442{
443 return common_assert(expression, file_name, line_number, _ReturnAddress());
444}
PCWSTR Expression
void _exit(int exitcode)
Definition: _exit.c:25
#define __cdecl
Definition: accygwin.h:79
static void __cdecl common_assert_to_stderr_direct(char const *, char const *, unsigned)
Definition: assert.cpp:123
Character const *const unsigned const line_number throw()
Definition: assert.cpp:168
static void __cdecl common_assert(Character const *const expression, Character const *const file_name, unsigned const line_number, void *const return_address)
Definition: assert.cpp:403
void __cdecl _wassert(wchar_t const *const expression, wchar_t const *const file_name, unsigned const line_number)
Definition: assert.cpp:437
#define MAXLINELEN
Definition: assert.cpp:22
#define ASSERTBUFSZ
Definition: assert.cpp:23
Character const *const file_name
Definition: assert.cpp:166
static void __cdecl common_assert_to_message_box(Character const *const expression, Character const *const file_name, unsigned const line_number, void *const return_address)
Definition: assert.cpp:350
void __cdecl _assert(char const *const expression, char const *const file_name, unsigned const line_number)
Definition: assert.cpp:428
static void __cdecl common_assert_to_message_box_build_string(Character *const assert_buffer, size_t const assert_buffer_count, Character const *const expression, Character const *const file_name, unsigned const line_number, void *const return_address)
Definition: assert.cpp:190
HANDLE WINAPI GetStdHandle(IN DWORD nStdHandle)
Definition: console.c:203
Definition: terminate.cpp:24
Definition: File.h:16
_In_ PSCSI_REQUEST_BLOCK _Out_ NTSTATUS _Inout_ BOOLEAN * Retry
Definition: classpnp.h:312
#define _ERRCHECK(e)
#define _GENERATE_TCHAR_STRING_FUNCTIONS(name, string)
@ _crt_console_app
_ACRTIMP _crt_app_type __cdecl _query_app_type(void)
int __cdecl raise(int _SigNum)
Definition: signal.c:71
#define SIGABRT
Definition: signal.h:28
#define __ImageBase
Definition: crt_handler.c:22
static PDB_INFORMATION information
Definition: db.cpp:178
int const char const *const int const line_number
Definition: debug_heap.cpp:499
static WCHAR unknown[MAX_STRING_RESOURCE_LEN]
Definition: object.c:1605
#define INVALID_HANDLE_VALUE
Definition: compat.h:731
#define MAX_PATH
Definition: compat.h:34
BOOL WINAPI DECLSPEC_HOTPATCH WriteConsoleW(IN HANDLE hConsoleOutput, IN CONST VOID *lpBuffer, IN DWORD nNumberOfCharsToWrite, OUT LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved)
Definition: readwrite.c:1447
DWORD WINAPI GetFileType(HANDLE hFile)
Definition: fileinfo.c:269
BOOL WINAPI GetModuleHandleExW(IN DWORD dwFlags, IN LPCWSTR lpwModuleName OPTIONAL, OUT HMODULE *phModule)
Definition: loader.c:866
const WCHAR * action
Definition: action.c:7509
#define swprintf
Definition: precomp.h:40
unsigned long DWORD
Definition: ntddk_ex.h:95
void __declspec(noinline) __cdecl _free_base(void *const block)
Definition: free_base.cpp:98
GLenum GLenum GLsizei const GLuint GLboolean enabled
Definition: glext.h:7750
GLdouble n
Definition: glext.h:7729
GLuint program
Definition: glext.h:6723
GLfloat GLfloat p
Definition: glext.h:8902
GLenum GLsizei len
Definition: glext.h:6722
#define abort()
Definition: i386-dis.c:34
#define stderr
Definition: stdio.h:100
_Check_return_opt_ _CRTIMP int __cdecl fflush(_Inout_opt_ FILE *_File)
#define _IONBF
Definition: stdio.h:129
_Check_return_opt_ _CRTIMP int __cdecl setvbuf(_Inout_ FILE *_File, _Inout_updates_opt_z_(_Size) char *_Buf, _In_ int _Mode, _In_ size_t _Size)
#define _REPORT_ERRMODE
Definition: stdlib.h:113
#define _OUT_TO_DEFAULT
Definition: stdlib.h:110
_Check_return_opt_ _CRTIMP int __cdecl _set_error_mode(_In_ int _Mode)
#define _OUT_TO_STDERR
Definition: stdlib.h:111
_CRTIMP size_t __cdecl wcslen(_In_z_ const wchar_t *_Str)
void __cdecl __debugbreak(void)
Definition: intrin_ppc.h:698
#define _ReturnAddress()
Definition: intrin_arm.h:35
#define debug(msg)
Definition: key_call.c:71
static char * program_name
Definition: mkdosfs.c:519
#define pch(ap)
Definition: match.c:418
#define _MAX_PATH
Definition: utility.h:77
int CDECL memcpy_s(void *dest, size_t numberOfElements, const void *src, size_t count)
Definition: heap.c:800
#define _countof(array)
Definition: sndvol32.h:70
Definition: ncftp.h:79
Definition: name.c:39
#define STD_ERROR_HANDLE
Definition: winbase.h:295
#define FILE_TYPE_CHAR
Definition: winbase.h:286
#define MB_SETFOREGROUND
Definition: winuser.h:817
#define MB_ICONHAND
Definition: winuser.h:791
#define MB_TASKMODAL
Definition: winuser.h:819
#define IDIGNORE
Definition: winuser.h:837
#define MB_ABORTRETRYIGNORE
Definition: winuser.h:794
#define IDABORT
Definition: winuser.h:835
#define IDRETRY
Definition: winuser.h:836
#define const
Definition: zconf.h:233