1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
use debug::dwarf;
use debug::image;
use fmt;
use format::elf;
use fs;
use io;
use os;
def MAX_FRAMES_TOP: size = 16z;
def MAX_FRAMES_BOTTOM: size = 16z;
def MAX_FRAMES: size = MAX_FRAMES_TOP + MAX_FRAMES_BOTTOM;
// Prints a backtrace to the standard error.
export fn backtrace(self: *image::image, frame: stackframe) void = {
let orig = frame;
let nframe = 1z;
for (true; nframe += 1) {
match (next(frame)) {
case let next: stackframe =>
frame = next;
case done => break;
};
};
frame = orig;
const st = match (os::fstat(self.fd)) {
case let st: fs::filestat =>
yield st;
case fs::error =>
yield fs::filestat { mask = 0, ... };
};
static let seen: [MAX_FRAMES]uintptr = [0: uintptr...];
let seen = seen[..0];
for (let i = 0z; i < nframe; i += 1) {
if (i < MAX_FRAMES_TOP || i > nframe - MAX_FRAMES_BOTTOM) {
printframe(self, &seen, &st, frame);
};
if (i == MAX_FRAMES_TOP && nframe > MAX_FRAMES) {
fmt::errorfln("\t({} additional frames omitted)",
nframe - MAX_FRAMES): void;
};
match (next(frame)) {
case let next: stackframe =>
frame = next;
case done =>
break;
};
};
};
fn printframe(
self: *image::image,
seen: *[]uintptr,
imgstat: *fs::filestat,
frame: stackframe,
) void = {
const pc = frame_pc(frame);
// Try to translate the address
match (translate(pc: uintptr)) {
case let ptr: uintptr =>
pc = ptr;
case =>
void;
};
const sym = match (symbol_byaddr(self, pc)) {
case let sym: elf::sym64 =>
yield sym;
case =>
fmt::errorfln("(unknown) [0x{:x}]", pc): void;
return;
};
const name = match (symbol_name(self, &sym)) {
case let name: const str =>
yield name;
case =>
fmt::errorfln("(unknown) [0x{:x}]", pc): void;
return;
};
// Look for DWARF line numbers, if possible
const (path, line, col) = match (dwarf::addr_to_line(self, pc)) {
case (void | io::error) =>
// No line number available, print what we've got
fmt::errorfln("{}+0x{:x} [0x{:x}]", symname_to_ident(name),
pc - sym.st_value: uintptr, pc): void;
return;
case let tuple: (const str, uint, uint) =>
yield tuple;
};
fmt::errorf("{}:{}:{} {}+0x{:x} [0x{:x}]",
path, line, col, symname_to_ident(name),
pc - sym.st_value: uintptr, pc): void;
// Skip context on frames we've already printed
for (let i = 0z; i < len(seen); i += 1) {
if (seen[i] == pc) {
fmt::errorfln(" (already shown)"): void;
return;
};
};
static append(seen, pc)!;
fmt::errorln(): void;
print_context(path, line, col, imgstat.mtime);
};
fn printframe_with_symbol(
sym: *elf::sym64,
name: str,
path: str,
loc: (uint, uint),
pc: uintptr,
) void = {
fmt::errorfln("{}:{}:{} {}+0x{:x} [0x{:x}]",
path, loc.0, loc.1, symname_to_ident(name),
pc - sym.st_value: uintptr, pc): void;
fmt::errorln(): void;
};
|