backtrace.ha (3863B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use bufio; 5 use debug::dwarf; 6 use debug::image; 7 use fmt; 8 use format::elf; 9 use fs; 10 use io; 11 use os; 12 use time; 13 14 def MAX_FRAMES_TOP: size = 16z; 15 def MAX_FRAMES_BOTTOM: size = 16z; 16 def MAX_FRAMES: size = MAX_FRAMES_TOP + MAX_FRAMES_BOTTOM; 17 18 fn backtrace(self: *image::image, frame: stackframe) void = { 19 let orig = frame; 20 let nframe = 1z; 21 for (true; nframe += 1) { 22 match (next(frame)) { 23 case let next: stackframe => 24 frame = next; 25 case done => break; 26 }; 27 }; 28 frame = orig; 29 30 const st = match (os::fstat(self.fd)) { 31 case let st: fs::filestat => 32 yield st; 33 case fs::error => 34 yield fs::filestat { mask = 0, ... }; 35 }; 36 37 static let seen: [MAX_FRAMES]uintptr = [0: uintptr...]; 38 let seen = seen[..0]; 39 40 for (let i = 0z; i < nframe; i += 1) { 41 if (i < MAX_FRAMES_TOP || i > nframe - MAX_FRAMES_BOTTOM) { 42 printframe(self, &seen, &st, frame); 43 }; 44 if (i == MAX_FRAMES_TOP && nframe > MAX_FRAMES) { 45 fmt::errorfln("\t({} additional frames omitted)", 46 nframe - MAX_FRAMES): void; 47 }; 48 49 match (next(frame)) { 50 case let next: stackframe => 51 frame = next; 52 case done => 53 break; 54 }; 55 }; 56 }; 57 58 fn printframe( 59 self: *image::image, 60 seen: *[]uintptr, 61 imgstat: *fs::filestat, 62 frame: stackframe, 63 ) void = { 64 const pc = frame_pc(frame); 65 66 // Try to translate the address 67 match (translate(pc: uintptr)) { 68 case let ptr: uintptr => 69 pc = ptr; 70 case => 71 void; 72 }; 73 74 const sym = match (symbol_byaddr(self, pc)) { 75 case let sym: elf::sym64 => 76 yield sym; 77 case => 78 fmt::errorfln("(unknown) [0x{:x}]", pc): void; 79 return; 80 }; 81 const name = match (symbol_name(self, &sym)) { 82 case let name: const str => 83 yield name; 84 case => 85 fmt::errorfln("(unknown) [0x{:x}]", pc): void; 86 return; 87 }; 88 89 // Look for DWARF line numbers, if possible 90 const (path, line, col) = match (dwarf::addr_to_line(self, pc)) { 91 case (void | io::error) => 92 // No line number available, print what we've got 93 fmt::errorfln("{}+0x{:x} [0x{:x}]", symname_to_ident(name), 94 pc - sym.st_value: uintptr, pc): void; 95 return; 96 case let tuple: (const str, uint, uint) => 97 yield tuple; 98 }; 99 100 const file = match (os::open(path)) { 101 case fs::error => 102 printframe_with_symbol(&sym, name, path, (line, col), pc); 103 return; 104 case let file: io::file => 105 yield file; 106 }; 107 defer io::close(file)!; 108 109 static let linebuf: [1024]u8 = [0...]; 110 const scan = bufio::newscanner_static(file, linebuf); 111 112 let context = ""; 113 for (let i = 0u; i < line; i += 1) { 114 match (bufio::scan_line(&scan)) { 115 case let s: const str => 116 context = s; 117 case io::EOF => 118 printframe_with_symbol(&sym, name, path, (line, col), pc); 119 return; 120 }; 121 }; 122 123 fmt::errorf("{}:{}:{} {}+0x{:x} [0x{:x}]", 124 path, line, col, symname_to_ident(name), 125 pc - sym.st_value: uintptr, pc): void; 126 127 // Skip context on frames we've already printed 128 for (let i = 0z; i < len(seen); i += 1) { 129 if (seen[i] == pc) { 130 fmt::errorfln(" (already shown)"): void; 131 return; 132 }; 133 }; 134 static append(seen, pc); 135 fmt::errorln(): void; 136 137 if (imgstat.mask & fs::stat_mask::MTIME != 0) { 138 match (os::fstat(file)) { 139 case let st: fs::filestat => 140 if (st.mask & fs::stat_mask::MTIME == 0) yield; 141 if (time::compare(st.mtime, imgstat.mtime) == 1) { 142 fmt::errorln("* Warning: file was modified after executable was built"): void; 143 }; 144 case => void; 145 }; 146 }; 147 148 fmt::errorfln("| {}", context): void; 149 150 if (col != 0) { 151 fmt::errorf(" "): void; 152 for (let i = 1u; i < col - 1; i += 1) { 153 fmt::errorf(" "): void; 154 }; 155 fmt::errorf("^"): void; 156 }; 157 158 fmt::errorln(): void; 159 160 }; 161 162 fn printframe_with_symbol( 163 sym: *elf::sym64, 164 name: str, 165 path: str, 166 loc: (uint, uint), 167 pc: uintptr, 168 ) void = { 169 fmt::errorfln("{}:{}:{} {}+0x{:x} [0x{:x}]", 170 path, loc.0, loc.1, symname_to_ident(name), 171 pc - sym.st_value: uintptr, pc): void; 172 fmt::errorln(): void; 173 };