| ... | @@ -29,17 +29,27 @@ pub coldcc fn panic(comptime format: []const u8, args: ...) -> noreturn { | ... | @@ -29,17 +29,27 @@ pub coldcc fn panic(comptime format: []const u8, args: ...) -> noreturn { |
| 29 | } | 29 | } |
| 30 | | 30 | |
| 31 | %%io.stderr.printf(format ++ "\n", args); | 31 | %%io.stderr.printf(format ++ "\n", args); |
| 32 | %%printStackTrace(); | 32 | %%writeStackTrace(&io.stderr, &global_allocator, io.stderr.isTty(), 1); |
| | 33 | %%io.stderr.flush(); |
| 33 | | 34 | |
| 34 | os.abort(); | 35 | os.abort(); |
| 35 | } | 36 | } |
| 36 | | 37 | |
| 37 | pub fn printStackTrace() -> %void { | 38 | pub fn printStackTrace() -> %void { |
| 38 | %return writeStackTrace(&io.stderr); | 39 | %return writeStackTrace(&io.stderr, &global_allocator, io.stderr.isTty(), 1); |
| 39 | %return io.stderr.flush(); | 40 | %return io.stderr.flush(); |
| 40 | } | 41 | } |
| 41 | | 42 | |
| 42 | pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { | 43 | const GREEN = "\x1b[32;1m"; |
| | 44 | const WHITE = "\x1b[37;1m"; |
| | 45 | const DIM = "\x1b[2m"; |
| | 46 | const RESET = "\x1b[0m"; |
| | 47 | |
| | 48 | pub var user_main_fn: ?fn() -> %void = null; |
| | 49 | |
| | 50 | pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool, |
| | 51 | ignore_frame_count: usize) -> %void |
| | 52 | { |
| 43 | switch (@compileVar("object_format")) { | 53 | switch (@compileVar("object_format")) { |
| 44 | ObjectFormat.elf => { | 54 | ObjectFormat.elf => { |
| 45 | var stack_trace = ElfStackTrace { | 55 | var stack_trace = ElfStackTrace { |
| ... | @@ -48,33 +58,67 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { | ... | @@ -48,33 +58,67 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { |
| 48 | .debug_info = undefined, | 58 | .debug_info = undefined, |
| 49 | .debug_abbrev = undefined, | 59 | .debug_abbrev = undefined, |
| 50 | .debug_str = undefined, | 60 | .debug_str = undefined, |
| 51 | .abbrev_table_list = List(AbbrevTableHeader).init(&global_allocator), | 61 | .debug_line = undefined, |
| 52 | .compile_unit_list = List(CompileUnit).init(&global_allocator), | 62 | .abbrev_table_list = List(AbbrevTableHeader).init(allocator), |
| | 63 | .compile_unit_list = List(CompileUnit).init(allocator), |
| 53 | }; | 64 | }; |
| 54 | const st = &stack_trace; | 65 | const st = &stack_trace; |
| 55 | st.self_exe_stream = %return io.openSelfExe(); | 66 | st.self_exe_stream = %return io.openSelfExe(); |
| 56 | defer st.self_exe_stream.close(); | 67 | defer st.self_exe_stream.close(); |
| 57 | | 68 | |
| 58 | %return st.elf.openStream(&global_allocator, &st.self_exe_stream); | 69 | %return st.elf.openStream(allocator, &st.self_exe_stream); |
| 59 | defer st.elf.close(); | 70 | defer st.elf.close(); |
| 60 | | 71 | |
| 61 | st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; | 72 | st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; |
| 62 | st.debug_abbrev = (%return st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; | 73 | st.debug_abbrev = (%return st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; |
| 63 | st.debug_str = (%return st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; | 74 | st.debug_str = (%return st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; |
| | 75 | st.debug_line = (%return st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; |
| 64 | %return scanAllCompileUnits(st); | 76 | %return scanAllCompileUnits(st); |
| 65 | | 77 | |
| 66 | %return out_stream.printf("(...work-in-progress stack unwinding code follows...)\n"); | 78 | var ignored_count: usize = 0; |
| 67 | | 79 | |
| 68 | var maybe_fp: ?&const u8 = @frameAddress(); | 80 | var fp = usize(@frameAddress()); |
| 69 | while (true) { | 81 | while (fp != 0; fp = *@intToPtr(&const usize, fp)) { |
| 70 | const fp = maybe_fp ?? break; | 82 | if (ignored_count < ignore_frame_count) { |
| 71 | const return_address = *@intToPtr(&const usize, usize(fp) + @sizeOf(usize)); | 83 | ignored_count += 1; |
| | 84 | continue; |
| | 85 | } |
| 72 | | 86 | |
| 73 | const compile_unit = findCompileUnit(st, return_address) ?? return error.MissingDebugInfo; | 87 | const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); |
| 74 | const name = %return compile_unit.die.getAttrString(st, DW.AT_name); | | |
| 75 | | 88 | |
| 76 | %return out_stream.printf("{} -> {}\n", return_address, name); | 89 | // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal |
| 77 | maybe_fp = *@ptrCast(&const ?&const u8, fp); | 90 | // at compile time. I'll call it issue #313 |
| | 91 | const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; |
| | 92 | |
| | 93 | const compile_unit = findCompileUnit(st, return_address) ?? return error.MissingDebugInfo; |
| | 94 | const compile_unit_name = %return compile_unit.die.getAttrString(st, DW.AT_name); |
| | 95 | try (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| { |
| | 96 | defer line_info.deinit(); |
| | 97 | %return out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ |
| | 98 | DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", |
| | 99 | line_info.file_name, line_info.line, line_info.column, |
| | 100 | return_address, compile_unit_name); |
| | 101 | try (printLineFromFile(st.allocator(), out_stream, line_info)) { |
| | 102 | if (line_info.column == 0) { |
| | 103 | %return out_stream.write("\n"); |
| | 104 | } else { |
| | 105 | {var col_i: usize = 1; while (col_i < line_info.column; col_i += 1) { |
| | 106 | %return out_stream.writeByte(' '); |
| | 107 | }} |
| | 108 | %return out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); |
| | 109 | } |
| | 110 | } else |err| switch (err) { |
| | 111 | error.EndOfFile, error.PathNotFound => {}, |
| | 112 | else => return err, |
| | 113 | } |
| | 114 | } else |err| switch (err) { |
| | 115 | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| | 116 | %return out_stream.print(ptr_hex ++ " in ??? ({})\n", |
| | 117 | return_address, compile_unit_name); |
| | 118 | }, |
| | 119 | else => return err, |
| | 120 | }; |
| | 121 | %return out_stream.flush(); |
| 78 | } | 122 | } |
| 79 | }, | 123 | }, |
| 80 | ObjectFormat.coff => { | 124 | ObjectFormat.coff => { |
| ... | @@ -89,14 +133,56 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { | ... | @@ -89,14 +133,56 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { |
| 89 | } | 133 | } |
| 90 | } | 134 | } |
| 91 | | 135 | |
| | 136 | fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_info: &const LineInfo) -> %void { |
| | 137 | var f = %return io.InStream.open(line_info.file_name, allocator); |
| | 138 | defer f.close(); |
| | 139 | // TODO fstat and make sure that the file has the correct size |
| | 140 | |
| | 141 | var buf: [os.page_size]u8 = undefined; |
| | 142 | var line: usize = 1; |
| | 143 | var column: usize = 1; |
| | 144 | var abs_index: usize = 0; |
| | 145 | while (true) { |
| | 146 | const amt_read = %return f.read(buf[0...]); |
| | 147 | const slice = buf[0...amt_read]; |
| | 148 | |
| | 149 | for (slice) |byte| { |
| | 150 | if (line == line_info.line) { |
| | 151 | %return out_stream.writeByte(byte); |
| | 152 | if (byte == '\n') { |
| | 153 | return; |
| | 154 | } |
| | 155 | } |
| | 156 | if (byte == '\n') { |
| | 157 | line += 1; |
| | 158 | column = 1; |
| | 159 | } else { |
| | 160 | column += 1; |
| | 161 | } |
| | 162 | } |
| | 163 | |
| | 164 | if (amt_read < buf.len) |
| | 165 | return error.EndOfFile; |
| | 166 | } |
| | 167 | } |
| | 168 | |
| 92 | const ElfStackTrace = struct { | 169 | const ElfStackTrace = struct { |
| 93 | self_exe_stream: io.InStream, | 170 | self_exe_stream: io.InStream, |
| 94 | elf: elf.Elf, | 171 | elf: elf.Elf, |
| 95 | debug_info: &elf.SectionHeader, | 172 | debug_info: &elf.SectionHeader, |
| 96 | debug_abbrev: &elf.SectionHeader, | 173 | debug_abbrev: &elf.SectionHeader, |
| 97 | debug_str: &elf.SectionHeader, | 174 | debug_str: &elf.SectionHeader, |
| | 175 | debug_line: &elf.SectionHeader, |
| 98 | abbrev_table_list: List(AbbrevTableHeader), | 176 | abbrev_table_list: List(AbbrevTableHeader), |
| 99 | compile_unit_list: List(CompileUnit), | 177 | compile_unit_list: List(CompileUnit), |
| | 178 | |
| | 179 | pub fn allocator(self: &const ElfStackTrace) -> &mem.Allocator { |
| | 180 | return self.abbrev_table_list.allocator; |
| | 181 | } |
| | 182 | |
| | 183 | pub fn readString(self: &ElfStackTrace) -> %[]u8 { |
| | 184 | return readStringRaw(self.allocator(), &self.self_exe_stream); |
| | 185 | } |
| 100 | }; | 186 | }; |
| 101 | | 187 | |
| 102 | const CompileUnit = struct { | 188 | const CompileUnit = struct { |
| ... | @@ -104,6 +190,7 @@ const CompileUnit = struct { | ... | @@ -104,6 +190,7 @@ const CompileUnit = struct { |
| 104 | die: &Die, | 190 | die: &Die, |
| 105 | pc_start: u64, | 191 | pc_start: u64, |
| 106 | pc_end: u64, | 192 | pc_end: u64, |
| | 193 | index: usize, |
| 107 | }; | 194 | }; |
| 108 | | 195 | |
| 109 | const AbbrevTable = List(AbbrevTableEntry); | 196 | const AbbrevTable = List(AbbrevTableEntry); |
| ... | @@ -197,44 +284,142 @@ const Die = struct { | ... | @@ -197,44 +284,142 @@ const Die = struct { |
| 197 | } | 284 | } |
| 198 | }; | 285 | }; |
| 199 | | 286 | |
| 200 | fn readString(in_stream: &io.InStream) -> %[]u8 { | 287 | const FileEntry = struct { |
| 201 | var buf = List(u8).init(&global_allocator); | 288 | file_name: []const u8, |
| | 289 | dir_index: usize, |
| | 290 | mtime: usize, |
| | 291 | len_bytes: usize, |
| | 292 | }; |
| | 293 | |
| | 294 | const LineInfo = struct { |
| | 295 | line: usize, |
| | 296 | column: usize, |
| | 297 | file_name: []u8, |
| | 298 | allocator: &mem.Allocator, |
| | 299 | |
| | 300 | fn deinit(self: &const LineInfo) { |
| | 301 | self.allocator.free(self.file_name); |
| | 302 | } |
| | 303 | }; |
| | 304 | |
| | 305 | const LineNumberProgram = struct { |
| | 306 | address: usize, |
| | 307 | file: usize, |
| | 308 | line: isize, |
| | 309 | column: usize, |
| | 310 | is_stmt: bool, |
| | 311 | basic_block: bool, |
| | 312 | end_sequence: bool, |
| | 313 | |
| | 314 | target_address: usize, |
| | 315 | include_dirs: []const []const u8, |
| | 316 | file_entries: &List(FileEntry), |
| | 317 | |
| | 318 | prev_address: usize, |
| | 319 | prev_file: usize, |
| | 320 | prev_line: isize, |
| | 321 | prev_column: usize, |
| | 322 | prev_is_stmt: bool, |
| | 323 | prev_basic_block: bool, |
| | 324 | prev_end_sequence: bool, |
| | 325 | |
| | 326 | pub fn init(is_stmt: bool, include_dirs: []const []const u8, |
| | 327 | file_entries: &List(FileEntry), target_address: usize) -> LineNumberProgram |
| | 328 | { |
| | 329 | LineNumberProgram { |
| | 330 | .address = 0, |
| | 331 | .file = 1, |
| | 332 | .line = 1, |
| | 333 | .column = 0, |
| | 334 | .is_stmt = is_stmt, |
| | 335 | .basic_block = false, |
| | 336 | .end_sequence = false, |
| | 337 | .include_dirs = include_dirs, |
| | 338 | .file_entries = file_entries, |
| | 339 | .target_address = target_address, |
| | 340 | .prev_address = 0, |
| | 341 | .prev_file = undefined, |
| | 342 | .prev_line = undefined, |
| | 343 | .prev_column = undefined, |
| | 344 | .prev_is_stmt = undefined, |
| | 345 | .prev_basic_block = undefined, |
| | 346 | .prev_end_sequence = undefined, |
| | 347 | } |
| | 348 | } |
| | 349 | |
| | 350 | pub fn checkLineMatch(self: &LineNumberProgram) -> %?LineInfo { |
| | 351 | if (self.target_address >= self.prev_address and self.target_address < self.address) { |
| | 352 | const file_entry = if (self.prev_file == 0) { |
| | 353 | return error.MissingDebugInfo; |
| | 354 | } else if (self.prev_file - 1 >= self.file_entries.len) { |
| | 355 | return error.InvalidDebugInfo; |
| | 356 | } else { |
| | 357 | &self.file_entries.items[self.prev_file - 1] |
| | 358 | }; |
| | 359 | const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { |
| | 360 | return error.InvalidDebugInfo; |
| | 361 | } else { |
| | 362 | self.include_dirs[file_entry.dir_index] |
| | 363 | }; |
| | 364 | const file_name = %return os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name); |
| | 365 | %defer self.file_entries.allocator.free(file_name); |
| | 366 | return LineInfo { |
| | 367 | .line = if (self.prev_line >= 0) usize(self.prev_line) else 0, |
| | 368 | .column = self.prev_column, |
| | 369 | .file_name = file_name, |
| | 370 | .allocator = self.file_entries.allocator, |
| | 371 | }; |
| | 372 | } |
| | 373 | |
| | 374 | self.prev_address = self.address; |
| | 375 | self.prev_file = self.file; |
| | 376 | self.prev_line = self.line; |
| | 377 | self.prev_column = self.column; |
| | 378 | self.prev_is_stmt = self.is_stmt; |
| | 379 | self.prev_basic_block = self.basic_block; |
| | 380 | self.prev_end_sequence = self.end_sequence; |
| | 381 | return null; |
| | 382 | } |
| | 383 | }; |
| | 384 | |
| | 385 | fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) -> %[]u8 { |
| | 386 | var buf = List(u8).init(allocator); |
| 202 | while (true) { | 387 | while (true) { |
| 203 | const byte = %return in_stream.readByte(); | 388 | const byte = %return in_stream.readByte(); |
| 204 | if (byte == 0) | 389 | if (byte == 0) |
| 205 | break; | 390 | break; |
| 206 | %return buf.append(byte); | 391 | %return buf.append(byte); |
| 207 | } | 392 | } |
| 208 | return buf.items; | 393 | return buf.toSlice(); |
| 209 | } | 394 | } |
| 210 | | 395 | |
| 211 | fn getString(st: &ElfStackTrace, offset: u64) -> %[]u8 { | 396 | fn getString(st: &ElfStackTrace, offset: u64) -> %[]u8 { |
| 212 | const pos = st.debug_str.offset + offset; | 397 | const pos = st.debug_str.offset + offset; |
| 213 | %return st.self_exe_stream.seekTo(pos); | 398 | %return st.self_exe_stream.seekTo(pos); |
| 214 | return readString(&st.self_exe_stream); | 399 | return st.readString(); |
| 215 | } | 400 | } |
| 216 | | 401 | |
| 217 | fn readAllocBytes(in_stream: &io.InStream, size: usize) -> %[]u8 { | 402 | fn readAllocBytes(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %[]u8 { |
| 218 | const buf = %return global_allocator.alloc(u8, size); | 403 | const buf = %return global_allocator.alloc(u8, size); |
| 219 | %defer global_allocator.free(buf); | 404 | %defer global_allocator.free(buf); |
| 220 | if ((%return in_stream.read(buf)) < size) return error.Eof; | 405 | if ((%return in_stream.read(buf)) < size) return error.EndOfFile; |
| 221 | return buf; | 406 | return buf; |
| 222 | } | 407 | } |
| 223 | | 408 | |
| 224 | fn parseFormValueBlockLen(in_stream: &io.InStream, size: usize) -> %FormValue { | 409 | fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { |
| 225 | const buf = %return readAllocBytes(in_stream, size); | 410 | const buf = %return readAllocBytes(allocator, in_stream, size); |
| 226 | return FormValue.Block { buf }; | 411 | return FormValue.Block { buf }; |
| 227 | } | 412 | } |
| 228 | | 413 | |
| 229 | fn parseFormValueBlock(in_stream: &io.InStream, size: usize) -> %FormValue { | 414 | fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { |
| 230 | const block_len = %return in_stream.readVarInt(false, usize, size); | 415 | const block_len = %return in_stream.readVarInt(false, usize, size); |
| 231 | return parseFormValueBlockLen(in_stream, block_len); | 416 | return parseFormValueBlockLen(allocator, in_stream, block_len); |
| 232 | } | 417 | } |
| 233 | | 418 | |
| 234 | fn parseFormValueConstant(in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { | 419 | fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { |
| 235 | FormValue.Const { Constant { | 420 | FormValue.Const { Constant { |
| 236 | .signed = signed, | 421 | .signed = signed, |
| 237 | .payload = %return readAllocBytes(in_stream, size), | 422 | .payload = %return readAllocBytes(allocator, in_stream, size), |
| 238 | }} | 423 | }} |
| 239 | } | 424 | } |
| 240 | | 425 | |
| ... | @@ -256,38 +441,38 @@ fn parseFormValueTargetAddrSize(in_stream: &io.InStream) -> %u64 { | ... | @@ -256,38 +441,38 @@ fn parseFormValueTargetAddrSize(in_stream: &io.InStream) -> %u64 { |
| 256 | }; | 441 | }; |
| 257 | } | 442 | } |
| 258 | | 443 | |
| 259 | fn parseFormValueRefLen(in_stream: &io.InStream, size: usize) -> %FormValue { | 444 | fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { |
| 260 | const buf = %return readAllocBytes(in_stream, size); | 445 | const buf = %return readAllocBytes(allocator, in_stream, size); |
| 261 | return FormValue.Ref { buf }; | 446 | return FormValue.Ref { buf }; |
| 262 | } | 447 | } |
| 263 | | 448 | |
| 264 | fn parseFormValueRef(in_stream: &io.InStream, comptime T: type) -> %FormValue { | 449 | fn parseFormValueRef(allocator: &mem.Allocator, in_stream: &io.InStream, comptime T: type) -> %FormValue { |
| 265 | const block_len = %return in_stream.readIntLe(T); | 450 | const block_len = %return in_stream.readIntLe(T); |
| 266 | return parseFormValueRefLen(in_stream, block_len); | 451 | return parseFormValueRefLen(allocator, in_stream, block_len); |
| 267 | } | 452 | } |
| 268 | | 453 | |
| 269 | fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue { | 454 | fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue { |
| 270 | return switch (form_id) { | 455 | return switch (form_id) { |
| 271 | DW.FORM_addr => FormValue.Address { %return parseFormValueTargetAddrSize(in_stream) }, | 456 | DW.FORM_addr => FormValue.Address { %return parseFormValueTargetAddrSize(in_stream) }, |
| 272 | DW.FORM_block1 => parseFormValueBlock(in_stream, 1), | 457 | DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), |
| 273 | DW.FORM_block2 => parseFormValueBlock(in_stream, 2), | 458 | DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2), |
| 274 | DW.FORM_block4 => parseFormValueBlock(in_stream, 4), | 459 | DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4), |
| 275 | DW.FORM_block => { | 460 | DW.FORM_block => { |
| 276 | const block_len = %return readULeb128(in_stream); | 461 | const block_len = %return readULeb128(in_stream); |
| 277 | parseFormValueBlockLen(in_stream, block_len) | 462 | parseFormValueBlockLen(allocator, in_stream, block_len) |
| 278 | }, | 463 | }, |
| 279 | DW.FORM_data1 => parseFormValueConstant(in_stream, false, 1), | 464 | DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1), |
| 280 | DW.FORM_data2 => parseFormValueConstant(in_stream, false, 2), | 465 | DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2), |
| 281 | DW.FORM_data4 => parseFormValueConstant(in_stream, false, 4), | 466 | DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4), |
| 282 | DW.FORM_data8 => parseFormValueConstant(in_stream, false, 8), | 467 | DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8), |
| 283 | DW.FORM_udata, DW.FORM_sdata => { | 468 | DW.FORM_udata, DW.FORM_sdata => { |
| 284 | const block_len = %return readULeb128(in_stream); | 469 | const block_len = %return readULeb128(in_stream); |
| 285 | const signed = form_id == DW.FORM_sdata; | 470 | const signed = form_id == DW.FORM_sdata; |
| 286 | parseFormValueConstant(in_stream, signed, block_len) | 471 | parseFormValueConstant(allocator, in_stream, signed, block_len) |
| 287 | }, | 472 | }, |
| 288 | DW.FORM_exprloc => { | 473 | DW.FORM_exprloc => { |
| 289 | const size = %return readULeb128(in_stream); | 474 | const size = %return readULeb128(in_stream); |
| 290 | const buf = %return readAllocBytes(in_stream, size); | 475 | const buf = %return readAllocBytes(allocator, in_stream, size); |
| 291 | return FormValue.ExprLoc { buf }; | 476 | return FormValue.ExprLoc { buf }; |
| 292 | }, | 477 | }, |
| 293 | DW.FORM_flag => FormValue.Flag { (%return in_stream.readByte()) != 0 }, | 478 | DW.FORM_flag => FormValue.Flag { (%return in_stream.readByte()) != 0 }, |
| ... | @@ -296,30 +481,31 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa | ... | @@ -296,30 +481,31 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa |
| 296 | %return parseFormValueDwarfOffsetSize(in_stream, is_64) | 481 | %return parseFormValueDwarfOffsetSize(in_stream, is_64) |
| 297 | }, | 482 | }, |
| 298 | | 483 | |
| 299 | DW.FORM_ref1 => parseFormValueRef(in_stream, u8), | 484 | DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, u8), |
| 300 | DW.FORM_ref2 => parseFormValueRef(in_stream, u16), | 485 | DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, u16), |
| 301 | DW.FORM_ref4 => parseFormValueRef(in_stream, u32), | 486 | DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, u32), |
| 302 | DW.FORM_ref8 => parseFormValueRef(in_stream, u64), | 487 | DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, u64), |
| 303 | DW.FORM_ref_udata => { | 488 | DW.FORM_ref_udata => { |
| 304 | const ref_len = %return readULeb128(in_stream); | 489 | const ref_len = %return readULeb128(in_stream); |
| 305 | parseFormValueRefLen(in_stream, ref_len) | 490 | parseFormValueRefLen(allocator, in_stream, ref_len) |
| 306 | }, | 491 | }, |
| 307 | | 492 | |
| 308 | DW.FORM_ref_addr => FormValue.RefAddr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, | 493 | DW.FORM_ref_addr => FormValue.RefAddr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, |
| 309 | DW.FORM_ref_sig8 => FormValue.RefSig8 { %return in_stream.readIntLe(u64) }, | 494 | DW.FORM_ref_sig8 => FormValue.RefSig8 { %return in_stream.readIntLe(u64) }, |
| 310 | | 495 | |
| 311 | DW.FORM_string => FormValue.String { %return readString(in_stream) }, | 496 | DW.FORM_string => FormValue.String { %return readStringRaw(allocator, in_stream) }, |
| 312 | DW.FORM_strp => FormValue.StrPtr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, | 497 | DW.FORM_strp => FormValue.StrPtr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, |
| 313 | DW.FORM_indirect => { | 498 | DW.FORM_indirect => { |
| 314 | const child_form_id = %return readULeb128(in_stream); | 499 | const child_form_id = %return readULeb128(in_stream); |
| 315 | parseFormValue(in_stream, child_form_id, is_64) | 500 | parseFormValue(allocator, in_stream, child_form_id, is_64) |
| 316 | }, | 501 | }, |
| 317 | else => error.InvalidDebugInfo, | 502 | else => error.InvalidDebugInfo, |
| 318 | } | 503 | } |
| 319 | } | 504 | } |
| 320 | | 505 | |
| 321 | fn parseAbbrevTable(in_stream: &io.InStream) -> %AbbrevTable { | 506 | fn parseAbbrevTable(st: &ElfStackTrace) -> %AbbrevTable { |
| 322 | var result = AbbrevTable.init(&global_allocator); | 507 | const in_stream = &st.self_exe_stream; |
| | 508 | var result = AbbrevTable.init(st.allocator()); |
| 323 | while (true) { | 509 | while (true) { |
| 324 | const abbrev_code = %return readULeb128(in_stream); | 510 | const abbrev_code = %return readULeb128(in_stream); |
| 325 | if (abbrev_code == 0) | 511 | if (abbrev_code == 0) |
| ... | @@ -328,7 +514,7 @@ fn parseAbbrevTable(in_stream: &io.InStream) -> %AbbrevTable { | ... | @@ -328,7 +514,7 @@ fn parseAbbrevTable(in_stream: &io.InStream) -> %AbbrevTable { |
| 328 | .abbrev_code = abbrev_code, | 514 | .abbrev_code = abbrev_code, |
| 329 | .tag_id = %return readULeb128(in_stream), | 515 | .tag_id = %return readULeb128(in_stream), |
| 330 | .has_children = (%return in_stream.readByte()) == DW.CHILDREN_yes, | 516 | .has_children = (%return in_stream.readByte()) == DW.CHILDREN_yes, |
| 331 | .attrs = List(AbbrevAttr).init(&global_allocator), | 517 | .attrs = List(AbbrevAttr).init(st.allocator()), |
| 332 | }); | 518 | }); |
| 333 | const attrs = &result.items[result.len - 1].attrs; | 519 | const attrs = &result.items[result.len - 1].attrs; |
| 334 | | 520 | |
| ... | @@ -356,7 +542,7 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) -> %&const AbbrevTable | ... | @@ -356,7 +542,7 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) -> %&const AbbrevTable |
| 356 | %return st.self_exe_stream.seekTo(st.debug_abbrev.offset + abbrev_offset); | 542 | %return st.self_exe_stream.seekTo(st.debug_abbrev.offset + abbrev_offset); |
| 357 | %return st.abbrev_table_list.append(AbbrevTableHeader { | 543 | %return st.abbrev_table_list.append(AbbrevTableHeader { |
| 358 | .offset = abbrev_offset, | 544 | .offset = abbrev_offset, |
| 359 | .table = %return parseAbbrevTable(&st.self_exe_stream), | 545 | .table = %return parseAbbrevTable(st), |
| 360 | }); | 546 | }); |
| 361 | return &st.abbrev_table_list.items[st.abbrev_table_list.len - 1].table; | 547 | return &st.abbrev_table_list.items[st.abbrev_table_list.len - 1].table; |
| 362 | } | 548 | } |
| ... | @@ -369,28 +555,235 @@ fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) -> ?& | ... | @@ -369,28 +555,235 @@ fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) -> ?& |
| 369 | return null; | 555 | return null; |
| 370 | } | 556 | } |
| 371 | | 557 | |
| 372 | fn parseDie(in_stream: &io.InStream, abbrev_table: &const AbbrevTable, is_64: bool) -> %Die { | 558 | fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) -> %Die { |
| | 559 | const in_stream = &st.self_exe_stream; |
| 373 | const abbrev_code = %return readULeb128(in_stream); | 560 | const abbrev_code = %return readULeb128(in_stream); |
| 374 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo; | 561 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo; |
| 375 | | 562 | |
| 376 | var result = Die { | 563 | var result = Die { |
| 377 | .tag_id = table_entry.tag_id, | 564 | .tag_id = table_entry.tag_id, |
| 378 | .has_children = table_entry.has_children, | 565 | .has_children = table_entry.has_children, |
| 379 | .attrs = List(Die.Attr).init(&global_allocator), | 566 | .attrs = List(Die.Attr).init(st.allocator()), |
| 380 | }; | 567 | }; |
| 381 | %return result.attrs.resize(table_entry.attrs.len); | 568 | %return result.attrs.resize(table_entry.attrs.len); |
| 382 | for (table_entry.attrs.toSliceConst()) |attr, i| { | 569 | for (table_entry.attrs.toSliceConst()) |attr, i| { |
| 383 | result.attrs.items[i] = Die.Attr { | 570 | result.attrs.items[i] = Die.Attr { |
| 384 | .id = attr.attr_id, | 571 | .id = attr.attr_id, |
| 385 | .value = %return parseFormValue(in_stream, attr.form_id, is_64), | 572 | .value = %return parseFormValue(st.allocator(), &st.self_exe_stream, attr.form_id, is_64), |
| 386 | }; | 573 | }; |
| 387 | } | 574 | } |
| 388 | return result; | 575 | return result; |
| 389 | } | 576 | } |
| 390 | | 577 | |
| | 578 | fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) -> %LineInfo { |
| | 579 | const compile_unit_cwd = %return compile_unit.die.getAttrString(st, DW.AT_comp_dir); |
| | 580 | |
| | 581 | const in_stream = &st.self_exe_stream; |
| | 582 | const debug_line_end = st.debug_line.offset + st.debug_line.size; |
| | 583 | var this_offset = st.debug_line.offset; |
| | 584 | var this_index: usize = 0; |
| | 585 | |
| | 586 | while (this_offset < debug_line_end; this_index += 1) { |
| | 587 | %return in_stream.seekTo(this_offset); |
| | 588 | |
| | 589 | var is_64: bool = undefined; |
| | 590 | const unit_length = %return readInitialLength(in_stream, &is_64); |
| | 591 | if (unit_length == 0) |
| | 592 | return error.MissingDebugInfo; |
| | 593 | const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); |
| | 594 | |
| | 595 | if (compile_unit.index != this_index) { |
| | 596 | this_offset += next_offset; |
| | 597 | continue; |
| | 598 | } |
| | 599 | |
| | 600 | const version = %return in_stream.readInt(st.elf.is_big_endian, u16); |
| | 601 | if (version != 2) return error.InvalidDebugInfo; |
| | 602 | |
| | 603 | const prologue_length = %return in_stream.readInt(st.elf.is_big_endian, u32); |
| | 604 | const prog_start_offset = (%return in_stream.getPos()) + prologue_length; |
| | 605 | |
| | 606 | const minimum_instruction_length = %return in_stream.readByte(); |
| | 607 | if (minimum_instruction_length == 0) return error.InvalidDebugInfo; |
| | 608 | |
| | 609 | const default_is_stmt = (%return in_stream.readByte()) != 0; |
| | 610 | const line_base = %return in_stream.readByteSigned(); |
| | 611 | |
| | 612 | const line_range = %return in_stream.readByte(); |
| | 613 | if (line_range == 0) |
| | 614 | return error.InvalidDebugInfo; |
| | 615 | |
| | 616 | const opcode_base = %return in_stream.readByte(); |
| | 617 | |
| | 618 | const standard_opcode_lengths = %return st.allocator().alloc(u8, opcode_base - 1); |
| | 619 | |
| | 620 | {var i: usize = 0; while (i < opcode_base - 1; i += 1) { |
| | 621 | standard_opcode_lengths[i] = %return in_stream.readByte(); |
| | 622 | }} |
| | 623 | |
| | 624 | var include_directories = List([]u8).init(st.allocator()); |
| | 625 | %return include_directories.append(compile_unit_cwd); |
| | 626 | while (true) { |
| | 627 | const dir = %return st.readString(); |
| | 628 | if (dir.len == 0) |
| | 629 | break; |
| | 630 | %return include_directories.append(dir); |
| | 631 | } |
| | 632 | |
| | 633 | var file_entries = List(FileEntry).init(st.allocator()); |
| | 634 | var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), |
| | 635 | &file_entries, target_address); |
| | 636 | |
| | 637 | while (true) { |
| | 638 | const file_name = %return st.readString(); |
| | 639 | if (file_name.len == 0) |
| | 640 | break; |
| | 641 | const dir_index = %return readULeb128(in_stream); |
| | 642 | const mtime = %return readULeb128(in_stream); |
| | 643 | const len_bytes = %return readULeb128(in_stream); |
| | 644 | %return file_entries.append(FileEntry { |
| | 645 | .file_name = file_name, |
| | 646 | .dir_index = dir_index, |
| | 647 | .mtime = mtime, |
| | 648 | .len_bytes = len_bytes, |
| | 649 | }); |
| | 650 | } |
| | 651 | |
| | 652 | %return in_stream.seekTo(prog_start_offset); |
| | 653 | |
| | 654 | while (true) { |
| | 655 | //const pos = (%return in_stream.getPos()) - this_offset; |
| | 656 | //if (pos == 0x1a3) @breakpoint(); |
| | 657 | //%%io.stderr.printf("\n{x8}\n", pos); |
| | 658 | |
| | 659 | const opcode = %return in_stream.readByte(); |
| | 660 | |
| | 661 | var sub_op: u8 = undefined; // TODO move this to the correct scope and fix the compiler crash |
| | 662 | if (opcode == DW.LNS_extended_op) { |
| | 663 | const op_size = %return readULeb128(in_stream); |
| | 664 | if (op_size < 1) |
| | 665 | return error.InvalidDebugInfo; |
| | 666 | sub_op = %return in_stream.readByte(); |
| | 667 | switch (sub_op) { |
| | 668 | DW.LNE_end_sequence => { |
| | 669 | //%%io.stdout.printf(" [0x{x8}] End Sequence\n", pos); |
| | 670 | prog.end_sequence = true; |
| | 671 | test (%return prog.checkLineMatch()) |info| return info; |
| | 672 | return error.MissingDebugInfo; |
| | 673 | }, |
| | 674 | DW.LNE_set_address => { |
| | 675 | const addr = %return in_stream.readInt(st.elf.is_big_endian, usize); |
| | 676 | prog.address = addr; |
| | 677 | |
| | 678 | //%%io.stdout.printf(" [0x{x8}] Extended opcode {}: set Address to 0x{x}\n", |
| | 679 | // pos, sub_op, addr); |
| | 680 | }, |
| | 681 | DW.LNE_define_file => { |
| | 682 | //%%io.stdout.printf(" [0x{x8}] Define File\n", pos); |
| | 683 | |
| | 684 | const file_name = %return st.readString(); |
| | 685 | const dir_index = %return readULeb128(in_stream); |
| | 686 | const mtime = %return readULeb128(in_stream); |
| | 687 | const len_bytes = %return readULeb128(in_stream); |
| | 688 | %return file_entries.append(FileEntry { |
| | 689 | .file_name = file_name, |
| | 690 | .dir_index = dir_index, |
| | 691 | .mtime = mtime, |
| | 692 | .len_bytes = len_bytes, |
| | 693 | }); |
| | 694 | }, |
| | 695 | else => { |
| | 696 | %return in_stream.seekForward(op_size - 1); |
| | 697 | }, |
| | 698 | } |
| | 699 | } else if (opcode >= opcode_base) { |
| | 700 | // special opcodes |
| | 701 | const adjusted_opcode = opcode - opcode_base; |
| | 702 | const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); |
| | 703 | const inc_line = i32(line_base) + i32(adjusted_opcode % line_range); |
| | 704 | prog.line += inc_line; |
| | 705 | prog.address += inc_addr; |
| | 706 | //%%io.stdout.printf( |
| | 707 | // " [0x{x8}] Special opcode {}: advance Address by {} to 0x{x} and Line by {} to {}\n", |
| | 708 | // pos, adjusted_opcode, inc_addr, prog.address, inc_line, prog.line); |
| | 709 | test (%return prog.checkLineMatch()) |info| return info; |
| | 710 | prog.basic_block = false; |
| | 711 | } else { |
| | 712 | switch (opcode) { |
| | 713 | DW.LNS_copy => { |
| | 714 | //%%io.stdout.printf(" [0x{x8}] Copy\n", pos); |
| | 715 | |
| | 716 | test (%return prog.checkLineMatch()) |info| return info; |
| | 717 | prog.basic_block = false; |
| | 718 | }, |
| | 719 | DW.LNS_advance_pc => { |
| | 720 | const arg = %return readULeb128(in_stream); |
| | 721 | prog.address += arg * minimum_instruction_length; |
| | 722 | |
| | 723 | //%%io.stdout.printf(" [0x{x8}] Advance PC by {} to 0x{x}\n", pos, arg, prog.address); |
| | 724 | }, |
| | 725 | DW.LNS_advance_line => { |
| | 726 | const arg = %return readILeb128(in_stream); |
| | 727 | prog.line += arg; |
| | 728 | |
| | 729 | //%%io.stdout.printf(" [0x{x8}] Advance Line by {} to {}\n", pos, arg, prog.line); |
| | 730 | }, |
| | 731 | DW.LNS_set_file => { |
| | 732 | const arg = %return readULeb128(in_stream); |
| | 733 | prog.file = arg; |
| | 734 | |
| | 735 | //%%io.stdout.printf(" [0x{x8}] Set File Name to entry {} in the File Name Table\n", |
| | 736 | // pos, arg); |
| | 737 | }, |
| | 738 | DW.LNS_set_column => { |
| | 739 | const arg = %return readULeb128(in_stream); |
| | 740 | prog.column = arg; |
| | 741 | |
| | 742 | //%%io.stdout.printf(" [0x{x8}] Set column to {}\n", pos, arg); |
| | 743 | }, |
| | 744 | DW.LNS_negate_stmt => { |
| | 745 | prog.is_stmt = !prog.is_stmt; |
| | 746 | |
| | 747 | //%%io.stdout.printf(" [0x{x8}] Set is_stmt to {}\n", pos, if (prog.is_stmt) u8(1) else u8(0)); |
| | 748 | }, |
| | 749 | DW.LNS_set_basic_block => { |
| | 750 | prog.basic_block = true; |
| | 751 | }, |
| | 752 | DW.LNS_const_add_pc => { |
| | 753 | const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); |
| | 754 | prog.address += inc_addr; |
| | 755 | |
| | 756 | //%%io.stdout.printf(" [0x{x8}] Advance PC by constant {} to 0x{x}\n", |
| | 757 | // pos, inc_addr, prog.address); |
| | 758 | }, |
| | 759 | DW.LNS_fixed_advance_pc => { |
| | 760 | const arg = %return in_stream.readInt(st.elf.is_big_endian, u16); |
| | 761 | prog.address += arg; |
| | 762 | }, |
| | 763 | DW.LNS_set_prologue_end => { |
| | 764 | //%%io.stdout.printf(" [0x{x8}] Set prologue_end to true\n", pos); |
| | 765 | }, |
| | 766 | else => { |
| | 767 | if (opcode - 1 >= standard_opcode_lengths.len) |
| | 768 | return error.InvalidDebugInfo; |
| | 769 | //%%io.stdout.printf(" [0x{x8}] unknown op code {}\n", pos, opcode); |
| | 770 | const len_bytes = standard_opcode_lengths[opcode - 1]; |
| | 771 | %return in_stream.seekForward(len_bytes); |
| | 772 | }, |
| | 773 | } |
| | 774 | } |
| | 775 | } |
| | 776 | |
| | 777 | this_offset += next_offset; |
| | 778 | } |
| | 779 | |
| | 780 | return error.MissingDebugInfo; |
| | 781 | } |
| | 782 | |
| 391 | fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { | 783 | fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { |
| 392 | const debug_info_end = st.debug_info.offset + st.debug_info.size; | 784 | const debug_info_end = st.debug_info.offset + st.debug_info.size; |
| 393 | var this_unit_offset = st.debug_info.offset; | 785 | var this_unit_offset = st.debug_info.offset; |
| | 786 | var cu_index: usize = 0; |
| 394 | while (this_unit_offset < debug_info_end) { | 787 | while (this_unit_offset < debug_info_end) { |
| 395 | %return st.self_exe_stream.seekTo(this_unit_offset); | 788 | %return st.self_exe_stream.seekTo(this_unit_offset); |
| 396 | | 789 | |
| ... | @@ -417,8 +810,8 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { | ... | @@ -417,8 +810,8 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { |
| 417 | | 810 | |
| 418 | %return st.self_exe_stream.seekTo(compile_unit_pos); | 811 | %return st.self_exe_stream.seekTo(compile_unit_pos); |
| 419 | | 812 | |
| 420 | const compile_unit_die = (%return global_allocator.alloc(Die, 1)).ptr; | 813 | const compile_unit_die = %return st.allocator().create(Die); |
| 421 | *compile_unit_die = %return parseDie(&st.self_exe_stream, abbrev_table, is_64); | 814 | *compile_unit_die = %return parseDie(st, abbrev_table, is_64); |
| 422 | | 815 | |
| 423 | if (compile_unit_die.tag_id != DW.TAG_compile_unit) | 816 | if (compile_unit_die.tag_id != DW.TAG_compile_unit) |
| 424 | return error.InvalidDebugInfo; | 817 | return error.InvalidDebugInfo; |
| ... | @@ -439,9 +832,11 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { | ... | @@ -439,9 +832,11 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { |
| 439 | .pc_start = low_pc, | 832 | .pc_start = low_pc, |
| 440 | .pc_end = pc_end, | 833 | .pc_end = pc_end, |
| 441 | .die = compile_unit_die, | 834 | .die = compile_unit_die, |
| | 835 | .index = cu_index, |
| 442 | }); | 836 | }); |
| 443 | | 837 | |
| 444 | this_unit_offset += next_offset; | 838 | this_unit_offset += next_offset; |
| | 839 | cu_index += 1; |
| 445 | } | 840 | } |
| 446 | } | 841 | } |
| 447 | | 842 | |