aboutsummaryrefslogtreecommitdiff
path: root/src/stdlib
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-04-12 13:44:04 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-04-12 13:44:04 -0400
commit90a323e3b34cfecdf9df55d8a03d03649609f162 (patch)
treec9dcf3b40e6b0de736f0c38864383a01b369439c /src/stdlib
parentba555a8aca47a78acb173780fd252b19d3cdd36e (diff)
Replace addr2line with libbacktrace to get improved stack traces
Diffstat (limited to 'src/stdlib')
-rw-r--r--src/stdlib/stacktrace.c126
-rw-r--r--src/stdlib/stacktrace.h5
-rw-r--r--src/stdlib/stdlib.c78
-rw-r--r--src/stdlib/stdlib.h18
-rw-r--r--src/stdlib/tomo.h3
5 files changed, 147 insertions, 83 deletions
diff --git a/src/stdlib/stacktrace.c b/src/stdlib/stacktrace.c
new file mode 100644
index 00000000..bd867c7b
--- /dev/null
+++ b/src/stdlib/stacktrace.c
@@ -0,0 +1,126 @@
+#include <backtrace.h>
+#include <err.h>
+#include <gc.h>
+#include <signal.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+extern bool USE_COLOR;
+
+static struct backtrace_state *bt_state = NULL;
+
+static void fprint_context(FILE *out, const char *filename, int lineno, int context_before, int context_after)
+{
+ FILE *f = fopen(filename, "r");
+ if (!f) return;
+ char *line = NULL;
+ size_t size = 0;
+ ssize_t nread;
+ int64_t cur_line = 1;
+
+ int num_width = 1;
+ for (int n = lineno + context_after; n >= 10; n /= 10)
+ num_width += 1;
+
+ while ((nread = getline(&line, &size, f)) != -1) {
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = '\0';
+
+ if (cur_line >= lineno - context_before)
+ fprintf(out, USE_COLOR ? "%s\033[2m%*d\033(0\x78\x1b(B%s%s\033[m\n" : "%s%*d| %s%s\n",
+ cur_line == lineno ? (USE_COLOR ? "\033[31;1m>\033[m " : "> ") : " ",
+ num_width, cur_line, USE_COLOR ? (cur_line == lineno ? "\033[0;31;1m" : "\033[0m") : "", line);
+
+ cur_line += 1;
+ if (cur_line > lineno + context_after)
+ break;
+ }
+ if (line) free(line);
+ fclose(f);
+}
+
+typedef struct stack_info_s {
+ const char *function, *filename;
+ int lineno;
+ struct stack_info_s *next;
+} stack_info_t;
+
+// Simple callback to print each frame
+static int print_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function)
+{
+ (void)pc;
+ stack_info_t *info = GC_MALLOC(sizeof(stack_info_t));
+ info->next = *(stack_info_t**)data;
+ info->function = function;
+ info->filename = filename;
+ info->lineno = lineno;
+ *(stack_info_t**)data = info;
+ return 0;
+}
+
+public void print_stacktrace(FILE *out, int offset)
+{
+ stack_info_t *backwards = NULL;
+
+ backtrace_full(bt_state, offset, print_callback, NULL, &backwards);
+
+ char cwd[PATH_MAX];
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ errx(1, "Path too large!");
+ size_t cwd_len = strlen(cwd);
+ if (cwd_len + 2 > sizeof(cwd))
+ errx(1, "Path too large!");
+ cwd[cwd_len++] = '/';
+ cwd[cwd_len] = '\0';
+
+ // Skip C entrypoint stuff:
+ while (backwards && backwards->function == NULL)
+ backwards = backwards->next;
+
+ if (backwards && strstr(backwards->function, "$parse_and_run"))
+ backwards = backwards->next;
+
+ for (stack_info_t *frame = backwards; frame; frame = frame->next) {
+ while (frame && frame->function == NULL) {
+ fprintf(out, USE_COLOR ? "\033[2m... unknown function ...\033[m\n" : "... unknown function ...\n");
+ if (frame->next)
+ fprintf(out, "\n");
+ frame = frame->next;
+ }
+ if (frame == NULL) break;
+
+ char *function_display = GC_MALLOC_ATOMIC(strlen(frame->function));
+ memcpy(function_display, frame->function, strlen(frame->function)+1);
+ function_display += strcspn(function_display, "$");
+ function_display += strspn(function_display, "$");
+ function_display += strcspn(function_display, "$");
+ function_display += strspn(function_display, "$");
+ for (char *p = function_display; *p; p++) {
+ if (*p == '$') *p = '.';
+ }
+
+ if (strncmp(frame->filename, cwd, cwd_len) == 0)
+ frame->filename += cwd_len;
+
+ fprintf(out, USE_COLOR ? "\033[1mIn \033[33m%s()\033[37m" : "In %s()", function_display);
+ if (frame->filename)
+ fprintf(out, USE_COLOR ? " in \033[35m%s:%d" : " in %s:%d", frame->filename, frame->lineno);
+ fprintf(out, USE_COLOR ? "\033[m\n" : "\n");
+ if (frame->filename)
+ fprint_context(out, frame->filename, frame->lineno, 3, 1);
+ if (frame->next)
+ fprintf(out, "\n");
+ }
+}
+
+public void initialize_stacktrace(const char *program)
+{
+ bt_state = backtrace_create_state(program, 1, NULL, NULL);
+ if (!bt_state)
+ errx(1, "Failed to create stacktrace state");
+}
diff --git a/src/stdlib/stacktrace.h b/src/stdlib/stacktrace.h
new file mode 100644
index 00000000..e3e04814
--- /dev/null
+++ b/src/stdlib/stacktrace.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <stdio.h>
+
+void initialize_stacktrace(const char *program);
+void print_stacktrace(FILE *out, int offset);
diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c
index e8cfe06a..6332c69d 100644
--- a/src/stdlib/stdlib.c
+++ b/src/stdlib/stdlib.c
@@ -12,16 +12,17 @@
#include <sys/param.h>
#include <time.h>
-#include "print.h"
#include "bools.h"
#include "files.h"
#include "functiontype.h"
#include "integers.h"
-#include "optionals.h"
#include "metamethods.h"
#include "nums.h"
+#include "optionals.h"
#include "paths.h"
+#include "print.h"
#include "siphash.h"
+#include "stacktrace.h"
#include "stdlib.h"
#include "tables.h"
#include "text.h"
@@ -48,7 +49,7 @@ static _Noreturn void signal_handler(int sig, siginfo_t *, void *)
fflush(stdout);
if (USE_COLOR) fputs("\x1b[31;7m ===== ILLEGAL INSTRUCTION ===== \n\n\x1b[m", stderr);
else fputs("===== ILLEGAL INSTRUCTION =====\n\n", stderr);
- print_stack_trace(stderr, 3, 4);
+ print_stacktrace(stderr, 3);
fflush(stderr);
raise(SIGABRT);
_exit(1);
@@ -441,75 +442,6 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help,
#pragma GCC diagnostic pop
#endif
-static void print_stack_line(FILE *out, OptionalText_t fn_name, const char *filename, int64_t line_num)
-{
- // NOTE: this function is a bit inefficient. Each time we print a line, we
- // do a linear scan through the whole file. However, performance shouldn't
- // really matter if we only print stack lines when there's a crash.
- if (filename) {
- fprint_inline(out, "\033[34mFile\033[m \033[35;1m", filename, "\033[m");
- if (line_num >= 1)
- fprint_inline(out, "\033[34m line\033[m \033[35;1m", line_num, "\033[m");
- }
- if (fn_name.length > 0) {
- if (filename)
- fprint_inline(out, "\033[34m, in \033[m \033[36;1m", fn_name, "\033[m");
- else
- fprint_inline(out, "\033[36;1m", fn_name, "\033[m");
- }
- fprint_inline(out, "\n");
-
- FILE *f = fopen(filename, "r");
- if (!f) return;
- char *line = NULL;
- size_t size = 0;
- ssize_t nread;
- int64_t cur_line = 1;
- while ((nread = getline(&line, &size, f)) != -1) {
- if (line[strlen(line)-1] == '\n')
- line[strlen(line)-1] = '\0';
-
- if (cur_line >= line_num)
- fprint(out, "\033[33;1m", line, "\033[m");
-
- cur_line += 1;
- if (cur_line > line_num)
- break;
- }
- if (line) free(line);
- fclose(f);
-}
-
-public void print_stack_trace(FILE *out, int start, int stop)
-{
- // Print stack trace:
- void *stack[1024];
- int64_t size = (int64_t)backtrace(stack, sizeof(stack)/sizeof(stack[0]));
- char **strings = strings = backtrace_symbols(stack, size);
- for (int64_t i = start; i < size - stop; i++) {
- char *filename = strings[i];
- char *paren = strings[i] + strcspn(strings[i], "(");
- char *addr_end = paren + 1 + strcspn(paren + 1, ")");
- ptrdiff_t offset = strtol(paren + 1, &addr_end, 16) - 1;
- const char *cmd = String("addr2line -e ", string_slice(filename, strcspn(filename, "(")), " -is +", hex((uint64_t)offset));
- FILE *fp = popen(cmd, "r");
- OptionalText_t fn_name = get_function_name(stack[i]);
- const char *src_filename = NULL;
- int64_t line_number = 0;
- if (fp) {
- char buf[PATH_MAX + 10] = {};
- if (fgets(buf, sizeof(buf), fp)) {
- char *saveptr, *line_num_str;
- if ((src_filename=strtok_r(buf, ":", &saveptr))
- && (line_num_str=strtok_r(NULL, ":", &saveptr)))
- line_number = atoi(line_num_str);
- }
- pclose(fp);
- }
- print_stack_line(out, fn_name, src_filename, line_number);
- }
-}
-
public _Noreturn void fail_text(Text_t message)
{
fail(message);
@@ -577,7 +509,7 @@ public void test_value(const char *filename, int64_t start, int64_t end, const v
bool success = Text$equal_values(expr_text, expected_text);
if (!success) {
- print_stack_trace(stderr, 2, 4);
+ print_stacktrace(stderr, 2);
fprint(stderr, "");
fflush(stderr);
diff --git a/src/stdlib/stdlib.h b/src/stdlib/stdlib.h
index aaa4a1d6..51983bc8 100644
--- a/src/stdlib/stdlib.h
+++ b/src/stdlib/stdlib.h
@@ -10,6 +10,7 @@
#include "datatypes.h"
#include "files.h"
#include "print.h"
+#include "stacktrace.h"
#include "types.h"
#include "util.h"
@@ -29,12 +30,14 @@ void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, int spe
#define fail(...) ({ \
fflush(stdout); \
- if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr); \
+ if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \033[m\n\n", stderr); \
else fputs("==================== ERROR ====================\n\n", stderr); \
- fprint_inline(stderr, __VA_ARGS__); \
- if (USE_COLOR) fputs("\x1b[m", stderr); \
- fputs("\n\n", stderr); \
- print_stack_trace(stderr, 2, 4); \
+ print_stacktrace(stderr, 2); \
+ if (USE_COLOR) fputs("\n\x1b[31;1m", stderr); \
+ else fputs("\n", stderr); \
+ fprint_inline(stderr, "Error: ", __VA_ARGS__); \
+ if (USE_COLOR) fputs("\x1b[m\n", stderr); \
+ else fputs("\n", stderr); \
fflush(stderr); \
raise(SIGABRT); \
_exit(1); \
@@ -53,9 +56,7 @@ void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, int spe
fputs("\n", stderr); \
} \
if (USE_COLOR) fputs("\x1b[m", stderr); \
- print_stack_trace(stderr, 2, 4); \
- fputs("\n\n", stderr); \
- print_stack_trace(stderr, 2, 4); \
+ print_stacktrace(stderr, 2); \
fflush(stderr); \
raise(SIGABRT); \
_exit(1); \
@@ -86,7 +87,6 @@ _Noreturn void tomo_exit(Text_t text, int32_t status);
Closure_t spawn(Closure_t fn);
bool pop_flag(char **argv, int *i, const char *flag, Text_t *result);
-void print_stack_trace(FILE *out, int start, int stop);
void sleep_num(double seconds);
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/stdlib/tomo.h b/src/stdlib/tomo.h
index 2d8d9908..63abd2d6 100644
--- a/src/stdlib/tomo.h
+++ b/src/stdlib/tomo.h
@@ -7,7 +7,6 @@
#include <stdint.h>
#include <sys/param.h>
-#include "lists.h"
#include "bools.h"
#include "bytes.h"
#include "c_strings.h"
@@ -15,6 +14,7 @@
#include "enums.h"
#include "functiontype.h"
#include "integers.h"
+#include "lists.h"
#include "memory.h"
#include "metamethods.h"
#include "nums.h"
@@ -23,6 +23,7 @@
#include "pointers.h"
#include "print.h"
#include "siphash.h"
+#include "stacktrace.h"
#include "structs.h"
#include "tables.h"
#include "text.h"