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