authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-08 01:16:11-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:15-04:00
log21d0154139fbbbb218dc7734ba0cd44b5baf2876
treebc5a63f34cf39f93dc64033bd686879cd1c6c763
parent021f5378630f8acb5525974305a52314e227c276

dwarf: skip register tests on unimplemented arch / os, add tests for type convesions

debug: dupeContext -> copyContext

3 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 {
135135
136/// Platform-specific thread state. This contains register state, and on some platforms136/// 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 platforms137/// 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`.
139pub const ThreadContext = blk: {139pub 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}
462462
463pub fn dupeContext(source: *const ThreadContext, dest: *ThreadContext) void {463pub 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 {};
466466
...@@ -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}
475475
476pub const UnwindError = if (have_ucontext)476pub const UnwindError = if (have_ucontext)
477 @typeInfo(@typeInfo(@TypeOf(StackIterator.next_dwarf)).Fn.return_type.?).ErrorUnion.error_set477 @typeInfo(@typeInfo(@TypeOf(StackIterator.next_unwind)).Fn.return_type.?).ErrorUnion.error_set
478else478else
479 void;479 void;
480480
...@@ -619,11 +619,21 @@ pub const StackIterator = struct {...@@ -619,11 +619,21 @@ pub const StackIterator = struct {
619 }619 }
620 }620 }
621621
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 };
18861897
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 }
19411952
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;
19451964
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 }
19581972
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 };
19771992
1978 // Add the debug info to the cache1993 // 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 }
20322047
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 {
16831683
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;
16861687
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));
17921793
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);
17951796
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 any17 /// The compilation unit this expression relates to, if any
18 compile_unit: ?*const dwarf.CompileUnit = null,18 compile_unit: ?*const dwarf.CompileUnit = null,
1919
20 // .debug_addr section20 /// 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,
2225
23 /// Thread context26 /// 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 };
11081113
1109 // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it1114 // Only test register operations on arch / os that have them implemented
1115 if (abi.regBytes(&thread_context, 0, reg_context)) |_| {
11101116
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);
11151118
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);
11201123
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));
11221128
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);
11271130
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 }
11321143
1133 // Stack operations1144 // 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);
12441255
1245 // TODO: Test OP.push_object_address1256 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_address1264 // TODO: Test OP.form_tls_address
12471265
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" {
14371455
1438 }1456 }
14391457
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
14401521
1441}1522}
14421523