authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-08 21:00:04-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:08-08:00
logbee8005fe6817ade9191de0493888b14cdbcac31
tree6001bd45ab1118e92a77fe6ebfbf6b332d371a2b
parent4a53e5b0b4131c6b8e18bb551e8215e425f8ac71

std.heap.DebugAllocator: never detect TTY config

instead, allow the user to set it as a field. this fixes a bug where leak printing and error printing would run tty config detection for stderr, and then emit a log, which is not necessary going to print to stderr. however, the nice defaults are gone; the user must explicitly assign the tty_config field during initialization or else the logging will not have color. related: https://github.com/ziglang/zig/issues/24510

25 files changed, 113 insertions(+), 86 deletions(-)

lib/compiler/build_runner.zig+1-1
......@@ -435,7 +435,7 @@ pub fn main() !void {
435435 if (builtin.single_threaded) fatal("'--webui' is not yet supported on single-threaded hosts", .{});
436436 }
437437
438 const ttyconf = color.detectTtyConf();
438 const ttyconf = color.detectTtyConf(io);
439439
440440 const main_progress_node = std.Progress.start(.{
441441 .disable_printing = (color == .off),
lib/std/Build/Fuzz.zig+10-9
......@@ -360,12 +360,13 @@ fn coverageRunCancelable(fuzz: *Fuzz) Io.Cancelable!void {
360360fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutOfMemory, AlreadyReported, Canceled }!void {
361361 assert(fuzz.mode == .forever);
362362 const ws = fuzz.mode.forever.ws;
363 const gpa = fuzz.gpa;
363364 const io = fuzz.io;
364365
365366 try fuzz.coverage_mutex.lock(io);
366367 defer fuzz.coverage_mutex.unlock(io);
367368
368 const gop = try fuzz.coverage_files.getOrPut(fuzz.gpa, coverage_id);
369 const gop = try fuzz.coverage_files.getOrPut(gpa, coverage_id);
369370 if (gop.found_existing) {
370371 // We are fuzzing the same executable with multiple threads.
371372 // Perhaps the same unit test; perhaps a different one. In any
......@@ -383,12 +384,12 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
383384 .entry_points = .{},
384385 .start_timestamp = ws.now(),
385386 };
386 errdefer gop.value_ptr.coverage.deinit(fuzz.gpa);
387 errdefer gop.value_ptr.coverage.deinit(gpa);
387388
388389 const rebuilt_exe_path = run_step.rebuilt_executable.?;
389390 const target = run_step.producer.?.rootModuleTarget();
390391 var debug_info = std.debug.Info.load(
391 fuzz.gpa,
392 gpa,
392393 io,
393394 rebuilt_exe_path,
394395 &gop.value_ptr.coverage,
......@@ -400,7 +401,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
400401 });
401402 return error.AlreadyReported;
402403 };
403 defer debug_info.deinit(fuzz.gpa);
404 defer debug_info.deinit(gpa);
404405
405406 const coverage_file_path: Build.Cache.Path = .{
406407 .root_dir = run_step.step.owner.cache_root,
......@@ -434,14 +435,14 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
434435
435436 const header: *const abi.SeenPcsHeader = @ptrCast(mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
436437 const pcs = header.pcAddrs();
437 const source_locations = try fuzz.gpa.alloc(Coverage.SourceLocation, pcs.len);
438 errdefer fuzz.gpa.free(source_locations);
438 const source_locations = try gpa.alloc(Coverage.SourceLocation, pcs.len);
439 errdefer gpa.free(source_locations);
439440
440441 // Unfortunately the PCs array that LLVM gives us from the 8-bit PC
441442 // counters feature is not sorted.
442443 var sorted_pcs: std.MultiArrayList(struct { pc: u64, index: u32, sl: Coverage.SourceLocation }) = .{};
443 defer sorted_pcs.deinit(fuzz.gpa);
444 try sorted_pcs.resize(fuzz.gpa, pcs.len);
444 defer sorted_pcs.deinit(gpa);
445 try sorted_pcs.resize(gpa, pcs.len);
445446 @memcpy(sorted_pcs.items(.pc), pcs);
446447 for (sorted_pcs.items(.index), 0..) |*v, i| v.* = @intCast(i);
447448 sorted_pcs.sortUnstable(struct {
......@@ -452,7 +453,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
452453 }
453454 }{ .addrs = sorted_pcs.items(.pc) });
454455
455 debug_info.resolveAddresses(fuzz.gpa, sorted_pcs.items(.pc), sorted_pcs.items(.sl)) catch |err| {
456 debug_info.resolveAddresses(gpa, io, sorted_pcs.items(.pc), sorted_pcs.items(.sl)) catch |err| {
456457 log.err("failed to resolve addresses to source locations: {t}", .{err});
457458 return error.AlreadyReported;
458459 };
lib/std/Build/Step/InstallArtifact.zig+1-1
......@@ -172,7 +172,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
172172 defer src_dir.close(io);
173173
174174 var it = try src_dir.walk(b.allocator);
175 next_entry: while (try it.next()) |entry| {
175 next_entry: while (try it.next(io)) |entry| {
176176 for (dir.options.exclude_extensions) |ext| {
177177 if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry;
178178 }
lib/std/Build/Step/WriteFile.zig+1-1
......@@ -309,7 +309,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
309309
310310 var it = try already_open_dir.walk(gpa);
311311 defer it.deinit();
312 while (try it.next()) |entry| {
312 while (try it.next(io)) |entry| {
313313 if (!dir.options.pathIncluded(entry.path)) continue;
314314
315315 const src_entry_path = try src_dir_path.join(arena, entry.path);
lib/std/Io/Dir.zig+4-4
......@@ -574,7 +574,7 @@ pub fn updateFile(
574574 error.WriteFailed => return atomic_file.file_writer.err.?,
575575 };
576576 try atomic_file.flush();
577 try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime);
577 try atomic_file.file_writer.file.setTimestamps(io, src_stat.atime, src_stat.mtime);
578578 try atomic_file.renameIntoPlace();
579579 return .stale;
580580}
......@@ -1238,7 +1238,7 @@ pub fn deleteTree(dir: Dir, io: Io, sub_path: []const u8) DeleteTreeError!void {
12381238
12391239 process_stack: while (stack.items.len != 0) {
12401240 var top = &stack.items[stack.items.len - 1];
1241 while (try top.iter.next()) |entry| {
1241 while (try top.iter.next(io)) |entry| {
12421242 var treat_as_dir = entry.kind == .directory;
12431243 handle_entry: while (true) {
12441244 if (treat_as_dir) {
......@@ -1695,9 +1695,9 @@ pub fn atomicFile(parent: Dir, io: Io, dest_path: []const u8, options: AtomicFil
16951695 else
16961696 try parent.openDir(io, dirname, .{});
16971697
1698 return .init(path.basename(dest_path), options.permissions, dir, true, options.write_buffer);
1698 return .init(io, path.basename(dest_path), options.permissions, dir, true, options.write_buffer);
16991699 } else {
1700 return .init(dest_path, options.permissions, parent, false, options.write_buffer);
1700 return .init(io, dest_path, options.permissions, parent, false, options.write_buffer);
17011701 }
17021702}
17031703
lib/std/Io/File.zig+1-1
......@@ -460,7 +460,7 @@ pub fn setTimestamps(
460460 last_accessed: Io.Timestamp,
461461 last_modified: Io.Timestamp,
462462) SetTimestampsError!void {
463 return io.vtable.fileUpdateTimes(io.userdata, file, last_accessed, last_modified);
463 return io.vtable.fileSetTimestamps(io.userdata, file, last_accessed, last_modified);
464464}
465465
466466/// Sets the accessed and modification timestamps of `file` to the current wall
lib/std/Io/File/Atomic.zig+1-1
......@@ -66,7 +66,7 @@ pub fn deinit(af: *Atomic) void {
6666 af.* = undefined;
6767}
6868
69pub const FlushError = File.WriteError;
69pub const FlushError = File.Writer.Error;
7070
7171pub fn flush(af: *Atomic) FlushError!void {
7272 af.file_writer.interface.flush() catch |err| switch (err) {
lib/std/Io/File/Writer.zig+5-4
......@@ -158,7 +158,7 @@ pub fn sendFile(io_w: *Io.Writer, file_reader: *Io.File.Reader, limit: Io.Limit)
158158fn sendFilePositional(w: *Writer, file_reader: *Io.File.Reader, limit: Io.Limit) Io.Writer.FileError!usize {
159159 const io = w.io;
160160 const header = w.interface.buffered();
161 const n = io.vtable.fileSendFilePositional(io.userdata, w.file, header, file_reader, limit, w.pos) catch |err| switch (err) {
161 const n = io.vtable.fileWriteFilePositional(io.userdata, w.file, header, file_reader, limit, w.pos) catch |err| switch (err) {
162162 error.Unseekable => {
163163 w.mode = w.mode.toStreaming();
164164 const pos = w.pos;
......@@ -187,7 +187,7 @@ fn sendFilePositional(w: *Writer, file_reader: *Io.File.Reader, limit: Io.Limit)
187187fn sendFileStreaming(w: *Writer, file_reader: *Io.File.Reader, limit: Io.Limit) Io.Writer.FileError!usize {
188188 const io = w.io;
189189 const header = w.interface.buffered();
190 const n = io.vtable.fileSendFileStreaming(io.userdata, w.file, header, file_reader, limit) catch |err| switch (err) {
190 const n = io.vtable.fileWriteFileStreaming(io.userdata, w.file, header, file_reader, limit) catch |err| switch (err) {
191191 error.Canceled => {
192192 w.err = error.Canceled;
193193 return error.WriteFailed;
......@@ -226,7 +226,7 @@ pub fn seekToUnbuffered(w: *Writer, offset: u64) SeekError!void {
226226 }
227227}
228228
229pub const EndError = File.SetEndPosError || Io.Writer.Error;
229pub const EndError = File.SetLengthError || Io.Writer.Error;
230230
231231/// Flushes any buffered data and sets the end position of the file.
232232///
......@@ -236,11 +236,12 @@ pub const EndError = File.SetEndPosError || Io.Writer.Error;
236236/// Flush failure is handled by setting `err` so that it can be handled
237237/// along with other write failures.
238238pub fn end(w: *Writer) EndError!void {
239 const io = w.io;
239240 try w.interface.flush();
240241 switch (w.mode) {
241242 .positional,
242243 .positional_reading,
243 => w.file.setLength(w.pos) catch |err| switch (err) {
244 => w.file.setLength(io, w.pos) catch |err| switch (err) {
244245 error.NonResizable => return,
245246 else => |e| return e,
246247 },
lib/std/Io/Threaded.zig+16-16
......@@ -4,8 +4,6 @@ const builtin = @import("builtin");
44const native_os = builtin.os.tag;
55const is_windows = native_os == .windows;
66const is_darwin = native_os.isDarwin();
7const windows = std.os.windows;
8const ws2_32 = std.os.windows.ws2_32;
97const is_debug = builtin.mode == .Debug;
108
119const std = @import("../std.zig");
......@@ -19,6 +17,8 @@ const Allocator = std.mem.Allocator;
1917const Alignment = std.mem.Alignment;
2018const assert = std.debug.assert;
2119const posix = std.posix;
20const windows = std.os.windows;
21const ws2_32 = std.os.windows.ws2_32;
2222
2323/// Thread-safe.
2424allocator: Allocator,
......@@ -1452,7 +1452,7 @@ const dirMake = switch (native_os) {
14521452 else => dirMakePosix,
14531453};
14541454
1455fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void {
1455fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void {
14561456 const t: *Threaded = @ptrCast(@alignCast(userdata));
14571457 const current_thread = Thread.getCurrent(t);
14581458
......@@ -1461,7 +1461,7 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir
14611461
14621462 try current_thread.beginSyscall();
14631463 while (true) {
1464 switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, mode))) {
1464 switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, permissions.toMode()))) {
14651465 .SUCCESS => {
14661466 current_thread.endSyscall();
14671467 return;
......@@ -1498,8 +1498,8 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir
14981498 }
14991499}
15001500
1501fn dirMakeWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void {
1502 if (builtin.link_libc) return dirMakePosix(userdata, dir, sub_path, mode);
1501fn dirMakeWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void {
1502 if (builtin.link_libc) return dirMakePosix(userdata, dir, sub_path, permissions);
15031503 const t: *Threaded = @ptrCast(@alignCast(userdata));
15041504 const current_thread = Thread.getCurrent(t);
15051505 try current_thread.beginSyscall();
......@@ -1540,13 +1540,13 @@ fn dirMakeWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.
15401540 }
15411541}
15421542
1543fn dirMakeWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void {
1543fn dirMakeWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void {
15441544 const t: *Threaded = @ptrCast(@alignCast(userdata));
15451545 const current_thread = Thread.getCurrent(t);
15461546 try current_thread.checkCancel();
15471547
15481548 const sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
1549 _ = mode;
1549 _ = permissions; // TODO use this value
15501550 const sub_dir_handle = windows.OpenFile(sub_path_w.span(), .{
15511551 .dir = dir.handle,
15521552 .access_mask = .{
......@@ -1570,7 +1570,7 @@ fn dirMakePath(
15701570 userdata: ?*anyopaque,
15711571 dir: Dir,
15721572 sub_path: []const u8,
1573 mode: Dir.Mode,
1573 permissions: Dir.Permissions,
15741574) Dir.MakePathError!Dir.MakePathStatus {
15751575 const t: *Threaded = @ptrCast(@alignCast(userdata));
15761576
......@@ -1578,7 +1578,7 @@ fn dirMakePath(
15781578 var status: Dir.MakePathStatus = .existed;
15791579 var component = it.last() orelse return error.BadPathName;
15801580 while (true) {
1581 if (dirMake(t, dir, component.path, mode)) |_| {
1581 if (dirMake(t, dir, component.path, permissions)) |_| {
15821582 status = .created;
15831583 } else |err| switch (err) {
15841584 error.PathAlreadyExists => {
......@@ -4945,7 +4945,7 @@ fn dirSetTimestamps(
49454945 sub_path: []const u8,
49464946 last_accessed: Io.Timestamp,
49474947 last_modified: Io.Timestamp,
4948 options: File.SetTimestampsOptions,
4948 options: Dir.SetTimestampsOptions,
49494949) File.SetTimestampsError!void {
49504950 const t: *Threaded = @ptrCast(@alignCast(userdata));
49514951 const current_thread = Thread.getCurrent(t);
......@@ -4997,7 +4997,7 @@ fn dirSetTimestampsNow(
49974997 userdata: ?*anyopaque,
49984998 dir: Dir,
49994999 sub_path: []const u8,
5000 options: File.SetTimestampsOptions,
5000 options: Dir.SetTimestampsOptions,
50015001) File.SetTimestampsError!void {
50025002 const t: *Threaded = @ptrCast(@alignCast(userdata));
50035003 const current_thread = Thread.getCurrent(t);
......@@ -6271,7 +6271,7 @@ fn fileWriteStreaming(
62716271 header: []const u8,
62726272 data: []const []const u8,
62736273 splat: usize,
6274) File.WriteStreamingError!usize {
6274) File.Writer.Error!usize {
62756275 const t: *Threaded = @ptrCast(@alignCast(userdata));
62766276 const current_thread = Thread.getCurrent(t);
62776277
......@@ -9690,7 +9690,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) File.Stat {
96909690 return .{
96919691 .inode = stx.ino,
96929692 .size = stx.size,
9693 .mode = stx.mode,
9693 .permissions = .fromMode(stx.mode),
96949694 .kind = switch (stx.mode & std.os.linux.S.IFMT) {
96959695 std.os.linux.S.IFDIR => .directory,
96969696 std.os.linux.S.IFCHR => .character_device,
......@@ -9714,7 +9714,7 @@ fn statFromPosix(st: *const posix.Stat) File.Stat {
97149714 return .{
97159715 .inode = st.ino,
97169716 .size = @bitCast(st.size),
9717 .mode = st.mode,
9717 .permissions = .fromMode(st.mode),
97189718 .kind = k: {
97199719 const m = st.mode & posix.S.IFMT;
97209720 switch (m) {
......@@ -10019,7 +10019,7 @@ fn lookupHosts(
1001910019 options: HostName.LookupOptions,
1002010020) !void {
1002110021 const t_io = io(t);
10022 const file = File.openAbsolute(t_io, "/etc/hosts", .{}) catch |err| switch (err) {
10022 const file = Dir.openFileAbsolute(t_io, "/etc/hosts", .{}) catch |err| switch (err) {
1002310023 error.FileNotFound,
1002410024 error.NotDir,
1002510025 error.AccessDenied,
lib/std/Io/net/HostName.zig+1-1
......@@ -343,7 +343,7 @@ pub const ResolvConf = struct {
343343 .attempts = 2,
344344 };
345345
346 const file = Io.File.openAbsolute(io, "/etc/resolv.conf", .{}) catch |err| switch (err) {
346 const file = Io.Dir.openFileAbsolute(io, "/etc/resolv.conf", .{}) catch |err| switch (err) {
347347 error.FileNotFound,
348348 error.NotDir,
349349 error.AccessDenied,
lib/std/Io/test.zig+3-2
......@@ -114,7 +114,7 @@ test "setEndPos" {
114114 try expect((try file.getPos()) == 100);
115115}
116116
117test "updateTimes" {
117test "setTimestamps" {
118118 const io = testing.io;
119119
120120 var tmp = tmpDir(.{});
......@@ -126,7 +126,8 @@ test "updateTimes" {
126126
127127 const stat_old = try file.stat(io);
128128 // Set atime and mtime to 5s before
129 try file.updateTimes(
129 try file.setTimestamps(
130 io,
130131 stat_old.atime.subDuration(.fromSeconds(5)),
131132 stat_old.mtime.subDuration(.fromSeconds(5)),
132133 );
lib/std/Io/tty.zig+5-4
......@@ -2,6 +2,7 @@ const builtin = @import("builtin");
22const native_os = builtin.os.tag;
33
44const std = @import("std");
5const Io = std.Io;
56const File = std.Io.File;
67const process = std.process;
78const windows = std.os.windows;
......@@ -39,7 +40,7 @@ pub const Config = union(enum) {
3940 /// This includes feature checks for ANSI escape codes and the Windows console API, as well as
4041 /// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default.
4142 /// Will attempt to enable ANSI escape code support if necessary/possible.
42 pub fn detect(file: File) Config {
43 pub fn detect(io: Io, file: File) Config {
4344 const force_color: ?bool = if (builtin.os.tag == .wasi)
4445 null // wasi does not support environment variables
4546 else if (process.hasNonEmptyEnvVarConstant("NO_COLOR"))
......@@ -51,7 +52,7 @@ pub const Config = union(enum) {
5152
5253 if (force_color == false) return .no_color;
5354
54 if (file.enableAnsiEscapeCodes()) |_| {
55 if (file.enableAnsiEscapeCodes(io)) |_| {
5556 return .escape_codes;
5657 } else |_| {}
5758
......@@ -74,9 +75,9 @@ pub const Config = union(enum) {
7475 reset_attributes: u16,
7576 };
7677
77 pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || std.Io.Writer.Error;
78 pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error;
7879
79 pub fn setColor(conf: Config, w: *std.Io.Writer, color: Color) SetColorError!void {
80 pub fn setColor(conf: Config, w: *Io.Writer, color: Color) SetColorError!void {
8081 nosuspend switch (conf) {
8182 .no_color => return,
8283 .escape_codes => {
lib/std/debug.zig+3-1
......@@ -286,11 +286,13 @@ pub fn unlockStdErr() void {
286286pub fn lockStderrWriter(buffer: []u8) struct { *Writer, tty.Config } {
287287 const global = struct {
288288 var conf: ?tty.Config = null;
289 var single_threaded_io: Io.Threaded = .init_single_threaded;
289290 };
291 const io = global.single_threaded_io.io();
290292 const w = std.Progress.lockStderrWriter(buffer);
291293 // The stderr lock also locks access to `global.conf`.
292294 if (global.conf == null) {
293 global.conf = .detect(.stderr());
295 global.conf = .detect(io, .stderr());
294296 }
295297 return .{ w, global.conf.? };
296298}
lib/std/debug/Info.zig+4-3
......@@ -42,7 +42,7 @@ pub fn load(
4242 var file = try path.root_dir.handle.openFile(io, path.sub_path, .{});
4343 defer file.close(io);
4444
45 var elf_file: ElfFile = try .load(gpa, file, null, &.none);
45 var elf_file: ElfFile = try .load(gpa, io, file, null, &.none);
4646 errdefer elf_file.deinit(gpa);
4747
4848 if (elf_file.dwarf == null) return error.MissingDebugInfo;
......@@ -58,7 +58,7 @@ pub fn load(
5858 const path_str = try path.toString(gpa);
5959 defer gpa.free(path_str);
6060
61 var macho_file: MachOFile = try .load(gpa, path_str, arch);
61 var macho_file: MachOFile = try .load(gpa, io, path_str, arch);
6262 errdefer macho_file.deinit(gpa);
6363
6464 return .{
......@@ -85,6 +85,7 @@ pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError || error{U
8585pub fn resolveAddresses(
8686 info: *Info,
8787 gpa: Allocator,
88 io: Io,
8889 /// Asserts the addresses are in ascending order.
8990 sorted_pc_addrs: []const u64,
9091 /// Asserts its length equals length of `sorted_pc_addrs`.
......@@ -97,7 +98,7 @@ pub fn resolveAddresses(
9798 // Resolving all of the addresses at once unfortunately isn't so easy in Mach-O binaries
9899 // due to split debug information. For now, we'll just resolve the addreses one by one.
99100 for (sorted_pc_addrs, output) |pc_addr, *src_loc| {
100 const dwarf, const dwarf_pc_addr = mf.getDwarfForAddress(gpa, pc_addr) catch |err| switch (err) {
101 const dwarf, const dwarf_pc_addr = mf.getDwarfForAddress(gpa, io, pc_addr) catch |err| switch (err) {
101102 error.InvalidMachO, error.InvalidDwarf => return error.InvalidDebugInfo,
102103 else => |e| return e,
103104 };
lib/std/heap/debug_allocator.zig+27-12
......@@ -179,6 +179,8 @@ pub fn DebugAllocator(comptime config: Config) type {
179179 total_requested_bytes: @TypeOf(total_requested_bytes_init) = total_requested_bytes_init,
180180 requested_memory_limit: @TypeOf(requested_memory_limit_init) = requested_memory_limit_init,
181181 mutex: @TypeOf(mutex_init) = mutex_init,
182 /// Set this value differently to affect how errors and leaks are logged.
183 tty_config: std.Io.tty.Config = .no_color,
182184
183185 const Self = @This();
184186
......@@ -458,9 +460,9 @@ pub fn DebugAllocator(comptime config: Config) type {
458460
459461 /// Emits log messages for leaks and then returns the number of detected leaks (0 if no leaks were detected).
460462 pub fn detectLeaks(self: *Self) usize {
461 var leaks: usize = 0;
463 const tty_config = self.tty_config;
462464
463 const tty_config: std.Io.tty.Config = .detect(.stderr());
465 var leaks: usize = 0;
464466
465467 for (self.buckets, 0..) |init_optional_bucket, size_class_index| {
466468 var optional_bucket = init_optional_bucket;
......@@ -533,10 +535,15 @@ pub fn DebugAllocator(comptime config: Config) type {
533535 @memset(addr_buf[@min(st.index, addr_buf.len)..], 0);
534536 }
535537
536 fn reportDoubleFree(ret_addr: usize, alloc_stack_trace: StackTrace, free_stack_trace: StackTrace) void {
538 fn reportDoubleFree(
539 tty_config: std.Io.tty.Config,
540 ret_addr: usize,
541 alloc_stack_trace: StackTrace,
542 free_stack_trace: StackTrace,
543 ) void {
544 @branchHint(.cold);
537545 var addr_buf: [stack_n]usize = undefined;
538546 const second_free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf);
539 const tty_config: std.Io.tty.Config = .detect(.stderr());
540547 log.err("Double free detected. Allocation: {f} First free: {f} Second free: {f}", .{
541548 std.debug.FormatStackTrace{
542549 .stack_trace = alloc_stack_trace,
......@@ -580,7 +587,7 @@ pub fn DebugAllocator(comptime config: Config) type {
580587
581588 if (config.retain_metadata and entry.value_ptr.freed) {
582589 if (config.safety) {
583 reportDoubleFree(ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free));
590 reportDoubleFree(self.tty_config, ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free));
584591 @panic("Unrecoverable double free");
585592 } else {
586593 unreachable;
......@@ -588,9 +595,10 @@ pub fn DebugAllocator(comptime config: Config) type {
588595 }
589596
590597 if (config.safety and old_mem.len != entry.value_ptr.bytes.len) {
598 @branchHint(.cold);
591599 var addr_buf: [stack_n]usize = undefined;
592600 const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf);
593 const tty_config: std.Io.tty.Config = .detect(.stderr());
601 const tty_config = self.tty_config;
594602 log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
595603 entry.value_ptr.bytes.len,
596604 old_mem.len,
......@@ -693,7 +701,7 @@ pub fn DebugAllocator(comptime config: Config) type {
693701
694702 if (config.retain_metadata and entry.value_ptr.freed) {
695703 if (config.safety) {
696 reportDoubleFree(ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free));
704 reportDoubleFree(self.tty_config, ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free));
697705 return;
698706 } else {
699707 unreachable;
......@@ -701,9 +709,10 @@ pub fn DebugAllocator(comptime config: Config) type {
701709 }
702710
703711 if (config.safety and old_mem.len != entry.value_ptr.bytes.len) {
712 @branchHint(.cold);
704713 var addr_buf: [stack_n]usize = undefined;
705714 const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf);
706 const tty_config: std.Io.tty.Config = .detect(.stderr());
715 const tty_config = self.tty_config;
707716 log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
708717 entry.value_ptr.bytes.len,
709718 old_mem.len,
......@@ -915,6 +924,7 @@ pub fn DebugAllocator(comptime config: Config) type {
915924 if (!is_used) {
916925 if (config.safety) {
917926 reportDoubleFree(
927 self.tty_config,
918928 return_address,
919929 bucketStackTrace(bucket, slot_count, slot_index, .alloc),
920930 bucketStackTrace(bucket, slot_count, slot_index, .free),
......@@ -935,7 +945,8 @@ pub fn DebugAllocator(comptime config: Config) type {
935945 var addr_buf: [stack_n]usize = undefined;
936946 const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = return_address }, &addr_buf);
937947 if (old_memory.len != requested_size) {
938 const tty_config: std.Io.tty.Config = .detect(.stderr());
948 @branchHint(.cold);
949 const tty_config = self.tty_config;
939950 log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
940951 requested_size,
941952 old_memory.len,
......@@ -950,7 +961,8 @@ pub fn DebugAllocator(comptime config: Config) type {
950961 });
951962 }
952963 if (alignment != slot_alignment) {
953 const tty_config: std.Io.tty.Config = .detect(.stderr());
964 @branchHint(.cold);
965 const tty_config = self.tty_config;
954966 log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {f} Free: {f}", .{
955967 slot_alignment.toByteUnits(),
956968 alignment.toByteUnits(),
......@@ -1028,6 +1040,7 @@ pub fn DebugAllocator(comptime config: Config) type {
10281040 const is_used = @as(u1, @truncate(used_byte.* >> used_bit_index)) != 0;
10291041 if (!is_used) {
10301042 reportDoubleFree(
1043 self.tty_config,
10311044 return_address,
10321045 bucketStackTrace(bucket, slot_count, slot_index, .alloc),
10331046 bucketStackTrace(bucket, slot_count, slot_index, .free),
......@@ -1044,7 +1057,8 @@ pub fn DebugAllocator(comptime config: Config) type {
10441057 var addr_buf: [stack_n]usize = undefined;
10451058 const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = return_address }, &addr_buf);
10461059 if (memory.len != requested_size) {
1047 const tty_config: std.Io.tty.Config = .detect(.stderr());
1060 @branchHint(.cold);
1061 const tty_config = self.tty_config;
10481062 log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
10491063 requested_size,
10501064 memory.len,
......@@ -1059,7 +1073,8 @@ pub fn DebugAllocator(comptime config: Config) type {
10591073 });
10601074 }
10611075 if (alignment != slot_alignment) {
1062 const tty_config: std.Io.tty.Config = .detect(.stderr());
1076 @branchHint(.cold);
1077 const tty_config = self.tty_config;
10631078 log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {f} Free: {f}", .{
10641079 slot_alignment.toByteUnits(),
10651080 alignment.toByteUnits(),
lib/std/process/Child.zig+1-1
......@@ -470,7 +470,7 @@ pub fn run(allocator: Allocator, io: Io, args: struct {
470470 return .{
471471 .stdout = try stdout.toOwnedSlice(allocator),
472472 .stderr = try stderr.toOwnedSlice(allocator),
473 .term = try child.wait(),
473 .term = try child.wait(io),
474474 };
475475}
476476
lib/std/zig.zig+2-2
......@@ -60,9 +60,9 @@ pub const Color = enum {
6060 .off => .no_color,
6161 };
6262 }
63 pub fn detectTtyConf(color: Color) Io.tty.Config {
63 pub fn detectTtyConf(color: Color, io: Io) Io.tty.Config {
6464 return switch (color) {
65 .auto => .detect(.stderr()),
65 .auto => .detect(io, .stderr()),
6666 .on => .escape_codes,
6767 .off => .no_color,
6868 };
src/link/Dwarf.zig+3-3
......@@ -51,10 +51,10 @@ pub const UpdateError = error{
5151} ||
5252 codegen.GenerateSymbolError ||
5353 Io.File.OpenError ||
54 Io.File.SetEndPosError ||
54 Io.File.LengthError ||
5555 Io.File.CopyRangeError ||
56 Io.File.PReadError ||
57 Io.File.PWriteError;
56 Io.File.ReadPositionalError ||
57 Io.File.WritePositionalError;
5858
5959pub const FlushError = UpdateError;
6060
src/link/MappedFile.zig+1-1
......@@ -28,7 +28,7 @@ writers: std.SinglyLinkedList,
2828
2929pub const growth_factor = 4;
3030
31pub const Error = std.posix.MMapError || std.posix.MRemapError || Io.File.SetEndPosError || error{
31pub const Error = std.posix.MMapError || std.posix.MRemapError || Io.File.LengthError || error{
3232 NotFile,
3333 SystemResources,
3434 IsDir,
src/main.zig+14-9
......@@ -162,17 +162,20 @@ var debug_allocator: std.heap.DebugAllocator(.{
162162 .stack_trace_frames = build_options.mem_leak_frames,
163163}) = .init;
164164
165const use_debug_allocator = build_options.debug_gpa or
166 (native_os != .wasi and !builtin.link_libc and switch (builtin.mode) {
167 .Debug, .ReleaseSafe => true,
168 .ReleaseFast, .ReleaseSmall => false,
169 });
170
165171pub fn main() anyerror!void {
166 const gpa, const is_debug = gpa: {
167 if (build_options.debug_gpa) break :gpa .{ debug_allocator.allocator(), true };
168 if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false };
169 if (builtin.link_libc) break :gpa .{ std.heap.c_allocator, false };
170 break :gpa switch (builtin.mode) {
171 .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true },
172 .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false },
173 };
172 const gpa = gpa: {
173 if (use_debug_allocator) break :gpa debug_allocator.allocator();
174 if (native_os == .wasi) break :gpa std.heap.wasm_allocator;
175 if (builtin.link_libc) break :gpa std.heap.c_allocator;
176 break :gpa std.heap.smp_allocator;
174177 };
175 defer if (is_debug) {
178 defer if (use_debug_allocator) {
176179 _ = debug_allocator.deinit();
177180 };
178181 var arena_instance = std.heap.ArenaAllocator.init(gpa);
......@@ -244,6 +247,8 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
244247 threaded.stack_size = thread_stack_size;
245248 const io = threaded.io();
246249
250 debug_allocator.tty_config = .detect(io, .stderr());
251
247252 const cmd = args[1];
248253 const cmd_args = args[2..];
249254 if (mem.eql(u8, cmd, "build-exe")) {
test/link/macho.zig+2-1
......@@ -868,9 +868,10 @@ fn testLayout(b: *Build, opts: Options) *Step {
868868}
869869
870870fn testLinkDirectlyCppTbd(b: *Build, opts: Options) *Step {
871 const io = b.graph.io;
871872 const test_step = addTestStep(b, "link-directly-cpp-tbd", opts);
872873
873 const sdk = std.zig.system.darwin.getSdk(b.allocator, &opts.target.result) orelse
874 const sdk = std.zig.system.darwin.getSdk(b.allocator, io, &opts.target.result) orelse
874875 @panic("macOS SDK is required to run the test");
875876
876877 const exe = addExecutable(b, opts, .{
test/src/Cases.zig+3-2
......@@ -339,7 +339,7 @@ fn addFromDirInner(
339339 var it = try iterable_dir.walk(ctx.arena);
340340 var filenames: ArrayList([]const u8) = .empty;
341341
342 while (try it.next()) |entry| {
342 while (try it.next(io)) |entry| {
343343 if (entry.kind != .file) continue;
344344
345345 // Ignore stuff such as .swp files
......@@ -431,9 +431,10 @@ fn addFromDirInner(
431431 }
432432}
433433
434pub fn init(gpa: Allocator, arena: Allocator) Cases {
434pub fn init(gpa: Allocator, arena: Allocator, io: Io) Cases {
435435 return .{
436436 .gpa = gpa,
437 .io = io,
437438 .cases = .init(gpa),
438439 .arena = arena,
439440 };
test/standalone/ios/build.zig+3-1
......@@ -23,7 +23,9 @@ pub fn build(b: *std.Build) void {
2323 }),
2424 });
2525
26 if (std.zig.system.darwin.getSdk(b.allocator, &target.result)) |sdk| {
26 const io = b.graph.io;
27
28 if (std.zig.system.darwin.getSdk(b.allocator, io, &target.result)) |sdk| {
2729 b.sysroot = sdk;
2830 exe.root_module.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/include" }) });
2931 exe.root_module.addSystemFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/System/Library/Frameworks" }) });
test/standalone/self_exe_symlink/build.zig-4
......@@ -9,10 +9,6 @@ pub fn build(b: *std.Build) void {
99 const optimize: std.builtin.OptimizeMode = .Debug;
1010 const target = b.graph.host;
1111
12 // The test requires getFdPath in order to to get the path of the
13 // File returned by openSelfExe
14 if (!std.os.isGetFdPathSupportedOnTarget(target.result.os)) return;
15
1612 const main = b.addExecutable(.{
1713 .name = "main",
1814 .root_module = b.createModule(.{
test/tests.zig+1-1
......@@ -2632,7 +2632,7 @@ pub fn addCases(
26322632 const gpa = b.allocator;
26332633 const io = b.graph.io;
26342634
2635 var cases = @import("src/Cases.zig").init(gpa, arena);
2635 var cases = @import("src/Cases.zig").init(gpa, arena, io);
26362636
26372637 var dir = try b.build_root.handle.openDir(io, "test/cases", .{ .iterate = true });
26382638 defer dir.close(io);