Zth (libzth)
util.cpp
Go to the documentation of this file.
1 /*
2  * Zth (libzth), a cooperative userspace multitasking library.
3  * Copyright (C) 2019-2022 Jochem Rutgers
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <libzth/macros.h>
11 #include <libzth/allocator.h>
12 #include <libzth/init.h>
13 #include <libzth/perf.h>
14 #include <libzth/util.h>
15 #include <libzth/version.h>
16 
17 #include <cstdarg>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <unistd.h>
21 
22 #ifdef ZTH_OS_WINDOWS
23 # include <windows.h>
24 #endif
25 
26 using namespace std;
27 
28 namespace zth {
29 
34 char const* banner() noexcept
35 {
36  // clang-format off
37  return "Zth " ZTH_VERSION
38 #ifdef __GNUC__
39  " g++-" ZTH_STRINGIFY(__GNUC__) "." ZTH_STRINGIFY(__GNUC_MINOR__)
40  "." ZTH_STRINGIFY(__GNUC_PATCHLEVEL__)
41 #endif
42 #if __cplusplus < 201103L
43  " C++98"
44 #elif __cplusplus == 201103L
45  " C++11"
46 #elif __cplusplus == 201402L
47  " C++14"
48 #elif __cplusplus == 201703L
49  " C++17"
50 #else
51  " C++" ZTH_STRINGIFY(__cplusplus)
52 #endif
53 #ifdef ZTH_OS_LINUX
54  " linux"
55 #endif
56 #ifdef ZTH_OS_MAC
57  " mac"
58 #endif
59 #ifdef ZTH_OS_BAREMETAL
60  " baremetal"
61  " newlib" _NEWLIB_VERSION
62 #endif
63 #ifdef ZTH_ARCH_X86_64
64  " x86_64"
65 #endif
66 #ifdef ZTH_ARCH_X86
67  " x86"
68 #endif
69 #ifdef ZTH_ARCH_ARM
70  " arm"
71 # ifdef __ARM_ARCH_PROFILE
72 # if __ARM_ARCH_PROFILE == 'A'
73  "A"
74 # elif __ARM_ARCH_PROFILE == 'R'
75  "R"
76 # elif __ARM_ARCH_PROFILE == 'M'
77  "M"
78 # elif __ARM_ARCH_PROFILE == 'S'
79  "S"
80 # endif
81 # endif
82 # ifdef __ARM_ARCH
83  "v" ZTH_STRINGIFY(__ARM_ARCH)
84 # endif
85 #endif
86 #ifdef ZTH_ARM_USE_PSP
87  " psp"
88 #endif
89 #ifdef _DEBUG
90  " debug"
91 #endif
92 #ifdef ZTH_DRAFT_API
93  " draft"
94 #endif
95 #if ZTH_THREADS
96  " threads"
97 #endif
98 #ifdef ZTH_CONTEXT_UCONTEXT
99  " ucontext"
100 #endif
101 #ifdef ZTH_CONTEXT_SIGALTSTACK
102  " sigaltstack"
103 #endif
104 #ifdef ZTH_CONTEXT_SJLJ
105  " sjlj"
106 #endif
107 #ifdef ZTH_CONTEXT_WINFIBER
108  " winfiber"
109 #endif
110 #ifdef ZTH_HAVE_ZMQ
111  " zmq"
112 #endif
113 #ifdef ZTH_ENABLE_ASAN
114  " asan"
115 #endif
116 #ifdef ZTH_ENABLE_LSAN
117  " lsan"
118 #endif
119 #ifdef ZTH_ENABLE_UBSAN
120  " ubsan"
121 #endif
122  ;
123  // clang-format on
124 }
125 
130 void abort(char const* fmt, ...) noexcept
131 {
132  va_list args;
133  va_start(args, fmt);
134  abortv(fmt, args);
135  va_end(args);
136 }
137 
142 void abortv(char const* fmt, va_list args) noexcept
143 {
144  static bool recurse = false;
145 
146  if(!recurse) {
147  recurse = true;
148  log("\n%s *** Zth ABORT: ", zth::Config::EnableColorLog ? "\x1b[41;1;37;1m" : "");
149  logv(fmt, args);
150  log(" *** %s\n\n", zth::Config::EnableColorLog ? "\x1b[0m" : "");
151 
152  Backtrace().print();
153  }
154 
155  ::abort();
156 }
157 
158 #ifndef ZTH_OS_BAREMETAL
159 static void log_init()
160 {
161 # ifdef ZTH_OS_WINDOWS
162  // Windows does not support line buffering.
163  // If set anyway, this implies full buffering.
164  // Only do that when we expect much debug output.
165  if(zth_config(EnableDebugPrint)) {
166 # endif
167  setvbuf(stdout, nullptr, _IOLBF, 4096);
168  setvbuf(stderr, nullptr, _IOLBF, 4096);
169 # ifdef ZTH_OS_WINDOWS
170  }
171 # endif
172 }
173 ZTH_INIT_CALL(log_init)
174 #endif
175 
180 {
181 #ifdef ZTH_OS_WINDOWS
182  static int support = 0;
183  if(likely(support))
184  return support > 0;
185 
186  // Win10 supports ANSI, but it should be enabled. This can be done to
187  // set the Console Mode to ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x0004).
188  // However, the application is not compiled for Win10 specifically, so
189  // we cannot ask if we are running on Win10, and this macro has not
190  // been defined. Therefore, we just try to set the flag by its number,
191  // and check if it succeeds. If so, we assume cmd will accept ANSI
192  // colors.
193  AttachConsole(ATTACH_PARENT_PROCESS);
194 
195  DWORD mode = 0;
196  if(!GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &mode))
197  goto nosupport;
198  if(!SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), mode | 0x0004))
199  goto nosupport;
200 
201  // Succeeded. We have support. Enable stderr too.
202  if(GetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), &mode))
203  SetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), mode | 0x0004);
204 
205  support = 1;
206  return true;
207 
208 nosupport:
209  support = -1;
210  return false;
211 #else // !ZTH_OS_WINDOWS
212  return true;
213 #endif // !ZTH_OS_WINDOWS
214 }
215 
221 void log_colorv(int color, char const* fmt, va_list args)
222 {
223  static bool do_color =
224  Config::EnableColorLog && log_supports_ansi_colors() && isatty(fileno(stdout));
225 
226  if(do_color && color > 0)
227  log("\x1b[%d%sm", (color % 8) + 30, color >= 8 ? ";1" : "");
228 
229  logv(fmt, args);
230 
231  if(do_color && color > 0)
232  log("\x1b[0m");
233 }
234 
238 string formatv(char const* fmt, va_list args)
239 {
240  int const maxstack = (int)sizeof(void*) * 8;
241 
242  char sbuf[maxstack];
243  char* hbuf = nullptr;
244  size_t hbuf_size = 0;
245  char* buf = sbuf;
246 
247  va_list args2;
248  va_copy(args2, args);
249  // NOLINTNEXTLINE
250  int c = vsnprintf(sbuf, maxstack, fmt, args);
251 
252  if(c >= maxstack) {
253  hbuf_size = (size_t)c + 1u;
254  hbuf = allocate_noexcept<char>(hbuf_size);
255 
256  if(unlikely(!hbuf)) {
257  c = 0;
258  } else {
259  // NOLINTNEXTLINE
260  int c2 = vsprintf(hbuf, fmt, args2);
261  zth_assert(c2 <= c);
262  c = c2;
263  buf = hbuf;
264  }
265  }
266 
267  va_end(args2);
268 
269  string res(buf, (size_t)(c < 0 ? 0 : c));
270  deallocate(hbuf, hbuf_size);
271  return res;
272 }
273 
274 } // namespace zth
275 
280 void zth_abort(char const* fmt, ...)
281 {
282  va_list va;
283  va_start(va, fmt);
284  zth::abortv(fmt, va);
285  va_end(va);
286 }
287 
288 #ifdef ZTH_OS_BAREMETAL
289 __attribute__((weak)) int asprintf(char** strp, const char* fmt, ...)
290 {
291  va_list args;
292  va_start(args, fmt);
293  int res = vasprintf(strp, fmt, args);
294  va_end(args);
295  return res;
296 }
297 
298 __attribute__((weak)) int vasprintf(char** strp, const char* fmt, va_list ap)
299 {
300  va_list ap_dup;
301  va_copy(ap_dup, ap);
302  int len = vsnprintf(nullptr, 0, fmt, ap_dup);
303  va_end(ap_dup);
304 
305  if(strp && (*strp = (char*)malloc(len + 1)))
306  vsnprintf(*strp, len + 1, fmt, ap);
307 
308  // cppcheck-suppress memleak
309  return len;
310 }
311 #endif
Save a backtrace.
Definition: perf.h:52
void print(int color=-1) const
Definition: perf.cpp:651
void zth_abort(char const *fmt,...)
Aborts the process after printing the given printf() formatted message.
Definition: util.cpp:280
#define zth_config(name)
Checks if the given zth::Config field is enabled.
Definition: config.h:46
void log_colorv(int color, char const *fmt, va_list args)
Logs a given printf()-like formatted string using an ANSI color code.
Definition: util.cpp:221
void abort(char const *fmt,...) noexcept
Aborts the process after printing the given printf() formatted message.
Definition: util.cpp:130
#define ZTH_VERSION
Zth version as string literal.
Definition: version.h:47
char const * banner() noexcept
Prints a banner line with version and configuration information.
Definition: util.cpp:34
void log(char const *fmt,...)
Logs a given printf()-like formatted string.
Definition: util.h:323
void logv(char const *fmt, va_list arg)
Logs a given printf()-like formatted string.
Definition: util.h:312
void abortv(char const *fmt, va_list args) noexcept
Aborts the process after printing the given printf() formatted message.
Definition: util.cpp:142
#define ZTH_INIT_CALL(f)
Definition: init.h:51
Definition: allocator.h:23
bool log_supports_ansi_colors() noexcept
Returns if the system supports ANSI colors.
Definition: util.cpp:179
string formatv(char const *fmt, va_list args)
Format like vsprintf(), but save the result in an zth::string.
Definition: util.cpp:238
static bool const EnableColorLog
Enable colored output.
Definition: config.h:113
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition: util.h:236
#define likely(expr)
Marks the given expression to likely be evaluated to true.
Definition: util.h:42
#define ZTH_STRINGIFY(x)
Converts the argument to a string literal.
Definition: util.h:32
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.
Definition: util.h:56