STX  1.0.0
default.h
Go to the documentation of this file.
1 
30 #pragma once
31 
32 #if !defined(STX_NO_STD_THREAD_MUTEX)
33 #include <mutex> // NOLINT
34 #include <thread> // NOLINT
35 #endif
36 
37 #include "stx/panic.h"
39 
40 #if defined(STX_ENABLE_PANIC_BACKTRACE)
41 #include "stx/backtrace.h"
42 #endif
43 
45 
46 // here, we can avoid any form of memory allocation that might be needed,
47 // therefore deferring the info string and report payload to the callee and can
48 // also use a stack allocated string especially in cases where dynamic memory
49 // allocation is undesired
50 // this is also thread-safe.
51 //
52 // most considerations here are for constrained systems. We can't just use
53 // `printf` as it is implementation-defined whether it uses dynamic memory
54 // allocation or not. Also, we can't pack the strings into a buffer and `fputs`
55 // at once as the buffer can likely not be enough, instead we reuse the buffer.
56 // May not be fast, but it is a panic anyway.
57 //
58 inline void panic_default(std::string_view const& info,
59  ReportPayload const& payload,
60  SourceLocation const& location) noexcept {
61  // probably too much, but enough
62  // this will at least hold a formatted uint128_t (40 digits)
63  static constexpr const int kFmtBufferSize = 64;
64 
65 #if !defined(STX_NO_STD_THREAD_MUTEX)
66 
67  static constexpr const auto kThreadIdHasher = std::hash<std::thread::id>{};
68 
69  static std::mutex stderr_lock;
70 
71  // we use this buffer for all formatting operations. as it is implementation
72  // defined whether fprintf uses dynamic mem alloc
73  // only one thread can print at a time so this is safe
74  static char fmt_buffer[kFmtBufferSize];
75 
76  stderr_lock.lock();
77 
78  thread_local size_t const thread_id_hash =
79  kThreadIdHasher(std::this_thread::get_id());
80 
81 #else
82 
83  // we can't be too sure that the user won't panic from multiple threads even
84  // though they seem to be disabled
85  char fmt_buffer[kFmtBufferSize];
86 
87 #endif
88 
89  std::fputs("\nthread", stderr);
90 
91 #if !defined(STX_NO_STD_THREAD_MUTEX)
92 
93  std::fputs(" with hash: '", stderr);
94 
95  STX_PANIC_EPRINTF_WITH(fmt_buffer, kFmtBufferSize, "%zu", thread_id_hash);
96 
97 #endif
98 
99  std::fputs("' panicked with: '", stderr);
100 
101  for (char c : info) {
102  std::fputc(c, stderr);
103  }
104 
105  if (!payload.data().empty()) {
106  std::fputs(": ", stderr);
107 
108  for (auto c : payload.data()) {
109  std::fputc(c, stderr);
110  }
111  }
112 
113  std::fputs("' at function: '", stderr);
114 
115  std::fputs(location.function_name(), stderr);
116 
117  std::fputs("' [", stderr);
118 
119  std::fputs(location.file_name(), stderr);
120 
121  std::fputc(':', stderr);
122 
123  auto line = location.line();
124 
125  if (line != 0) {
126  STX_PANIC_EPRINTF_WITH(fmt_buffer, kFmtBufferSize, "%" PRIuLEAST32, line);
127  } else {
128  std::fputs("unknown", stderr);
129  }
130 
131  std::fputc(':', stderr);
132 
133  auto column = location.column();
134 
135  if (column != 0) {
136  STX_PANIC_EPRINTF_WITH(fmt_buffer, kFmtBufferSize, "%" PRIuLEAST32, column);
137  } else {
138  std::fputs("unknown", stderr);
139  }
140 
141  std::fputs("]\n", stderr);
142 
143  std::fflush(stderr);
144 
145 #if defined(STX_ENABLE_PANIC_BACKTRACE)
146 
147  std::fputs(
148  "\nBacktrace:\nip: Instruction Pointer, sp: Stack "
149  "Pointer\n\n",
150  stderr);
151 
152  int frames = backtrace::trace(
153  [](backtrace::Frame frame, int i) {
154  auto const print_none = []() { std::fputs("unknown", stderr); };
155 
156  auto const print_ptr = [](uintptr_t ip) {
157  STX_PANIC_EPRINTF(internal::kxPtrFmtSize, "0x%" PRIxPTR, ip);
158  };
159 
160  // int varies
161  STX_PANIC_EPRINTF(internal::kI32FmtSize + 8, "#%d\t\t", i);
162 
163  frame.symbol.match(
164  [](backtrace::Symbol& sym) {
165  for (char c : sym.raw()) {
166  std::fputc(c, stderr);
167  }
168  },
169  print_none);
170 
171  std::fputs("\t (ip: ", stderr);
172 
173  frame.ip.match(print_ptr, print_none);
174 
175  std::fputs(", sp: ", stderr);
176 
177  frame.sp.match(print_ptr, print_none);
178 
179  std::fputs(")\n", stderr);
180 
181  return false;
182  },
183  1);
184 
185  if (frames <= 0) {
186  std::fputs(
187  R"(WARNING >> The stack frames couldn't be identified, debug information was possibly stripped, unavailable, or elided by compiler
188 )",
189  stderr);
190  }
191 
192  std::fputs("\n", stderr);
193 
194 #endif
195 
196 #if !defined(STX_NO_STD_THREAD_MUTEX)
197  // other threads will still be able to log for some nanoseconds
198  stderr_lock.unlock();
199 #endif
200 }
201 
constexpr auto match(SomeFn &&some_fn, NoneFn &&none_fn) &&-> invoke_result< SomeFn &&, T &&>
Definition: option_result.h:1470
Option< uintptr_t > sp
address on the call stack
Definition: backtrace.h:102
int trace(Callback callback, int skip_count=0)
Option< Symbol > symbol
function&#39;s symbol name. possibly demangled.
Definition: backtrace.h:106
auto raw() const noexcept -> std::string_view
gets the raw symbol name, the symbol is pre-demangled if possible.
Definition: report.h:304
reperesents an active stack frame.
Definition: backtrace.h:98
void panic_default(std::string_view const &info, ReportPayload const &payload, SourceLocation const &location) noexcept
Definition: default.h:58
Option< uintptr_t > ip
instruction pointer
Definition: backtrace.h:100
#define STX_END_NAMESPACE
Definition: config.h:329
constexpr int kI32FmtSize
10 digits + 1 sign
Definition: common.h:118
constexpr int kxPtrFmtSize
Definition: common.h:110
Definition: backtrace.h:82
Definition: source_location.h:48
#define STX_BEGIN_NAMESPACE
Definition: config.h:325