code / tomo

Lines41.3K C23.7K Markdown9.7K YAML5.0K Tomo2.3K
7 others 763
Python231 Shell230 make212 INI47 Text21 SVG16 Lua6
(131 lines)
1 // This file defines some code to print stack traces.
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE
5 #endif
7 #include <dlfcn.h>
8 #include <err.h>
9 #include <execinfo.h>
10 #include <gc.h>
11 #include <limits.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
17 #include "../config.h"
18 #include "print.h"
19 #include "simpleparse.h"
20 #include "util.h"
22 extern bool USE_COLOR;
24 static void fprint_context(FILE *out, const char *filename, int lineno, int context_before, int context_after) {
25 FILE *f = fopen(filename, "r");
26 if (!f) return;
27 char *line = NULL;
28 size_t size = 0;
29 ssize_t nread;
30 int64_t cur_line = 1;
32 int num_width = 1;
33 for (int n = lineno + context_after; n >= 10; n /= 10)
34 num_width += 1;
36 while ((nread = getline(&line, &size, f)) != -1) {
37 if (line[nread - 1] == '\n') line[nread - 1] = '\0';
39 if (cur_line >= lineno - context_before) {
40 int w = 1;
41 for (int n = cur_line; n >= 10; n /= 10)
42 w += 1;
44 if (USE_COLOR) {
45 fprint(out, cur_line == lineno ? "\033[31;1m>\033[m " : " ", "\033[2m",
46 repeated_char(' ', num_width - w), cur_line, "\033(0\x78\033(B",
47 cur_line == lineno ? "\033[0;31;1m" : "\033[0m", line, "\033[m");
48 } else {
49 fprint(out, cur_line == lineno ? "> " : " ", repeated_char(' ', num_width - w), cur_line, "| ", line);
53 cur_line += 1;
54 if (cur_line > lineno + context_after) break;
56 if (line) free(line);
57 fclose(f);
60 static void _print_stack_frame(FILE *out, const char *cwd, const char *install_dir, const char *function,
61 const char *filename, int lineno) {
62 if (function == NULL) {
63 fprint(out, USE_COLOR ? "\033[2m...unknown function...\033[m" : "...unknown function...");
64 return;
67 function = String(string_slice(function, strcspn(function, "+")));
68 if (function[0] == '\0') function = "???";
70 char *function_display = GC_MALLOC_ATOMIC(strlen(function));
71 memcpy(function_display, function, strlen(function) + 1);
72 char *last_dollar = strrchr(function_display, '$');
73 if (last_dollar) *last_dollar = '\0';
74 for (char *p = function_display; *p; p++) {
75 if (*p == '$') *p = '.';
78 if (filename) {
79 if (strncmp(filename, cwd, strlen(cwd)) == 0) filename += strlen(cwd);
81 fprint_inline(out, USE_COLOR ? "\033[1mIn \033[33m" : "In ", function_display, USE_COLOR ? "()\033[37m" : "()");
82 if (install_dir[0] && strncmp(filename, install_dir, strlen(install_dir)) == 0)
83 fprint_inline(out, USE_COLOR ? " in package \033[35m" : " in package ", filename, ":", lineno);
84 else fprint(out, USE_COLOR ? " in \033[35m" : " in ", filename, ":", lineno);
85 fprint(out, USE_COLOR ? "\033[m" : "");
86 fprint_context(out, filename, lineno, 3, 1);
87 } else {
88 fprint(out, "LINE: ", function);
92 __attribute__((noinline)) public
93 void print_stacktrace(FILE *out, int offset) {
94 char cwd[PATH_MAX];
95 if (getcwd(cwd, sizeof(cwd)) == NULL) errx(1, "Path too large!");
96 size_t cwd_len = strlen(cwd);
97 if (cwd_len + 2 > sizeof(cwd)) errx(1, "Path too large!");
98 cwd[cwd_len++] = '/';
99 cwd[cwd_len] = '\0';
101 const char *install_dir = String(TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/");
103 static void *stack[1024];
104 int64_t size = (int64_t)backtrace(stack, sizeof(stack) / sizeof(stack[0]));
105 char **strings = backtrace_symbols(stack, size);
106 bool main_func_onwards = false;
107 for (int64_t i = size - 1; i > offset; i--) {
108 Dl_info info;
109 void *call_address = stack[i] - 1;
110 if (dladdr(call_address, &info) && info.dli_fname) {
111 const char *file = info.dli_fname;
112 uintptr_t frame_offset = (uintptr_t)call_address - (uintptr_t)info.dli_fbase;
113 FILE *fp = popen(String("addr2line -f -e '", file, "' ", (void *)frame_offset, " 2>/dev/null"), "r");
114 if (fp) {
115 const char *function = NULL, *filename = NULL;
116 long line_num = 0;
117 if (fparse(fp, &function, "\n", &filename, ":", &line_num) == NULL) {
118 if (starts_with(function, "main$")) main_func_onwards = true;
119 if (main_func_onwards) _print_stack_frame(out, cwd, install_dir, function, filename, line_num);
120 } else {
121 if (main_func_onwards) _print_stack_frame(out, cwd, install_dir, NULL, NULL, line_num);
123 pclose(fp);
125 } else {
126 if (main_func_onwards) _print_stack_frame(out, cwd, install_dir, NULL, NULL, 0);
128 if (main_func_onwards && i - 1 > offset) fputs("\n", out);
130 free(strings);