1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
|
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
use hash;
use hash::fnv;
def TRACE_BUCKETS = 65535;
let traces: [TRACE_BUCKETS][]trace = [[]...];
export type trace = struct {
id: u64,
frames: *stackframe,
};
// Retrives a stack trace by its ID.
export fn trace_by_id(id: u64) (stackframe | void) = {
let bucket = &traces[id % TRACE_BUCKETS];
for (let trace &.. bucket) {
if (trace.id == id) {
return *trace.frames;
};
};
};
// Stores a stack trace and returns its ID. Stored stack traces are hashed and
// de-duplicated in a global stack list.
export fn trace_store(frame: stackframe) (u64 | nomem) = {
static let pc: []uintptr = [];
pc = pc[..0];
const prev_heap = begin_altheap();
defer end_altheap(prev_heap);
const hash = fnv::fnv64a();
for (true) {
hash::write(&hash, &frame_pc(frame): *[size(uintptr)]u8);
append(pc, frame_pc(frame))?;
match (next(frame)) {
case let next: stackframe =>
frame = next;
case done => break;
};
};
const id = fnv::sum64(&hash);
let bucket = &traces[id % TRACE_BUCKETS];
for (let trace &.. bucket) {
if (trace.id == id) {
return id;
};
};
let frames: []stackframe = alloc([stackframe { ... }...], len(pc) + 1)?;
for (let i = 0z; i < len(pc); i += 1) {
frames[i] = mkframe(&frames[i + 1], pc[i]);
};
append(bucket, trace {
id = id,
frames = &frames[0],
})?;
return id;
};
|