13#ifdef ASE_WITH_CPPTRACE
14#include <cpptrace/cpptrace.hpp>
49[[noreturn]]
static void abort_debug_friendly (
const char *msg,
const char *file,
int line,
const char *func)
noexcept;
65 struct timeval tv = { 0, 0 };
67 return tv.tv_sec * 1000000ULL + tv.tv_usec;
75logging_startup_stats()
82static bool init_startup_stats = [] { logging_startup_stats();
return 1; } ();
85logging_timestamp (uint64_t stamp)
87 const unsigned long long secs = stamp / 1000000ULL;
88 const unsigned long long usec = stamp - (secs * 1000000ULL);
95static int logging_fd = -2;
100 if (logging_fd >= -1)
103 if (logging_fd >= -1)
105 const uint64 programstart_timestamp = logging_startup_stats().timestamp;
106 if (!logging_buffer) {
108 const time_t now = programstart_timestamp / 1000000;
111 char tbuf[128] = { 0, };
112 strftime (tbuf,
sizeof (tbuf) - 1,
"%Y-%m-%d %H:%M:%S %z", &stm);
113 char pidbuf[64] = { 0, };
115 auto start_msg = logging_timestamp (programstart_timestamp) +
" " +
executable_name() +
": programstart=\"" + tbuf +
"\"" + pidbuf +
" executable=\"" +
executable_path() +
"\"\n";
126 if (logging_fd == -1)
128 if (logging_fd >= 0) {
132 while (r < 0 && (errno == EINTR || errno == EAGAIN));
141rfind_debug_value (
const char *kvlist,
const char *key,
const char *fallback)
147static Logging logging_level = TRACE;
150parse_log_level (
const char *lvl, Logging fallback)
152 static const struct {
const char *name; Logging level; } levels[] = {
154 {
"ASSERTION", ASSERTION },
159 {
"DEBUGALL", DEBUGALL },
161 for (
int i = 0; i <
sizeof (levels) /
sizeof (levels[0]); i++)
162 if (strncasecmp (lvl, levels[i].name, strlen (levels[i].name)) == 0)
163 return levels[i].level;
168logging_configure (
bool to_file, Logging level)
170 logging_level = level;
171 if (logging_level < FATAL) {
173 if (strcmp (
"0", rfind_debug_value (getenv_ase_debug(),
"all",
"0")) != 0)
174 logging_level = TRACE;
175 else if (strcmp (
"0", rfind_debug_value (getenv_ase_debug(),
"trace",
"0")) != 0)
176 logging_level = TRACE;
177 else if (strcmp (
"0", rfind_debug_value (getenv_ase_debug(),
"diag",
"0")) != 0)
178 logging_level = DIAG;
180 logging_level = parse_log_level (rfind_debug_value (getenv_ase_debug(),
"loglevel",
""), INFO);
188 if (logging_buffer) {
189 delete logging_buffer;
190 logging_buffer =
nullptr;
194 const String logdir = Path::join (Path::xdg_dir (
"CACHE"),
"anklang");
196 const int OFLAGS = O_CREAT | O_EXCL | O_WRONLY | O_NOCTTY | O_NOFOLLOW | O_CLOEXEC;
197 const int OMODE = 0640;
206 logging_fd =
open (fname.c_str(), OFLAGS, OMODE);
207 if (logging_fd < 0 && errno == EEXIST) {
208 const String oldname = fname +
".old";
209 if (rename (fname.c_str(), oldname.c_str()) < 0) {
213 logging_fd =
open (fname.c_str(), OFLAGS, OMODE);
215 if (logging_fd < 0) {
220 if (logging_buffer) {
222 for (
const auto &lines : *logging_buffer)
225 while (r < 0 && (errno == EINTR || errno == EAGAIN));
226 delete logging_buffer;
227 logging_buffer =
nullptr;
235backtrace_command (
const char *dbgr)
237#if 0 && defined (__linux__)
238 bool allow_ptrace =
true;
240 const char *
const ptrace_scope =
"/proc/sys/kernel/yama/ptrace_scope";
241 int fd =
open (ptrace_scope, 0);
243 if (read (fd, b, 8) > 0)
244 allow_ptrace = b[0] ==
'0';
250 const char *
const usr_bin_lldb =
"/usr/bin/lldb";
251 if ((!dbgr || strcmp (dbgr,
"lldb") == 0) &&
252 access (usr_bin_lldb, X_OK) == 0) {
254 "%s -Q -x --batch -p %u "
256 usr_bin_lldb, gettid());
259 const char *
const usr_bin_gdb =
"/usr/bin/gdb";
260 if ((!dbgr || strcmp (dbgr,
"gdb") == 0) &&
261 access (usr_bin_gdb, X_OK) == 0) {
263 "%s -q -n --nx -p %u --batch "
264 "-iex 'set auto-load python-scripts off' "
265 "-iex 'set script-extension off' "
266 "-ex 'set print address off' "
268 "-ex 'backtrace 99' "
270 usr_bin_gdb, gettid());
287 static const char *
const ase_debug = [] {
288 const char *
const d =
getenv (
"ASE_DEBUG");
315 using namespace AnsiColors;
316 const auto G = color (FG_GREEN), U = color (FG_BLUE), E = color (FG_RED), R = color (RESET);
318 char **symbols = backtrace_symbols (&frames[0], n);
319 if (!symbols)
return;
320 static constexpr size_t MAXLEN = 4096;
321 char exe[MAXLEN], symb[MAXLEN], addr[MAXLEN];
324 if (strlen (symbols[i]) <= MAXLEN) {
326 if (sscanf (symbols[i],
"%[^(](%[^)]) [%[^]]", exe, symb, addr) == 3) {
327 char *offs =
strchr (symb,
'+');
331 if (demangled.
size()) {
332 if (demangled == symb)
334 fprintf (stdio,
"#%zu%s %s in %s%s%s from %s\n", i, i < 10 ?
" " :
"", (U + addr + R).c_str(), (E + demangled + R).c_str(), offs[0] ?
"+" :
"", offs, (G + exe + R).c_str());
341 fprintf (stdio,
"#%zu%s %s\n", i, i < 10 ?
" " :
"", symbols[i]);
355logging_print_backtrace (
const char *func)
360 const char *asedebug =
getenv (
"ASE_DEBUG"), *btr = asedebug ?
strstr (asedebug,
"backtrace") : nullptr;
363 const char *xdb = !
strncmp (&btr[9],
"=lldb", 5) ?
"lldb" : !
strncmp (&btr[9],
"=gdb", 4) ?
"gdb" :
nullptr;
364 xdb_cmd = backtrace_command (xdb);
366 if (!xdb_cmd.
empty()) {
367 (void) system (xdb_cmd.
c_str());
370#ifdef ASE_WITH_CPPTRACE
371 cpptrace::generate_trace().print();
378 const int n = backtrace (&addrs[0], addrs.size());
381 fprintf (stderr,
"Stack Trace (most recent call first):\n");
382 print_backtrace (stderr, addrs);
389 fprintf (stderr,
"Stack Trace:\n");
390 fprintf (stderr,
"#0 %p in %s\n", __builtin_return_address (0), func);
396[[noreturn]]
static void
397logging_terminate_handler()
404 msg =
"Uncaught exception";
409 msg =
"Uncaught non-std exception";
412 msg =
"Terminate called without exception";
414 using namespace AnsiColors;
416 ScopedPosixLocale posix_locale;
419 msg +=
":" + S +
" " + what;
425 logging_print_backtrace (__func__);
430logging_handle_terminate ()
433#ifdef ASE_WITH_CPPTRACE
434 cpptrace::register_terminate_handler();
439logging (Logging level,
const std::string &cond,
std::string message,
const char *filename, uint32_t line,
const char *function_name)
noexcept
442 if (!pabort && level > logging_level)
445 using namespace AnsiColors;
446 const auto C =
color (FG_CYAN), G =
color (BOLD, FG_GREEN), U =
color (FG_BLUE), Y =
color (FG_YELLOW);
448 ScopedPosixLocale posix_locale;
449 auto location = [&] {
455 s += function_name +
String (
":");
460 String logprefix = logging_timestamp (
timestamp_now()) +
':', printprefix = Y + logprefix + S, kind;
465 kind = location() +
' ' + B + R +
"fatal:" + S;
469 kind = location() +
' ' + B + R +
"assertion failed:" + S;
471 message = R +
"code unreached" + S;
475 kind = Y +
"warning:" + S;
479 kind = C +
"Hint:" + S;
491 kind = cond.empty() ? location() : U + cond +
':' + S;
494 String sout = printprefix.
empty() ?
"" : printprefix +
' ';
498 const std::string HEXINT =
"0[xX][0-9abcdefABCDEF]+";
499 const std::string FULLFLOAT =
"([1-9][0-9]*|0)([.][0-9]*)?([eE][+-]?[0-9]+)?";
500 const std::string FRACTFLOAT =
"[.][0-9]+([eE][+-]?[0-9]+)?";
501 const std::string NUMBER = HEXINT +
"|" + FULLFLOAT +
"|" + FRACTFLOAT;
503 w =
Re::sub (
"=(" + NUMBER +
")",
"=" + Y +
"$1" + S, w);
504 w =
Re::sub (
"=(\"(?:[^\"\\\\]|\\\\.)*\")",
"=" + U +
"$1" + S, w);
505 w =
Re::sub (
" (\\w+)=",
" " + C +
"$1" + S +
"=", w);
506 w =
Re::sub (
": ([a-zA-Z.0-9_:-]+): ",
": " + G +
"$1:" + S +
" ", w);
507 w =
Re::sub (
"^(\\d+[.]\\d+):", Y +
"$1:" + S, w, Re::M);
511 if (sout.size() && sout[sout.size()-1] !=
'\n')
513 if (pabort && level > FATAL)
514 sout +=
"Aborting... (fatal-warnings)\n";
516 fputs (sout.c_str(), stderr);
518 if (logprefix.size())
519 sout = logprefix +
' ' + sout;
520 sout =
Re::sub (
"\\x1b\\[[0-9;]*[mK]",
"", sout, Re::M);
523 logging_print_backtrace (__func__);
524 abort_debug_friendly (message.c_str(), filename, line, function_name);
529logging (Logging level,
const std::string &message,
const char *file, uint32_t line,
const char *func)
noexcept
531 logging (level,
"", message, file, line, func);
539 logging (cond ? DEBUGALL : DIAG, cond ? cond :
"", message,
nullptr, 0,
nullptr);
543logging_abort (Logging level,
const std::string &message,
const char *file, uint32_t line,
const char *func)
noexcept
545 logging (level,
"", message, file, line, func);
557abort_debug_friendly (
const char *msg,
const char *file,
int line,
const char *func)
noexcept
559#if defined (_ASSERT_H_DECLS) && defined(__GLIBC__)
561 __assert_fail (msg && msg[0] ? msg :
"assertion unreachable\n", file, line, func);
static String sub(const String ®ex, const String &subst, const String &input, Flags=DEFAULT)
Substitute regex in input by sbref with backreferences $00…$99 or $&.
T current_exception(T... args)
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
std::string color(Colors acolor, Colors c1, Colors c2, Colors c3, Colors c4, Colors c5, Colors c6)
Return ANSI code for the specified color if stdout & stderr should be colorized, see colorize_tty().
bool colorize_tty(int fd)
Check whether the tty fd should use colorization, checks ASE_COLOR if fd == -1.
bool mkdirs(const String &dirpath, uint mode)
Create the directories in dirpath with mode, check errno on false returns.
The Anklang C++ API namespace.
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
void stdio_flush(uint8 code, const String &txt) noexcept
Handle stdout and stderr printing with flushing.
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
::std::string debug_key_value(const char *conditional)
Retrieve the value assigned to debug key conditional in $ASE_DEBUG.
uint8_t uint8
An 8-bit unsigned integer.
const char * cxx_demangle(const std::type_info &typeinfo) noexcept
Demangle a std::typeinfo.name() string into a proper C++ type name.
std::string executable_path()
Retrieve the path to the currently running executable.
void logging_debug(const char *cond, const std::string &message) noexcept
Print a debug/diag message, called from Ase::debug().
uint64_t timestamp_now()
Current time in µseconds.
String string_from_uint(uint64 value)
Convert a 64bit unsigned integer into a string.
bool ase_debugging_enabled
Flag to optimize checks for debugging.
bool logging_fatal_warnings
Global flag to cause the program to abort on warnings.
String program_alias()
Retrieve the program name as used for logging or debug messages.
std::string String
Convenience alias for std::string.
std::string executable_name()
Retrieve the name part of executable_path().
bool string_to_bool(const String &string, bool fallback)
Interpret a string as boolean value.
bool debug_key_enabled(const char *conditional) noexcept
Check if conditional is enabled by $ASE_DEBUG.
std::string_view string_option_find_value(const char *string, const char *feature, const char *fallback, const char *denied, bool matchallnone)
Low level option search, avoids dynamic allocations.
T rethrow_exception(T... args)
T set_terminate(T... args)