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 // If 1, call C's standard abort() function on assert failure.
30 #ifndef PW_ASSERT_BASIC_ABORT
31 #define PW_ASSERT_BASIC_ABORT 1
32 #endif // PW_ASSERT_BASIC_ABORT
33
34 // TODO(pwbug/17): Expose these through the config system.
35 #define PW_ASSERT_BASIC_SHOW_BANNER 1
36 #define PW_ASSERT_BASIC_USE_COLORS 1
37
38 // ANSI color constants to control the terminal. Not Windows compatible.
39 // clang-format off
40 #if PW_ASSERT_BASIC_USE_COLORS
41 #define MAGENTA "\033[35m"
42 #define YELLOW "\033[33m"
43 #define RED "\033[31m"
44 #define GREEN "\033[32m"
45 #define BLUE "\033[96m"
46 #define BLACK "\033[30m"
47 #define YELLOW_BG "\033[43m"
48 #define WHITE_BG "\033[47m"
49 #define RED_BG "\033[41m"
50 #define BOLD "\033[1m"
51 #define RESET "\033[0m"
52 #else
53 #define MAGENTA ""
54 #define YELLOW ""
55 #define RED ""
56 #define GREEN ""
57 #define BLUE ""
58 #define BLACK ""
59 #define YELLOW_BG ""
60 #define WHITE_BG ""
61 #define RED_BG ""
62 #define BOLD ""
63 #define RESET ""
64 #endif // PW_ASSERT_BASIC_USE_COLORS
65 // clang-format on
66
67 static const char* kCrashBanner[] = {
68 " ",
69 " ▄████▄ ██▀███ ▄▄▄ ██████ ██░ ██ ",
70 " ▒██▀ ▀█ ▓██ ▒ ██▒ ▒████▄ ▒██ ▒ ▓██░ ██▒ ",
71 " ▒▓█ ▄ ▓██ ░▄█ ▒ ▒██ ▀█▄ ░ ▓██▄ ▒██▀▀██░ ",
72 " ▒▓▓▄ ▄██▒ ▒██▀▀█▄ ░██▄▄▄▄██ ▒ ██▒ ░▓█ ░██ ",
73 " ▒ ▓███▀ ░ ░██▓ ▒██▒ ▓█ ▓██▒ ▒██████▒▒ ░▓█▒░██▓ ",
74 " ░ ░▒ ▒ ░ ░ ▒▓ ░▒▓░ ▒▒ ▓▒█░ ▒ ▒▓▒ ▒ ░ ▒ ░░▒░▒ ",
75 " ░ ▒ ░▒ ░ ▒░ ▒ ▒▒ ░ ░ ░▒ ░ ░ ▒ ░▒░ ░ ",
76 " ░ ░░ ░ ░ ▒ ░ ░ ░ ░ ░░ ░ ",
77 " ░ ░ ░ ░ ░ ░ ░ ░ ░ ",
78 " ░",
79 " ",
80 };
81
WriteLine(const std::string_view & s)82 static void WriteLine(const std::string_view& s) {
83 pw::sys_io::WriteLine(s)
84 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
85 }
86
87 typedef pw::StringBuffer<150> Buffer;
88
pw_assert_basic_HandleFailure(const char * file_name,int line_number,const char * function_name,const char * format,...)89 extern "C" void pw_assert_basic_HandleFailure(const char* file_name,
90 int line_number,
91 const char* function_name,
92 const char* format,
93 ...) {
94 // As a matter of usability, crashes should be visible; make it so.
95 #if PW_ASSERT_BASIC_SHOW_BANNER
96 WriteLine(RED);
97 for (const char* line : kCrashBanner) {
98 WriteLine(line);
99 }
100 WriteLine(RESET);
101 #endif // PW_ASSERT_BASIC_SHOW_BANNER
102
103 WriteLine(
104 " Welp, that didn't go as planned. "
105 "It seems we crashed. Terribly sorry!");
106 WriteLine("");
107 WriteLine(YELLOW " CRASH MESSAGE" RESET);
108 WriteLine("");
109 {
110 Buffer buffer;
111 buffer << " ";
112 va_list args;
113 va_start(args, format);
114 buffer.FormatVaList(format, args);
115 va_end(args);
116 WriteLine(buffer.view());
117 }
118
119 if (file_name != nullptr && line_number != -1) {
120 WriteLine("");
121 WriteLine(YELLOW " CRASH FILE & LINE" RESET);
122 WriteLine("");
123 {
124 Buffer buffer;
125 buffer.Format(" %s:%d", file_name, line_number);
126 WriteLine(buffer.view());
127 }
128 }
129 if (function_name != nullptr) {
130 WriteLine("");
131 WriteLine(YELLOW " CRASH FUNCTION" RESET);
132 WriteLine("");
133 {
134 Buffer buffer;
135 buffer.Format(" %s", function_name);
136 WriteLine(buffer.view());
137 }
138 }
139
140 // TODO(pwbug/95): Perhaps surprisingly, this doesn't actually crash the
141 // device. At some point we'll have a reboot BSP function or similar, but for
142 // now this is acceptable since no one is using this basic backend.
143 if (!PW_ASSERT_BASIC_DISABLE_NORETURN) {
144 if (PW_ASSERT_BASIC_ABORT) {
145 // abort() doesn't flush stderr/stdout, so manually flush them before
146 // aborting. abort() is preferred to exit(1) because debuggers catch it.
147 std::fflush(stderr);
148 std::fflush(stdout);
149 std::abort();
150 } else {
151 WriteLine("");
152 WriteLine(MAGENTA " HANG TIME" RESET);
153 WriteLine("");
154 WriteLine(
155 " ... until a debugger joins. System is waiting in a while(1)");
156 while (true) {
157 }
158 }
159 PW_UNREACHABLE;
160 } else {
161 WriteLine("");
162 WriteLine(MAGENTA " NOTE: YOU ARE IN ASSERT BASIC TEST MODE" RESET);
163 WriteLine("");
164 WriteLine(" This build returns from the crash handler for testing.");
165 WriteLine(" If you see this message in production, your build is ");
166 WriteLine(" incorrectly configured. Search for");
167 WriteLine(" PW_ASSERT_BASIC_DISABLE_NORETURN to fix it.");
168 WriteLine("");
169 }
170 }
171