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;...@@ -13,6 +13,10 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
13error MissingDebugInfo;13error MissingDebugInfo;
14error InvalidDebugInfo;14error InvalidDebugInfo;
15error UnsupportedDebugInfo;15error UnsupportedDebugInfo;
16error UnknownObjectFormat;
17error TodoSupportCoffDebugInfo;
18error TodoSupportMachoDebugInfo;
19error TodoSupportCOFFDebugInfo;
1620
1721
18/// Tries to write to stderr, unbuffered, and ignores any error returned.22/// Tries to write to stderr, unbuffered, and ignores any error returned.
...@@ -40,13 +44,29 @@ fn getStderrStream() -> %&io.OutStream {...@@ -40,13 +44,29 @@ fn getStderrStream() -> %&io.OutStream {
40/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.44/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
41pub fn dumpCurrentStackTrace() {45pub fn dumpCurrentStackTrace() {
42 const stderr = getStderrStream() catch return;46 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 };
44}56}
4557
46/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.58/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
47pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) {59pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) {
48 const stderr = getStderrStream() catch return;60 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 };
50}70}
5171
52/// This function invokes undefined behavior when `ok` is `false`.72/// This function invokes undefined behavior when `ok` is `false`.
...@@ -94,7 +114,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {...@@ -94,7 +114,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
94114
95 const stderr = getStderrStream() catch os.abort();115 const stderr = getStderrStream() catch os.abort();
96 stderr.print(format ++ "\n", args) catch os.abort();116 stderr.print(format ++ "\n", args) catch os.abort();
97 writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort();117 dumpCurrentStackTrace();
98118
99 os.abort();119 os.abort();
100}120}
...@@ -107,108 +127,88 @@ const RESET = "\x1b[0m";...@@ -107,108 +127,88 @@ const RESET = "\x1b[0m";
107error PathNotFound;127error PathNotFound;
108error InvalidDebugInfo;128error InvalidDebugInfo;
109129
110pub fn writeStackTrace(st_addrs: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool) -> %void {130pub fn writeStackTrace(stack_trace: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
111 switch (builtin.object_format) {131 debug_info: &ElfStackTrace, tty_color: bool) -> %void
112 builtin.ObjectFormat.elf => {132{
113 var stack_trace = ElfStackTrace {133 var frame_index: usize = undefined;
114 .self_exe_file = undefined,134 var frames_left: usize = undefined;
115 .elf = undefined,135 if (stack_trace.index < stack_trace.instruction_addresses.len) {
116 .debug_info = undefined,136 frame_index = 0;
117 .debug_abbrev = undefined,137 frames_left = stack_trace.index;
118 .debug_str = undefined,138 } else {
119 .debug_line = undefined,139 frame_index = (stack_trace.index + 1) % stack_trace.instruction_addresses.len;
120 .debug_ranges = null,140 frames_left = stack_trace.instruction_addresses.len;
121 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),141 }
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();
127142
128 try st.elf.openFile(allocator, &st.self_exe_file);143 while (frames_left != 0) : ({
129 defer st.elf.close();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;152pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator,
132 st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;153 debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) -> %void
133 st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo;154{
134 st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;155 var ignored_count: usize = 0;
135 st.debug_ranges = (try st.elf.findSection(".debug_ranges"));156
136 try scanAllCompileUnits(st);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;169fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void {
141 var frames_left: usize = undefined;170 // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
142 if (st_addrs.index < st_addrs.instruction_addresses.len) {171 // at compile time. I'll call it issue #313
143 frame_index = 0;172 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
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 }
149173
150 while (frames_left != 0) : ({frames_left -= 1; frame_index = (frame_index + 1) % st_addrs.instruction_addresses.len;}) {174 const compile_unit = findCompileUnit(debug_info, address) catch {
151 const return_address = st_addrs.instruction_addresses[frame_index];175 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
152176 address);
153 // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal177 return;
154 // at compile time. I'll call it issue #313178 };
155 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";179 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
156180 if (getLineNumberInfo(debug_info, compile_unit, usize(address) - 1)) |line_info| {
157 const compile_unit = findCompileUnit(st, return_address) catch {181 defer line_info.deinit();
158 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",182 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
159 return_address);183 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
160 continue;184 line_info.file_name, line_info.line, line_info.column,
161 };185 address, compile_unit_name);
162 const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name);186 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
163 if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| {187 if (line_info.column == 0) {
164 defer line_info.deinit();188 try out_stream.write("\n");
165 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++189 } else {
166 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",190 {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
167 line_info.file_name, line_info.line, line_info.column,191 try out_stream.writeByte(' ');
168 return_address, compile_unit_name);192 }}
169 if (printLineFromFile(st.allocator(), out_stream, line_info)) {193 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
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 }
189 }194 }
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);
190 },202 },
191 builtin.ObjectFormat.coff => {203 else => return err,
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 }204 }
204}205}
205206
206pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool,207pub fn openSelfDebugInfo(allocator: &mem.Allocator) -> %&ElfStackTrace {
207 ignore_frame_count: usize) -> %void
208{
209 switch (builtin.object_format) {208 switch (builtin.object_format) {
210 builtin.ObjectFormat.elf => {209 builtin.ObjectFormat.elf => {
211 var stack_trace = ElfStackTrace {210 const st = try allocator.create(ElfStackTrace);
211 *st = ElfStackTrace {
212 .self_exe_file = undefined,212 .self_exe_file = undefined,
213 .elf = undefined,213 .elf = undefined,
214 .debug_info = undefined,214 .debug_info = undefined,
...@@ -219,12 +219,11 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat...@@ -219,12 +219,11 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
219 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),219 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
220 .compile_unit_list = ArrayList(CompileUnit).init(allocator),220 .compile_unit_list = ArrayList(CompileUnit).init(allocator),
221 };221 };
222 const st = &stack_trace;
223 st.self_exe_file = try os.openSelfExe();222 st.self_exe_file = try os.openSelfExe();
224 defer st.self_exe_file.close();223 %defer st.self_exe_file.close();
225224
226 try st.elf.openFile(allocator, &st.self_exe_file);225 try st.elf.openFile(allocator, &st.self_exe_file);
227 defer st.elf.close();226 %defer st.elf.close();
228227
229 st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;228 st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
230 st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;229 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...@@ -232,67 +231,19 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
232 st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;231 st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
233 st.debug_ranges = (try st.elf.findSection(".debug_ranges"));232 st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
234 try scanAllCompileUnits(st);233 try scanAllCompileUnits(st);
235234 return st;
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 }
284 },235 },
285 builtin.ObjectFormat.coff => {236 builtin.ObjectFormat.coff => {
286 try out_stream.write("(stack trace unavailable for COFF object format)\n");237 return error.TodoSupportCoffDebugInfo;
287 },238 },
288 builtin.ObjectFormat.macho => {239 builtin.ObjectFormat.macho => {
289 try out_stream.write("(stack trace unavailable for Mach-O object format)\n");240 return error.TodoSupportMachoDebugInfo;
290 },241 },
291 builtin.ObjectFormat.wasm => {242 builtin.ObjectFormat.wasm => {
292 try out_stream.write("(stack trace unavailable for WASM object format)\n");243 return error.TodoSupportCOFFDebugInfo;
293 },244 },
294 builtin.ObjectFormat.unknown => {245 builtin.ObjectFormat.unknown => {
295 try out_stream.write("(stack trace unavailable for unknown object format)\n");246 return error.UnknownObjectFormat;
296 },247 },
297 }248 }
298}249}
...@@ -330,7 +281,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_...@@ -330,7 +281,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
330 }281 }
331}282}
332283
333const ElfStackTrace = struct {284pub const ElfStackTrace = struct {
334 self_exe_file: io.File,285 self_exe_file: io.File,
335 elf: elf.Elf,286 elf: elf.Elf,
336 debug_info: &elf.SectionHeader,287 debug_info: &elf.SectionHeader,
...@@ -350,6 +301,11 @@ const ElfStackTrace = struct {...@@ -350,6 +301,11 @@ const ElfStackTrace = struct {
350 const in_stream = &in_file_stream.stream;301 const in_stream = &in_file_stream.stream;
351 return readStringRaw(self.allocator(), in_stream);302 return readStringRaw(self.allocator(), in_stream);
352 }303 }
304
305 pub fn close(self: &ElfStackTrace) {
306 self.self_exe_file.close();
307 self.elf.close();
308 }
353};309};
354310
355const PcRange = struct {311const PcRange = struct {
std/special/test_runner.zig+8-4
...@@ -8,10 +8,14 @@ pub fn main() -> %void {...@@ -8,10 +8,14 @@ pub fn main() -> %void {
8 for (test_fn_list) |test_fn, i| {8 for (test_fn_list) |test_fn, i| {
9 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);9 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
1010
11 test_fn.func() catch |err| {11 if (builtin.is_test) {
12 warn("{}\n", err);12 test_fn.func() catch unreachable;
13 return err;13 } else {
14 };14 test_fn.func() catch |err| {
15 warn("{}\n", err);
16 return err;
17 };
18 }
1519
16 warn("OK\n");20 warn("OK\n");
17 }21 }