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(
824824 }
825825 if (@bitSizeOf(usize) != 64) {
826826 // 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 case
827 // being compatible with file system's u64 return value. This is not the case
828828 // on 32-bit platforms.
829829 // Affects or affected by issues #5185, #22523, and #22464.
830830 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
413413 };
414414 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| {
417417 log.err("unable to check len of coverage file '{f}': {t}", .{ coverage_file_path, err });
418418 return error.AlreadyReported;
419419 };
lib/std/Io/test.zig+6-6
......@@ -47,7 +47,7 @@ test "write a file, read it, then delete it" {
4747 var file = try tmp.dir.openFile(io, tmp_file_name, .{});
4848 defer file.close(io);
4949
50 const file_size = try file.getEndPos();
50 const file_size = try file.length(io);
5151 const expected_file_size: u64 = "begin".len + data.len + "end".len;
5252 try expectEqual(expected_file_size, file_size);
5353
......@@ -77,7 +77,7 @@ test "File seek ops" {
7777
7878 // Seek to the end
7979 try file.seekFromEnd(0);
80 try expect((try file.getPos()) == try file.getEndPos());
80 try expect((try file.getPos()) == try file.length(io));
8181 // Negative delta
8282 try file.seekBy(-4096);
8383 try expect((try file.getPos()) == 4096);
......@@ -100,17 +100,17 @@ test "setEndPos" {
100100 defer file.close(io);
101101
102102 // 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);
104104 try expect((try file.getPos()) == 0);
105105 try file.setEndPos(8192);
106 try expect((try file.getEndPos()) == 8192);
106 try expect((try file.length(io)) == 8192);
107107 try expect((try file.getPos()) == 0);
108108 try file.seekTo(100);
109109 try file.setEndPos(4096);
110 try expect((try file.getEndPos()) == 4096);
110 try expect((try file.length(io)) == 4096);
111111 try expect((try file.getPos()) == 100);
112112 try file.setEndPos(0);
113 try expect((try file.getEndPos()) == 0);
113 try expect((try file.length(io)) == 0);
114114 try expect((try file.getPos()) == 100);
115115}
116116
lib/std/debug.zig+24-50
......@@ -558,9 +558,9 @@ pub fn defaultPanic(
558558 stderr.print("{s}\n", .{msg}) catch break :trace;
559559
560560 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;
562562 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;
564564 };
565565 writeCurrentStackTrace(.{
566566 .first_address = first_trace_addr orelse @returnAddress(),
......@@ -617,6 +617,8 @@ pub const StackUnwindOptions = struct {
617617///
618618/// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it.
619619pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace {
620 var threaded: Io.Threaded = .init_single_threaded;
621 const io = threaded.ioBasic();
620622 const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} };
621623 if (!std.options.allow_stack_tracing) return empty_trace;
622624 var it: StackIterator = .init(options.context);
......@@ -628,7 +630,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf:
628630 // Ideally, we would iterate the whole stack so that the `index` in the returned trace was
629631 // indicative of how many frames were skipped. However, this has a significant runtime cost
630632 // 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)) {
632634 .switch_to_fp => if (!it.stratOk(options.allow_unsafe_unwind)) break,
633635 .end => break,
634636 .frame => |ret_addr| {
......@@ -684,7 +686,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri
684686 var total_frames: usize = 0;
685687 var wait_for = options.first_address;
686688 var printed_any_frame = false;
687 while (true) switch (it.next()) {
689 while (true) switch (it.next(io)) {
688690 .switch_to_fp => |unwind_error| {
689691 switch (StackIterator.fp_usability) {
690692 .useless, .unsafe => {},
......@@ -1196,54 +1198,26 @@ fn printLineFromFile(io: Io, writer: *Writer, source_location: SourceLocation) !
11961198 // Need this to always block even in async I/O mode, because this could potentially
11971199 // be called from e.g. the event loop code crashing.
11981200 const cwd: Io.Dir = .cwd();
1199 var f = try cwd.openFile(io, source_location.file_name, .{});
1200 defer f.close(io);
1201 var file = try cwd.openFile(io, source_location.file_name, .{});
1202 defer file.close(io);
12011203 // TODO fstat and make sure that the file has the correct size
12021204
1203 var buf: [4096]u8 = undefined;
1204 var amt_read = try f.read(buf[0..]);
1205 const line_start = seek: {
1206 var current_line_start: usize = 0;
1207 var next_line: usize = 1;
1208 while (next_line != source_location.line) {
1209 const slice = buf[current_line_start..amt_read];
1210 if (mem.findScalar(u8, slice, '\n')) |pos| {
1211 next_line += 1;
1212 if (pos == slice.len - 1) {
1213 amt_read = try f.read(buf[0..]);
1214 current_line_start = 0;
1215 } else current_line_start += pos + 1;
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 }
1205 var buffer: [4096]u8 = undefined;
1206 var file_reader: File.Reader = .init(file, io, &buffer);
1207 const r = &file_reader.interface;
1208 var line_index: usize = 0;
1209 while (r.takeDelimiterExclusive('\n')) |line| {
1210 line_index += 1;
1211 if (line_index == source_location.line) {
1212 // TODO delete hard tabs from the language
1213 mem.replaceScalar(u8, line, '\t', ' ');
1214 try writer.writeAll(line);
1215 // Make sure printing last line of file inserts extra newline.
1216 try writer.writeByte('\n');
1217 return;
12441218 }
1245 // Make sure printing last line of file inserts extra newline
1246 try writer.writeByte('\n');
1219 } else |err| {
1220 return err;
12471221 }
12481222}
12491223
......@@ -1598,7 +1572,7 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex
15981572 // We're still holding the mutex but that's fine as we're going to
15991573 // call abort().
16001574 const stderr, _ = lockStderrWriter(&.{});
1601 stderr().writeAll("aborting due to recursive panic\n") catch {};
1575 stderr.writeAll("aborting due to recursive panic\n") catch {};
16021576 },
16031577 else => {}, // Panicked while printing the recursive panic message.
16041578 }
lib/std/debug/ElfFile.zig+10-8
......@@ -123,6 +123,7 @@ pub const LoadError = error{
123123
124124pub fn load(
125125 gpa: Allocator,
126 io: Io,
126127 elf_file: Io.File,
127128 opt_build_id: ?[]const u8,
128129 di_search_paths: *const DebugInfoSearchPaths,
......@@ -131,7 +132,7 @@ pub fn load(
131132 errdefer arena_instance.deinit();
132133 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) {
135136 error.CrcMismatch => unreachable, // we passed crc as null
136137 else => |e| return e,
137138 };
......@@ -156,7 +157,7 @@ pub fn load(
156157 if (build_id.len < 3) break :build_id;
157158
158159 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", .{
160161 global_debug,
161162 build_id[0..1],
162163 build_id[1..],
......@@ -164,7 +165,7 @@ pub fn load(
164165 }
165166
166167 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", .{
168169 components[0],
169170 components[1],
170171 build_id,
......@@ -181,18 +182,18 @@ pub fn load(
181182
182183 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}", .{
185186 exe_dir,
186187 debug_filename,
187188 })) |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}", .{
189190 exe_dir,
190191 debug_filename,
191192 })) |mapped| break :load_di mapped;
192193 for (di_search_paths.global_debug) |global_debug| {
193194 // This looks like a bug; it isn't. They really do embed the absolute path to the
194195 // 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}", .{
196197 global_debug,
197198 exe_dir,
198199 debug_filename,
......@@ -378,7 +379,7 @@ fn loadSeparateDebugFile(
378379 const elf_file = Io.Dir.cwd().openFile(io, path, .{}) catch return null;
379380 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) {
382383 error.OutOfMemory => |e| return e,
383384 error.CrcMismatch => return null,
384385 else => return null,
......@@ -423,13 +424,14 @@ const LoadInnerResult = struct {
423424};
424425fn loadInner(
425426 arena: Allocator,
427 io: Io,
426428 elf_file: Io.File,
427429 opt_crc: ?u32,
428430) (LoadError || error{ CrcMismatch, Streaming, Canceled })!LoadInnerResult {
429431 const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: {
430432 const file_len = std.math.cast(
431433 usize,
432 elf_file.getEndPos() catch |err| switch (err) {
434 elf_file.length(io) catch |err| switch (err) {
433435 error.PermissionDenied => unreachable, // not asking for PROT_EXEC
434436 else => |e| return e,
435437 },
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
520520
521521 const file_len = std.math.cast(
522522 usize,
523 file.getEndPos() catch return error.ReadFailed,
523 file.length(io) catch return error.ReadFailed,
524524 ) orelse return error.ReadFailed;
525525
526526 return posix.mmap(
lib/std/debug/SelfInfo/Elf.zig+2-2
......@@ -326,7 +326,7 @@ const Module = struct {
326326 const load_result = if (mod.name.len > 0) res: {
327327 var file = Io.Dir.cwd().openFile(io, mod.name, .{}) catch return error.MissingDebugInfo;
328328 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));
330330 } else res: {
331331 const path = std.process.executablePathAlloc(io, gpa) catch |err| switch (err) {
332332 error.OutOfMemory => |e| return e,
......@@ -335,7 +335,7 @@ const Module = struct {
335335 defer gpa.free(path);
336336 var file = Io.Dir.cwd().openFile(io, path, .{}) catch return error.MissingDebugInfo;
337337 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));
339339 };
340340
341341 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
622622 };
623623 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) {
626626 error.Unexpected => |e| return e,
627627 else => return error.ReadFailed,
628628 };
lib/std/os/uefi/protocol/file.zig-9
......@@ -163,15 +163,6 @@ pub const File = extern struct {
163163 }
164164 }
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
175166 pub fn setPosition(self: *File, position: u64) SeekError!void {
176167 switch (self._set_position(self, position)) {
177168 .success => {},