| ... | ... | @@ -81,10 +81,20 @@ pub fn getSelfDebugInfo() !*DebugInfo { |
| 81 | 81 | } |
| 82 | 82 | } |
| 83 | 83 | |
| 84 | | fn wantTtyColor() bool { |
| 84 | fn detectTTYConfig() TTY.Config { |
| 85 | 85 | var bytes: [128]u8 = undefined; |
| 86 | 86 | const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; |
| 87 | | return if (process.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty(); |
| 87 | if (process.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| { |
| 88 | return .escape_codes; |
| 89 | } else |_| { |
| 90 | if (stderr_file.supportsAnsiEscapeCodes()) { |
| 91 | return .escape_codes; |
| 92 | } else if (builtin.os == .windows) { |
| 93 | return .windows_api; |
| 94 | } else { |
| 95 | return .no_color; |
| 96 | } |
| 97 | } |
| 88 | 98 | } |
| 89 | 99 | |
| 90 | 100 | /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. |
| ... | ... | @@ -99,7 +109,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 99 | 109 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; |
| 100 | 110 | return; |
| 101 | 111 | }; |
| 102 | | writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| { |
| 112 | writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| { |
| 103 | 113 | stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; |
| 104 | 114 | return; |
| 105 | 115 | }; |
| ... | ... | @@ -118,16 +128,16 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { |
| 118 | 128 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; |
| 119 | 129 | return; |
| 120 | 130 | }; |
| 121 | | const tty_color = wantTtyColor(); |
| 122 | | printSourceAtAddress(debug_info, stderr, ip, tty_color) catch return; |
| 131 | const tty_config = detectTTYConfig(); |
| 132 | printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return; |
| 123 | 133 | const first_return_address = @intToPtr(*const usize, bp + @sizeOf(usize)).*; |
| 124 | | printSourceAtAddress(debug_info, stderr, first_return_address - 1, tty_color) catch return; |
| 134 | printSourceAtAddress(debug_info, stderr, first_return_address - 1, tty_config) catch return; |
| 125 | 135 | var it = StackIterator{ |
| 126 | 136 | .first_addr = null, |
| 127 | 137 | .fp = bp, |
| 128 | 138 | }; |
| 129 | 139 | while (it.next()) |return_address| { |
| 130 | | printSourceAtAddress(debug_info, stderr, return_address - 1, tty_color) catch return; |
| 140 | printSourceAtAddress(debug_info, stderr, return_address - 1, tty_config) catch return; |
| 131 | 141 | } |
| 132 | 142 | } |
| 133 | 143 | |
| ... | ... | @@ -191,7 +201,7 @@ pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { |
| 191 | 201 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; |
| 192 | 202 | return; |
| 193 | 203 | }; |
| 194 | | writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| { |
| 204 | writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig()) catch |err| { |
| 195 | 205 | stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; |
| 196 | 206 | return; |
| 197 | 207 | }; |
| ... | ... | @@ -264,7 +274,7 @@ pub fn writeStackTrace( |
| 264 | 274 | out_stream: var, |
| 265 | 275 | allocator: *mem.Allocator, |
| 266 | 276 | debug_info: *DebugInfo, |
| 267 | | tty_color: bool, |
| 277 | tty_config: TTY.Config, |
| 268 | 278 | ) !void { |
| 269 | 279 | if (builtin.strip_debug_info) return error.MissingDebugInfo; |
| 270 | 280 | var frame_index: usize = 0; |
| ... | ... | @@ -275,7 +285,7 @@ pub fn writeStackTrace( |
| 275 | 285 | frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; |
| 276 | 286 | }) { |
| 277 | 287 | const return_address = stack_trace.instruction_addresses[frame_index]; |
| 278 | | try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_color); |
| 288 | try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_config); |
| 279 | 289 | } |
| 280 | 290 | } |
| 281 | 291 | |
| ... | ... | @@ -319,20 +329,25 @@ pub const StackIterator = struct { |
| 319 | 329 | } |
| 320 | 330 | }; |
| 321 | 331 | |
| 322 | | pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { |
| 332 | pub fn writeCurrentStackTrace( |
| 333 | out_stream: var, |
| 334 | debug_info: *DebugInfo, |
| 335 | tty_config: TTY.Config, |
| 336 | start_addr: ?usize, |
| 337 | ) !void { |
| 323 | 338 | if (builtin.os == .windows) { |
| 324 | | return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr); |
| 339 | return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr); |
| 325 | 340 | } |
| 326 | 341 | var it = StackIterator.init(start_addr); |
| 327 | 342 | while (it.next()) |return_address| { |
| 328 | | try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_color); |
| 343 | try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_config); |
| 329 | 344 | } |
| 330 | 345 | } |
| 331 | 346 | |
| 332 | 347 | pub fn writeCurrentStackTraceWindows( |
| 333 | 348 | out_stream: var, |
| 334 | 349 | debug_info: *DebugInfo, |
| 335 | | tty_color: bool, |
| 350 | tty_config: TTY.Config, |
| 336 | 351 | start_addr: ?usize, |
| 337 | 352 | ) !void { |
| 338 | 353 | var addr_buf: [1024]usize = undefined; |
| ... | ... | @@ -345,23 +360,28 @@ pub fn writeCurrentStackTraceWindows( |
| 345 | 360 | return; |
| 346 | 361 | } else 0; |
| 347 | 362 | for (addrs[start_i..]) |addr| { |
| 348 | | try printSourceAtAddress(debug_info, out_stream, addr, tty_color); |
| 363 | try printSourceAtAddress(debug_info, out_stream, addr, tty_config); |
| 349 | 364 | } |
| 350 | 365 | } |
| 351 | 366 | |
| 352 | 367 | /// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented, |
| 353 | 368 | /// make this `noasync fn` and remove the individual noasync calls. |
| 354 | | pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { |
| 369 | pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { |
| 355 | 370 | if (builtin.os == .windows) { |
| 356 | | return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_color); |
| 371 | return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_config); |
| 357 | 372 | } |
| 358 | 373 | if (comptime std.Target.current.isDarwin()) { |
| 359 | | return noasync printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color); |
| 374 | return noasync printSourceAtAddressMacOs(debug_info, out_stream, address, tty_config); |
| 360 | 375 | } |
| 361 | | return noasync printSourceAtAddressPosix(debug_info, out_stream, address, tty_color); |
| 376 | return noasync printSourceAtAddressPosix(debug_info, out_stream, address, tty_config); |
| 362 | 377 | } |
| 363 | 378 | |
| 364 | | fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void { |
| 379 | fn printSourceAtAddressWindows( |
| 380 | di: *DebugInfo, |
| 381 | out_stream: var, |
| 382 | relocated_address: usize, |
| 383 | tty_config: TTY.Config, |
| 384 | ) !void { |
| 365 | 385 | const allocator = getDebugInfoAllocator(); |
| 366 | 386 | const base_address = process.getBaseAddress(); |
| 367 | 387 | const relative_address = relocated_address - base_address; |
| ... | ... | @@ -379,7 +399,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres |
| 379 | 399 | } |
| 380 | 400 | } else { |
| 381 | 401 | // we have no information to add to the address |
| 382 | | return printLineInfo(out_stream, null, relocated_address, "???", "???", tty_color, printLineFromFileAnyOs); |
| 402 | return printLineInfo(out_stream, null, relocated_address, "???", "???", tty_config, printLineFromFileAnyOs); |
| 383 | 403 | }; |
| 384 | 404 | |
| 385 | 405 | const mod = &di.modules[mod_index]; |
| ... | ... | @@ -507,84 +527,80 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres |
| 507 | 527 | relocated_address, |
| 508 | 528 | symbol_name, |
| 509 | 529 | obj_basename, |
| 510 | | tty_color, |
| 530 | tty_config, |
| 511 | 531 | printLineFromFileAnyOs, |
| 512 | 532 | ); |
| 513 | 533 | } |
| 514 | 534 | |
| 515 | | const TtyColor = enum { |
| 516 | | Red, |
| 517 | | Green, |
| 518 | | Cyan, |
| 519 | | White, |
| 520 | | Dim, |
| 521 | | Bold, |
| 522 | | Reset, |
| 523 | | }; |
| 524 | | |
| 525 | | /// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt |
| 526 | | fn setTtyColor(tty_color: TtyColor) void { |
| 527 | | if (stderr_file.supportsAnsiEscapeCodes()) { |
| 528 | | switch (tty_color) { |
| 529 | | TtyColor.Red => { |
| 530 | | stderr_file.write(RED) catch return; |
| 531 | | }, |
| 532 | | TtyColor.Green => { |
| 533 | | stderr_file.write(GREEN) catch return; |
| 534 | | }, |
| 535 | | TtyColor.Cyan => { |
| 536 | | stderr_file.write(CYAN) catch return; |
| 537 | | }, |
| 538 | | TtyColor.White, TtyColor.Bold => { |
| 539 | | stderr_file.write(WHITE) catch return; |
| 540 | | }, |
| 541 | | TtyColor.Dim => { |
| 542 | | stderr_file.write(DIM) catch return; |
| 543 | | }, |
| 544 | | TtyColor.Reset => { |
| 545 | | stderr_file.write(RESET) catch return; |
| 546 | | }, |
| 547 | | } |
| 548 | | |
| 549 | | return; |
| 550 | | } |
| 535 | pub const TTY = struct { |
| 536 | pub const Color = enum { |
| 537 | Red, |
| 538 | Green, |
| 539 | Cyan, |
| 540 | White, |
| 541 | Dim, |
| 542 | Bold, |
| 543 | Reset, |
| 544 | }; |
| 551 | 545 | |
| 552 | | if (builtin.os == .windows) { |
| 553 | | const S = struct { |
| 554 | | var attrs: windows.WORD = undefined; |
| 555 | | var init_attrs = false; |
| 556 | | }; |
| 557 | | if (!S.init_attrs) { |
| 558 | | S.init_attrs = true; |
| 559 | | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; |
| 560 | | // TODO handle error |
| 561 | | _ = windows.kernel32.GetConsoleScreenBufferInfo(stderr_file.handle, &info); |
| 562 | | S.attrs = info.wAttributes; |
| 563 | | } |
| 546 | pub const Config = enum { |
| 547 | no_color, |
| 548 | escape_codes, |
| 549 | // TODO give this a payload of file handle |
| 550 | windows_api, |
| 551 | |
| 552 | fn setColor(conf: Config, out_stream: var, color: Color) void { |
| 553 | switch (conf) { |
| 554 | .no_color => return, |
| 555 | .escape_codes => switch (color) { |
| 556 | .Red => out_stream.write(RED) catch return, |
| 557 | .Green => out_stream.write(GREEN) catch return, |
| 558 | .Cyan => out_stream.write(CYAN) catch return, |
| 559 | .White, .Bold => out_stream.write(WHITE) catch return, |
| 560 | .Dim => out_stream.write(DIM) catch return, |
| 561 | .Reset => out_stream.write(RESET) catch return, |
| 562 | }, |
| 563 | .windows_api => if (builtin.os == .windows) { |
| 564 | const S = struct { |
| 565 | var attrs: windows.WORD = undefined; |
| 566 | var init_attrs = false; |
| 567 | }; |
| 568 | if (!S.init_attrs) { |
| 569 | S.init_attrs = true; |
| 570 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; |
| 571 | // TODO handle error |
| 572 | _ = windows.kernel32.GetConsoleScreenBufferInfo(stderr_file.handle, &info); |
| 573 | S.attrs = info.wAttributes; |
| 574 | } |
| 564 | 575 | |
| 565 | | // TODO handle errors |
| 566 | | switch (tty_color) { |
| 567 | | TtyColor.Red => { |
| 568 | | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY) catch {}; |
| 569 | | }, |
| 570 | | TtyColor.Green => { |
| 571 | | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY) catch {}; |
| 572 | | }, |
| 573 | | TtyColor.Cyan => { |
| 574 | | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}; |
| 575 | | }, |
| 576 | | TtyColor.White, TtyColor.Bold => { |
| 577 | | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}; |
| 578 | | }, |
| 579 | | TtyColor.Dim => { |
| 580 | | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY) catch {}; |
| 581 | | }, |
| 582 | | TtyColor.Reset => { |
| 583 | | _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs) catch {}; |
| 584 | | }, |
| 576 | // TODO handle errors |
| 577 | switch (color) { |
| 578 | .Red => { |
| 579 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY) catch {}; |
| 580 | }, |
| 581 | .Green => { |
| 582 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY) catch {}; |
| 583 | }, |
| 584 | .Cyan => { |
| 585 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}; |
| 586 | }, |
| 587 | .White, .Bold => { |
| 588 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}; |
| 589 | }, |
| 590 | .Dim => { |
| 591 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY) catch {}; |
| 592 | }, |
| 593 | .Reset => { |
| 594 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs) catch {}; |
| 595 | }, |
| 596 | } |
| 597 | } else { |
| 598 | unreachable; |
| 599 | }, |
| 600 | } |
| 585 | 601 | } |
| 586 | | } |
| 587 | | } |
| 602 | }; |
| 603 | }; |
| 588 | 604 | |
| 589 | 605 | fn populateModule(di: *DebugInfo, mod: *Module) !void { |
| 590 | 606 | if (mod.populated) |
| ... | ... | @@ -650,12 +666,12 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach |
| 650 | 666 | return null; |
| 651 | 667 | } |
| 652 | 668 | |
| 653 | | fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { |
| 669 | fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { |
| 654 | 670 | const base_addr = process.getBaseAddress(); |
| 655 | 671 | const adjusted_addr = 0x100000000 + (address - base_addr); |
| 656 | 672 | |
| 657 | 673 | const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { |
| 658 | | return printLineInfo(out_stream, null, address, "???", "???", tty_color, printLineFromFileAnyOs); |
| 674 | return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); |
| 659 | 675 | }; |
| 660 | 676 | |
| 661 | 677 | const symbol_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + symbol.nlist.n_strx)); |
| ... | ... | @@ -676,13 +692,13 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt |
| 676 | 692 | address, |
| 677 | 693 | symbol_name, |
| 678 | 694 | compile_unit_name, |
| 679 | | tty_color, |
| 695 | tty_config, |
| 680 | 696 | printLineFromFileAnyOs, |
| 681 | 697 | ); |
| 682 | 698 | } |
| 683 | 699 | |
| 684 | | pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { |
| 685 | | return debug_info.printSourceAtAddress(out_stream, address, tty_color, printLineFromFileAnyOs); |
| 700 | pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { |
| 701 | return debug_info.printSourceAtAddress(out_stream, address, tty_config, printLineFromFileAnyOs); |
| 686 | 702 | } |
| 687 | 703 | |
| 688 | 704 | fn printLineInfo( |
| ... | ... | @@ -691,10 +707,10 @@ fn printLineInfo( |
| 691 | 707 | address: usize, |
| 692 | 708 | symbol_name: []const u8, |
| 693 | 709 | compile_unit_name: []const u8, |
| 694 | | tty_color: bool, |
| 710 | tty_config: TTY.Config, |
| 695 | 711 | comptime printLineFromFile: var, |
| 696 | 712 | ) !void { |
| 697 | | if (tty_color) setTtyColor(.White); |
| 713 | tty_config.setColor(out_stream, .White); |
| 698 | 714 | |
| 699 | 715 | if (line_info) |*li| { |
| 700 | 716 | try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column }); |
| ... | ... | @@ -702,11 +718,11 @@ fn printLineInfo( |
| 702 | 718 | try out_stream.print("???:?:?", .{}); |
| 703 | 719 | } |
| 704 | 720 | |
| 705 | | if (tty_color) setTtyColor(.Reset); |
| 721 | tty_config.setColor(out_stream, .Reset); |
| 706 | 722 | try out_stream.write(": "); |
| 707 | | if (tty_color) setTtyColor(.Dim); |
| 723 | tty_config.setColor(out_stream, .Dim); |
| 708 | 724 | try out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name }); |
| 709 | | if (tty_color) setTtyColor(.Reset); |
| 725 | tty_config.setColor(out_stream, .Reset); |
| 710 | 726 | try out_stream.write("\n"); |
| 711 | 727 | |
| 712 | 728 | // Show the matching source code line if possible |
| ... | ... | @@ -717,9 +733,9 @@ fn printLineInfo( |
| 717 | 733 | const space_needed = @intCast(usize, li.column - 1); |
| 718 | 734 | |
| 719 | 735 | try out_stream.writeByteNTimes(' ', space_needed); |
| 720 | | if (tty_color) setTtyColor(.Green); |
| 736 | tty_config.setColor(out_stream, .Green); |
| 721 | 737 | try out_stream.write("^"); |
| 722 | | if (tty_color) setTtyColor(.Reset); |
| 738 | tty_config.setColor(out_stream, .Reset); |
| 723 | 739 | } |
| 724 | 740 | try out_stream.write("\n"); |
| 725 | 741 | } else |err| switch (err) { |
| ... | ... | @@ -1167,11 +1183,11 @@ pub const DwarfInfo = struct { |
| 1167 | 1183 | self: *DwarfInfo, |
| 1168 | 1184 | out_stream: var, |
| 1169 | 1185 | address: usize, |
| 1170 | | tty_color: bool, |
| 1186 | tty_config: TTY.Config, |
| 1171 | 1187 | comptime printLineFromFile: var, |
| 1172 | 1188 | ) !void { |
| 1173 | 1189 | const compile_unit = self.findCompileUnit(address) catch { |
| 1174 | | return printLineInfo(out_stream, null, address, "???", "???", tty_color, printLineFromFile); |
| 1190 | return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFile); |
| 1175 | 1191 | }; |
| 1176 | 1192 | |
| 1177 | 1193 | const compile_unit_name = try compile_unit.die.getAttrString(self, DW.AT_name); |
| ... | ... | @@ -1188,7 +1204,7 @@ pub const DwarfInfo = struct { |
| 1188 | 1204 | address, |
| 1189 | 1205 | symbol_name, |
| 1190 | 1206 | compile_unit_name, |
| 1191 | | tty_color, |
| 1207 | tty_config, |
| 1192 | 1208 | printLineFromFile, |
| 1193 | 1209 | ); |
| 1194 | 1210 | } |