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
This commit is contained in:
Andrew Kelley
2025-12-08 21:00:04 -08:00
parent 4a53e5b0b4
commit bee8005fe6
25 changed files with 113 additions and 86 deletions
+1 -1
View File
@@ -435,7 +435,7 @@ pub fn main() !void {
if (builtin.single_threaded) fatal("'--webui' is not yet supported on single-threaded hosts", .{});
}
const ttyconf = color.detectTtyConf();
const ttyconf = color.detectTtyConf(io);
const main_progress_node = std.Progress.start(.{
.disable_printing = (color == .off),
+10 -9
View File
@@ -360,12 +360,13 @@ fn coverageRunCancelable(fuzz: *Fuzz) Io.Cancelable!void {
fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutOfMemory, AlreadyReported, Canceled }!void {
assert(fuzz.mode == .forever);
const ws = fuzz.mode.forever.ws;
const gpa = fuzz.gpa;
const io = fuzz.io;
try fuzz.coverage_mutex.lock(io);
defer fuzz.coverage_mutex.unlock(io);
const gop = try fuzz.coverage_files.getOrPut(fuzz.gpa, coverage_id);
const gop = try fuzz.coverage_files.getOrPut(gpa, coverage_id);
if (gop.found_existing) {
// We are fuzzing the same executable with multiple threads.
// 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
.entry_points = .{},
.start_timestamp = ws.now(),
};
errdefer gop.value_ptr.coverage.deinit(fuzz.gpa);
errdefer gop.value_ptr.coverage.deinit(gpa);
const rebuilt_exe_path = run_step.rebuilt_executable.?;
const target = run_step.producer.?.rootModuleTarget();
var debug_info = std.debug.Info.load(
fuzz.gpa,
gpa,
io,
rebuilt_exe_path,
&gop.value_ptr.coverage,
@@ -400,7 +401,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
});
return error.AlreadyReported;
};
defer debug_info.deinit(fuzz.gpa);
defer debug_info.deinit(gpa);
const coverage_file_path: Build.Cache.Path = .{
.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
const header: *const abi.SeenPcsHeader = @ptrCast(mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
const pcs = header.pcAddrs();
const source_locations = try fuzz.gpa.alloc(Coverage.SourceLocation, pcs.len);
errdefer fuzz.gpa.free(source_locations);
const source_locations = try gpa.alloc(Coverage.SourceLocation, pcs.len);
errdefer gpa.free(source_locations);
// Unfortunately the PCs array that LLVM gives us from the 8-bit PC
// counters feature is not sorted.
var sorted_pcs: std.MultiArrayList(struct { pc: u64, index: u32, sl: Coverage.SourceLocation }) = .{};
defer sorted_pcs.deinit(fuzz.gpa);
try sorted_pcs.resize(fuzz.gpa, pcs.len);
defer sorted_pcs.deinit(gpa);
try sorted_pcs.resize(gpa, pcs.len);
@memcpy(sorted_pcs.items(.pc), pcs);
for (sorted_pcs.items(.index), 0..) |*v, i| v.* = @intCast(i);
sorted_pcs.sortUnstable(struct {
@@ -452,7 +453,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
}
}{ .addrs = sorted_pcs.items(.pc) });
debug_info.resolveAddresses(fuzz.gpa, sorted_pcs.items(.pc), sorted_pcs.items(.sl)) catch |err| {
debug_info.resolveAddresses(gpa, io, sorted_pcs.items(.pc), sorted_pcs.items(.sl)) catch |err| {
log.err("failed to resolve addresses to source locations: {t}", .{err});
return error.AlreadyReported;
};
+1 -1
View File
@@ -172,7 +172,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
defer src_dir.close(io);
var it = try src_dir.walk(b.allocator);
next_entry: while (try it.next()) |entry| {
next_entry: while (try it.next(io)) |entry| {
for (dir.options.exclude_extensions) |ext| {
if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry;
}
+1 -1
View File
@@ -309,7 +309,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
var it = try already_open_dir.walk(gpa);
defer it.deinit();
while (try it.next()) |entry| {
while (try it.next(io)) |entry| {
if (!dir.options.pathIncluded(entry.path)) continue;
const src_entry_path = try src_dir_path.join(arena, entry.path);
+4 -4
View File
@@ -574,7 +574,7 @@ pub fn updateFile(
error.WriteFailed => return atomic_file.file_writer.err.?,
};
try atomic_file.flush();
try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime);
try atomic_file.file_writer.file.setTimestamps(io, src_stat.atime, src_stat.mtime);
try atomic_file.renameIntoPlace();
return .stale;
}
@@ -1238,7 +1238,7 @@ pub fn deleteTree(dir: Dir, io: Io, sub_path: []const u8) DeleteTreeError!void {
process_stack: while (stack.items.len != 0) {
var top = &stack.items[stack.items.len - 1];
while (try top.iter.next()) |entry| {
while (try top.iter.next(io)) |entry| {
var treat_as_dir = entry.kind == .directory;
handle_entry: while (true) {
if (treat_as_dir) {
@@ -1695,9 +1695,9 @@ pub fn atomicFile(parent: Dir, io: Io, dest_path: []const u8, options: AtomicFil
else
try parent.openDir(io, dirname, .{});
return .init(path.basename(dest_path), options.permissions, dir, true, options.write_buffer);
return .init(io, path.basename(dest_path), options.permissions, dir, true, options.write_buffer);
} else {
return .init(dest_path, options.permissions, parent, false, options.write_buffer);
return .init(io, dest_path, options.permissions, parent, false, options.write_buffer);
}
}
+1 -1
View File
@@ -460,7 +460,7 @@ pub fn setTimestamps(
last_accessed: Io.Timestamp,
last_modified: Io.Timestamp,
) SetTimestampsError!void {
return io.vtable.fileUpdateTimes(io.userdata, file, last_accessed, last_modified);
return io.vtable.fileSetTimestamps(io.userdata, file, last_accessed, last_modified);
}
/// Sets the accessed and modification timestamps of `file` to the current wall
+1 -1
View File
@@ -66,7 +66,7 @@ pub fn deinit(af: *Atomic) void {
af.* = undefined;
}
pub const FlushError = File.WriteError;
pub const FlushError = File.Writer.Error;
pub fn flush(af: *Atomic) FlushError!void {
af.file_writer.interface.flush() catch |err| switch (err) {
+5 -4
View File
@@ -158,7 +158,7 @@ pub fn sendFile(io_w: *Io.Writer, file_reader: *Io.File.Reader, limit: Io.Limit)
fn sendFilePositional(w: *Writer, file_reader: *Io.File.Reader, limit: Io.Limit) Io.Writer.FileError!usize {
const io = w.io;
const header = w.interface.buffered();
const n = io.vtable.fileSendFilePositional(io.userdata, w.file, header, file_reader, limit, w.pos) catch |err| switch (err) {
const n = io.vtable.fileWriteFilePositional(io.userdata, w.file, header, file_reader, limit, w.pos) catch |err| switch (err) {
error.Unseekable => {
w.mode = w.mode.toStreaming();
const pos = w.pos;
@@ -187,7 +187,7 @@ fn sendFilePositional(w: *Writer, file_reader: *Io.File.Reader, limit: Io.Limit)
fn sendFileStreaming(w: *Writer, file_reader: *Io.File.Reader, limit: Io.Limit) Io.Writer.FileError!usize {
const io = w.io;
const header = w.interface.buffered();
const n = io.vtable.fileSendFileStreaming(io.userdata, w.file, header, file_reader, limit) catch |err| switch (err) {
const n = io.vtable.fileWriteFileStreaming(io.userdata, w.file, header, file_reader, limit) catch |err| switch (err) {
error.Canceled => {
w.err = error.Canceled;
return error.WriteFailed;
@@ -226,7 +226,7 @@ pub fn seekToUnbuffered(w: *Writer, offset: u64) SeekError!void {
}
}
pub const EndError = File.SetEndPosError || Io.Writer.Error;
pub const EndError = File.SetLengthError || Io.Writer.Error;
/// Flushes any buffered data and sets the end position of the file.
///
@@ -236,11 +236,12 @@ pub const EndError = File.SetEndPosError || Io.Writer.Error;
/// Flush failure is handled by setting `err` so that it can be handled
/// along with other write failures.
pub fn end(w: *Writer) EndError!void {
const io = w.io;
try w.interface.flush();
switch (w.mode) {
.positional,
.positional_reading,
=> w.file.setLength(w.pos) catch |err| switch (err) {
=> w.file.setLength(io, w.pos) catch |err| switch (err) {
error.NonResizable => return,
else => |e| return e,
},
+16 -16
View File
@@ -4,8 +4,6 @@ const builtin = @import("builtin");
const native_os = builtin.os.tag;
const is_windows = native_os == .windows;
const is_darwin = native_os.isDarwin();
const windows = std.os.windows;
const ws2_32 = std.os.windows.ws2_32;
const is_debug = builtin.mode == .Debug;
const std = @import("../std.zig");
@@ -19,6 +17,8 @@ const Allocator = std.mem.Allocator;
const Alignment = std.mem.Alignment;
const assert = std.debug.assert;
const posix = std.posix;
const windows = std.os.windows;
const ws2_32 = std.os.windows.ws2_32;
/// Thread-safe.
allocator: Allocator,
@@ -1452,7 +1452,7 @@ const dirMake = switch (native_os) {
else => dirMakePosix,
};
fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void {
fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
const current_thread = Thread.getCurrent(t);
@@ -1461,7 +1461,7 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir
try current_thread.beginSyscall();
while (true) {
switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, mode))) {
switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, permissions.toMode()))) {
.SUCCESS => {
current_thread.endSyscall();
return;
@@ -1498,8 +1498,8 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir
}
}
fn dirMakeWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void {
if (builtin.link_libc) return dirMakePosix(userdata, dir, sub_path, mode);
fn dirMakeWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void {
if (builtin.link_libc) return dirMakePosix(userdata, dir, sub_path, permissions);
const t: *Threaded = @ptrCast(@alignCast(userdata));
const current_thread = Thread.getCurrent(t);
try current_thread.beginSyscall();
@@ -1540,13 +1540,13 @@ fn dirMakeWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.
}
}
fn dirMakeWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void {
fn dirMakeWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
const current_thread = Thread.getCurrent(t);
try current_thread.checkCancel();
const sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
_ = mode;
_ = permissions; // TODO use this value
const sub_dir_handle = windows.OpenFile(sub_path_w.span(), .{
.dir = dir.handle,
.access_mask = .{
@@ -1570,7 +1570,7 @@ fn dirMakePath(
userdata: ?*anyopaque,
dir: Dir,
sub_path: []const u8,
mode: Dir.Mode,
permissions: Dir.Permissions,
) Dir.MakePathError!Dir.MakePathStatus {
const t: *Threaded = @ptrCast(@alignCast(userdata));
@@ -1578,7 +1578,7 @@ fn dirMakePath(
var status: Dir.MakePathStatus = .existed;
var component = it.last() orelse return error.BadPathName;
while (true) {
if (dirMake(t, dir, component.path, mode)) |_| {
if (dirMake(t, dir, component.path, permissions)) |_| {
status = .created;
} else |err| switch (err) {
error.PathAlreadyExists => {
@@ -4945,7 +4945,7 @@ fn dirSetTimestamps(
sub_path: []const u8,
last_accessed: Io.Timestamp,
last_modified: Io.Timestamp,
options: File.SetTimestampsOptions,
options: Dir.SetTimestampsOptions,
) File.SetTimestampsError!void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
const current_thread = Thread.getCurrent(t);
@@ -4997,7 +4997,7 @@ fn dirSetTimestampsNow(
userdata: ?*anyopaque,
dir: Dir,
sub_path: []const u8,
options: File.SetTimestampsOptions,
options: Dir.SetTimestampsOptions,
) File.SetTimestampsError!void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
const current_thread = Thread.getCurrent(t);
@@ -6271,7 +6271,7 @@ fn fileWriteStreaming(
header: []const u8,
data: []const []const u8,
splat: usize,
) File.WriteStreamingError!usize {
) File.Writer.Error!usize {
const t: *Threaded = @ptrCast(@alignCast(userdata));
const current_thread = Thread.getCurrent(t);
@@ -9690,7 +9690,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) File.Stat {
return .{
.inode = stx.ino,
.size = stx.size,
.mode = stx.mode,
.permissions = .fromMode(stx.mode),
.kind = switch (stx.mode & std.os.linux.S.IFMT) {
std.os.linux.S.IFDIR => .directory,
std.os.linux.S.IFCHR => .character_device,
@@ -9714,7 +9714,7 @@ fn statFromPosix(st: *const posix.Stat) File.Stat {
return .{
.inode = st.ino,
.size = @bitCast(st.size),
.mode = st.mode,
.permissions = .fromMode(st.mode),
.kind = k: {
const m = st.mode & posix.S.IFMT;
switch (m) {
@@ -10019,7 +10019,7 @@ fn lookupHosts(
options: HostName.LookupOptions,
) !void {
const t_io = io(t);
const file = File.openAbsolute(t_io, "/etc/hosts", .{}) catch |err| switch (err) {
const file = Dir.openFileAbsolute(t_io, "/etc/hosts", .{}) catch |err| switch (err) {
error.FileNotFound,
error.NotDir,
error.AccessDenied,
+1 -1
View File
@@ -343,7 +343,7 @@ pub const ResolvConf = struct {
.attempts = 2,
};
const file = Io.File.openAbsolute(io, "/etc/resolv.conf", .{}) catch |err| switch (err) {
const file = Io.Dir.openFileAbsolute(io, "/etc/resolv.conf", .{}) catch |err| switch (err) {
error.FileNotFound,
error.NotDir,
error.AccessDenied,
+3 -2
View File
@@ -114,7 +114,7 @@ test "setEndPos" {
try expect((try file.getPos()) == 100);
}
test "updateTimes" {
test "setTimestamps" {
const io = testing.io;
var tmp = tmpDir(.{});
@@ -126,7 +126,8 @@ test "updateTimes" {
const stat_old = try file.stat(io);
// Set atime and mtime to 5s before
try file.updateTimes(
try file.setTimestamps(
io,
stat_old.atime.subDuration(.fromSeconds(5)),
stat_old.mtime.subDuration(.fromSeconds(5)),
);
+5 -4
View File
@@ -2,6 +2,7 @@ const builtin = @import("builtin");
const native_os = builtin.os.tag;
const std = @import("std");
const Io = std.Io;
const File = std.Io.File;
const process = std.process;
const windows = std.os.windows;
@@ -39,7 +40,7 @@ pub const Config = union(enum) {
/// This includes feature checks for ANSI escape codes and the Windows console API, as well as
/// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default.
/// Will attempt to enable ANSI escape code support if necessary/possible.
pub fn detect(file: File) Config {
pub fn detect(io: Io, file: File) Config {
const force_color: ?bool = if (builtin.os.tag == .wasi)
null // wasi does not support environment variables
else if (process.hasNonEmptyEnvVarConstant("NO_COLOR"))
@@ -51,7 +52,7 @@ pub const Config = union(enum) {
if (force_color == false) return .no_color;
if (file.enableAnsiEscapeCodes()) |_| {
if (file.enableAnsiEscapeCodes(io)) |_| {
return .escape_codes;
} else |_| {}
@@ -74,9 +75,9 @@ pub const Config = union(enum) {
reset_attributes: u16,
};
pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || std.Io.Writer.Error;
pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error;
pub fn setColor(conf: Config, w: *std.Io.Writer, color: Color) SetColorError!void {
pub fn setColor(conf: Config, w: *Io.Writer, color: Color) SetColorError!void {
nosuspend switch (conf) {
.no_color => return,
.escape_codes => {
+3 -1
View File
@@ -286,11 +286,13 @@ pub fn unlockStdErr() void {
pub fn lockStderrWriter(buffer: []u8) struct { *Writer, tty.Config } {
const global = struct {
var conf: ?tty.Config = null;
var single_threaded_io: Io.Threaded = .init_single_threaded;
};
const io = global.single_threaded_io.io();
const w = std.Progress.lockStderrWriter(buffer);
// The stderr lock also locks access to `global.conf`.
if (global.conf == null) {
global.conf = .detect(.stderr());
global.conf = .detect(io, .stderr());
}
return .{ w, global.conf.? };
}
+4 -3
View File
@@ -42,7 +42,7 @@ pub fn load(
var file = try path.root_dir.handle.openFile(io, path.sub_path, .{});
defer file.close(io);
var elf_file: ElfFile = try .load(gpa, file, null, &.none);
var elf_file: ElfFile = try .load(gpa, io, file, null, &.none);
errdefer elf_file.deinit(gpa);
if (elf_file.dwarf == null) return error.MissingDebugInfo;
@@ -58,7 +58,7 @@ pub fn load(
const path_str = try path.toString(gpa);
defer gpa.free(path_str);
var macho_file: MachOFile = try .load(gpa, path_str, arch);
var macho_file: MachOFile = try .load(gpa, io, path_str, arch);
errdefer macho_file.deinit(gpa);
return .{
@@ -85,6 +85,7 @@ pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError || error{U
pub fn resolveAddresses(
info: *Info,
gpa: Allocator,
io: Io,
/// Asserts the addresses are in ascending order.
sorted_pc_addrs: []const u64,
/// Asserts its length equals length of `sorted_pc_addrs`.
@@ -97,7 +98,7 @@ pub fn resolveAddresses(
// Resolving all of the addresses at once unfortunately isn't so easy in Mach-O binaries
// due to split debug information. For now, we'll just resolve the addreses one by one.
for (sorted_pc_addrs, output) |pc_addr, *src_loc| {
const dwarf, const dwarf_pc_addr = mf.getDwarfForAddress(gpa, pc_addr) catch |err| switch (err) {
const dwarf, const dwarf_pc_addr = mf.getDwarfForAddress(gpa, io, pc_addr) catch |err| switch (err) {
error.InvalidMachO, error.InvalidDwarf => return error.InvalidDebugInfo,
else => |e| return e,
};
+27 -12
View File
@@ -179,6 +179,8 @@ pub fn DebugAllocator(comptime config: Config) type {
total_requested_bytes: @TypeOf(total_requested_bytes_init) = total_requested_bytes_init,
requested_memory_limit: @TypeOf(requested_memory_limit_init) = requested_memory_limit_init,
mutex: @TypeOf(mutex_init) = mutex_init,
/// Set this value differently to affect how errors and leaks are logged.
tty_config: std.Io.tty.Config = .no_color,
const Self = @This();
@@ -458,9 +460,9 @@ pub fn DebugAllocator(comptime config: Config) type {
/// Emits log messages for leaks and then returns the number of detected leaks (0 if no leaks were detected).
pub fn detectLeaks(self: *Self) usize {
var leaks: usize = 0;
const tty_config = self.tty_config;
const tty_config: std.Io.tty.Config = .detect(.stderr());
var leaks: usize = 0;
for (self.buckets, 0..) |init_optional_bucket, size_class_index| {
var optional_bucket = init_optional_bucket;
@@ -533,10 +535,15 @@ pub fn DebugAllocator(comptime config: Config) type {
@memset(addr_buf[@min(st.index, addr_buf.len)..], 0);
}
fn reportDoubleFree(ret_addr: usize, alloc_stack_trace: StackTrace, free_stack_trace: StackTrace) void {
fn reportDoubleFree(
tty_config: std.Io.tty.Config,
ret_addr: usize,
alloc_stack_trace: StackTrace,
free_stack_trace: StackTrace,
) void {
@branchHint(.cold);
var addr_buf: [stack_n]usize = undefined;
const second_free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf);
const tty_config: std.Io.tty.Config = .detect(.stderr());
log.err("Double free detected. Allocation: {f} First free: {f} Second free: {f}", .{
std.debug.FormatStackTrace{
.stack_trace = alloc_stack_trace,
@@ -580,7 +587,7 @@ pub fn DebugAllocator(comptime config: Config) type {
if (config.retain_metadata and entry.value_ptr.freed) {
if (config.safety) {
reportDoubleFree(ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free));
reportDoubleFree(self.tty_config, ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free));
@panic("Unrecoverable double free");
} else {
unreachable;
@@ -588,9 +595,10 @@ pub fn DebugAllocator(comptime config: Config) type {
}
if (config.safety and old_mem.len != entry.value_ptr.bytes.len) {
@branchHint(.cold);
var addr_buf: [stack_n]usize = undefined;
const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf);
const tty_config: std.Io.tty.Config = .detect(.stderr());
const tty_config = self.tty_config;
log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
entry.value_ptr.bytes.len,
old_mem.len,
@@ -693,7 +701,7 @@ pub fn DebugAllocator(comptime config: Config) type {
if (config.retain_metadata and entry.value_ptr.freed) {
if (config.safety) {
reportDoubleFree(ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free));
reportDoubleFree(self.tty_config, ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free));
return;
} else {
unreachable;
@@ -701,9 +709,10 @@ pub fn DebugAllocator(comptime config: Config) type {
}
if (config.safety and old_mem.len != entry.value_ptr.bytes.len) {
@branchHint(.cold);
var addr_buf: [stack_n]usize = undefined;
const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf);
const tty_config: std.Io.tty.Config = .detect(.stderr());
const tty_config = self.tty_config;
log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
entry.value_ptr.bytes.len,
old_mem.len,
@@ -915,6 +924,7 @@ pub fn DebugAllocator(comptime config: Config) type {
if (!is_used) {
if (config.safety) {
reportDoubleFree(
self.tty_config,
return_address,
bucketStackTrace(bucket, slot_count, slot_index, .alloc),
bucketStackTrace(bucket, slot_count, slot_index, .free),
@@ -935,7 +945,8 @@ pub fn DebugAllocator(comptime config: Config) type {
var addr_buf: [stack_n]usize = undefined;
const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = return_address }, &addr_buf);
if (old_memory.len != requested_size) {
const tty_config: std.Io.tty.Config = .detect(.stderr());
@branchHint(.cold);
const tty_config = self.tty_config;
log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
requested_size,
old_memory.len,
@@ -950,7 +961,8 @@ pub fn DebugAllocator(comptime config: Config) type {
});
}
if (alignment != slot_alignment) {
const tty_config: std.Io.tty.Config = .detect(.stderr());
@branchHint(.cold);
const tty_config = self.tty_config;
log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {f} Free: {f}", .{
slot_alignment.toByteUnits(),
alignment.toByteUnits(),
@@ -1028,6 +1040,7 @@ pub fn DebugAllocator(comptime config: Config) type {
const is_used = @as(u1, @truncate(used_byte.* >> used_bit_index)) != 0;
if (!is_used) {
reportDoubleFree(
self.tty_config,
return_address,
bucketStackTrace(bucket, slot_count, slot_index, .alloc),
bucketStackTrace(bucket, slot_count, slot_index, .free),
@@ -1044,7 +1057,8 @@ pub fn DebugAllocator(comptime config: Config) type {
var addr_buf: [stack_n]usize = undefined;
const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = return_address }, &addr_buf);
if (memory.len != requested_size) {
const tty_config: std.Io.tty.Config = .detect(.stderr());
@branchHint(.cold);
const tty_config = self.tty_config;
log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{
requested_size,
memory.len,
@@ -1059,7 +1073,8 @@ pub fn DebugAllocator(comptime config: Config) type {
});
}
if (alignment != slot_alignment) {
const tty_config: std.Io.tty.Config = .detect(.stderr());
@branchHint(.cold);
const tty_config = self.tty_config;
log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {f} Free: {f}", .{
slot_alignment.toByteUnits(),
alignment.toByteUnits(),
+1 -1
View File
@@ -470,7 +470,7 @@ pub fn run(allocator: Allocator, io: Io, args: struct {
return .{
.stdout = try stdout.toOwnedSlice(allocator),
.stderr = try stderr.toOwnedSlice(allocator),
.term = try child.wait(),
.term = try child.wait(io),
};
}
+2 -2
View File
@@ -60,9 +60,9 @@ pub const Color = enum {
.off => .no_color,
};
}
pub fn detectTtyConf(color: Color) Io.tty.Config {
pub fn detectTtyConf(color: Color, io: Io) Io.tty.Config {
return switch (color) {
.auto => .detect(.stderr()),
.auto => .detect(io, .stderr()),
.on => .escape_codes,
.off => .no_color,
};
+3 -3
View File
@@ -51,10 +51,10 @@ pub const UpdateError = error{
} ||
codegen.GenerateSymbolError ||
Io.File.OpenError ||
Io.File.SetEndPosError ||
Io.File.LengthError ||
Io.File.CopyRangeError ||
Io.File.PReadError ||
Io.File.PWriteError;
Io.File.ReadPositionalError ||
Io.File.WritePositionalError;
pub const FlushError = UpdateError;
+1 -1
View File
@@ -28,7 +28,7 @@ writers: std.SinglyLinkedList,
pub const growth_factor = 4;
pub const Error = std.posix.MMapError || std.posix.MRemapError || Io.File.SetEndPosError || error{
pub const Error = std.posix.MMapError || std.posix.MRemapError || Io.File.LengthError || error{
NotFile,
SystemResources,
IsDir,
+14 -9
View File
@@ -162,17 +162,20 @@ var debug_allocator: std.heap.DebugAllocator(.{
.stack_trace_frames = build_options.mem_leak_frames,
}) = .init;
const use_debug_allocator = build_options.debug_gpa or
(native_os != .wasi and !builtin.link_libc and switch (builtin.mode) {
.Debug, .ReleaseSafe => true,
.ReleaseFast, .ReleaseSmall => false,
});
pub fn main() anyerror!void {
const gpa, const is_debug = gpa: {
if (build_options.debug_gpa) break :gpa .{ debug_allocator.allocator(), true };
if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false };
if (builtin.link_libc) break :gpa .{ std.heap.c_allocator, false };
break :gpa switch (builtin.mode) {
.Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true },
.ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false },
};
const gpa = gpa: {
if (use_debug_allocator) break :gpa debug_allocator.allocator();
if (native_os == .wasi) break :gpa std.heap.wasm_allocator;
if (builtin.link_libc) break :gpa std.heap.c_allocator;
break :gpa std.heap.smp_allocator;
};
defer if (is_debug) {
defer if (use_debug_allocator) {
_ = debug_allocator.deinit();
};
var arena_instance = std.heap.ArenaAllocator.init(gpa);
@@ -244,6 +247,8 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
threaded.stack_size = thread_stack_size;
const io = threaded.io();
debug_allocator.tty_config = .detect(io, .stderr());
const cmd = args[1];
const cmd_args = args[2..];
if (mem.eql(u8, cmd, "build-exe")) {
+2 -1
View File
@@ -868,9 +868,10 @@ fn testLayout(b: *Build, opts: Options) *Step {
}
fn testLinkDirectlyCppTbd(b: *Build, opts: Options) *Step {
const io = b.graph.io;
const test_step = addTestStep(b, "link-directly-cpp-tbd", opts);
const sdk = std.zig.system.darwin.getSdk(b.allocator, &opts.target.result) orelse
const sdk = std.zig.system.darwin.getSdk(b.allocator, io, &opts.target.result) orelse
@panic("macOS SDK is required to run the test");
const exe = addExecutable(b, opts, .{
+3 -2
View File
@@ -339,7 +339,7 @@ fn addFromDirInner(
var it = try iterable_dir.walk(ctx.arena);
var filenames: ArrayList([]const u8) = .empty;
while (try it.next()) |entry| {
while (try it.next(io)) |entry| {
if (entry.kind != .file) continue;
// Ignore stuff such as .swp files
@@ -431,9 +431,10 @@ fn addFromDirInner(
}
}
pub fn init(gpa: Allocator, arena: Allocator) Cases {
pub fn init(gpa: Allocator, arena: Allocator, io: Io) Cases {
return .{
.gpa = gpa,
.io = io,
.cases = .init(gpa),
.arena = arena,
};
+3 -1
View File
@@ -23,7 +23,9 @@ pub fn build(b: *std.Build) void {
}),
});
if (std.zig.system.darwin.getSdk(b.allocator, &target.result)) |sdk| {
const io = b.graph.io;
if (std.zig.system.darwin.getSdk(b.allocator, io, &target.result)) |sdk| {
b.sysroot = sdk;
exe.root_module.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/include" }) });
exe.root_module.addSystemFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/System/Library/Frameworks" }) });
@@ -9,10 +9,6 @@ pub fn build(b: *std.Build) void {
const optimize: std.builtin.OptimizeMode = .Debug;
const target = b.graph.host;
// The test requires getFdPath in order to to get the path of the
// File returned by openSelfExe
if (!std.os.isGetFdPathSupportedOnTarget(target.result.os)) return;
const main = b.addExecutable(.{
.name = "main",
.root_module = b.createModule(.{
+1 -1
View File
@@ -2632,7 +2632,7 @@ pub fn addCases(
const gpa = b.allocator;
const io = b.graph.io;
var cases = @import("src/Cases.zig").init(gpa, arena);
var cases = @import("src/Cases.zig").init(gpa, arena, io);
var dir = try b.build_root.handle.openDir(io, "test/cases", .{ .iterate = true });
defer dir.close(io);