From 87fdb5956efd8eca8d1a3b1ff603389adaab97be Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 18 Nov 2023 14:18:09 +0100 Subject: [PATCH] Improved backtrace on platforms without glibc. Added $$frameaddress and $$returnaddress properly. --- lib/std/core/allocators/tracking_allocator.c3 | 24 +- lib/std/core/builtin.c3 | 284 ++++++++++++++++++ lib/std/libc/os/win32.c3 | 2 +- lib/std/os/posix/process.c3 | 15 +- src/compiler/enums.h | 1 + src/compiler/llvm_codegen.c | 1 + src/compiler/llvm_codegen_builtins.c | 17 +- src/compiler/llvm_codegen_internal.h | 1 + src/compiler/sema_builtins.c | 15 +- src/compiler/symtab.c | 1 + src/version.h | 2 +- 11 files changed, 347 insertions(+), 16 deletions(-) diff --git a/lib/std/core/allocators/tracking_allocator.c3 b/lib/std/core/allocators/tracking_allocator.c3 index b8704b3f4..7974c2cb4 100644 --- a/lib/std/core/allocators/tracking_allocator.c3 +++ b/lib/std/core/allocators/tracking_allocator.c3 @@ -6,7 +6,7 @@ module std::core::mem::allocator; import std::collections::map; import std::collections::list; -const MAX_BACKTRACE = 8; +const MAX_BACKTRACE = 16; struct Allocation { void* ptr; @@ -104,7 +104,7 @@ fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz align } if (data) { - void*[8] bt; + void*[MAX_BACKTRACE] bt; backtrace::capture_current(&bt); self.map.set((uptr)data, { data, size, bt }); self.mem_total += size; @@ -115,8 +115,14 @@ fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz align fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic { + if (old_pointer) + { + if (catch self.map.remove((uptr)old_pointer)) + { + assert(false, "Attempt to release untracked pointer %p, this is likely a bug.", old_pointer); + } + } self.inner_allocator.release(old_pointer, is_aligned); - if (old_pointer) self.map.remove((uptr)old_pointer); } fn void TrackingAllocator.clear(&self) @@ -205,7 +211,17 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream* out) io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!; foreach (trace : list) { - io::fprintfn(out, " %s", trace); + if (trace.has_file()) + { + io::fprintfn(out, " %s (in %s:%d)", trace.function, trace.file, trace.line); + continue; + } + if (trace.is_unknown()) + { + io::fprintfn(out, " ??? (in unknown)"); + continue; + } + io::fprintfn(out, " %s (source unavailable)", trace.function); } } } diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index 76eb92da0..2160a97a0 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -331,6 +331,289 @@ macro uint String.hash(String c) => (uint)fnv32a::encode(c); macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c); macro uint void*.hash(void* ptr) => ((ulong)(uptr)ptr).hash(); +const MAX_FRAMEADDRESS = 128; +/** + * @require n >= 0 + **/ +macro void* get_frameaddress(int n) +{ + if (n > MAX_FRAMEADDRESS) return null; + switch (n) + { + case 0: return $$frameaddress(0); + case 1: return $$frameaddress(1); + case 2: return $$frameaddress(2); + case 3: return $$frameaddress(3); + case 4: return $$frameaddress(4); + case 5: return $$frameaddress(5); + case 6: return $$frameaddress(6); + case 7: return $$frameaddress(7); + case 8: return $$frameaddress(8); + case 9: return $$frameaddress(9); + case 10: return $$frameaddress(10); + case 11: return $$frameaddress(11); + case 12: return $$frameaddress(12); + case 13: return $$frameaddress(13); + case 14: return $$frameaddress(14); + case 15: return $$frameaddress(15); + case 16: return $$frameaddress(16); + case 17: return $$frameaddress(17); + case 18: return $$frameaddress(18); + case 19: return $$frameaddress(19); + case 20: return $$frameaddress(20); + case 21: return $$frameaddress(21); + case 22: return $$frameaddress(22); + case 23: return $$frameaddress(23); + case 24: return $$frameaddress(24); + case 25: return $$frameaddress(25); + case 26: return $$frameaddress(26); + case 27: return $$frameaddress(27); + case 28: return $$frameaddress(28); + case 29: return $$frameaddress(29); + case 30: return $$frameaddress(30); + case 31: return $$frameaddress(31); + case 32: return $$frameaddress(32); + case 33: return $$frameaddress(33); + case 34: return $$frameaddress(34); + case 35: return $$frameaddress(35); + case 36: return $$frameaddress(36); + case 37: return $$frameaddress(37); + case 38: return $$frameaddress(38); + case 39: return $$frameaddress(39); + case 40: return $$frameaddress(40); + case 41: return $$frameaddress(41); + case 42: return $$frameaddress(42); + case 43: return $$frameaddress(43); + case 44: return $$frameaddress(44); + case 45: return $$frameaddress(45); + case 46: return $$frameaddress(46); + case 47: return $$frameaddress(47); + case 48: return $$frameaddress(48); + case 49: return $$frameaddress(49); + case 50: return $$frameaddress(50); + case 51: return $$frameaddress(51); + case 52: return $$frameaddress(52); + case 53: return $$frameaddress(53); + case 54: return $$frameaddress(54); + case 55: return $$frameaddress(55); + case 56: return $$frameaddress(56); + case 57: return $$frameaddress(57); + case 58: return $$frameaddress(58); + case 59: return $$frameaddress(59); + case 60: return $$frameaddress(60); + case 61: return $$frameaddress(61); + case 62: return $$frameaddress(62); + case 63: return $$frameaddress(63); + case 64: return $$frameaddress(64); + case 65: return $$frameaddress(65); + case 66: return $$frameaddress(66); + case 67: return $$frameaddress(67); + case 68: return $$frameaddress(68); + case 69: return $$frameaddress(69); + case 70: return $$frameaddress(70); + case 71: return $$frameaddress(71); + case 72: return $$frameaddress(72); + case 73: return $$frameaddress(73); + case 74: return $$frameaddress(74); + case 75: return $$frameaddress(75); + case 76: return $$frameaddress(76); + case 77: return $$frameaddress(77); + case 78: return $$frameaddress(78); + case 79: return $$frameaddress(79); + case 80: return $$frameaddress(80); + case 81: return $$frameaddress(81); + case 82: return $$frameaddress(82); + case 83: return $$frameaddress(83); + case 84: return $$frameaddress(84); + case 85: return $$frameaddress(85); + case 86: return $$frameaddress(86); + case 87: return $$frameaddress(87); + case 88: return $$frameaddress(88); + case 89: return $$frameaddress(89); + case 90: return $$frameaddress(90); + case 91: return $$frameaddress(91); + case 92: return $$frameaddress(92); + case 93: return $$frameaddress(93); + case 94: return $$frameaddress(94); + case 95: return $$frameaddress(95); + case 96: return $$frameaddress(96); + case 97: return $$frameaddress(97); + case 98: return $$frameaddress(98); + case 99: return $$frameaddress(99); + case 100: return $$frameaddress(100); + case 101: return $$frameaddress(101); + case 102: return $$frameaddress(102); + case 103: return $$frameaddress(103); + case 104: return $$frameaddress(104); + case 105: return $$frameaddress(105); + case 106: return $$frameaddress(106); + case 107: return $$frameaddress(107); + case 108: return $$frameaddress(108); + case 109: return $$frameaddress(109); + case 110: return $$frameaddress(110); + case 111: return $$frameaddress(111); + case 112: return $$frameaddress(112); + case 113: return $$frameaddress(113); + case 114: return $$frameaddress(114); + case 115: return $$frameaddress(115); + case 116: return $$frameaddress(116); + case 117: return $$frameaddress(117); + case 118: return $$frameaddress(118); + case 119: return $$frameaddress(119); + case 120: return $$frameaddress(120); + case 121: return $$frameaddress(121); + case 122: return $$frameaddress(122); + case 123: return $$frameaddress(123); + case 124: return $$frameaddress(124); + case 125: return $$frameaddress(125); + case 126: return $$frameaddress(126); + case 127: return $$frameaddress(127); + case 128: return $$frameaddress(128); + default: unreachable(); + } +} + +/** + * @require n >= 0 + **/ +macro void* get_returnaddress(int n) +{ + if (n > MAX_FRAMEADDRESS) return null; + switch (n) + { + case 0: return $$returnaddress(0); + case 1: return $$returnaddress(1); + case 2: return $$returnaddress(2); + case 3: return $$returnaddress(3); + case 4: return $$returnaddress(4); + case 5: return $$returnaddress(5); + case 6: return $$returnaddress(6); + case 7: return $$returnaddress(7); + case 8: return $$returnaddress(8); + case 9: return $$returnaddress(9); + case 10: return $$returnaddress(10); + case 11: return $$returnaddress(11); + case 12: return $$returnaddress(12); + case 13: return $$returnaddress(13); + case 14: return $$returnaddress(14); + case 15: return $$returnaddress(15); + case 16: return $$returnaddress(16); + case 17: return $$returnaddress(17); + case 18: return $$returnaddress(18); + case 19: return $$returnaddress(19); + case 20: return $$returnaddress(20); + case 21: return $$returnaddress(21); + case 22: return $$returnaddress(22); + case 23: return $$returnaddress(23); + case 24: return $$returnaddress(24); + case 25: return $$returnaddress(25); + case 26: return $$returnaddress(26); + case 27: return $$returnaddress(27); + case 28: return $$returnaddress(28); + case 29: return $$returnaddress(29); + case 30: return $$returnaddress(30); + case 31: return $$returnaddress(31); + case 32: return $$returnaddress(32); + case 33: return $$returnaddress(33); + case 34: return $$returnaddress(34); + case 35: return $$returnaddress(35); + case 36: return $$returnaddress(36); + case 37: return $$returnaddress(37); + case 38: return $$returnaddress(38); + case 39: return $$returnaddress(39); + case 40: return $$returnaddress(40); + case 41: return $$returnaddress(41); + case 42: return $$returnaddress(42); + case 43: return $$returnaddress(43); + case 44: return $$returnaddress(44); + case 45: return $$returnaddress(45); + case 46: return $$returnaddress(46); + case 47: return $$returnaddress(47); + case 48: return $$returnaddress(48); + case 49: return $$returnaddress(49); + case 50: return $$returnaddress(50); + case 51: return $$returnaddress(51); + case 52: return $$returnaddress(52); + case 53: return $$returnaddress(53); + case 54: return $$returnaddress(54); + case 55: return $$returnaddress(55); + case 56: return $$returnaddress(56); + case 57: return $$returnaddress(57); + case 58: return $$returnaddress(58); + case 59: return $$returnaddress(59); + case 60: return $$returnaddress(60); + case 61: return $$returnaddress(61); + case 62: return $$returnaddress(62); + case 63: return $$returnaddress(63); + case 64: return $$returnaddress(64); + case 65: return $$returnaddress(65); + case 66: return $$returnaddress(66); + case 67: return $$returnaddress(67); + case 68: return $$returnaddress(68); + case 69: return $$returnaddress(69); + case 70: return $$returnaddress(70); + case 71: return $$returnaddress(71); + case 72: return $$returnaddress(72); + case 73: return $$returnaddress(73); + case 74: return $$returnaddress(74); + case 75: return $$returnaddress(75); + case 76: return $$returnaddress(76); + case 77: return $$returnaddress(77); + case 78: return $$returnaddress(78); + case 79: return $$returnaddress(79); + case 80: return $$returnaddress(80); + case 81: return $$returnaddress(81); + case 82: return $$returnaddress(82); + case 83: return $$returnaddress(83); + case 84: return $$returnaddress(84); + case 85: return $$returnaddress(85); + case 86: return $$returnaddress(86); + case 87: return $$returnaddress(87); + case 88: return $$returnaddress(88); + case 89: return $$returnaddress(89); + case 90: return $$returnaddress(90); + case 91: return $$returnaddress(91); + case 92: return $$returnaddress(92); + case 93: return $$returnaddress(93); + case 94: return $$returnaddress(94); + case 95: return $$returnaddress(95); + case 96: return $$returnaddress(96); + case 97: return $$returnaddress(97); + case 98: return $$returnaddress(98); + case 99: return $$returnaddress(99); + case 100: return $$returnaddress(100); + case 101: return $$returnaddress(101); + case 102: return $$returnaddress(102); + case 103: return $$returnaddress(103); + case 104: return $$returnaddress(104); + case 105: return $$returnaddress(105); + case 106: return $$returnaddress(106); + case 107: return $$returnaddress(107); + case 108: return $$returnaddress(108); + case 109: return $$returnaddress(109); + case 110: return $$returnaddress(110); + case 111: return $$returnaddress(111); + case 112: return $$returnaddress(112); + case 113: return $$returnaddress(113); + case 114: return $$returnaddress(114); + case 115: return $$returnaddress(115); + case 116: return $$returnaddress(116); + case 117: return $$returnaddress(117); + case 118: return $$returnaddress(118); + case 119: return $$returnaddress(119); + case 120: return $$returnaddress(120); + case 121: return $$returnaddress(121); + case 122: return $$returnaddress(122); + case 123: return $$returnaddress(123); + case 124: return $$returnaddress(124); + case 125: return $$returnaddress(125); + case 126: return $$returnaddress(126); + case 127: return $$returnaddress(127); + case 128: return $$returnaddress(128); + default: unreachable(); + } +} + module std::core::builtin @if((env::LINUX || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS); import libc; @@ -385,3 +668,4 @@ fn void install_signal_handlers() @init(101) @local install_signal_handler(libc::SIGBUS, &sig_bus_error); install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault); } + diff --git a/lib/std/libc/os/win32.c3 b/lib/std/libc/os/win32.c3 index 643b60928..35175056f 100644 --- a/lib/std/libc/os/win32.c3 +++ b/lib/std/libc/os/win32.c3 @@ -45,7 +45,7 @@ extern fn CInt get_system_info(SystemInfo*) @extern("GetSystemInfo"); // Aliases to simplify libc use macro Tm* localtime_r(Time_t* timer, Tm* buf) => _localtime64_s(buf, timer); -macro CInt setjmp(JmpBuf* buffer) => _setjmp($$frameaddress(), buffer); +macro CInt setjmp(JmpBuf* buffer) => _setjmp($$frameaddress(0), buffer); macro Tm* gmtime_r(Time_t* timer, Tm* buf) => _gmtime64_s(buf, timer); macro isz read(Fd fd, void* buffer, usz buffer_size) => _read(fd, buffer, (CUInt)buffer_size); macro isz write(Fd fd, void* buffer, usz count) => _write(fd, buffer, (CUInt)count); \ No newline at end of file diff --git a/lib/std/os/posix/process.c3 b/lib/std/os/posix/process.c3 index eab619517..bfe48d117 100644 --- a/lib/std/os/posix/process.c3 +++ b/lib/std/os/posix/process.c3 @@ -37,7 +37,6 @@ def spawn = posix_spawn; extern fn CInt kill(Pid_t pid, CInt sig); extern fn Pid_t waitpid(Pid_t pid, CInt* stat_loc, int options); extern fn CInt raise(CInt sig); -extern fn CInt backtrace(void **buffer, CInt size); extern fn ZString* backtrace_symbols(void** buffer, CInt size); extern fn void backtrace_symbols_fd(void** buffer, CInt size, CInt fd); macro CInt wEXITSTATUS(CInt status) => (status & 0xff00) >> 8; @@ -55,3 +54,17 @@ const CInt __W_CONTINUED = 0xffff; const CInt WNOHANG = 1; const CInt WUNTRACES = 2; +extern fn CInt backtrace(void** buffer, CInt size) @if(env::DARWIN); + +fn CInt backtrace(void** buffer, CInt size) @if(!env::DARWIN) +{ + if (size < 1) return 0; + void*[128] buffer_first; + CInt i; + for (i = 0; (uptr)builtin::get_frameaddress(i + 1) > 1 && i < size; i++) + { + buffer[i] = builtin::get_returnaddress(i); + if (!buffer[i]) break; + } + return i; +} diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 3e2c347aa..16692130e 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -919,6 +919,7 @@ typedef enum BUILTIN_REDUCE_OR, BUILTIN_REDUCE_XOR, BUILTIN_REVERSE, + BUILTIN_RETURNADDRESS, BUILTIN_RINT, BUILTIN_ROUND, BUILTIN_ROUNDEVEN, diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 950745cc4..ebda80702 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -702,6 +702,7 @@ static void llvm_codegen_setup() intrinsic_id.powi = lookup_intrinsic("llvm.powi"); intrinsic_id.prefetch = lookup_intrinsic("llvm.prefetch"); intrinsic_id.readcyclecounter = lookup_intrinsic("llvm.readcyclecounter"); + intrinsic_id.returnaddress = lookup_intrinsic("llvm.returnaddress"); intrinsic_id.rint = lookup_intrinsic("llvm.rint"); intrinsic_id.round = lookup_intrinsic("llvm.round"); intrinsic_id.roundeven = lookup_intrinsic("llvm.roundeven"); diff --git a/src/compiler/llvm_codegen_builtins.c b/src/compiler/llvm_codegen_builtins.c index 936e69ed5..66efd6c44 100644 --- a/src/compiler/llvm_codegen_builtins.c +++ b/src/compiler/llvm_codegen_builtins.c @@ -794,11 +794,20 @@ void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr) llvm_emit_compare_exchange(c, result_value, expr); return; case BUILTIN_FRAMEADDRESS: + { + llvm_emit_expr(c, result_value, expr->call_expr.arguments[0]); + llvm_value_rvalue(c, result_value); + LLVMValueRef res = llvm_emit_call_intrinsic(c, func == BUILTIN_FRAMEADDRESS ? intrinsic_id.frameaddress : intrinsic_id.returnaddress, &c->ptr_type, 1, &result_value->value, 1); + llvm_value_set(result_value, res, expr->type); + return; + } + case BUILTIN_RETURNADDRESS: { - LLVMTypeRef type[2] = { c->ptr_type, llvm_get_type(c, type_int) }; - LLVMValueRef value = LLVMConstNull(type[1]); - value = llvm_emit_call_intrinsic(c, intrinsic_id.frameaddress, type, 1, &value, 1); - llvm_value_set(result_value, value, expr->type); + llvm_emit_expr(c, result_value, expr->call_expr.arguments[0]); + llvm_value_rvalue(c, result_value); + LLVMTypeRef type = LLVMTypeOf(result_value->value); + LLVMValueRef res = llvm_emit_call_intrinsic(c, func == BUILTIN_FRAMEADDRESS ? intrinsic_id.frameaddress : intrinsic_id.returnaddress, NULL, 0, &result_value->value, 1); + llvm_value_set(result_value, res, expr->type); return; } case BUILTIN_SELECT: diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 27d253f5d..0168175e8 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -178,6 +178,7 @@ typedef struct unsigned powi; unsigned prefetch; unsigned readcyclecounter; + unsigned returnaddress; unsigned rint; unsigned round; unsigned roundeven; diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index e38b12832..e5fbcc437 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -338,9 +338,6 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) case BUILTIN_GET_ROUNDING_MODE: expr->type = type_int; return true; - case BUILTIN_FRAMEADDRESS: - expr->type = type_voidptr; - return true; case BUILTIN_COMPARE_EXCHANGE: return sema_expr_analyse_compare_exchange(context, expr); default: @@ -514,6 +511,14 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) arg_count)) return false; rtype = args[0]->type; break; + case BUILTIN_FRAMEADDRESS: + case BUILTIN_RETURNADDRESS: + assert(arg_count); + if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_INTEGER }, arg_count)) return false; + if (!cast_implicit(context, args[0], type_int)) return false; + if (!expr_is_const_int(args[0])) RETURN_SEMA_ERROR(args[0], "Expected a compile time constant integer."); + rtype = type_voidptr; + break; case BUILTIN_WASM_MEMORY_SIZE: assert(arg_count == 1); if (!cast_implicit(context, args[0], type_uint)) return false; @@ -865,7 +870,6 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) } case BUILTIN_NONE: case BUILTIN_COMPARE_EXCHANGE: - case BUILTIN_FRAMEADDRESS: case BUILTIN_GET_ROUNDING_MODE: case BUILTIN_SWIZZLE: case BUILTIN_SWIZZLE2: @@ -892,7 +896,6 @@ static inline int builtin_expected_args(BuiltinFunction func) case BUILTIN_SYSCLOCK: case BUILTIN_TRAP: case BUILTIN_UNREACHABLE: - case BUILTIN_FRAMEADDRESS: return 0; case BUILTIN_ABS: case BUILTIN_BITREVERSE: @@ -931,6 +934,8 @@ static inline int builtin_expected_args(BuiltinFunction func) case BUILTIN_REDUCE_MIN: case BUILTIN_SET_ROUNDING_MODE: case BUILTIN_WASM_MEMORY_SIZE: + case BUILTIN_FRAMEADDRESS: + case BUILTIN_RETURNADDRESS: return 1; case BUILTIN_COPYSIGN: case BUILTIN_EXACT_ADD: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index bf364851f..ac83475e9 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -265,6 +265,7 @@ void symtab_init(uint32_t capacity) builtin_list[BUILTIN_REDUCE_OR] = KW_DEF("reduce_or"); builtin_list[BUILTIN_REDUCE_XOR] = KW_DEF("reduce_xor"); builtin_list[BUILTIN_REVERSE] = KW_DEF("reverse"); + builtin_list[BUILTIN_RETURNADDRESS] = KW_DEF("returnaddress"); builtin_list[BUILTIN_RINT] = KW_DEF("rint"); builtin_list[BUILTIN_ROUND] = KW_DEF("round"); builtin_list[BUILTIN_ROUNDEVEN] = KW_DEF("roundeven"); diff --git a/src/version.h b/src/version.h index 95d4db64a..5f71dec58 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.706" +#define COMPILER_VERSION "0.4.707"