add optional native backtrace printing

This commit is contained in:
Stone Tickle 2025-12-24 10:54:47 -05:00
parent 43c18b047c
commit 45ab272c61
8 changed files with 408 additions and 1 deletions

View File

@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: Stone Tickle <lattis@mochiro.moe>
* SPDX-License-Identifier: GPL-3.0-only
*/
#ifndef MUON_PLATFORM_BACKTRACE_H
#define MUON_PLATFORM_BACKTRACE_H
#include <stddef.h>
#include "datastructures/arr.h"
struct platform_backtrace_frame {
void *addr, *symbol;
const char *symbol_name, *file_name;
ptrdiff_t offset;
};
struct platform_backtrace {
struct arr frames;
};
void platform_backtrace_capture(struct arena *a, struct platform_backtrace *bt);
extern bool have_platform_backtrace_capture;
#endif

View File

@ -106,16 +106,18 @@ foreach native : [false, true]
'-Wno-missing-field-initializers',
'-Wno-unused-parameter',
'-Wold-style-definition',
'-Wno-frame-address',
'-Woverflow',
'-Wstrict-aliasing=2',
'-Wstrict-prototypes',
'-Wundef',
'-Wvla',
'-fstrict-aliasing',
'-std=c99',
'-fno-omit-frame-pointer',
'-fmacro-prefix-map=@0@/='.format(
fs.relative_to(meson.current_source_dir(), meson.current_build_dir()),
),
'-std=c99',
]
endif
@ -139,6 +141,7 @@ foreach native : [false, true]
endforeach
include_dir = [include_directories('include')]
muon_export_dynamic = false
subdir('tools')
subdir('src')
@ -195,6 +198,7 @@ muon = executable(
c_args: muon_c_args,
cpp_args: muon_c_args,
install: true,
export_dynamic: muon_export_dynamic,
)
subdir('tests')

View File

@ -55,6 +55,13 @@ option(
description: 'select readline implementation',
)
option(
'native_backtrace',
type: 'feature',
value: 'auto',
description: 'enable native backtrace capture',
)
################################################################################
# build settings
################################################################################

View File

@ -117,6 +117,7 @@
#include "platform/assert.c"
#include "platform/filesystem.c"
#include "platform/mem.c"
#include "platform/null/backtrace.c"
#include "platform/os.c"
#include "platform/path.c"
#include "platform/run_cmd.c"

View File

@ -36,6 +36,7 @@
#include "options.h"
#include "opts.h"
#include "platform/assert.h"
#include "platform/backtrace.h"
#include "platform/init.h"
#include "platform/os.h"
#include "platform/path.h"
@ -1324,6 +1325,7 @@ cmd_version(struct workspace *wk, uint32_t argc, uint32_t argi, char *const argv
#ifdef __SANITIZE_MEMORY__
{ "msan", true },
#endif
{ "native backtrace", have_platform_backtrace_capture },
};
uint32_t i;
@ -1467,6 +1469,16 @@ signal_handler(int signal, const char *signal_name, void *_ctx)
LOG_I("caught signal %d (%s)", signal, signal_name);
struct platform_backtrace bt = { 0 };
platform_backtrace_capture(wk->a, &bt);
LOG_I("native backtrace (%d frames):", bt.frames.len);
for (uint32_t i = 0; i < bt.frames.len; i++) {
const struct platform_backtrace_frame *frame = arr_get(&bt.frames, i);
LOG_I("%p <%s+%d> at %s", frame->addr, frame->symbol_name, (int)frame->offset, frame->file_name);
}
log_flush();
if (wk->vm.run) {
vm_error(wk, "encountered unhandled runtime error");
@ -1475,6 +1487,7 @@ signal_handler(int signal, const char *signal_name, void *_ctx)
backend_print_stack(wk);
}
log_flush();
}
int

View File

@ -31,6 +31,7 @@ if host_machine.system() == 'windows'
platform_sources += files(
'windows/rpath_fixer.c',
'windows/win32_error.c',
'null/backtrace.c',
)
endif
@ -40,4 +41,26 @@ if platform == 'posix'
else
platform_sources += files('posix/rpath_fixer.c')
endif
native_backtrace = get_option('native_backtrace').enabled()
if get_option('native_backtrace').auto()
libdl = cc.find_library('dl', required: false)
native_backtrace = (
cc.has_function('dladdr', prefix: '#include <dlfcn.h>', args: ['-D_BSD_SOURCE'], dependencies: libdl)
and cc.has_function('__builtin_frame_address')
and cc.has_function('__builtin_return_address')
)
if native_backtrace and libdl.found()
deps += libdl
endif
endif
if native_backtrace
muon_export_dynamic = true
platform_sources += files('posix/backtrace.c')
else
platform_sources += files('null/backtrace.c')
endif
endif

