hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };