mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 01:24:49 +01:00
std.Io.Threaded: debug file handle leaks
This commit is contained in:
parent
4e806f2521
commit
479e62e8ee
1 changed files with 64 additions and 4 deletions
|
|
@ -69,6 +69,19 @@ random_file: RandomFile = .{},
|
||||||
|
|
||||||
csprng: Csprng = .{},
|
csprng: Csprng = .{},
|
||||||
|
|
||||||
|
/// Tracks open file handles for debugging purposes.
|
||||||
|
open_file_map: OpenFileMap = if (OpenFileMap != void) .{} else {},
|
||||||
|
|
||||||
|
pub const OpenFileMap = switch (builtin.mode) {
|
||||||
|
.Debug => struct {
|
||||||
|
map: std.AutoArrayHashMapUnmanaged(File.Handle, StackTrace) = .empty,
|
||||||
|
oom: bool = false,
|
||||||
|
},
|
||||||
|
else => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const StackTrace = [6]usize;
|
||||||
|
|
||||||
pub const Csprng = struct {
|
pub const Csprng = struct {
|
||||||
rng: std.Random.DefaultCsprng = .{
|
rng: std.Random.DefaultCsprng = .{
|
||||||
.state = undefined,
|
.state = undefined,
|
||||||
|
|
@ -1315,6 +1328,7 @@ pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(t: *Threaded) void {
|
pub fn deinit(t: *Threaded) void {
|
||||||
|
const gpa = t.allocator;
|
||||||
t.join();
|
t.join();
|
||||||
if (is_windows and t.wsa.status == .initialized) {
|
if (is_windows and t.wsa.status == .initialized) {
|
||||||
if (ws2_32.WSACleanup() != 0) recoverableOsBugDetected();
|
if (ws2_32.WSACleanup() != 0) recoverableOsBugDetected();
|
||||||
|
|
@ -1325,6 +1339,22 @@ pub fn deinit(t: *Threaded) void {
|
||||||
}
|
}
|
||||||
t.null_file.deinit();
|
t.null_file.deinit();
|
||||||
t.random_file.deinit();
|
t.random_file.deinit();
|
||||||
|
if (OpenFileMap != void) {
|
||||||
|
for (t.open_file_map.map.keys(), t.open_file_map.map.values()) |handle, *value| {
|
||||||
|
const st: std.builtin.StackTrace = .{
|
||||||
|
.instruction_addresses = value,
|
||||||
|
.index = value.len,
|
||||||
|
};
|
||||||
|
std.log.err("file handle {any} leaked: {f}", .{
|
||||||
|
handle,
|
||||||
|
std.debug.FormatStackTrace{
|
||||||
|
.stack_trace = st,
|
||||||
|
.terminal_mode = std.log.terminalMode(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
t.open_file_map.map.deinit(gpa);
|
||||||
|
}
|
||||||
t.* = undefined;
|
t.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3863,9 +3893,35 @@ fn dirOpenFilePosix(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackOpenFile(t, fd, @returnAddress());
|
||||||
return .{ .handle = fd };
|
return .{ .handle = fd };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trackOpenFile(t: *Threaded, handle: File.Handle, ra: usize) void {
|
||||||
|
if (OpenFileMap == void) return;
|
||||||
|
t.mutex.lock();
|
||||||
|
defer t.mutex.unlock();
|
||||||
|
if (t.open_file_map.oom) return;
|
||||||
|
const gop = t.open_file_map.map.getOrPut(t.allocator, handle) catch |err| switch (err) {
|
||||||
|
error.OutOfMemory => {
|
||||||
|
t.open_file_map.oom = true;
|
||||||
|
t.open_file_map.map.clearAndFree(t.allocator);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert(!gop.found_existing);
|
||||||
|
const st = std.debug.captureCurrentStackTrace(.{ .first_address = ra }, gop.value_ptr);
|
||||||
|
@memset(gop.value_ptr[@min(st.index, gop.value_ptr.len)..], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trackCloseFile(t: *Threaded, handle: File.Handle) void {
|
||||||
|
if (OpenFileMap == void) return;
|
||||||
|
t.mutex.lock();
|
||||||
|
defer t.mutex.unlock();
|
||||||
|
if (t.open_file_map.oom) return;
|
||||||
|
assert(t.open_file_map.map.swapRemove(handle));
|
||||||
|
}
|
||||||
|
|
||||||
fn dirOpenFileWindows(
|
fn dirOpenFileWindows(
|
||||||
userdata: ?*anyopaque,
|
userdata: ?*anyopaque,
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
|
|
@ -3873,14 +3929,14 @@ fn dirOpenFileWindows(
|
||||||
flags: File.OpenFlags,
|
flags: File.OpenFlags,
|
||||||
) File.OpenError!File {
|
) File.OpenError!File {
|
||||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||||
_ = t;
|
|
||||||
const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
|
const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
|
||||||
const sub_path_w = sub_path_w_array.span();
|
const sub_path_w = sub_path_w_array.span();
|
||||||
const dir_handle = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle;
|
const dir_handle = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle;
|
||||||
return dirOpenFileWtf16(dir_handle, sub_path_w, flags);
|
return dirOpenFileWtf16(t, dir_handle, sub_path_w, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dirOpenFileWtf16(
|
pub fn dirOpenFileWtf16(
|
||||||
|
t: *Threaded,
|
||||||
dir_handle: ?windows.HANDLE,
|
dir_handle: ?windows.HANDLE,
|
||||||
sub_path_w: [:0]const u16,
|
sub_path_w: [:0]const u16,
|
||||||
flags: File.OpenFlags,
|
flags: File.OpenFlags,
|
||||||
|
|
@ -4022,6 +4078,7 @@ pub fn dirOpenFileWtf16(
|
||||||
.ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
|
.ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
|
||||||
else => |status| return syscall.unexpectedNtstatus(status),
|
else => |status| return syscall.unexpectedNtstatus(status),
|
||||||
};
|
};
|
||||||
|
trackOpenFile(t, handle, @returnAddress());
|
||||||
return .{ .handle = handle };
|
return .{ .handle = handle };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4114,6 +4171,7 @@ fn dirOpenFileWasi(
|
||||||
if (is_dir) return error.IsDir;
|
if (is_dir) return error.IsDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackOpenFile(t, fd, @returnAddress());
|
||||||
return .{ .handle = fd };
|
return .{ .handle = fd };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7856,8 +7914,10 @@ fn dirHardLink(
|
||||||
|
|
||||||
fn fileClose(userdata: ?*anyopaque, files: []const File) void {
|
fn fileClose(userdata: ?*anyopaque, files: []const File) void {
|
||||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||||
_ = t;
|
for (files) |file| {
|
||||||
for (files) |file| posix.close(file.handle);
|
trackCloseFile(t, file.handle);
|
||||||
|
posix.close(file.handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileReadStreaming = switch (native_os) {
|
const fileReadStreaming = switch (native_os) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue