Backlog/engine/core/src/stacks.zig

195 lines
6.2 KiB
Zig

const std = @import("std");
const core = @import("core.zig");
const BumpArena = core.BumpArena;
const builtin = @import("builtin");
pub fn hashStackList(stack: []const usize) u32 {
var c: u32 = 0;
for (stack) |l| {
c = combineHash32(c, hashPtr(l));
}
return c;
}
pub inline fn combineHash32(lhs: u32, rhs: u32) u32 {
return lhs ^ (rhs +% 0x9e3779b9 +% (lhs << 6) +% (lhs >> 2));
}
pub inline fn hashPtr(value: usize) u32 {
var k = value +% 1;
if (@sizeOf(@TypeOf(value)) == 8) {
k = (~k) +% (k << 18);
k = k ^ std.math.rotr(usize, k, 31);
k = @mulWithOverflow(k, 21)[0];
k = k ^ std.math.rotr(usize, k, 11);
k = k +% (k << 6);
k = k ^ std.math.rotr(usize, k, 22);
return @truncate(k);
} else {}
}
test "1 million stacks." {
// see how fast i can walk 1 million stacks
var timer = std.time.Timer.start() catch unreachable;
const startTime = timer.read();
for (0..100000) |_| {
core.stacks.pushCallStack();
}
const endTime = timer.read();
std.debug.print("timeElapsed 100k callstacks {d}s", .{@as(f64, @floatFromInt(endTime - startTime)) / 1000000000});
}
test "test hash64s" {
const pointers: []const usize = &.{
0x0,
0x1,
0xffffffff1eed1234,
0xffffffff_ffffffff,
0xffffffff_ffffffff - 1,
};
var combined: u32 = 0;
for (pointers) |l| {
const h = hashPtr(l);
std.debug.print("l = {x}, hash = {x}\n", .{ l, h });
combined = combineHash32(combined, h);
}
std.debug.print("combined hash = {x} ({x})\n", .{ combined, hashStackList(pointers) });
}
pub const StackCompactor = struct {
allocator: std.mem.Allocator,
stackArena: BumpArena,
stackMap: std.AutoHashMapUnmanaged(u32, *CallStack) = .{},
const CallStack = struct {
pointers: []usize,
debugStr: []?[]u8,
};
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
.stackArena = try BumpArena.init(allocator),
};
return self;
}
pub fn addNewCallstack(self: *@This(), stack: []const usize) !u32 {
const hash = hashStackList(stack);
const bumpAllocator = self.stackArena.allocator();
if (!self.stackMap.contains(hash)) {
const ownedStack: *CallStack = try bumpAllocator.create(CallStack);
ownedStack.pointers = try bumpAllocator.dupe(usize, stack);
ownedStack.debugStr = try bumpAllocator.alloc(?[]u8, stack.len);
const debug_info = std.debug.getSelfDebugInfo() catch {
try self.stackMap.put(self.allocator, hash, ownedStack);
return hash;
};
for (stack, 0..) |address, i| {
ownedStack.debugStr[i] = null;
const module = debug_info.getModuleForAddress(address) catch continue;
const symbol_info = module.getSymbolAtAddress(debug_info.allocator, address) catch continue;
if (symbol_info.source_location) |line_info| {
ownedStack.debugStr[i] = try std.fmt.allocPrintSentinel(
bumpAllocator,
"{d}> @0x{x} symbol_name: {s} {s} > {s}: {d}",
.{ i, address, symbol_info.name, symbol_info.compile_unit_name, line_info.file_name, line_info.line },
0,
);
} else {
ownedStack.debugStr[i] = try std.fmt.allocPrintSentinel(bumpAllocator, "{d}> @0x{x} symbol_name: {s} {s} > no file info", .{ i, address, symbol_info.name, symbol_info.compile_unit_name }, 0);
}
}
try self.stackMap.put(self.allocator, hash, ownedStack);
}
return hash;
}
pub fn deinit(self: @This()) void {
self.stackList.deinit(self.allocator);
self.allocator.destroy(self);
}
pub fn getCallStack(self: *@This()) u32 {
if (builtin.os.tag != .windows) {
return 0;
}
var context: std.debug.ThreadContext = undefined;
const has_context = std.debug.getContext(&context);
if (!has_context) {
return 0;
}
var addr_buf: [1024]usize = undefined;
const n = std.debug.walkStackWindows(addr_buf[0..], &context);
return self.addNewCallstack(addr_buf[0..n]) catch unreachable;
}
};
var stackCompactor: *core.StackCompactor = undefined;
pub fn initStackCompactor() void {
stackCompactor = core.StackCompactor.create(std.heap.c_allocator) catch unreachable;
}
pub inline fn pushCallStack() void {
var context: std.debug.ThreadContext = undefined;
const has_context = std.debug.getContext(&context);
if (!has_context) {
return;
}
var addr_buf: [1024]usize = undefined;
const n = std.debug.walkStackWindows(addr_buf[0..], &context);
stackCompactor.addNewCallstack(addr_buf[0..n]) catch unreachable;
}
pub fn getStackCompactor() *StackCompactor {
return stackCompactor;
}
pub fn walkAndPrintStack() void {
var context: std.debug.ThreadContext = undefined;
const has_context = std.debug.getContext(&context);
if (!has_context) {
return;
}
var addr_buf: [1024]usize = undefined;
const n = std.debug.walkStackWindows(addr_buf[0..], &context);
const debug_info = std.debug.getSelfDebugInfo() catch {
return;
};
var i: u32 = 0;
while (i < n) : (i += 1) {
const address = addr_buf[i];
const module = debug_info.getModuleForAddress(address) catch continue;
const symbol_info = module.getSymbolAtAddress(debug_info.allocator, address) catch continue;
std.debug.print("@0x{x} symbol_name: {s} {s} > ", .{ address, symbol_info.symbol_name, symbol_info.compile_unit_name });
if (symbol_info.line_info) |line_info| {
std.debug.print(" {s}:{d}\n", .{ line_info.file_name, line_info.line });
} else {
std.debug.print(" no file info\n", .{});
}
}
}