| author | |
| committer | |
| log | 21d0154139fbbbb218dc7734ba0cd44b5baf2876 |
| tree | bc5a63f34cf39f93dc64033bd686879cd1c6c763 |
| parent | 021f5378630f8acb5525974305a52314e227c276 |
debug: dupeContext -> copyContext3 files changed, 139 insertions(+), 42 deletions(-)
lib/std/debug.zig+34-19| ... | @@ -135,7 +135,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { | ... | @@ -135,7 +135,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 135 | 135 | ||
| 136 | /// Platform-specific thread state. This contains register state, and on some platforms | 136 | /// Platform-specific thread state. This contains register state, and on some platforms |
| 137 | /// information about the stack. This is not safe to trivially copy, because some platforms | 137 | /// information about the stack. This is not safe to trivially copy, because some platforms |
| 138 | /// use internal pointers within this structure. To make a copy, use `dupeContext`. | 138 | /// use internal pointers within this structure. To make a copy, use `copyContext`. |
| 139 | pub const ThreadContext = blk: { | 139 | pub const ThreadContext = blk: { |
| 140 | if (native_os == .windows) { | 140 | if (native_os == .windows) { |
| 141 | break :blk std.os.windows.CONTEXT; | 141 | break :blk std.os.windows.CONTEXT; |
| ... | @@ -460,7 +460,7 @@ pub inline fn getContext(context: *ThreadContext) bool { | ... | @@ -460,7 +460,7 @@ pub inline fn getContext(context: *ThreadContext) bool { |
| 460 | return result; | 460 | return result; |
| 461 | } | 461 | } |
| 462 | 462 | ||
| 463 | pub fn dupeContext(source: *const ThreadContext, dest: *ThreadContext) void { | 463 | pub fn copyContext(source: *const ThreadContext, dest: *ThreadContext) void { |
| 464 | if (native_os == .windows) dest.* = source.*; | 464 | if (native_os == .windows) dest.* = source.*; |
| 465 | if (!have_ucontext) return {}; | 465 | if (!have_ucontext) return {}; |
| 466 | 466 | ||
| ... | @@ -474,7 +474,7 @@ pub fn dupeContext(source: *const ThreadContext, dest: *ThreadContext) void { | ... | @@ -474,7 +474,7 @@ pub fn dupeContext(source: *const ThreadContext, dest: *ThreadContext) void { |
| 474 | } | 474 | } |
| 475 | 475 | ||
| 476 | pub const UnwindError = if (have_ucontext) | 476 | pub const UnwindError = if (have_ucontext) |
| 477 | @typeInfo(@typeInfo(@TypeOf(StackIterator.next_dwarf)).Fn.return_type.?).ErrorUnion.error_set | 477 | @typeInfo(@typeInfo(@TypeOf(StackIterator.next_unwind)).Fn.return_type.?).ErrorUnion.error_set |
| 478 | else | 478 | else |
| 479 | void; | 479 | void; |
| 480 | 480 | ||
| ... | @@ -619,11 +619,21 @@ pub const StackIterator = struct { | ... | @@ -619,11 +619,21 @@ pub const StackIterator = struct { |
| 619 | } | 619 | } |
| 620 | } | 620 | } |
| 621 | 621 | ||
| 622 | fn next_dwarf(self: *StackIterator) !usize { | 622 | fn next_unwind(self: *StackIterator) !usize { |
| 623 | const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc); | 623 | const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc); |
| 624 | switch (native_os) { | ||
| 625 | .macos, .ios, .watchos, .tvos => { | ||
| 626 | const o_file_info = try module.getOFileInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc); | ||
| 627 | if (o_file_info.unwind_info == null) return error.MissingUnwindInfo; | ||
| 628 | |||
| 629 | // TODO: Unwind using __unwind_info, | ||
| 630 | unreachable; | ||
| 631 | |||
| 632 | }, | ||
| 633 | else => {}, | ||
| 634 | } | ||
| 635 | |||
| 624 | if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| { | 636 | if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| { |
| 625 | self.dwarf_context.reg_context.eh_frame = true; | ||
| 626 | self.dwarf_context.reg_context.is_macho = di.is_macho; | ||
| 627 | return di.unwindFrame(&self.dwarf_context, module.base_address); | 637 | return di.unwindFrame(&self.dwarf_context, module.base_address); |
| 628 | } else return error.MissingDebugInfo; | 638 | } else return error.MissingDebugInfo; |
| 629 | } | 639 | } |
| ... | @@ -631,7 +641,7 @@ pub const StackIterator = struct { | ... | @@ -631,7 +641,7 @@ pub const StackIterator = struct { |
| 631 | fn next_internal(self: *StackIterator) ?usize { | 641 | fn next_internal(self: *StackIterator) ?usize { |
| 632 | if (have_ucontext and self.debug_info != null) { | 642 | if (have_ucontext and self.debug_info != null) { |
| 633 | if (self.dwarf_context.pc == 0) return null; | 643 | if (self.dwarf_context.pc == 0) return null; |
| 634 | if (self.next_dwarf()) |return_address| { | 644 | if (self.next_unwind()) |return_address| { |
| 635 | return return_address; | 645 | return return_address; |
| 636 | } else |err| { | 646 | } else |err| { |
| 637 | self.last_error = err; | 647 | self.last_error = err; |
| ... | @@ -1882,6 +1892,7 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1882,6 +1892,7 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1882 | const OFileInfo = struct { | 1892 | const OFileInfo = struct { |
| 1883 | di: DW.DwarfInfo, | 1893 | di: DW.DwarfInfo, |
| 1884 | addr_table: std.StringHashMap(u64), | 1894 | addr_table: std.StringHashMap(u64), |
| 1895 | unwind_info: ?[]const u8, | ||
| 1885 | }; | 1896 | }; |
| 1886 | 1897 | ||
| 1887 | fn deinit(self: *@This(), allocator: mem.Allocator) void { | 1898 | fn deinit(self: *@This(), allocator: mem.Allocator) void { |
| ... | @@ -1939,21 +1950,24 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1939,21 +1950,24 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1939 | addr_table.putAssumeCapacityNoClobber(sym_name, sym.n_value); | 1950 | addr_table.putAssumeCapacityNoClobber(sym_name, sym.n_value); |
| 1940 | } | 1951 | } |
| 1941 | 1952 | ||
| 1953 | var unwind_info: ?[]const u8 = null; | ||
| 1942 | var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array; | 1954 | var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array; |
| 1943 | for (segcmd.?.getSections()) |sect| { | 1955 | for (segcmd.?.getSections()) |sect| { |
| 1944 | if (!std.mem.eql(u8, "__DWARF", sect.segName())) continue; | 1956 | if (std.mem.eql(u8, "__TEXT", sect.segName()) and mem.eql(u8, "__unwind_info", sect.sectName())) { |
| 1957 | unwind_info = try chopSlice(mapped_mem, sect.offset, sect.size); | ||
| 1958 | } else if (std.mem.eql(u8, "__DWARF", sect.segName())) { | ||
| 1959 | var section_index: ?usize = null; | ||
| 1960 | inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| { | ||
| 1961 | if (mem.eql(u8, "__" ++ section.name, sect.sectName())) section_index = i; | ||
| 1962 | } | ||
| 1963 | if (section_index == null) continue; | ||
| 1945 | 1964 | ||
| 1946 | var section_index: ?usize = null; | 1965 | const section_bytes = try chopSlice(mapped_mem, sect.offset, sect.size); |
| 1947 | inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| { | 1966 | sections[section_index.?] = .{ |
| 1948 | if (mem.eql(u8, "__" ++ section.name, sect.sectName())) section_index = i; | 1967 | .data = section_bytes, |
| 1968 | .owned = false, | ||
| 1969 | }; | ||
| 1949 | } | 1970 | } |
| 1950 | if (section_index == null) continue; | ||
| 1951 | |||
| 1952 | const section_bytes = try chopSlice(mapped_mem, sect.offset, sect.size); | ||
| 1953 | sections[section_index.?] = .{ | ||
| 1954 | .data = section_bytes, | ||
| 1955 | .owned = false, | ||
| 1956 | }; | ||
| 1957 | } | 1971 | } |
| 1958 | 1972 | ||
| 1959 | const missing_debug_info = | 1973 | const missing_debug_info = |
| ... | @@ -1973,6 +1987,7 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1973,6 +1987,7 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1973 | var info = OFileInfo{ | 1987 | var info = OFileInfo{ |
| 1974 | .di = di, | 1988 | .di = di, |
| 1975 | .addr_table = addr_table, | 1989 | .addr_table = addr_table, |
| 1990 | .unwind_info = unwind_info, | ||
| 1976 | }; | 1991 | }; |
| 1977 | 1992 | ||
| 1978 | // Add the debug info to the cache | 1993 | // Add the debug info to the cache |
| ... | @@ -2030,7 +2045,7 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -2030,7 +2045,7 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 2030 | } | 2045 | } |
| 2031 | } | 2046 | } |
| 2032 | 2047 | ||
| 2033 | fn getOFileInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !struct { | 2048 | pub fn getOFileInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !struct { |
| 2034 | relocated_address: usize, | 2049 | relocated_address: usize, |
| 2035 | symbol: ?*const MachoSymbol = null, | 2050 | symbol: ?*const MachoSymbol = null, |
| 2036 | o_file_info: ?*OFileInfo = null, | 2051 | o_file_info: ?*OFileInfo = null, |
lib/std/dwarf.zig+2-1| ... | @@ -1683,6 +1683,7 @@ pub const DwarfInfo = struct { | ... | @@ -1683,6 +1683,7 @@ pub const DwarfInfo = struct { |
| 1683 | 1683 | ||
| 1684 | context.vm.reset(); | 1684 | context.vm.reset(); |
| 1685 | context.reg_context.eh_frame = cie.version != 4; | 1685 | context.reg_context.eh_frame = cie.version != 4; |
| 1686 | context.reg_context.is_macho = di.is_macho; | ||
| 1686 | 1687 | ||
| 1687 | _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde); | 1688 | _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde); |
| 1688 | const row = &context.vm.current_row; | 1689 | const row = &context.vm.current_row; |
| ... | @@ -1791,7 +1792,7 @@ pub const UnwindContext = struct { | ... | @@ -1791,7 +1792,7 @@ pub const UnwindContext = struct { |
| 1791 | const pc = mem.readIntSliceNative(usize, try abi.regBytes(thread_context, abi.ipRegNum(), null)); | 1792 | const pc = mem.readIntSliceNative(usize, try abi.regBytes(thread_context, abi.ipRegNum(), null)); |
| 1792 | 1793 | ||
| 1793 | const context_copy = try allocator.create(debug.ThreadContext); | 1794 | const context_copy = try allocator.create(debug.ThreadContext); |
| 1794 | debug.dupeContext(thread_context, context_copy); | 1795 | debug.copyContext(thread_context, context_copy); |
| 1795 | 1796 | ||
| 1796 | return .{ | 1797 | return .{ |
| 1797 | .allocator = allocator, | 1798 | .allocator = allocator, |
lib/std/dwarf/expressions.zig+103-22| ... | @@ -17,7 +17,10 @@ pub const ExpressionContext = struct { | ... | @@ -17,7 +17,10 @@ pub const ExpressionContext = struct { |
| 17 | /// The compilation unit this expression relates to, if any | 17 | /// The compilation unit this expression relates to, if any |
| 18 | compile_unit: ?*const dwarf.CompileUnit = null, | 18 | compile_unit: ?*const dwarf.CompileUnit = null, |
| 19 | 19 | ||
| 20 | // .debug_addr section | 20 | /// When evaluating a user-presented expression, this is the address of the object being evaluated |
| 21 | object_address: ?*const anyopaque = null, | ||
| 22 | |||
| 23 | /// .debug_addr section | ||
| 21 | debug_addr: ?[]const u8 = null, | 24 | debug_addr: ?[]const u8 = null, |
| 22 | 25 | ||
| 23 | /// Thread context | 26 | /// Thread context |
| ... | @@ -465,9 +468,11 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { | ... | @@ -465,9 +468,11 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 465 | }, | 468 | }, |
| 466 | } | 469 | } |
| 467 | }, | 470 | }, |
| 468 | OP.push_object_address, | 471 | OP.push_object_address => { |
| 469 | OP.form_tls_address, | 472 | if (context.object_address == null) return error.IncompleteExpressionContext; |
| 470 | => { | 473 | try self.stack.append(allocator, .{ .generic = @intFromPtr(context.object_address.?) }); |
| 474 | }, | ||
| 475 | OP.form_tls_address => { | ||
| 471 | return error.UnimplementedExpressionOpcode; | 476 | return error.UnimplementedExpressionOpcode; |
| 472 | }, | 477 | }, |
| 473 | OP.call_frame_cfa => { | 478 | OP.call_frame_cfa => { |
| ... | @@ -1106,28 +1111,34 @@ test "DWARF expressions" { | ... | @@ -1106,28 +1111,34 @@ test "DWARF expressions" { |
| 1106 | .reg_context = reg_context, | 1111 | .reg_context = reg_context, |
| 1107 | }; | 1112 | }; |
| 1108 | 1113 | ||
| 1109 | // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it | 1114 | // Only test register operations on arch / os that have them implemented |
| 1115 | if (abi.regBytes(&thread_context, 0, reg_context)) |_| { | ||
| 1110 | 1116 | ||
| 1111 | mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, 0, reg_context), 0xee); | 1117 | // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it |
| 1112 | mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.fpRegNum(reg_context), reg_context), 1); | ||
| 1113 | mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.spRegNum(reg_context), reg_context), 2); | ||
| 1114 | mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.ipRegNum(), reg_context), 3); | ||
| 1115 | 1118 | ||
| 1116 | try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100)); | 1119 | mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, 0, reg_context), 0xee); |
| 1117 | try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200)); | 1120 | mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.fpRegNum(reg_context), reg_context), 1); |
| 1118 | try b.writeBregx(writer, abi.ipRegNum(), @as(usize, 300)); | 1121 | mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.spRegNum(reg_context), reg_context), 2); |
| 1119 | try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400)); | 1122 | mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.ipRegNum(), reg_context), 3); |
| 1120 | 1123 | ||
| 1121 | _ = try stack_machine.run(program.items, allocator, context, 0); | 1124 | try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100)); |
| 1125 | try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200)); | ||
| 1126 | try b.writeBregx(writer, abi.ipRegNum(), @as(usize, 300)); | ||
| 1127 | try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400)); | ||
| 1122 | 1128 | ||
| 1123 | const regval_type = stack_machine.stack.popOrNull().?.regval_type; | 1129 | _ = try stack_machine.run(program.items, allocator, context, 0); |
| 1124 | try testing.expectEqual(@as(usize, 400), regval_type.type_offset); | ||
| 1125 | try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size); | ||
| 1126 | try testing.expectEqual(@as(usize, 0xee), regval_type.value); | ||
| 1127 | 1130 | ||
| 1128 | try testing.expectEqual(@as(usize, 303), stack_machine.stack.popOrNull().?.generic); | 1131 | const regval_type = stack_machine.stack.popOrNull().?.regval_type; |
| 1129 | try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic); | 1132 | try testing.expectEqual(@as(usize, 400), regval_type.type_offset); |
| 1130 | try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic); | 1133 | try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size); |
| 1134 | try testing.expectEqual(@as(usize, 0xee), regval_type.value); | ||
| 1135 | |||
| 1136 | try testing.expectEqual(@as(usize, 303), stack_machine.stack.popOrNull().?.generic); | ||
| 1137 | try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic); | ||
| 1138 | try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic); | ||
| 1139 | } else |err| { | ||
| 1140 | if (err != error.UnimplementedArch and err != error.UnimplementedOs) return err; | ||
| 1141 | } | ||
| 1131 | } | 1142 | } |
| 1132 | 1143 | ||
| 1133 | // Stack operations | 1144 | // Stack operations |
| ... | @@ -1242,7 +1253,14 @@ test "DWARF expressions" { | ... | @@ -1242,7 +1253,14 @@ test "DWARF expressions" { |
| 1242 | try testing.expectEqual(@as(u8, 1), xderef_type.type_size); | 1253 | try testing.expectEqual(@as(u8, 1), xderef_type.type_size); |
| 1243 | try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), xderef_type.value); | 1254 | try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), xderef_type.value); |
| 1244 | 1255 | ||
| 1245 | // TODO: Test OP.push_object_address | 1256 | context.object_address = &deref_target; |
| 1257 | |||
| 1258 | stack_machine.reset(); | ||
| 1259 | program.clearRetainingCapacity(); | ||
| 1260 | try b.writeOpcode(writer, OP.push_object_address); | ||
| 1261 | _ = try stack_machine.run(program.items, allocator, context, null); | ||
| 1262 | try testing.expectEqual(@as(usize, @intFromPtr(context.object_address.?)), stack_machine.stack.popOrNull().?.generic); | ||
| 1263 | |||
| 1246 | // TODO: Test OP.form_tls_address | 1264 | // TODO: Test OP.form_tls_address |
| 1247 | 1265 | ||
| 1248 | context.cfa = @truncate(0xccddccdd_ccddccdd); | 1266 | context.cfa = @truncate(0xccddccdd_ccddccdd); |
| ... | @@ -1437,6 +1455,69 @@ test "DWARF expressions" { | ... | @@ -1437,6 +1455,69 @@ test "DWARF expressions" { |
| 1437 | 1455 | ||
| 1438 | } | 1456 | } |
| 1439 | 1457 | ||
| 1458 | // Type conversions | ||
| 1459 | { | ||
| 1460 | var context = ExpressionContext{}; | ||
| 1461 | stack_machine.reset(); | ||
| 1462 | program.clearRetainingCapacity(); | ||
| 1463 | |||
| 1464 | // TODO: Test typed OP.convert once implemented | ||
| 1465 | |||
| 1466 | const value: usize = @truncate(0xffeeffee_ffeeffee); | ||
| 1467 | var value_bytes: [options.addr_size]u8 = undefined; | ||
| 1468 | mem.writeIntSliceNative(usize, &value_bytes, value); | ||
| 1469 | |||
| 1470 | // Convert to generic type | ||
| 1471 | stack_machine.reset(); | ||
| 1472 | program.clearRetainingCapacity(); | ||
| 1473 | try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes); | ||
| 1474 | try b.writeConvert(writer, @as(usize, 0)); | ||
| 1475 | _ = try stack_machine.run(program.items, allocator, context, null); | ||
| 1476 | try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic); | ||
| 1477 | |||
| 1478 | // Reinterpret to generic type | ||
| 1479 | stack_machine.reset(); | ||
| 1480 | program.clearRetainingCapacity(); | ||
| 1481 | try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes); | ||
| 1482 | try b.writeReinterpret(writer, @as(usize, 0)); | ||
| 1483 | _ = try stack_machine.run(program.items, allocator, context, null); | ||
| 1484 | try testing.expectEqual(value, stack_machine.stack.popOrNull().?.generic); | ||
| 1485 | |||
| 1486 | // Reinterpret to new type | ||
| 1487 | const die_offset: usize = 0xffee; | ||
| 1488 | |||
| 1489 | stack_machine.reset(); | ||
| 1490 | program.clearRetainingCapacity(); | ||
| 1491 | try b.writeConstType(writer, @as(usize, 0), options.addr_size, &value_bytes); | ||
| 1492 | try b.writeReinterpret(writer, die_offset); | ||
| 1493 | _ = try stack_machine.run(program.items, allocator, context, null); | ||
| 1494 | const const_type = stack_machine.stack.popOrNull().?.const_type; | ||
| 1495 | try testing.expectEqual(die_offset, const_type.type_offset); | ||
| 1496 | |||
| 1497 | stack_machine.reset(); | ||
| 1498 | program.clearRetainingCapacity(); | ||
| 1499 | try b.writeLiteral(writer, 0); | ||
| 1500 | try b.writeReinterpret(writer, die_offset); | ||
| 1501 | _ = try stack_machine.run(program.items, allocator, context, null); | ||
| 1502 | const regval_type = stack_machine.stack.popOrNull().?.regval_type; | ||
| 1503 | try testing.expectEqual(die_offset, regval_type.type_offset); | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | // Special operations | ||
| 1507 | { | ||
| 1508 | var context = ExpressionContext{}; | ||
| 1509 | stack_machine.reset(); | ||
| 1510 | program.clearRetainingCapacity(); | ||
| 1511 | |||
| 1512 | try b.writeOpcode(writer, OP.nop); | ||
| 1513 | _ = try stack_machine.run(program.items, allocator, context, null); | ||
| 1514 | try testing.expect(stack_machine.stack.popOrNull() == null); | ||
| 1515 | |||
| 1516 | |||
| 1517 | |||
| 1518 | |||
| 1519 | } | ||
| 1520 | |||
| 1440 | 1521 | ||
| 1441 | } | 1522 | } |
| 1442 | 1523 |