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