/src/serenity/AK/Assertions.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/Assertions.h> |
8 | | #include <AK/Format.h> |
9 | | #include <AK/Platform.h> |
10 | | #include <AK/StringBuilder.h> |
11 | | #include <AK/StringView.h> |
12 | | |
13 | | #if (defined(AK_OS_LINUX) && defined(AK_LIBC_GLIBC)) || defined(AK_OS_BSD_GENERIC) || defined(AK_OS_SOLARIS) || defined(AK_OS_HAIKU) || defined(AK_OS_GNU_HURD) |
14 | | # define EXECINFO_BACKTRACE |
15 | | #endif |
16 | | |
17 | 0 | #define PRINT_ERROR(s) (void)fputs((s), stderr) |
18 | | |
19 | | #if defined(EXECINFO_BACKTRACE) |
20 | | # include <cxxabi.h> |
21 | | # include <execinfo.h> |
22 | | #endif |
23 | | |
24 | | #if defined(AK_OS_SERENITY) |
25 | | # define ERRORLN dbgln |
26 | | #else |
27 | 0 | # define ERRORLN warnln |
28 | | #endif |
29 | | |
30 | | #if !defined(KERNEL) |
31 | | |
32 | | # if defined(EXECINFO_BACKTRACE) |
33 | | namespace { |
34 | | ALWAYS_INLINE void dump_backtrace() |
35 | 0 | { |
36 | | // Grab symbols and dso name for up to 256 frames |
37 | 0 | void* trace[256] = {}; |
38 | 0 | int const num_frames = backtrace(trace, array_size(trace)); |
39 | 0 | char** syms = backtrace_symbols(trace, num_frames); |
40 | |
|
41 | 0 | for (auto i = 0; i < num_frames; ++i) { |
42 | | // If there is a C++ symbol name in the line of the backtrace, demangle it |
43 | 0 | StringView sym(syms[i], strlen(syms[i])); |
44 | 0 | StringBuilder error_builder; |
45 | 0 | if (auto idx = sym.find("_Z"sv); idx.has_value()) { |
46 | | // Play C games with the original string so we can print before and after the mangled symbol with a C API |
47 | | // We don't want to call dbgln() here on substring StringView because we might VERIFY() within AK::Format |
48 | 0 | syms[i][idx.value() - 1] = '\0'; |
49 | 0 | error_builder.append(syms[i], strlen(syms[i])); |
50 | 0 | error_builder.append(' '); |
51 | |
|
52 | 0 | auto sym_substring = sym.substring_view(idx.value()); |
53 | 0 | auto end_of_sym = sym_substring.find_any_of("+ "sv).value_or(sym_substring.length() - 1); |
54 | 0 | syms[i][idx.value() + end_of_sym] = '\0'; |
55 | |
|
56 | 0 | size_t buf_size = 128u; |
57 | 0 | char* buf = static_cast<char*>(malloc(buf_size)); |
58 | 0 | auto* raw_str = &syms[i][idx.value()]; |
59 | 0 | buf = abi::__cxa_demangle(raw_str, buf, &buf_size, nullptr); |
60 | |
|
61 | 0 | auto* buf_to_print = buf ? buf : raw_str; |
62 | 0 | error_builder.append(buf_to_print, strlen(buf_to_print)); |
63 | 0 | free(buf); |
64 | |
|
65 | 0 | error_builder.append(' '); |
66 | 0 | auto* end_of_line = &syms[i][idx.value() + end_of_sym + 1]; |
67 | 0 | error_builder.append(end_of_line, strlen(end_of_line)); |
68 | 0 | } else { |
69 | 0 | error_builder.append(sym); |
70 | 0 | } |
71 | 0 | error_builder.append('\n'); |
72 | 0 | error_builder.append('\0'); |
73 | 0 | PRINT_ERROR(error_builder.string_view().characters_without_null_termination()); |
74 | 0 | } |
75 | 0 | free(syms); |
76 | 0 | } |
77 | | } |
78 | | # endif |
79 | | |
80 | | extern "C" { |
81 | | |
82 | | void ak_verification_failed(char const* message) |
83 | 0 | { |
84 | | # if defined(AK_OS_SERENITY) |
85 | | bool colorize_output = true; |
86 | | # elif defined(AK_OS_WINDOWS) |
87 | | bool colorize_output = false; |
88 | | # else |
89 | 0 | bool colorize_output = isatty(STDERR_FILENO) == 1; |
90 | 0 | # endif |
91 | |
|
92 | 0 | if (colorize_output) |
93 | 0 | ERRORLN("\033[31;1mVERIFICATION FAILED\033[0m: {}", message); |
94 | 0 | else |
95 | 0 | ERRORLN("VERIFICATION FAILED: {}", message); |
96 | |
|
97 | 0 | # if defined(EXECINFO_BACKTRACE) |
98 | 0 | dump_backtrace(); |
99 | 0 | # endif |
100 | 0 | __builtin_trap(); |
101 | 0 | } |
102 | | } |
103 | | |
104 | | #endif |