authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-24 14:55:55-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-24 14:55:55-04:00
log4e7c255e4dde7e059e84605b7f4281d59a5b5b9c
tree0e609dbdb4d7319c7d7ca1e8705c31a4b3a0959f
parent95e197667e52d0e9b772b00fb2118414eead05aa

macos stack traces have address-to-line translation


2 files changed, 261 insertions(+), 8 deletions(-)

std/debug/index.zig+255-8
...@@ -271,10 +271,11 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt...@@ -271,10 +271,11 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt
271 const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx);271 const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx);
272 break :blk os.path.basename(ofile_path);272 break :blk os.path.basename(ofile_path);
273 } else "???";273 } else "???";
274 if (getLineNumberInfoMacOs(di, symbol.*, address)) |line_info| {274 if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| {
275 defer line_info.deinit();
275 try printLineInfo(di, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color);276 try printLineInfo(di, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color);
276 } else |err| switch (err) {277 } else |err| switch (err) {
277 error.MissingDebugInfo => {278 error.MissingDebugInfo, error.InvalidDebugInfo => {
278 if (tty_color) {279 if (tty_color) {
279 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name);280 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name);
280 } else {281 } else {
...@@ -427,12 +428,16 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {...@@ -427,12 +428,16 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
427 const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);428 const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);
428429
429 var ofile: ?*std.c.nlist_64 = null;430 var ofile: ?*std.c.nlist_64 = null;
431 var reloc: u64 = 0;
430 var symbol_index: usize = 0;432 var symbol_index: usize = 0;
431 var last_len: u64 = 0;433 var last_len: u64 = 0;
432 for (syms) |*sym| {434 for (syms) |*sym| {
433 if (sym.n_type & std.c.N_STAB != 0) {435 if (sym.n_type & std.c.N_STAB != 0) {
434 switch (sym.n_type) {436 switch (sym.n_type) {
435 std.c.N_OSO => ofile = sym,437 std.c.N_OSO => {
438 ofile = sym;
439 reloc = 0;
440 },
436 std.c.N_FUN => {441 std.c.N_FUN => {
437 if (sym.n_sect == 0) {442 if (sym.n_sect == 0) {
438 last_len = sym.n_value;443 last_len = sym.n_value;
...@@ -440,10 +445,16 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {...@@ -440,10 +445,16 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
440 symbols_buf[symbol_index] = MachoSymbol{445 symbols_buf[symbol_index] = MachoSymbol{
441 .nlist = sym,446 .nlist = sym,
442 .ofile = ofile,447 .ofile = ofile,
448 .reloc = reloc,
443 };449 };
444 symbol_index += 1;450 symbol_index += 1;
445 }451 }
446 },452 },
453 std.c.N_BNSYM => {
454 if (reloc == 0) {
455 reloc = sym.n_value;
456 }
457 },
447 else => continue,458 else => continue,
448 }459 }
449 }460 }
...@@ -506,6 +517,7 @@ fn printLineFromFile(out_stream: var, line_info: *const LineInfo) !void {...@@ -506,6 +517,7 @@ fn printLineFromFile(out_stream: var, line_info: *const LineInfo) !void {
506const MachoSymbol = struct {517const MachoSymbol = struct {
507 nlist: *std.c.nlist_64,518 nlist: *std.c.nlist_64,
508 ofile: ?*std.c.nlist_64,519 ofile: ?*std.c.nlist_64,
520 reloc: u64,
509521
510 /// Returns the address from the macho file522 /// Returns the address from the macho file
511 fn address(self: MachoSymbol) u64 {523 fn address(self: MachoSymbol) u64 {
...@@ -535,6 +547,10 @@ pub const DebugInfo = switch (builtin.os) {...@@ -535,6 +547,10 @@ pub const DebugInfo = switch (builtin.os) {
535 std.hash_map.getHashPtrAddrFn(*std.c.nlist_64),547 std.hash_map.getHashPtrAddrFn(*std.c.nlist_64),
536 std.hash_map.getTrivialEqlFn(*std.c.nlist_64),548 std.hash_map.getTrivialEqlFn(*std.c.nlist_64),
537 );549 );
550
551 pub fn allocator(self: DebugInfo) *mem.Allocator {
552 return self.ofiles.allocator;
553 }
538 },554 },
539 else => struct {555 else => struct {
540 self_exe_file: os.File,556 self_exe_file: os.File,
...@@ -965,7 +981,6 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u...@@ -965,7 +981,6 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
965 const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: {981 const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: {
966 errdefer _ = di.ofiles.remove(ofile);982 errdefer _ = di.ofiles.remove(ofile);
967 const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx);983 const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx);
968 std.debug.warn("reading .o file: {}\n", ofile_path);
969984
970 gop.kv.value = MachOFile{985 gop.kv.value = MachOFile{
971 .bytes = try std.io.readFileAllocAligned(di.ofiles.allocator, ofile_path, @alignOf(std.c.mach_header_64)),986 .bytes = try std.io.readFileAllocAligned(di.ofiles.allocator, ofile_path, @alignOf(std.c.mach_header_64)),
...@@ -973,7 +988,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u...@@ -973,7 +988,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
973 .sect_debug_line = null,988 .sect_debug_line = null,
974 };989 };
975 const hdr = @ptrCast(*const std.c.mach_header_64, gop.kv.value.bytes.ptr);990 const hdr = @ptrCast(*const std.c.mach_header_64, gop.kv.value.bytes.ptr);
976 assert(hdr.magic == std.c.MH_MAGIC_64);991 if (hdr.magic != std.c.MH_MAGIC_64) return error.InvalidDebugInfo;
977992
978 const hdr_base = @ptrCast([*]const u8, hdr);993 const hdr_base = @ptrCast([*]const u8, hdr);
979 var ptr = hdr_base + @sizeOf(std.c.mach_header_64);994 var ptr = hdr_base + @sizeOf(std.c.mach_header_64);
...@@ -998,13 +1013,163 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u...@@ -998,13 +1013,163 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
998 } else if (mem.eql(u8, sect_name, "__debug_info")) {1013 } else if (mem.eql(u8, sect_name, "__debug_info")) {
999 gop.kv.value.sect_debug_info = sect;1014 gop.kv.value.sect_debug_info = sect;
1000 }1015 }
1001 std.debug.warn("sect: {}\n", sect_name);
1002 }1016 }
1003 }1017 }
10041018
1005 break :blk &gop.kv.value;1019 break :blk &gop.kv.value;
1006 };1020 };
10071021
1022 const sect_debug_line = mach_o_file.sect_debug_line orelse return error.MissingDebugInfo;
1023 var ptr = mach_o_file.bytes.ptr + sect_debug_line.offset;
1024
1025 var is_64: bool = undefined;
1026 const unit_length = try readInitialLengthMem(&ptr, &is_64);
1027 if (unit_length == 0) return error.MissingDebugInfo;
1028
1029 const version = readIntMem(&ptr, u16, builtin.Endian.Little);
1030 // TODO support 3 and 5
1031 if (version != 2 and version != 4) return error.InvalidDebugInfo;
1032
1033 const prologue_length = if (is_64)
1034 readIntMem(&ptr, u64, builtin.Endian.Little)
1035 else
1036 readIntMem(&ptr, u32, builtin.Endian.Little);
1037 const prog_start = ptr + prologue_length;
1038
1039 const minimum_instruction_length = readByteMem(&ptr);
1040 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
1041
1042 if (version >= 4) {
1043 // maximum_operations_per_instruction
1044 ptr += 1;
1045 }
1046
1047 const default_is_stmt = readByteMem(&ptr) != 0;
1048 const line_base = readByteSignedMem(&ptr);
1049
1050 const line_range = readByteMem(&ptr);
1051 if (line_range == 0) return error.InvalidDebugInfo;
1052
1053 const opcode_base = readByteMem(&ptr);
1054
1055 const standard_opcode_lengths = ptr[0..opcode_base - 1];
1056 ptr += opcode_base - 1;
1057
1058 var include_directories = ArrayList([]const u8).init(di.allocator());
1059 //try include_directories.append(compile_unit_cwd);
1060 try include_directories.append("./");
1061 while (true) {
1062 const dir = readStringMem(&ptr);
1063 if (dir.len == 0) break;
1064 try include_directories.append(dir);
1065 }
1066
1067 var file_entries = ArrayList(FileEntry).init(di.allocator());
1068 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
1069
1070 while (true) {
1071 const file_name = readStringMem(&ptr);
1072 if (file_name.len == 0) break;
1073 const dir_index = try readULeb128Mem(&ptr);
1074 const mtime = try readULeb128Mem(&ptr);
1075 const len_bytes = try readULeb128Mem(&ptr);
1076 try file_entries.append(FileEntry{
1077 .file_name = file_name,
1078 .dir_index = dir_index,
1079 .mtime = mtime,
1080 .len_bytes = len_bytes,
1081 });
1082 }
1083
1084 ptr = prog_start;
1085 while (true) {
1086 const opcode = readByteMem(&ptr);
1087
1088 if (opcode == DW.LNS_extended_op) {
1089 const op_size = try readULeb128Mem(&ptr);
1090 if (op_size < 1) return error.InvalidDebugInfo;
1091 var sub_op = readByteMem(&ptr);
1092 switch (sub_op) {
1093 DW.LNE_end_sequence => {
1094 prog.end_sequence = true;
1095 if (try prog.checkLineMatch()) |info| return info;
1096 return error.MissingDebugInfo;
1097 },
1098 DW.LNE_set_address => {
1099 const addr = readIntMem(&ptr, usize, builtin.Endian.Little);
1100 prog.address = symbol.reloc + addr;
1101 },
1102 DW.LNE_define_file => {
1103 const file_name = readStringMem(&ptr);
1104 const dir_index = try readULeb128Mem(&ptr);
1105 const mtime = try readULeb128Mem(&ptr);
1106 const len_bytes = try readULeb128Mem(&ptr);
1107 try file_entries.append(FileEntry{
1108 .file_name = file_name,
1109 .dir_index = dir_index,
1110 .mtime = mtime,
1111 .len_bytes = len_bytes,
1112 });
1113 },
1114 else => {
1115 ptr += op_size - 1;
1116 },
1117 }
1118 } else if (opcode >= opcode_base) {
1119 // special opcodes
1120 const adjusted_opcode = opcode - opcode_base;
1121 const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
1122 const inc_line = i32(line_base) + i32(adjusted_opcode % line_range);
1123 prog.line += inc_line;
1124 prog.address += inc_addr;
1125 if (try prog.checkLineMatch()) |info| return info;
1126 prog.basic_block = false;
1127 } else {
1128 switch (opcode) {
1129 DW.LNS_copy => {
1130 if (try prog.checkLineMatch()) |info| return info;
1131 prog.basic_block = false;
1132 },
1133 DW.LNS_advance_pc => {
1134 const arg = try readULeb128Mem(&ptr);
1135 prog.address += arg * minimum_instruction_length;
1136 },
1137 DW.LNS_advance_line => {
1138 const arg = try readILeb128Mem(&ptr);
1139 prog.line += arg;
1140 },
1141 DW.LNS_set_file => {
1142 const arg = try readULeb128Mem(&ptr);
1143 prog.file = arg;
1144 },
1145 DW.LNS_set_column => {
1146 const arg = try readULeb128Mem(&ptr);
1147 prog.column = arg;
1148 },
1149 DW.LNS_negate_stmt => {
1150 prog.is_stmt = !prog.is_stmt;
1151 },
1152 DW.LNS_set_basic_block => {
1153 prog.basic_block = true;
1154 },
1155 DW.LNS_const_add_pc => {
1156 const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
1157 prog.address += inc_addr;
1158 },
1159 DW.LNS_fixed_advance_pc => {
1160 const arg = readIntMem(&ptr, u16, builtin.Endian.Little);
1161 prog.address += arg;
1162 },
1163 DW.LNS_set_prologue_end => {},
1164 else => {
1165 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
1166 const len_bytes = standard_opcode_lengths[opcode - 1];
1167 ptr += len_bytes;
1168 },
1169 }
1170 }
1171 }
1172
1008 return error.MissingDebugInfo;1173 return error.MissingDebugInfo;
1009}1174}
10101175
...@@ -1094,11 +1259,10 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ...@@ -1094,11 +1259,10 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ
1094 while (true) {1259 while (true) {
1095 const opcode = try in_stream.readByte();1260 const opcode = try in_stream.readByte();
10961261
1097 var sub_op: u8 = undefined; // TODO move this to the correct scope and fix the compiler crash
1098 if (opcode == DW.LNS_extended_op) {1262 if (opcode == DW.LNS_extended_op) {
1099 const op_size = try readULeb128(in_stream);1263 const op_size = try readULeb128(in_stream);
1100 if (op_size < 1) return error.InvalidDebugInfo;1264 if (op_size < 1) return error.InvalidDebugInfo;
1101 sub_op = try in_stream.readByte();1265 var sub_op = try in_stream.readByte();
1102 switch (sub_op) {1266 switch (sub_op) {
1103 DW.LNE_end_sequence => {1267 DW.LNE_end_sequence => {
1104 prog.end_sequence = true;1268 prog.end_sequence = true;
...@@ -1291,6 +1455,89 @@ fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit {...@@ -1291,6 +1455,89 @@ fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit {
1291 return error.MissingDebugInfo;1455 return error.MissingDebugInfo;
1292}1456}
12931457
1458fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T {
1459 const result = mem.readInt(ptr.*[0..@sizeOf(T)], T, endian);
1460 ptr.* += @sizeOf(T);
1461 return result;
1462}
1463
1464fn readByteMem(ptr: *[*]const u8) u8 {
1465 const result = ptr.*[0];
1466 ptr.* += 1;
1467 return result;
1468}
1469
1470fn readByteSignedMem(ptr: *[*]const u8) i8 {
1471 return @bitCast(i8, readByteMem(ptr));
1472}
1473
1474fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 {
1475 const first_32_bits = mem.readIntLE(u32, ptr.*[0..4]);
1476 is_64.* = (first_32_bits == 0xffffffff);
1477 if (is_64.*) {
1478 ptr.* += 4;
1479 const result = mem.readIntLE(u64, ptr.*[0..8]);
1480 ptr.* += 8;
1481 return result;
1482 } else {
1483 if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
1484 ptr.* += 4;
1485 return u64(first_32_bits);
1486 }
1487}
1488
1489fn readStringMem(ptr: *[*]const u8) []const u8 {
1490 const result = mem.toSliceConst(u8, ptr.*);
1491 ptr.* += result.len + 1;
1492 return result;
1493}
1494
1495fn readULeb128Mem(ptr: *[*]const u8) !u64 {
1496 var result: u64 = 0;
1497 var shift: usize = 0;
1498 var i: usize = 0;
1499
1500 while (true) {
1501 const byte = ptr.*[i];
1502 i += 1;
1503
1504 var operand: u64 = undefined;
1505
1506 if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
1507
1508 result |= operand;
1509
1510 if ((byte & 0b10000000) == 0) {
1511 ptr.* += i;
1512 return result;
1513 }
1514
1515 shift += 7;
1516 }
1517}
1518fn readILeb128Mem(ptr: *[*]const u8) !i64 {
1519 var result: i64 = 0;
1520 var shift: usize = 0;
1521 var i: usize = 0;
1522
1523 while (true) {
1524 const byte = ptr.*[i];
1525 i += 1;
1526
1527 var operand: i64 = undefined;
1528 if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
1529
1530 result |= operand;
1531 shift += 7;
1532
1533 if ((byte & 0b10000000) == 0) {
1534 if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift));
1535 ptr.* += i;
1536 return result;
1537 }
1538 }
1539}
1540
1294fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {1541fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
1295 const first_32_bits = try in_stream.readIntLe(u32);1542 const first_32_bits = try in_stream.readIntLe(u32);
1296 is_64.* = (first_32_bits == 0xffffffff);1543 is_64.* = (first_32_bits == 0xffffffff);
std/io.zig+6
...@@ -207,6 +207,12 @@ pub fn InStream(comptime ReadError: type) type {...@@ -207,6 +207,12 @@ pub fn InStream(comptime ReadError: type) type {
207 _ = try self.readByte();207 _ = try self.readByte();
208 }208 }
209 }209 }
210
211 pub fn readStruct(self: *Self, comptime T: type, ptr: *T) !void {
212 // Only extern and packed structs have defined in-memory layout.
213 assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
214 return self.readNoEof(@sliceToBytes((*[1]T)(ptr)[0..]));
215 }
210 };216 };
211}217}
212218