1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 // This is a very basic direct output log implementation with no buffering.
16
17 // #define PW_LOG_MODULE_NAME "ASRT"
18 // #include "pw_log/log.h"
19
20 #include <cstdio>
21 #include <cstring>
22
23 #include "pw_assert/config.h"
24 #include "pw_assert_basic/handler.h"
25 #include "pw_preprocessor/util.h"
26 #include "pw_string/string_builder.h"
27 #include "pw_sys_io/sys_io.h"
28
29 // ANSI color constants to control the terminal. Not Windows compatible.
30 // clang-format off
31 #if PW_ASSERT_BASIC_USE_COLORS
32 #define MAGENTA "\033[35m"
33 #define YELLOW "\033[33m"
34 #define RED "\033[31m"
35 #define GREEN "\033[32m"
36 #define BLUE "\033[96m"
37 #define BLACK "\033[30m"
38 #define YELLOW_BG "\033[43m"
39 #define WHITE_BG "\033[47m"
40 #define RED_BG "\033[41m"
41 #define BOLD "\033[1m"
42 #define RESET "\033[0m"
43 #else
44 #define MAGENTA ""
45 #define YELLOW ""
46 #define RED ""
47 #define GREEN ""
48 #define BLUE ""
49 #define BLACK ""
50 #define YELLOW_BG ""
51 #define WHITE_BG ""
52 #define RED_BG ""
53 #define BOLD ""
54 #define RESET ""
55 #endif // PW_ASSERT_BASIC_USE_COLORS
56 // clang-format on
57
58 static const char* kCrashBanner[] = {
59 " ",
60 " ▄████▄ ██▀███ ▄▄▄ ██████ ██░ ██ ",
61 " ▒██▀ ▀█ ▓██ ▒ ██▒ ▒████▄ ▒██ ▒ ▓██░ ██▒ ",
62 " ▒▓█ ▄ ▓██ ░▄█ ▒ ▒██ ▀█▄ ░ ▓██▄ ▒██▀▀██░ ",
63 " ▒▓▓▄ ▄██▒ ▒██▀▀█▄ ░██▄▄▄▄██ ▒ ██▒ ░▓█ ░██ ",
64 " ▒ ▓███▀ ░ ░██▓ ▒██▒ ▓█ ▓██▒ ▒██████▒▒ ░▓█▒░██▓ ",
65 " ░ ░▒ ▒ ░ ░ ▒▓ ░▒▓░ ▒▒ ▓▒█░ ▒ ▒▓▒ ▒ ░ ▒ ░░▒░▒ ",
66 " ░ ▒ ░▒ ░ ▒░ ▒ ▒▒ ░ ░ ░▒ ░ ░ ▒ ░▒░ ░ ",
67 " ░ ░░ ░ ░ ▒ ░ ░ ░ ░ ░░ ░ ",
68 " ░ ░ ░ ░ ░ ░ ░ ░ ░ ",
69 " ░",
70 " ",
71 };
72
WriteLine(const std::string_view & s)73 static void WriteLine(const std::string_view& s) {
74 pw::sys_io::WriteLine(s)
75 .IgnoreError(); // TODO(b/242598609): Handle Status properly
76 }
77
78 typedef pw::StringBuffer<150> Buffer;
79
pw_assert_basic_HandleFailure(const char * file_name,int line_number,const char * function_name,const char * format,...)80 extern "C" void pw_assert_basic_HandleFailure(const char* file_name,
81 int line_number,
82 const char* function_name,
83 const char* format,
84 ...) {
85 // As a matter of usability, crashes should be visible; make it so.
86 #if PW_ASSERT_BASIC_SHOW_BANNER
87 WriteLine(RED);
88 for (const char* line : kCrashBanner) {
89 WriteLine(line);
90 }
91 WriteLine(RESET);
92 #endif // PW_ASSERT_BASIC_SHOW_BANNER
93
94 WriteLine(
95 " Welp, that didn't go as planned. "
96 "It seems we crashed. Terribly sorry!");
97 WriteLine("");
98 WriteLine(YELLOW " CRASH MESSAGE" RESET);
99 WriteLine("");
100 {
101 Buffer buffer;
102 buffer << " ";
103 va_list args;
104 va_start(args, format);
105 buffer.FormatVaList(format, args);
106 va_end(args);
107 WriteLine(buffer.view());
108 }
109
110 if (file_name != nullptr && line_number != -1) {
111 WriteLine("");
112 WriteLine(YELLOW " CRASH FILE & LINE" RESET);
113 WriteLine("");
114 {
115 Buffer buffer;
116 buffer.Format(" %s:%d", file_name, line_number);
117 WriteLine(buffer.view());
118 }
119 }
120 if (function_name != nullptr) {
121 WriteLine("");
122 WriteLine(YELLOW " CRASH FUNCTION" RESET);
123 WriteLine("");
124 {
125 Buffer buffer;
126 buffer.Format(" %s", function_name);
127 WriteLine(buffer.view());
128 }
129 }
130
131 // TODO(pwbug/95): Perhaps surprisingly, this doesn't actually crash the
132 // device. At some point we'll have a reboot BSP function or similar, but for
133 // now this is acceptable since no one is using this basic backend.
134 if (!PW_ASSERT_BASIC_DISABLE_NORETURN) {
135 #if (PW_ASSERT_BASIC_ACTION == PW_ASSERT_BASIC_ACTION_ABORT)
136 // abort() doesn't flush stderr/stdout, so manually flush them before
137 // aborting. abort() is preferred to exit(1) because debuggers catch it.
138 std::fflush(stderr);
139 std::fflush(stdout);
140 std::abort();
141 #elif (PW_ASSERT_BASIC_ACTION == PW_ASSERT_BASIC_ACTION_EXIT)
142 // Use _Exit to not run destructors or atexit hooks in case they cause
143 // further crashes.
144 std::_Exit(-1);
145 #elif (PW_ASSERT_BASIC_ACTION == PW_ASSERT_BASIC_ACTION_LOOP)
146 WriteLine("");
147 WriteLine(MAGENTA " HANG TIME" RESET);
148 WriteLine("");
149 WriteLine(
150 " ... until a debugger joins. System is waiting in a while(1)");
151 while (true) {
152 }
153 #else
154 #error PW_ASSERT_BASIC_ACTION Must be set to valid option.
155 #endif
156 PW_UNREACHABLE;
157 } else {
158 WriteLine("");
159 WriteLine(MAGENTA " NOTE: YOU ARE IN ASSERT BASIC TEST MODE" RESET);
160 WriteLine("");
161 WriteLine(" This build returns from the crash handler for testing.");
162 WriteLine(" If you see this message in production, your build is ");
163 WriteLine(" incorrectly configured. Search for");
164 WriteLine(" PW_ASSERT_BASIC_DISABLE_NORETURN to fix it.");
165 WriteLine("");
166 }
167 }
168