• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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