authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-08 16:41:24-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:08-08:00
log314c906dba32e72317947a15254519b22745b13f
tree7472446d60c250ecaddbbbde31c0d80e478dfd60
parent9ccd68de0b79c3723bd11071fd836bc24ff25b33

std.debug: simplify printLineFromFile


9 files changed, 46 insertions(+), 79 deletions(-)

lib/compiler/build_runner.zig+1-1
...@@ -824,7 +824,7 @@ fn runStepNames(...@@ -824,7 +824,7 @@ fn runStepNames(
824 }824 }
825 if (@bitSizeOf(usize) != 64) {825 if (@bitSizeOf(usize) != 64) {
826 // Current implementation depends on posix.mmap()'s second parameter, `length: usize`,826 // Current implementation depends on posix.mmap()'s second parameter, `length: usize`,
827 // being compatible with `std.fs.getEndPos() u64`'s return value. This is not the case827 // being compatible with file system's u64 return value. This is not the case
828 // on 32-bit platforms.828 // on 32-bit platforms.
829 // Affects or affected by issues #5185, #22523, and #22464.829 // Affects or affected by issues #5185, #22523, and #22464.
830 fatal("--fuzz not yet implemented on {d}-bit platforms", .{@bitSizeOf(usize)});830 fatal("--fuzz not yet implemented on {d}-bit platforms", .{@bitSizeOf(usize)});
lib/std/Build/Fuzz.zig+1-1
...@@ -413,7 +413,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -413,7 +413,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
413 };413 };
414 defer coverage_file.close(io);414 defer coverage_file.close(io);
415415
416 const file_size = coverage_file.getEndPos() catch |err| {416 const file_size = coverage_file.length(io) catch |err| {
417 log.err("unable to check len of coverage file '{f}': {t}", .{ coverage_file_path, err });417 log.err("unable to check len of coverage file '{f}': {t}", .{ coverage_file_path, err });
418 return error.AlreadyReported;418 return error.AlreadyReported;
419 };419 };
lib/std/Io/test.zig+6-6
...@@ -47,7 +47,7 @@ test "write a file, read it, then delete it" {...@@ -47,7 +47,7 @@ test "write a file, read it, then delete it" {
47 var file = try tmp.dir.openFile(io, tmp_file_name, .{});47 var file = try tmp.dir.openFile(io, tmp_file_name, .{});
48 defer file.close(io);48 defer file.close(io);
4949
50 const file_size = try file.getEndPos();50 const file_size = try file.length(io);
51 const expected_file_size: u64 = "begin".len + data.len + "end".len;51 const expected_file_size: u64 = "begin".len + data.len + "end".len;
52 try expectEqual(expected_file_size, file_size);52 try expectEqual(expected_file_size, file_size);
5353
...@@ -77,7 +77,7 @@ test "File seek ops" {...@@ -77,7 +77,7 @@ test "File seek ops" {
7777
78 // Seek to the end78 // Seek to the end
79 try file.seekFromEnd(0);79 try file.seekFromEnd(0);
80 try expect((try file.getPos()) == try file.getEndPos());80 try expect((try file.getPos()) == try file.length(io));
81 // Negative delta81 // Negative delta
82 try file.seekBy(-4096);82 try file.seekBy(-4096);
83 try expect((try file.getPos()) == 4096);83 try expect((try file.getPos()) == 4096);
...@@ -100,17 +100,17 @@ test "setEndPos" {...@@ -100,17 +100,17 @@ test "setEndPos" {
100 defer file.close(io);100 defer file.close(io);
101101
102 // Verify that the file size changes and the file offset is not moved102 // Verify that the file size changes and the file offset is not moved
103 try expect((try file.getEndPos()) == 0);103 try expect((try file.length(io)) == 0);
104 try expect((try file.getPos()) == 0);104 try expect((try file.getPos()) == 0);
105 try file.setEndPos(8192);105 try file.setEndPos(8192);
106 try expect((try file.getEndPos()) == 8192);106 try expect((try file.length(io)) == 8192);
107 try expect((try file.getPos()) == 0);107 try expect((try file.getPos()) == 0);
108 try file.seekTo(100);108 try file.seekTo(100);
109 try file.setEndPos(4096);109 try file.setEndPos(4096);
110 try expect((try file.getEndPos()) == 4096);110 try expect((try file.length(io)) == 4096);
111 try expect((try file.getPos()) == 100);111 try expect((try file.getPos()) == 100);
112 try file.setEndPos(0);112 try file.setEndPos(0);
113 try expect((try file.getEndPos()) == 0);113 try expect((try file.length(io)) == 0);
114 try expect((try file.getPos()) == 100);114 try expect((try file.getPos()) == 100);
115}115}
116116
lib/std/debug.zig+24-50
...@@ -558,9 +558,9 @@ pub fn defaultPanic(...@@ -558,9 +558,9 @@ pub fn defaultPanic(
558 stderr.print("{s}\n", .{msg}) catch break :trace;558 stderr.print("{s}\n", .{msg}) catch break :trace;
559559
560 if (@errorReturnTrace()) |t| if (t.index > 0) {560 if (@errorReturnTrace()) |t| if (t.index > 0) {
561 stderr.writeStreamingAll("error return context:\n") catch break :trace;561 stderr.writeAll("error return context:\n") catch break :trace;
562 writeStackTrace(t, stderr, tty_config) catch break :trace;562 writeStackTrace(t, stderr, tty_config) catch break :trace;
563 stderr.writeStreamingAll("\nstack trace:\n") catch break :trace;563 stderr.writeAll("\nstack trace:\n") catch break :trace;
564 };564 };
565 writeCurrentStackTrace(.{565 writeCurrentStackTrace(.{
566 .first_address = first_trace_addr orelse @returnAddress(),566 .first_address = first_trace_addr orelse @returnAddress(),
...@@ -617,6 +617,8 @@ pub const StackUnwindOptions = struct {...@@ -617,6 +617,8 @@ pub const StackUnwindOptions = struct {
617///617///
618/// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it.618/// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it.
619pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace {619pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace {
620 var threaded: Io.Threaded = .init_single_threaded;
621 const io = threaded.ioBasic();
620 const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} };622 const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} };
621 if (!std.options.allow_stack_tracing) return empty_trace;623 if (!std.options.allow_stack_tracing) return empty_trace;
622 var it: StackIterator = .init(options.context);624 var it: StackIterator = .init(options.context);
...@@ -628,7 +630,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf:...@@ -628,7 +630,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf:
628 // Ideally, we would iterate the whole stack so that the `index` in the returned trace was630 // Ideally, we would iterate the whole stack so that the `index` in the returned trace was
629 // indicative of how many frames were skipped. However, this has a significant runtime cost631 // indicative of how many frames were skipped. However, this has a significant runtime cost
630 // in some cases, so at least for now, we don't do that.632 // in some cases, so at least for now, we don't do that.
631 while (index < addr_buf.len) switch (it.next()) {633 while (index < addr_buf.len) switch (it.next(io)) {
632 .switch_to_fp => if (!it.stratOk(options.allow_unsafe_unwind)) break,634 .switch_to_fp => if (!it.stratOk(options.allow_unsafe_unwind)) break,
633 .end => break,635 .end => break,
634 .frame => |ret_addr| {636 .frame => |ret_addr| {
...@@ -684,7 +686,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri...@@ -684,7 +686,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri
684 var total_frames: usize = 0;686 var total_frames: usize = 0;
685 var wait_for = options.first_address;687 var wait_for = options.first_address;
686 var printed_any_frame = false;688 var printed_any_frame = false;
687 while (true) switch (it.next()) {689 while (true) switch (it.next(io)) {
688 .switch_to_fp => |unwind_error| {690 .switch_to_fp => |unwind_error| {
689 switch (StackIterator.fp_usability) {691 switch (StackIterator.fp_usability) {
690 .useless, .unsafe => {},692 .useless, .unsafe => {},
...@@ -1196,54 +1198,26 @@ fn printLineFromFile(io: Io, writer: *Writer, source_location: SourceLocation) !...@@ -1196,54 +1198,26 @@ fn printLineFromFile(io: Io, writer: *Writer, source_location: SourceLocation) !
1196 // Need this to always block even in async I/O mode, because this could potentially1198 // Need this to always block even in async I/O mode, because this could potentially
1197 // be called from e.g. the event loop code crashing.1199 // be called from e.g. the event loop code crashing.
1198 const cwd: Io.Dir = .cwd();1200 const cwd: Io.Dir = .cwd();
1199 var f = try cwd.openFile(io, source_location.file_name, .{});1201 var file = try cwd.openFile(io, source_location.file_name, .{});
1200 defer f.close(io);1202 defer file.close(io);
1201 // TODO fstat and make sure that the file has the correct size1203 // TODO fstat and make sure that the file has the correct size
12021204
1203 var buf: [4096]u8 = undefined;1205 var buffer: [4096]u8 = undefined;
1204 var amt_read = try f.read(buf[0..]);1206 var file_reader: File.Reader = .init(file, io, &buffer);
1205 const line_start = seek: {1207 const r = &file_reader.interface;
1206 var current_line_start: usize = 0;1208 var line_index: usize = 0;
1207 var next_line: usize = 1;1209 while (r.takeDelimiterExclusive('\n')) |line| {
1208 while (next_line != source_location.line) {1210 line_index += 1;
1209 const slice = buf[current_line_start..amt_read];1211 if (line_index == source_location.line) {
1210 if (mem.findScalar(u8, slice, '\n')) |pos| {1212 // TODO delete hard tabs from the language
1211 next_line += 1;1213 mem.replaceScalar(u8, line, '\t', ' ');
1212 if (pos == slice.len - 1) {1214 try writer.writeAll(line);
1213 amt_read = try f.read(buf[0..]);1215 // Make sure printing last line of file inserts extra newline.
1214 current_line_start = 0;1216 try writer.writeByte('\n');
1215 } else current_line_start += pos + 1;1217 return;
1216 } else if (amt_read < buf.len) {
1217 return error.EndOfFile;
1218 } else {
1219 amt_read = try f.read(buf[0..]);
1220 current_line_start = 0;
1221 }
1222 }
1223 break :seek current_line_start;
1224 };
1225 const slice = buf[line_start..amt_read];
1226 if (mem.findScalar(u8, slice, '\n')) |pos| {
1227 const line = slice[0 .. pos + 1];
1228 mem.replaceScalar(u8, line, '\t', ' ');
1229 return writer.writeAll(line);
1230 } else { // Line is the last inside the buffer, and requires another read to find delimiter. Alternatively the file ends.
1231 mem.replaceScalar(u8, slice, '\t', ' ');
1232 try writer.writeAll(slice);
1233 while (amt_read == buf.len) {
1234 amt_read = try f.read(buf[0..]);
1235 if (mem.findScalar(u8, buf[0..amt_read], '\n')) |pos| {
1236 const line = buf[0 .. pos + 1];
1237 mem.replaceScalar(u8, line, '\t', ' ');
1238 return writer.writeAll(line);
1239 } else {
1240 const line = buf[0..amt_read];
1241 mem.replaceScalar(u8, line, '\t', ' ');
1242 try writer.writeAll(line);
1243 }
1244 }1218 }
1245 // Make sure printing last line of file inserts extra newline1219 } else |err| {
1246 try writer.writeByte('\n');1220 return err;
1247 }1221 }
1248}1222}
12491223
...@@ -1598,7 +1572,7 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex...@@ -1598,7 +1572,7 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex
1598 // We're still holding the mutex but that's fine as we're going to1572 // We're still holding the mutex but that's fine as we're going to
1599 // call abort().1573 // call abort().
1600 const stderr, _ = lockStderrWriter(&.{});1574 const stderr, _ = lockStderrWriter(&.{});
1601 stderr().writeAll("aborting due to recursive panic\n") catch {};1575 stderr.writeAll("aborting due to recursive panic\n") catch {};
1602 },1576 },
1603 else => {}, // Panicked while printing the recursive panic message.1577 else => {}, // Panicked while printing the recursive panic message.
1604 }1578 }
lib/std/debug/ElfFile.zig+10-8
...@@ -123,6 +123,7 @@ pub const LoadError = error{...@@ -123,6 +123,7 @@ pub const LoadError = error{
123123
124pub fn load(124pub fn load(
125 gpa: Allocator,125 gpa: Allocator,
126 io: Io,
126 elf_file: Io.File,127 elf_file: Io.File,
127 opt_build_id: ?[]const u8,128 opt_build_id: ?[]const u8,
128 di_search_paths: *const DebugInfoSearchPaths,129 di_search_paths: *const DebugInfoSearchPaths,
...@@ -131,7 +132,7 @@ pub fn load(...@@ -131,7 +132,7 @@ pub fn load(
131 errdefer arena_instance.deinit();132 errdefer arena_instance.deinit();
132 const arena = arena_instance.allocator();133 const arena = arena_instance.allocator();
133134
134 var result = loadInner(arena, elf_file, null) catch |err| switch (err) {135 var result = loadInner(arena, io, elf_file, null) catch |err| switch (err) {
135 error.CrcMismatch => unreachable, // we passed crc as null136 error.CrcMismatch => unreachable, // we passed crc as null
136 else => |e| return e,137 else => |e| return e,
137 };138 };
...@@ -156,7 +157,7 @@ pub fn load(...@@ -156,7 +157,7 @@ pub fn load(
156 if (build_id.len < 3) break :build_id;157 if (build_id.len < 3) break :build_id;
157158
158 for (di_search_paths.global_debug) |global_debug| {159 for (di_search_paths.global_debug) |global_debug| {
159 if (try loadSeparateDebugFile(arena, &result, null, "{s}/.build-id/{x}/{x}.debug", .{160 if (try loadSeparateDebugFile(arena, io, &result, null, "{s}/.build-id/{x}/{x}.debug", .{
160 global_debug,161 global_debug,
161 build_id[0..1],162 build_id[0..1],
162 build_id[1..],163 build_id[1..],
...@@ -164,7 +165,7 @@ pub fn load(...@@ -164,7 +165,7 @@ pub fn load(
164 }165 }
165166
166 if (di_search_paths.debuginfod_client) |components| {167 if (di_search_paths.debuginfod_client) |components| {
167 if (try loadSeparateDebugFile(arena, &result, null, "{s}{s}/{x}/debuginfo", .{168 if (try loadSeparateDebugFile(arena, io, &result, null, "{s}{s}/{x}/debuginfo", .{
168 components[0],169 components[0],
169 components[1],170 components[1],
170 build_id,171 build_id,
...@@ -181,18 +182,18 @@ pub fn load(...@@ -181,18 +182,18 @@ pub fn load(
181182
182 const exe_dir = di_search_paths.exe_dir orelse break :debug_link;183 const exe_dir = di_search_paths.exe_dir orelse break :debug_link;
183184
184 if (try loadSeparateDebugFile(arena, &result, debug_crc, "{s}/{s}", .{185 if (try loadSeparateDebugFile(arena, io, &result, debug_crc, "{s}/{s}", .{
185 exe_dir,186 exe_dir,
186 debug_filename,187 debug_filename,
187 })) |mapped| break :load_di mapped;188 })) |mapped| break :load_di mapped;
188 if (try loadSeparateDebugFile(arena, &result, debug_crc, "{s}/.debug/{s}", .{189 if (try loadSeparateDebugFile(arena, io, &result, debug_crc, "{s}/.debug/{s}", .{
189 exe_dir,190 exe_dir,
190 debug_filename,191 debug_filename,
191 })) |mapped| break :load_di mapped;192 })) |mapped| break :load_di mapped;
192 for (di_search_paths.global_debug) |global_debug| {193 for (di_search_paths.global_debug) |global_debug| {
193 // This looks like a bug; it isn't. They really do embed the absolute path to the194 // This looks like a bug; it isn't. They really do embed the absolute path to the
194 // exe's dirname, *under* the global debug path.195 // exe's dirname, *under* the global debug path.
195 if (try loadSeparateDebugFile(arena, &result, debug_crc, "{s}/{s}/{s}", .{196 if (try loadSeparateDebugFile(arena, io, &result, debug_crc, "{s}/{s}/{s}", .{
196 global_debug,197 global_debug,
197 exe_dir,198 exe_dir,
198 debug_filename,199 debug_filename,
...@@ -378,7 +379,7 @@ fn loadSeparateDebugFile(...@@ -378,7 +379,7 @@ fn loadSeparateDebugFile(
378 const elf_file = Io.Dir.cwd().openFile(io, path, .{}) catch return null;379 const elf_file = Io.Dir.cwd().openFile(io, path, .{}) catch return null;
379 defer elf_file.close(io);380 defer elf_file.close(io);
380381
381 const result = loadInner(arena, elf_file, opt_crc) catch |err| switch (err) {382 const result = loadInner(arena, io, elf_file, opt_crc) catch |err| switch (err) {
382 error.OutOfMemory => |e| return e,383 error.OutOfMemory => |e| return e,
383 error.CrcMismatch => return null,384 error.CrcMismatch => return null,
384 else => return null,385 else => return null,
...@@ -423,13 +424,14 @@ const LoadInnerResult = struct {...@@ -423,13 +424,14 @@ const LoadInnerResult = struct {
423};424};
424fn loadInner(425fn loadInner(
425 arena: Allocator,426 arena: Allocator,
427 io: Io,
426 elf_file: Io.File,428 elf_file: Io.File,
427 opt_crc: ?u32,429 opt_crc: ?u32,
428) (LoadError || error{ CrcMismatch, Streaming, Canceled })!LoadInnerResult {430) (LoadError || error{ CrcMismatch, Streaming, Canceled })!LoadInnerResult {
429 const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: {431 const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: {
430 const file_len = std.math.cast(432 const file_len = std.math.cast(
431 usize,433 usize,
432 elf_file.getEndPos() catch |err| switch (err) {434 elf_file.length(io) catch |err| switch (err) {
433 error.PermissionDenied => unreachable, // not asking for PROT_EXEC435 error.PermissionDenied => unreachable, // not asking for PROT_EXEC
434 else => |e| return e,436 else => |e| return e,
435 },437 },
lib/std/debug/MachOFile.zig+1-1
...@@ -520,7 +520,7 @@ fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) c...@@ -520,7 +520,7 @@ fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) c
520520
521 const file_len = std.math.cast(521 const file_len = std.math.cast(
522 usize,522 usize,
523 file.getEndPos() catch return error.ReadFailed,523 file.length(io) catch return error.ReadFailed,
524 ) orelse return error.ReadFailed;524 ) orelse return error.ReadFailed;
525525
526 return posix.mmap(526 return posix.mmap(
lib/std/debug/SelfInfo/Elf.zig+2-2
...@@ -326,7 +326,7 @@ const Module = struct {...@@ -326,7 +326,7 @@ const Module = struct {
326 const load_result = if (mod.name.len > 0) res: {326 const load_result = if (mod.name.len > 0) res: {
327 var file = Io.Dir.cwd().openFile(io, mod.name, .{}) catch return error.MissingDebugInfo;327 var file = Io.Dir.cwd().openFile(io, mod.name, .{}) catch return error.MissingDebugInfo;
328 defer file.close(io);328 defer file.close(io);
329 break :res std.debug.ElfFile.load(gpa, file, mod.build_id, &.native(mod.name));329 break :res std.debug.ElfFile.load(gpa, io, file, mod.build_id, &.native(mod.name));
330 } else res: {330 } else res: {
331 const path = std.process.executablePathAlloc(io, gpa) catch |err| switch (err) {331 const path = std.process.executablePathAlloc(io, gpa) catch |err| switch (err) {
332 error.OutOfMemory => |e| return e,332 error.OutOfMemory => |e| return e,
...@@ -335,7 +335,7 @@ const Module = struct {...@@ -335,7 +335,7 @@ const Module = struct {
335 defer gpa.free(path);335 defer gpa.free(path);
336 var file = Io.Dir.cwd().openFile(io, path, .{}) catch return error.MissingDebugInfo;336 var file = Io.Dir.cwd().openFile(io, path, .{}) catch return error.MissingDebugInfo;
337 defer file.close(io);337 defer file.close(io);
338 break :res std.debug.ElfFile.load(gpa, file, mod.build_id, &.native(path));338 break :res std.debug.ElfFile.load(gpa, io, file, mod.build_id, &.native(path));
339 };339 };
340340
341 var elf_file = load_result catch |err| switch (err) {341 var elf_file = load_result catch |err| switch (err) {
lib/std/debug/SelfInfo/MachO.zig+1-1
...@@ -622,7 +622,7 @@ fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) c...@@ -622,7 +622,7 @@ fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) c
622 };622 };
623 defer file.close(io);623 defer file.close(io);
624624
625 const file_end_pos = file.getEndPos() catch |err| switch (err) {625 const file_end_pos = file.length(io) catch |err| switch (err) {
626 error.Unexpected => |e| return e,626 error.Unexpected => |e| return e,
627 else => return error.ReadFailed,627 else => return error.ReadFailed,
628 };628 };
lib/std/os/uefi/protocol/file.zig-9
...@@ -163,15 +163,6 @@ pub const File = extern struct {...@@ -163,15 +163,6 @@ pub const File = extern struct {
163 }163 }
164 }164 }
165165
166 fn getEndPos(self: *File) SeekError!u64 {
167 const start_pos = try self.getPosition();
168 // ignore error
169 defer self.setPosition(start_pos) catch {};
170
171 try self.setPosition(end_of_file);
172 return self.getPosition();
173 }
174
175 pub fn setPosition(self: *File, position: u64) SeekError!void {166 pub fn setPosition(self: *File, position: u64) SeekError!void {
176 switch (self._set_position(self, position)) {167 switch (self._set_position(self, position)) {
177 .success => {},168 .success => {},