diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2025-09-19 13:35:12 +0100 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2025-09-30 13:44:55 +0100 |
| commit | 099a95041054e456ebefbd75f6a4f9f6961002be (patch) | |
| tree | 6293805ebdf2664d5b5906e614297972a7c8da6f /lib/std/debug/SelfInfo/WindowsModule.zig | |
| parent | 9c1821d3bfadc5eddd4dff271a4920c03ee0ffea (diff) | |
| download | zig-099a95041054e456ebefbd75f6a4f9f6961002be.tar.gz zig-099a95041054e456ebefbd75f6a4f9f6961002be.zip | |
std.debug.SelfInfo: thread safety
This has been a TODO for ages, but in the past it didn't really matter
because stack traces are typically printed to stderr for which a mutex
is held so in practice there was a mutex guarding usage of `SelfInfo`.
However, now that `SelfInfo` is also used for simply capturing traces,
thread safety is needed. Instead of just a single mutex, though, there
are a couple of different mutexes involved; this helps make critical
sections smaller, particularly when unwinding the stack as `unwindFrame`
doesn't typically need to hold any lock at all.
Diffstat (limited to 'lib/std/debug/SelfInfo/WindowsModule.zig')
| -rw-r--r-- | lib/std/debug/SelfInfo/WindowsModule.zig | 23 |
1 files changed, 19 insertions, 4 deletions
diff --git a/lib/std/debug/SelfInfo/WindowsModule.zig b/lib/std/debug/SelfInfo/WindowsModule.zig index 1fdf69b2a0..1f4139583e 100644 --- a/lib/std/debug/SelfInfo/WindowsModule.zig +++ b/lib/std/debug/SelfInfo/WindowsModule.zig @@ -9,14 +9,14 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) std.debug.Sel if (lookupInCache(cache, address)) |m| return m; { // Check a new module hasn't been loaded + cache.rwlock.lock(); + defer cache.rwlock.unlock(); cache.modules.clearRetainingCapacity(); - const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0); if (handle == windows.INVALID_HANDLE_VALUE) { return windows.unexpectedError(windows.GetLastError()); } defer windows.CloseHandle(handle); - var entry: windows.MODULEENTRY32 = undefined; entry.dwSize = @sizeOf(windows.MODULEENTRY32); if (windows.kernel32.Module32First(handle, &entry) != 0) { @@ -30,12 +30,18 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) std.debug.Sel return error.MissingDebugInfo; } pub fn getSymbolAtAddress(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, address: usize) std.debug.SelfInfo.Error!std.debug.Symbol { + // The `Pdb` API doesn't really allow us *any* thread-safe access, and the `Dwarf` API isn't + // great for it either; just lock the whole thing. + di.mutex.lock(); + defer di.mutex.unlock(); + if (!di.loaded) module.loadDebugInfo(gpa, di) catch |err| switch (err) { error.OutOfMemory, error.InvalidDebugInfo, error.MissingDebugInfo, error.Unexpected => |e| return e, error.FileNotFound => return error.MissingDebugInfo, error.UnknownPDBVersion => return error.UnsupportedDebugInfo, else => return error.ReadFailed, }; + // Translate the runtime address into a virtual address into the module const vaddr = address - module.base_address; @@ -50,7 +56,9 @@ pub fn getSymbolAtAddress(module: *const WindowsModule, gpa: Allocator, di: *Deb return error.MissingDebugInfo; } -fn lookupInCache(cache: *const LookupCache, address: usize) ?WindowsModule { +fn lookupInCache(cache: *LookupCache, address: usize) ?WindowsModule { + cache.rwlock.lockShared(); + defer cache.rwlock.unlockShared(); for (cache.modules.items) |*entry| { const base_address = @intFromPtr(entry.modBaseAddr); if (address >= base_address and address < base_address + entry.modBaseSize) { @@ -182,13 +190,19 @@ fn loadDebugInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo) ! di.loaded = true; } pub const LookupCache = struct { + rwlock: std.Thread.RwLock, modules: std.ArrayListUnmanaged(windows.MODULEENTRY32), - pub const init: LookupCache = .{ .modules = .empty }; + pub const init: LookupCache = .{ + .rwlock = .{}, + .modules = .empty, + }; pub fn deinit(lc: *LookupCache, gpa: Allocator) void { lc.modules.deinit(gpa); } }; pub const DebugInfo = struct { + mutex: std.Thread.Mutex, + loaded: bool, coff_image_base: u64, @@ -205,6 +219,7 @@ pub const DebugInfo = struct { coff_section_headers: []coff.SectionHeader, pub const init: DebugInfo = .{ + .mutex = .{}, .loaded = false, .coff_image_base = undefined, .mapped_file = null, |
