authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-09 14:45:34-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:15-04:00
log203d96ae97682703f39d7d7ea4e45f971a3a6043
tree8d690a707bb80e06cb8a2d1c15019aba797785e3
parent94354aa6aa16274070e49cc261778f1924432ecc

debug: add relocateContext

dwarf: fixup tests that used a ThreadContext

3 files changed, 69 insertions(+), 55 deletions(-)

lib/std/debug.zig+49-47
...@@ -133,6 +133,12 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -133,6 +133,12 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
133 }133 }
134}134}
135135
136pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and
137 (builtin.os.tag != .linux or switch (builtin.cpu.arch) {
138 .mips, .mipsel, .mips64, .mips64el, .riscv64 => false,
139 else => true,
140});
141
136/// Platform-specific thread state. This contains register state, and on some platforms142/// 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 platforms143/// 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 `copyContext`.144/// use internal pointers within this structure. To make a copy, use `copyContext`.
...@@ -146,6 +152,47 @@ pub const ThreadContext = blk: {...@@ -146,6 +152,47 @@ pub const ThreadContext = blk: {
146 }152 }
147};153};
148154
155/// Copies one context to another, updating any internal pointers
156pub fn copyContext(source: *const ThreadContext, dest: *ThreadContext) void {
157 if (!have_ucontext) return {};
158 dest.* = source.*;
159 relocateContext(dest);
160}
161
162/// Updates any internal points in the context to reflect its current location
163pub fn relocateContext(context: *ThreadContext) void {
164 return switch (native_os) {
165 .macos => {
166 context.mcontext = &context.__mcontext_data;
167 },
168 else => {},
169 };
170}
171
172pub const have_getcontext = @hasDecl(os.system, "getcontext") and
173 (builtin.os.tag != .linux or switch (builtin.cpu.arch) {
174 .x86, .x86_64 => true,
175 else => false,
176});
177
178/// Capture the current context. The register values in the context will reflect the
179/// state after the platform `getcontext` function returned.
180///
181/// It is valid to call this if the platform doesn't have context capturing support,
182/// in that case false will be returned.
183pub inline fn getContext(context: *ThreadContext) bool {
184 if (native_os == .windows) {
185 context.* = std.mem.zeroes(windows.CONTEXT);
186 windows.ntdll.RtlCaptureContext(context);
187 return true;
188 }
189
190 const result = have_getcontext and os.system.getcontext(context) == 0;
191 if (native_os == .macos) assert(context.mcsize == @sizeOf(std.c.mcontext_t));
192
193 return result;
194}
195
149/// Tries to print the stack trace starting from the supplied base pointer to stderr,196/// Tries to print the stack trace starting from the supplied base pointer to stderr,
150/// unbuffered, and ignores any error returned.197/// unbuffered, and ignores any error returned.
151/// TODO multithreaded awareness198/// TODO multithreaded awareness
...@@ -428,51 +475,6 @@ pub fn writeStackTrace(...@@ -428,51 +475,6 @@ pub fn writeStackTrace(
428 }475 }
429}476}
430477
431pub const have_getcontext = @hasDecl(os.system, "getcontext") and
432 (builtin.os.tag != .linux or switch (builtin.cpu.arch) {
433 .x86, .x86_64 => true,
434 else => false,
435});
436
437pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and
438 (builtin.os.tag != .linux or switch (builtin.cpu.arch) {
439 .mips, .mipsel, .mips64, .mips64el, .riscv64 => false,
440 else => true,
441});
442
443pub inline fn getContext(context: *ThreadContext) bool {
444 if (native_os == .windows) {
445 context.* = std.mem.zeroes(windows.CONTEXT);
446 windows.ntdll.RtlCaptureContext(context);
447 return true;
448 }
449
450 const result = have_getcontext and os.system.getcontext(context) == 0;
451 if (native_os == .macos) {
452 // TODO: Temp, to discover this size via aarch64 CI
453 if (context.mcsize != @sizeOf(std.c.mcontext_t)) {
454 print("context.mcsize does not match! {} vs {}\n", .{ context.mcsize, @sizeOf(std.c.mcontext_t) });
455 }
456
457 assert(context.mcsize == @sizeOf(std.c.mcontext_t));
458 }
459
460 return result;
461}
462
463pub fn copyContext(source: *const ThreadContext, dest: *ThreadContext) void {
464 if (native_os == .windows) dest.* = source.*;
465 if (!have_ucontext) return {};
466
467 return switch (native_os) {
468 .macos => {
469 dest.* = source.*;
470 dest.mcontext = &dest.__mcontext_data;
471 },
472 else => dest.* = source.*,
473 };
474}
475
476pub const UnwindError = if (have_ucontext)478pub const UnwindError = if (have_ucontext)
477 @typeInfo(@typeInfo(@TypeOf(StackIterator.next_unwind)).Fn.return_type.?).ErrorUnion.error_set479 @typeInfo(@typeInfo(@TypeOf(StackIterator.next_unwind)).Fn.return_type.?).ErrorUnion.error_set
478else480else
...@@ -855,7 +857,7 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz...@@ -855,7 +857,7 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz
855pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void {857pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void {
856 const module_name = debug_info.getModuleNameForAddress(address) orelse "???";858 const module_name = debug_info.getModuleNameForAddress(address) orelse "???";
857 try tty_config.setColor(out_stream, .dim);859 try tty_config.setColor(out_stream, .dim);
858 try out_stream.print("Unwind information for {s} was not available ({}), trace may be incomplete\n\n", .{ module_name, err });860 try out_stream.print("Unwind information for `{s}` was not available ({}), trace may be incomplete\n\n", .{ module_name, err });
859 try tty_config.setColor(out_stream, .reset);861 try tty_config.setColor(out_stream, .reset);
860}862}
861863
...@@ -1641,7 +1643,7 @@ pub const DebugInfo = struct {...@@ -1641,7 +1643,7 @@ pub const DebugInfo = struct {
1641 const seg_start = segment_cmd.vmaddr;1643 const seg_start = segment_cmd.vmaddr;
1642 const seg_end = seg_start + segment_cmd.vmsize;1644 const seg_end = seg_start + segment_cmd.vmsize;
1643 if (original_address >= seg_start and original_address < seg_end) {1645 if (original_address >= seg_start and original_address < seg_end) {
1644 return mem.sliceTo(std.c._dyld_get_image_name(i), 0);1646 return fs.path.basename(mem.sliceTo(std.c._dyld_get_image_name(i), 0));
1645 }1647 }
1646 },1648 },
1647 else => {},1649 else => {},
lib/std/dwarf/expressions.zig+8-5
...@@ -48,6 +48,7 @@ pub const ExpressionOptions = struct {...@@ -48,6 +48,7 @@ pub const ExpressionOptions = struct {
48 call_frame_context: bool = false,48 call_frame_context: bool = false,
49};49};
5050
51// Explcitly defined to support executing sub-expressions
51pub const ExpressionError = error{52pub const ExpressionError = error{
52 UnimplementedExpressionCall,53 UnimplementedExpressionCall,
53 UnimplementedOpcode,54 UnimplementedOpcode,
...@@ -1178,20 +1179,21 @@ test "DWARF expressions" {...@@ -1178,20 +1179,21 @@ test "DWARF expressions" {
1178 .is_macho = builtin.os.tag == .macos,1179 .is_macho = builtin.os.tag == .macos,
1179 };1180 };
1180 var thread_context: std.debug.ThreadContext = undefined;1181 var thread_context: std.debug.ThreadContext = undefined;
1182 std.debug.relocateContext(&thread_context);
1181 const context = ExpressionContext{1183 const context = ExpressionContext{
1182 .thread_context = &thread_context,1184 .thread_context = &thread_context,
1183 .reg_context = reg_context,1185 .reg_context = reg_context,
1184 };1186 };
11851187
1186 // Only test register operations on arch / os that have them implemented1188 // Only test register operations on arch / os that have them implemented
1187 if (abi.regBytes(&thread_context, 0, reg_context)) |_| {1189 if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| {
11881190
1189 // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it1191 // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
11901192
1191 mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, 0, reg_context), 0xee);1193 mem.writeIntSliceNative(usize, reg_bytes, 0xee);
1192 mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.fpRegNum(reg_context), reg_context), 1);1194 (try abi.regValueNative(usize, &thread_context, abi.fpRegNum(reg_context), reg_context)).* = 1;
1193 mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.spRegNum(reg_context), reg_context), 2);1195 (try abi.regValueNative(usize, &thread_context, abi.spRegNum(reg_context), reg_context)).* = 2;
1194 mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.ipRegNum(), reg_context), 3);1196 (try abi.regValueNative(usize, &thread_context, abi.ipRegNum(), reg_context)).* = 3;
11951197
1196 try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100));1198 try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100));
1197 try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200));1199 try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200));
...@@ -1609,6 +1611,7 @@ test "DWARF expressions" {...@@ -1609,6 +1611,7 @@ test "DWARF expressions" {
1609 .is_macho = builtin.os.tag == .macos,1611 .is_macho = builtin.os.tag == .macos,
1610 };1612 };
1611 var thread_context: std.debug.ThreadContext = undefined;1613 var thread_context: std.debug.ThreadContext = undefined;
1614 std.debug.relocateContext(&thread_context);
1612 context = ExpressionContext{1615 context = ExpressionContext{
1613 .thread_context = &thread_context,1616 .thread_context = &thread_context,
1614 .reg_context = reg_context,1617 .reg_context = reg_context,
test/standalone/dwarf_unwinding/zig_unwind.zig+12-3
...@@ -19,9 +19,19 @@ noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {...@@ -19,9 +19,19 @@ noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
19}19}
2020
21noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {21noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
22 if (builtin.os.tag == .macos) {22 // Excercise different __unwind_info / DWARF CFI encodings by forcing some registers to be restored
23 // Excercise different __unwind_info encodings by forcing some registers to be restored23 if (builtin.target.ofmt != .c) {
24 switch (builtin.cpu.arch) {24 switch (builtin.cpu.arch) {
25 .x86 => {
26 asm volatile (
27 \\movl $3, %%ebx
28 \\movl $1, %%ecx
29 \\movl $2, %%edx
30 \\movl $7, %%edi
31 \\movl $6, %%esi
32 \\movl $5, %%ebp
33 ::: "ebx", "ecx", "edx", "edi", "esi", "ebp");
34 },
25 .x86_64 => {35 .x86_64 => {
26 asm volatile (36 asm volatile (
27 \\movq $3, %%rbx37 \\movq $3, %%rbx
...@@ -32,7 +42,6 @@ noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {...@@ -32,7 +42,6 @@ noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
32 \\movq $6, %%rbp42 \\movq $6, %%rbp
33 ::: "rbx", "r12", "r13", "r14", "r15", "rbp");43 ::: "rbx", "r12", "r13", "r14", "r15", "rbp");
34 },44 },
35 .aarch64 => {},
36 else => {},45 else => {},
37 }46 }
38 }47 }