Zth (libzth)
Loading...
Searching...
No Matches
util.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2019-2026 Jochem Rutgers
3 *
4 * SPDX-License-Identifier: MPL-2.0
5 */
6
7#include <libzth/macros.h>
8
9#include <libzth/allocator.h>
10#include <libzth/backtrace.h>
11#include <libzth/init.h>
12#include <libzth/perf.h>
13#include <libzth/util.h>
14#include <libzth/version.h>
15
16#include <cstdarg>
17#include <cstdio>
18#include <cstdlib>
19#include <exception>
20#include <unistd.h>
21
22#ifdef ZTH_OS_WINDOWS
23# include <windows.h>
24#endif
25
26#if __cplusplus < 201103L && !defined(va_copy)
27# define va_copy(...) __va_copy(__VA_ARGS__)
28#endif
29
30using namespace std;
31
32namespace zth {
33
38char const* banner() noexcept
39{
40 // clang-format off
41 return "Zth " ZTH_VERSION
42#ifdef __GNUC__
43 " g++-" ZTH_STRINGIFY(__GNUC__) "." ZTH_STRINGIFY(__GNUC_MINOR__)
44 "." ZTH_STRINGIFY(__GNUC_PATCHLEVEL__)
45#endif
46#if __cplusplus < 201103L
47 " C++98"
48#elif __cplusplus == 201103L
49 " C++11"
50#elif __cplusplus == 201402L
51 " C++14"
52#elif __cplusplus == 201703L
53 " C++17"
54#elif __cplusplus == 202002L
55 " C++20"
56#elif __cplusplus == 202302L
57 " C++23"
58#else
59 " C++" ZTH_STRINGIFY(__cplusplus)
60#endif
61#if !ZTH_HAVE_EXCEPTIONS
62 " noexc"
63#endif
64#ifdef ZTH_OS_LINUX
65 " linux"
66#endif
67#ifdef ZTH_OS_MAC
68 " mac"
69#endif
70#ifdef ZTH_OS_BAREMETAL
71 " baremetal"
72#endif
73#if ZTH_SHARED_LIB
74 " shared"
75#endif
76#ifdef _NEWLIB_VERSION
77 " newlib" _NEWLIB_VERSION
78#endif
79#ifdef ZTH_ARCH_X86_64
80 " x86_64"
81#endif
82#ifdef ZTH_ARCH_X86
83 " x86"
84#endif
85#ifdef ZTH_ARCH_ARM64
86 " arm64"
87#endif
88#ifdef ZTH_ARCH_ARM
89 " arm"
90# ifdef __ARM_ARCH_PROFILE
91# if __ARM_ARCH_PROFILE == 'A'
92 "A"
93# elif __ARM_ARCH_PROFILE == 'R'
94 "R"
95# elif __ARM_ARCH_PROFILE == 'M'
96 "M"
97# elif __ARM_ARCH_PROFILE == 'S'
98 "S"
99# endif
100# endif
101# ifdef __ARM_ARCH
102 "v" ZTH_STRINGIFY(__ARM_ARCH)
103# endif
104#endif
105#ifdef ZTH_ARM_USE_PSP
106 " psp"
107#endif
108#if defined(_DEBUG) && (!defined(ZTH_CONFIG_DEBUG) || ZTH_CONFIG_DEBUG)
109 " debug"
110#endif
111#ifdef ZTH_DRAFT_API
112 " draft"
113#endif
114#if ZTH_THREADS
115 " threads"
116#endif
117#ifdef ZTH_CONTEXT_UCONTEXT
118 " ucontext"
119#endif
120#ifdef ZTH_CONTEXT_SIGALTSTACK
121 " sigaltstack"
122#endif
123#ifdef ZTH_CONTEXT_SJLJ
124 " sjlj"
125#endif
126#ifdef ZTH_CONTEXT_WINFIBER
127 " winfiber"
128#endif
129#ifdef ZTH_HAVE_ZMQ
130 " zmq"
131#endif
132#ifdef ZTH_ENABLE_ASAN
133 " asan"
134#endif
135#ifdef ZTH_ENABLE_LSAN
136 " lsan"
137#endif
138#ifdef ZTH_ENABLE_UBSAN
139 " ubsan"
140#endif
141 ;
142 // clang-format on
143}
144
149void abort(char const* fmt, ...) noexcept
150{
151 va_list args;
152 va_start(args, fmt);
153 abortv(fmt, args);
154 va_end(args);
155}
156
161void abortv(char const* fmt, va_list args) noexcept
162{
163 static bool recurse = false;
164
165 if(!recurse) {
166 recurse = true;
167 log("\n%s *** Zth ABORT: ", zth::Config::EnableColorLog ? "\x1b[41;1;37;1m" : "");
168 logv(fmt, args);
169 log(" *** %s\n\n", zth::Config::EnableColorLog ? "\x1b[0m" : "");
170
171 Backtrace().print();
172 }
173
174 (void)fflush(nullptr);
176}
177
178#ifndef ZTH_OS_BAREMETAL
179static void log_init()
180{
181# ifdef ZTH_OS_WINDOWS
182 // Windows does not support line buffering.
183 // If set anyway, this implies full buffering.
184 // Only do that when we expect much debug output.
185 if(zth_config(EnableDebugPrint)) {
186# endif
187 (void)setvbuf(stdout, nullptr, _IOLBF, 4096);
188 (void)setvbuf(stderr, nullptr, _IOLBF, 4096);
189# ifdef ZTH_OS_WINDOWS
190 }
191# endif
192}
193ZTH_INIT_CALL(log_init)
194#endif
195
200{
201#ifdef ZTH_OS_WINDOWS
202 static int support = 0;
203 if(likely(support))
204 return support > 0;
205
206 // Win10 supports ANSI, but it should be enabled. This can be done to
207 // set the Console Mode to ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x0004).
208 // However, the application is not compiled for Win10 specifically, so
209 // we cannot ask if we are running on Win10, and this macro has not
210 // been defined. Therefore, we just try to set the flag by its number,
211 // and check if it succeeds. If so, we assume cmd will accept ANSI
212 // colors.
213 AttachConsole(ATTACH_PARENT_PROCESS);
214
215 DWORD mode = 0;
216 if(!GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &mode))
217 goto nosupport;
218 if(!SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), mode | 0x0004))
219 goto nosupport;
220
221 // Succeeded. We have support. Enable stderr too.
222 if(GetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), &mode))
223 SetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), mode | 0x0004);
224
225 support = 1;
226 return true;
227
228nosupport:
229 support = -1;
230 return false;
231#else // !ZTH_OS_WINDOWS
232 return true;
233#endif // !ZTH_OS_WINDOWS
234}
235
241void log_colorv(int color, char const* fmt, va_list args)
242{
243 static bool do_color =
244 Config::EnableColorLog && log_supports_ansi_colors() && isatty(fileno(stdout));
245
246 if(do_color && color > 0)
247 zth::log("\x1b[%d%sm", (color % 8) + 30, color >= 8 ? ";1" : "");
248
249 zth::logv(fmt, args);
250
251 if(do_color && color > 0)
252 zth::log("\x1b[0m");
253}
254
258#if __cplusplus >= 201703L
259string formatv(char const* fmt, va_list args)
260{
261 // std::string has a small internal buffer. Try to use that first.
262 string res;
263
264# if defined(__cpp_lib_string_resize_and_overwrite) \
265 && __cpp_lib_string_resize_and_overwrite >= 202110L
266 // C++23: Don't initialize the buffer.
267 res.resize_and_overwrite(
268 res.capacity(), [](char* /*buf*/, size_t size) -> size_t { return size; });
269# else // !resize_and_overwrite
270 // This will initialize the buffer with zeros.
271 res.resize(res.capacity());
272# endif // resize_and_overwrite
273
274 va_list args2;
275 va_copy(args2, args);
276 // NOLINTNEXTLINE
277 int c = vsnprintf(res.data(), res.size(), fmt, args2);
278 va_end(args2);
279
280 if(unlikely((size_t)c >= res.size())) {
281 // Buffer too small.
282# if defined(__cpp_lib_string_resize_and_overwrite) \
283 && __cpp_lib_string_resize_and_overwrite >= 202110L
284 res.resize_and_overwrite(
285 (size_t)c + 1U, [](char* /*buf*/, size_t size) -> size_t { return size; });
286# else // !resize_and_overwrite
287 res.resize((size_t)c + 1U);
288# endif // resize_and_overwrite
289
290 // NOLINTNEXTLINE
291 c = vsnprintf(res.data(), res.size(), fmt, args);
292 }
293
294 // Shrink to actual size.
295 res.resize(c > 0 ? (size_t)c : 0);
296 return res; // RVO
297}
298#else // < C++17
299// Pre C++17 version without guaranteed contiguous string storage.
300string formatv(char const* fmt, va_list args)
301{
302 int const maxstack = (int)sizeof(void*) * 8;
303
304 char sbuf[maxstack];
305 char* hbuf = nullptr;
306 size_t hbuf_size = 0;
307 char* buf = sbuf;
308
309 va_list args2;
310 va_copy(args2, args);
311 // NOLINTNEXTLINE
312 int c = vsnprintf(sbuf, maxstack, fmt, args2);
313 va_end(args2);
314
315 if(c >= maxstack) {
316 hbuf_size = (size_t)c + 1U;
317 hbuf = allocate_noexcept<char>(hbuf_size);
318
319 if(unlikely(!hbuf)) {
320 c = 0;
321 } else {
322 // NOLINTNEXTLINE
323 int c2 = vsprintf(hbuf, fmt, args);
324 zth_assert(c2 <= c);
325 c = c2;
326 buf = hbuf;
327 }
328 }
329
330 string res(buf, (size_t)(c < 0 ? 0 : c));
331 deallocate(hbuf, hbuf_size);
332 return res;
333}
334#endif // C++17
335
336} // namespace zth
337
342void zth_abort(char const* fmt, ...)
343{
344 va_list va;
345 va_start(va, fmt);
346 zth::abortv(fmt, va);
347 va_end(va);
348}
349
350#ifdef ZTH_OS_BAREMETAL
351__attribute__((weak)) int asprintf(char** strp, const char* fmt, ...)
352{
353 va_list args;
354 va_start(args, fmt);
355 int res = vasprintf(strp, fmt, args);
356 va_end(args);
357 return res;
358}
359
360__attribute__((weak)) int vasprintf(char** strp, const char* fmt, va_list ap)
361{
362 va_list ap_dup;
363 va_copy(ap_dup, ap);
364 int len = vsnprintf(nullptr, 0, fmt, ap_dup);
365 va_end(ap_dup);
366
367 if(strp && (*strp = static_cast<char*>(malloc(len + 1))))
368 vsnprintf(*strp, len + 1, fmt, ap);
369
370 // cppcheck-suppress[memleak,unmatchedSuppression]
371 return len;
372}
373#endif
void zth_abort(char const *fmt,...)
Aborts the process after printing the given printf() formatted message.
Definition util.cpp:342
#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:241
void abort(char const *fmt,...) noexcept
Aborts the process after printing the given printf() formatted message.
Definition util.cpp:149
impl::PickBacktrace ::type Backtrace
Save a backtrace.
Definition backtrace.h:168
#define ZTH_VERSION
Zth version as string literal.
Definition version.h:44
char const * banner() noexcept
Returns a banner line with version and configuration information.
Definition util.cpp:38
void log(char const *fmt,...)
Logs a given printf()-like formatted string.
Definition util.h:318
void logv(char const *fmt, va_list arg)
Logs a given printf()-like formatted string.
Definition util.h:307
void abortv(char const *fmt, va_list args) noexcept
Aborts the process after printing the given printf() formatted message.
Definition util.cpp:161
#define ZTH_INIT_CALL(f)
Definition init.h:41
STL namespace.
bool log_supports_ansi_colors() noexcept
Returns if the system supports ANSI colors.
Definition util.cpp:199
string formatv(char const *fmt, va_list args)
Format like vsprintf(), but save the result in an zth::string.
Definition util.cpp:259
static bool const EnableColorLog
Enable colored output.
Definition config.h:143
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition util.h:217
#define likely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:45
#define ZTH_STRINGIFY(x)
Converts the argument to a string literal.
Definition util.h:35
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:60
void zth_terminate()
Terminate immediately.