View File

@ -0,0 +1,13 @@
/*
* SPDX-FileCopyrightText: Stone Tickle <lattis@mochiro.moe>
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "platform/backtrace.h"
bool have_platform_backtrace_capture = false;
void
platform_backtrace_capture(struct arena *a, struct platform_backtrace *bt)
{
}

View File

@ -0,0 +1,320 @@
/*
* SPDX-FileCopyrightText: Stone Tickle <lattis@mochiro.moe>
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "compat.h"
// dladdr requires _GNU_SOURCE or _BSD_SOURCE
#undef _POSIX_C_SOURCE
#define _BSD_SOURCE
#include <dlfcn.h>
#include "platform/backtrace.h"
bool have_platform_backtrace_capture = true;
static void *
backtrace_getreturnaddr(uint8_t level)
{
switch (level) {
case 0: return __builtin_return_address(1);
case 1: return __builtin_return_address(2);
case 2: return __builtin_return_address(3);
case 3: return __builtin_return_address(4);
case 4: return __builtin_return_address(5);
case 5: return __builtin_return_address(6);
case 6: return __builtin_return_address(7);
case 7: return __builtin_return_address(8);
case 8: return __builtin_return_address(9);
case 9: return __builtin_return_address(10);
case 10: return __builtin_return_address(11);
case 11: return __builtin_return_address(12);
case 12: return __builtin_return_address(13);
case 13: return __builtin_return_address(14);
case 14: return __builtin_return_address(15);
case 15: return __builtin_return_address(16);
case 16: return __builtin_return_address(17);
case 17: return __builtin_return_address(18);
case 18: return __builtin_return_address(19);
case 19: return __builtin_return_address(20);
case 20: return __builtin_return_address(21);
case 21: return __builtin_return_address(22);
case 22: return __builtin_return_address(23);
case 23: return __builtin_return_address(24);
case 24: return __builtin_return_address(25);
case 25: return __builtin_return_address(26);
case 26: return __builtin_return_address(27);
case 27: return __builtin_return_address(28);
case 28: return __builtin_return_address(29);
case 29: return __builtin_return_address(30);
case 30: return __builtin_return_address(31);
case 31: return __builtin_return_address(32);
case 32: return __builtin_return_address(33);
case 33: return __builtin_return_address(34);
case 34: return __builtin_return_address(35);
case 35: return __builtin_return_address(36);
case 36: return __builtin_return_address(37);
case 37: return __builtin_return_address(38);
case 38: return __builtin_return_address(39);
case 39: return __builtin_return_address(40);
case 40: return __builtin_return_address(41);
case 41: return __builtin_return_address(42);
case 42: return __builtin_return_address(43);
case 43: return __builtin_return_address(44);
case 44: return __builtin_return_address(45);
case 45: return __builtin_return_address(46);
case 46: return __builtin_return_address(47);
case 47: return __builtin_return_address(48);
case 48: return __builtin_return_address(49);
case 49: return __builtin_return_address(50);
case 50: return __builtin_return_address(51);
case 51: return __builtin_return_address(52);
case 52: return __builtin_return_address(53);
case 53: return __builtin_return_address(54);
case 54: return __builtin_return_address(55);
case 55: return __builtin_return_address(56);
case 56: return __builtin_return_address(57);
case 57: return __builtin_return_address(58);
case 58: return __builtin_return_address(59);
case 59: return __builtin_return_address(60);
case 60: return __builtin_return_address(61);
case 61: return __builtin_return_address(62);
case 62: return __builtin_return_address(63);
case 63: return __builtin_return_address(64);
case 64: return __builtin_return_address(65);
case 65: return __builtin_return_address(66);
case 66: return __builtin_return_address(67);
case 67: return __builtin_return_address(68);
case 68: return __builtin_return_address(69);
case 69: return __builtin_return_address(70);
case 70: return __builtin_return_address(71);
case 71: return __builtin_return_address(72);
case 72: return __builtin_return_address(73);
case 73: return __builtin_return_address(74);
case 74: return __builtin_return_address(75);
case 75: return __builtin_return_address(76);
case 76: return __builtin_return_address(77);
case 77: return __builtin_return_address(78);
case 78: return __builtin_return_address(79);
case 79: return __builtin_return_address(80);
case 80: return __builtin_return_address(81);
case 81: return __builtin_return_address(82);
case 82: return __builtin_return_address(83);
case 83: return __builtin_return_address(84);
case 84: return __builtin_return_address(85);
case 85: return __builtin_return_address(86);
case 86: return __builtin_return_address(87);
case 87: return __builtin_return_address(88);
case 88: return __builtin_return_address(89);
case 89: return __builtin_return_address(90);
case 90: return __builtin_return_address(91);
case 91: return __builtin_return_address(92);
case 92: return __builtin_return_address(93);
case 93: return __builtin_return_address(94);
case 94: return __builtin_return_address(95);
case 95: return __builtin_return_address(96);
case 96: return __builtin_return_address(97);
case 97: return __builtin_return_address(98);
case 98: return __builtin_return_address(99);
case 99: return __builtin_return_address(100);
case 100: return __builtin_return_address(101);
case 101: return __builtin_return_address(102);
case 102: return __builtin_return_address(103);
case 103: return __builtin_return_address(104);
case 104: return __builtin_return_address(105);
case 105: return __builtin_return_address(106);
case 106: return __builtin_return_address(107);
case 107: return __builtin_return_address(108);
case 108: return __builtin_return_address(109);
case 109: return __builtin_return_address(110);
case 110: return __builtin_return_address(111);
case 111: return __builtin_return_address(112);
case 112: return __builtin_return_address(113);
case 113: return __builtin_return_address(114);
case 114: return __builtin_return_address(115);
case 115: return __builtin_return_address(116);
case 116: return __builtin_return_address(117);
case 117: return __builtin_return_address(118);
case 118: return __builtin_return_address(119);
case 119: return __builtin_return_address(120);
case 120: return __builtin_return_address(121);
case 121: return __builtin_return_address(122);
case 122: return __builtin_return_address(123);
case 123: return __builtin_return_address(124);
case 124: return __builtin_return_address(125);
case 125: return __builtin_return_address(126);
case 126: return __builtin_return_address(127);
case 127: return __builtin_return_address(128);
default: return 0;
}
}
static void *
backtrace_getframeaddr(uint8_t level)
{
switch (level) {
case 0: return __builtin_frame_address(1);
case 1: return __builtin_frame_address(2);
case 2: return __builtin_frame_address(3);
case 3: return __builtin_frame_address(4);
case 4: return __builtin_frame_address(5);
case 5: return __builtin_frame_address(6);
case 6: return __builtin_frame_address(7);
case 7: return __builtin_frame_address(8);
case 8: return __builtin_frame_address(9);
case 9: return __builtin_frame_address(10);
case 10: return __builtin_frame_address(11);
case 11: return __builtin_frame_address(12);
case 12: return __builtin_frame_address(13);
case 13: return __builtin_frame_address(14);
case 14: return __builtin_frame_address(15);
case 15: return __builtin_frame_address(16);
case 16: return __builtin_frame_address(17);
case 17: return __builtin_frame_address(18);
case 18: return __builtin_frame_address(19);
case 19: return __builtin_frame_address(20);
case 20: return __builtin_frame_address(21);
case 21: return __builtin_frame_address(22);
case 22: return __builtin_frame_address(23);
case 23: return __builtin_frame_address(24);
case 24: return __builtin_frame_address(25);
case 25: return __builtin_frame_address(26);
case 26: return __builtin_frame_address(27);
case 27: return __builtin_frame_address(28);
case 28: return __builtin_frame_address(29);
case 29: return __builtin_frame_address(30);
case 30: return __builtin_frame_address(31);
case 31: return __builtin_frame_address(32);
case 32: return __builtin_frame_address(33);
case 33: return __builtin_frame_address(34);
case 34: return __builtin_frame_address(35);
case 35: return __builtin_frame_address(36);
case 36: return __builtin_frame_address(37);
case 37: return __builtin_frame_address(38);
case 38: return __builtin_frame_address(39);
case 39: return __builtin_frame_address(40);
case 40: return __builtin_frame_address(41);
case 41: return __builtin_frame_address(42);
case 42: return __builtin_frame_address(43);
case 43: return __builtin_frame_address(44);
case 44: return __builtin_frame_address(45);
case 45: return __builtin_frame_address(46);
case 46: return __builtin_frame_address(47);
case 47: return __builtin_frame_address(48);
case 48: return __builtin_frame_address(49);
case 49: return __builtin_frame_address(50);
case 50: return __builtin_frame_address(51);
case 51: return __builtin_frame_address(52);
case 52: return __builtin_frame_address(53);
case 53: return __builtin_frame_address(54);
case 54: return __builtin_frame_address(55);
case 55: return __builtin_frame_address(56);
case 56: return __builtin_frame_address(57);
case 57: return __builtin_frame_address(58);
case 58: return __builtin_frame_address(59);
case 59: return __builtin_frame_address(60);
case 60: return __builtin_frame_address(61);
case 61: return __builtin_frame_address(62);
case 62: return __builtin_frame_address(63);
case 63: return __builtin_frame_address(64);
case 64: return __builtin_frame_address(65);
case 65: return __builtin_frame_address(66);
case 66: return __builtin_frame_address(67);
case 67: return __builtin_frame_address(68);
case 68: return __builtin_frame_address(69);
case 69: return __builtin_frame_address(70);
case 70: return __builtin_frame_address(71);
case 71: return __builtin_frame_address(72);
case 72: return __builtin_frame_address(73);
case 73: return __builtin_frame_address(74);
case 74: return __builtin_frame_address(75);
case 75: return __builtin_frame_address(76);
case 76: return __builtin_frame_address(77);
case 77: return __builtin_frame_address(78);
case 78: return __builtin_frame_address(79);
case 79: return __builtin_frame_address(80);
case 80: return __builtin_frame_address(81);
case 81: return __builtin_frame_address(82);
case 82: return __builtin_frame_address(83);
case 83: return __builtin_frame_address(84);
case 84: return __builtin_frame_address(85);
case 85: return __builtin_frame_address(86);
case 86: return __builtin_frame_address(87);
case 87: return __builtin_frame_address(88);
case 88: return __builtin_frame_address(89);
case 89: return __builtin_frame_address(90);
case 90: return __builtin_frame_address(91);
case 91: return __builtin_frame_address(92);
case 92: return __builtin_frame_address(93);
case 93: return __builtin_frame_address(94);
case 94: return __builtin_frame_address(95);
case 95: return __builtin_frame_address(96);
case 96: return __builtin_frame_address(97);
case 97: return __builtin_frame_address(98);
case 98: return __builtin_frame_address(99);
case 99: return __builtin_frame_address(100);
case 100: return __builtin_frame_address(101);
case 101: return __builtin_frame_address(102);
case 102: return __builtin_frame_address(103);
case 103: return __builtin_frame_address(104);
case 104: return __builtin_frame_address(105);
case 105: return __builtin_frame_address(106);
case 106: return __builtin_frame_address(107);
case 107: return __builtin_frame_address(108);
case 108: return __builtin_frame_address(109);
case 109: return __builtin_frame_address(110);
case 110: return __builtin_frame_address(111);
case 111: return __builtin_frame_address(112);
case 112: return __builtin_frame_address(113);
case 113: return __builtin_frame_address(114);
case 114: return __builtin_frame_address(115);
case 115: return __builtin_frame_address(116);
case 116: return __builtin_frame_address(117);
case 117: return __builtin_frame_address(118);
case 118: return __builtin_frame_address(119);
case 119: return __builtin_frame_address(120);
case 120: return __builtin_frame_address(121);
case 121: return __builtin_frame_address(122);
case 122: return __builtin_frame_address(123);
case 123: return __builtin_frame_address(124);
case 124: return __builtin_frame_address(125);
case 125: return __builtin_frame_address(126);
case 126: return __builtin_frame_address(127);
case 127: return __builtin_frame_address(128);
default: return 0;
}
}
static void
platform_backtrace_resolve(struct platform_backtrace *bt)
{
for (uint32_t i = 0; i < bt->frames.len; i++) {
struct platform_backtrace_frame *frame = arr_get(&bt->frames, i);
Dl_info info = { 0 };
if (dladdr(frame->addr, &info) != 0) {
frame->symbol_name = info.dli_sname;
frame->file_name = info.dli_fname;
frame->symbol = info.dli_saddr ? info.dli_saddr : frame->addr;
frame->offset = (char *)frame->addr - (char *)frame->symbol;
}
}
}
void
platform_backtrace_capture(struct arena *a, struct platform_backtrace *bt)
{
*bt = (struct platform_backtrace){ 0 };
arr_init(a, &bt->frames, 32, struct platform_backtrace_frame);
for (uint32_t i = 1; backtrace_getframeaddr(i + 1); i++) {
void *returnaddr = backtrace_getreturnaddr(i);
if (!returnaddr) {
break;
}
arr_push(a, &bt->frames, &(struct platform_backtrace_frame){ .addr = returnaddr });
}
platform_backtrace_resolve(bt);
}