195 lines
6.2 KiB
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", .{});
|
|
}
|
|
}
|
|
}
|