authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-01-14 10:19:21-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-01-14 10:19:21-05:00
log971a6fc5317b0e2d3a1f611702a4892f4aace942
treeab81293317fe791531de5c3973fb3ee61f6e77e0
parent4551489b924fad262eb3343b68fc4c0e18ed6a97

fix duplicate stack trace code


2 files changed, 114 insertions(+), 154 deletions(-)

std/debug/index.zig+106-150
......@@ -13,6 +13,10 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
1313error MissingDebugInfo;
1414error InvalidDebugInfo;
1515error UnsupportedDebugInfo;
16error UnknownObjectFormat;
17error TodoSupportCoffDebugInfo;
18error TodoSupportMachoDebugInfo;
19error TodoSupportCOFFDebugInfo;
1620
1721
1822/// Tries to write to stderr, unbuffered, and ignores any error returned.
......@@ -40,13 +44,29 @@ fn getStderrStream() -> %&io.OutStream {
4044/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
4145pub fn dumpCurrentStackTrace() {
4246 const stderr = getStderrStream() catch return;
43 writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return;
47 const debug_info = openSelfDebugInfo(global_allocator) catch |err| {
48 stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
49 return;
50 };
51 defer debug_info.close();
52 writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), 1) catch |err| {
53 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
54 return;
55 };
4456}
4557
4658/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
4759pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) {
4860 const stderr = getStderrStream() catch return;
49 writeStackTrace(stack_trace, stderr, global_allocator, stderr_file.isTty()) catch return;
61 const debug_info = openSelfDebugInfo(global_allocator) catch |err| {
62 stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
63 return;
64 };
65 defer debug_info.close();
66 writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| {
67 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
68 return;
69 };
5070}
5171
5272/// This function invokes undefined behavior when `ok` is `false`.
......@@ -94,7 +114,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
94114
95115 const stderr = getStderrStream() catch os.abort();
96116 stderr.print(format ++ "\n", args) catch os.abort();
97 writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort();
117 dumpCurrentStackTrace();
98118
99119 os.abort();
100120}
......@@ -107,108 +127,88 @@ const RESET = "\x1b[0m";
107127error PathNotFound;
108128error InvalidDebugInfo;
109129
110pub fn writeStackTrace(st_addrs: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool) -> %void {
111 switch (builtin.object_format) {
112 builtin.ObjectFormat.elf => {
113 var stack_trace = ElfStackTrace {
114 .self_exe_file = undefined,
115 .elf = undefined,
116 .debug_info = undefined,
117 .debug_abbrev = undefined,
118 .debug_str = undefined,
119 .debug_line = undefined,
120 .debug_ranges = null,
121 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
122 .compile_unit_list = ArrayList(CompileUnit).init(allocator),
123 };
124 const st = &stack_trace;
125 st.self_exe_file = try os.openSelfExe();
126 defer st.self_exe_file.close();
130pub fn writeStackTrace(stack_trace: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
131 debug_info: &ElfStackTrace, tty_color: bool) -> %void
132{
133 var frame_index: usize = undefined;
134 var frames_left: usize = undefined;
135 if (stack_trace.index < stack_trace.instruction_addresses.len) {
136 frame_index = 0;
137 frames_left = stack_trace.index;
138 } else {
139 frame_index = (stack_trace.index + 1) % stack_trace.instruction_addresses.len;
140 frames_left = stack_trace.instruction_addresses.len;
141 }
127142
128 try st.elf.openFile(allocator, &st.self_exe_file);
129 defer st.elf.close();
143 while (frames_left != 0) : ({
144 frames_left -= 1;
145 frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
146 }) {
147 const return_address = stack_trace.instruction_addresses[frame_index];
148 try printSourceAtAddress(debug_info, out_stream, return_address);
149 }
150}
130151
131 st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
132 st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
133 st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo;
134 st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
135 st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
136 try scanAllCompileUnits(st);
152pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator,
153 debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) -> %void
154{
155 var ignored_count: usize = 0;
156
157 var fp = @ptrToInt(@frameAddress());
158 while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) {
159 if (ignored_count < ignore_frame_count) {
160 ignored_count += 1;
161 continue;
162 }
137163
138 var ignored_count: usize = 0;
164 const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize));
165 try printSourceAtAddress(debug_info, out_stream, return_address);
166 }
167}
139168
140 var frame_index: usize = undefined;
141 var frames_left: usize = undefined;
142 if (st_addrs.index < st_addrs.instruction_addresses.len) {
143 frame_index = 0;
144 frames_left = st_addrs.index;
145 } else {
146 frame_index = (st_addrs.index + 1) % st_addrs.instruction_addresses.len;
147 frames_left = st_addrs.instruction_addresses.len;
148 }
169fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void {
170 // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
171 // at compile time. I'll call it issue #313
172 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
149173
150 while (frames_left != 0) : ({frames_left -= 1; frame_index = (frame_index + 1) % st_addrs.instruction_addresses.len;}) {
151 const return_address = st_addrs.instruction_addresses[frame_index];
152
153 // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
154 // at compile time. I'll call it issue #313
155 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
156
157 const compile_unit = findCompileUnit(st, return_address) catch {
158 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
159 return_address);
160 continue;
161 };
162 const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name);
163 if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| {
164 defer line_info.deinit();
165 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
166 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
167 line_info.file_name, line_info.line, line_info.column,
168 return_address, compile_unit_name);
169 if (printLineFromFile(st.allocator(), out_stream, line_info)) {
170 if (line_info.column == 0) {
171 try out_stream.write("\n");
172 } else {
173 {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
174 try out_stream.writeByte(' ');
175 }}
176 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
177 }
178 } else |err| switch (err) {
179 error.EndOfFile, error.PathNotFound => {},
180 else => return err,
181 }
182 } else |err| switch (err) {
183 error.MissingDebugInfo, error.InvalidDebugInfo => {
184 try out_stream.print(ptr_hex ++ " in ??? ({})\n",
185 return_address, compile_unit_name);
186 },
187 else => return err,
188 }
174 const compile_unit = findCompileUnit(debug_info, address) catch {
175 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
176 address);
177 return;
178 };
179 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
180 if (getLineNumberInfo(debug_info, compile_unit, usize(address) - 1)) |line_info| {
181 defer line_info.deinit();
182 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
183 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
184 line_info.file_name, line_info.line, line_info.column,
185 address, compile_unit_name);
186 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
187 if (line_info.column == 0) {
188 try out_stream.write("\n");
189 } else {
190 {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
191 try out_stream.writeByte(' ');
192 }}
193 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
189194 }
195 } else |err| switch (err) {
196 error.EndOfFile, error.PathNotFound => {},
197 else => return err,
198 }
199 } else |err| switch (err) {
200 error.MissingDebugInfo, error.InvalidDebugInfo => {
201 try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
190202 },
191 builtin.ObjectFormat.coff => {
192 try out_stream.write("(stack trace unavailable for COFF object format)\n");
193 },
194 builtin.ObjectFormat.macho => {
195 try out_stream.write("(stack trace unavailable for Mach-O object format)\n");
196 },
197 builtin.ObjectFormat.wasm => {
198 try out_stream.write("(stack trace unavailable for WASM object format)\n");
199 },
200 builtin.ObjectFormat.unknown => {
201 try out_stream.write("(stack trace unavailable for unknown object format)\n");
202 },
203 else => return err,
203204 }
204205}
205206
206pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool,
207 ignore_frame_count: usize) -> %void
208{
207pub fn openSelfDebugInfo(allocator: &mem.Allocator) -> %&ElfStackTrace {
209208 switch (builtin.object_format) {
210209 builtin.ObjectFormat.elf => {
211 var stack_trace = ElfStackTrace {
210 const st = try allocator.create(ElfStackTrace);
211 *st = ElfStackTrace {
212212 .self_exe_file = undefined,
213213 .elf = undefined,
214214 .debug_info = undefined,
......@@ -219,12 +219,11 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
219219 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
220220 .compile_unit_list = ArrayList(CompileUnit).init(allocator),
221221 };
222 const st = &stack_trace;
223222 st.self_exe_file = try os.openSelfExe();
224 defer st.self_exe_file.close();
223 %defer st.self_exe_file.close();
225224
226225 try st.elf.openFile(allocator, &st.self_exe_file);
227 defer st.elf.close();
226 %defer st.elf.close();
228227
229228 st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
230229 st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
......@@ -232,67 +231,19 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
232231 st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
233232 st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
234233 try scanAllCompileUnits(st);
235
236 var ignored_count: usize = 0;
237
238 var fp = @ptrToInt(@frameAddress());
239 while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) {
240 if (ignored_count < ignore_frame_count) {
241 ignored_count += 1;
242 continue;
243 }
244
245 const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize));
246
247 // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
248 // at compile time. I'll call it issue #313
249 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
250
251 const compile_unit = findCompileUnit(st, return_address) catch {
252 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
253 return_address);
254 continue;
255 };
256 const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name);
257 if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| {
258 defer line_info.deinit();
259 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
260 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
261 line_info.file_name, line_info.line, line_info.column,
262 return_address, compile_unit_name);
263 if (printLineFromFile(st.allocator(), out_stream, line_info)) {
264 if (line_info.column == 0) {
265 try out_stream.write("\n");
266 } else {
267 {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
268 try out_stream.writeByte(' ');
269 }}
270 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
271 }
272 } else |err| switch (err) {
273 error.EndOfFile, error.PathNotFound => {},
274 else => return err,
275 }
276 } else |err| switch (err) {
277 error.MissingDebugInfo, error.InvalidDebugInfo => {
278 try out_stream.print(ptr_hex ++ " in ??? ({})\n",
279 return_address, compile_unit_name);
280 },
281 else => return err,
282 }
283 }
234 return st;
284235 },
285236 builtin.ObjectFormat.coff => {
286 try out_stream.write("(stack trace unavailable for COFF object format)\n");
237 return error.TodoSupportCoffDebugInfo;
287238 },
288239 builtin.ObjectFormat.macho => {
289 try out_stream.write("(stack trace unavailable for Mach-O object format)\n");
240 return error.TodoSupportMachoDebugInfo;
290241 },
291242 builtin.ObjectFormat.wasm => {
292 try out_stream.write("(stack trace unavailable for WASM object format)\n");
243 return error.TodoSupportCOFFDebugInfo;
293244 },
294245 builtin.ObjectFormat.unknown => {
295 try out_stream.write("(stack trace unavailable for unknown object format)\n");
246 return error.UnknownObjectFormat;
296247 },
297248 }
298249}
......@@ -330,7 +281,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
330281 }
331282}
332283
333const ElfStackTrace = struct {
284pub const ElfStackTrace = struct {
334285 self_exe_file: io.File,
335286 elf: elf.Elf,
336287 debug_info: &elf.SectionHeader,
......@@ -350,6 +301,11 @@ const ElfStackTrace = struct {
350301 const in_stream = &in_file_stream.stream;
351302 return readStringRaw(self.allocator(), in_stream);
352303 }
304
305 pub fn close(self: &ElfStackTrace) {
306 self.self_exe_file.close();
307 self.elf.close();
308 }
353309};
354310
355311const PcRange = struct {
std/special/test_runner.zig+8-4
......@@ -8,10 +8,14 @@ pub fn main() -> %void {
88 for (test_fn_list) |test_fn, i| {
99 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
1010
11 test_fn.func() catch |err| {
12 warn("{}\n", err);
13 return err;
14 };
11 if (builtin.is_test) {
12 test_fn.func() catch unreachable;
13 } else {
14 test_fn.func() catch |err| {
15 warn("{}\n", err);
16 return err;
17 };
18 }
1519
1620 warn("OK\n");
1721 }