1/* @title: Stack Depot */
2#pragma once
3#include <dbg.h>
4#include <math/hash.h>
5#include <mem/alloc.h>
6#include <structures/list.h>
7#include <sync/spinlock.h>
8#include <types/refcount.h>
9
10/* This is a single item in the hash table, and the idea here is that
11 * when the current stack matches one of these, we return the existing one */
12struct stack_depot_record {
13 struct list_head hash_list;
14 refcount_t refcount;
15 size_t num_entries;
16 uint32_t hash;
17
18 /* TODO: Different const for this */
19 uintptr_t entries[STACK_TRACE_MAX_DEPTH];
20};
21
22struct stack_depot_record_chain {
23 struct list_head list;
24 struct spinlock lock;
25};
26
27#define STACK_DEPOT_HASH_SIZE 2048
28
29/* TODO: Someday we'll implement fixed_size_range support for this */
30#define STACK_DEPOT_ALLOW_ALLOC_FLAG ALLOC_FLAG_AVAIL_BIT(0)
31
32struct stack_depot_globals {
33 uint32_t starting_seed;
34 struct stack_depot_record_chain chains[STACK_DEPOT_HASH_SIZE];
35 _Atomic size_t num_records;
36};
37
38extern struct stack_depot_globals stack_depot_global;
39
40static inline uint32_t stack_depot_hash(uintptr_t *entries,
41 size_t num_entries) {
42 size_t entries_to_trace = num_entries > STACK_TRACE_MAX_DEPTH
43 ? STACK_TRACE_MAX_DEPTH
44 : num_entries;
45
46 size_t len = entries_to_trace * 8;
47
48 return hash_murmur3_32(key: entries, len, seed: stack_depot_global.starting_seed);
49}
50
51void stack_depot_init();
52stack_handle_t stack_depot_save(uintptr_t *entries, size_t num_entries,
53 enum alloc_flags flags);
54struct stack_depot_record *stack_depot_get_record(stack_handle_t key);
55size_t stack_depot_read(stack_handle_t key, uintptr_t *entries);
56void stack_depot_print(stack_handle_t key);
57void stack_depot_put(stack_handle_t key);
58stack_handle_t stack_depot_save_current();
59
60static inline size_t stack_depot_get_record_count() {
61 return atomic_load(&stack_depot_global.num_records);
62}
63