1use alloc::boxed::Box;
2
3use crate::unwind_rule::UnwindRule;
4
5const CACHE_ENTRY_COUNT: usize = 509;
6
7pub struct RuleCache<R: UnwindRule> {
8 entries: Box<[Option<CacheEntry<R>>; CACHE_ENTRY_COUNT]>,
9 stats: CacheStats,
10}
11
12impl<R: UnwindRule> RuleCache<R> {
13 pub fn new() -> Self {
14 Self {
15 entries: Box::new([None; CACHE_ENTRY_COUNT]),
16 stats: CacheStats::new(),
17 }
18 }
19
20 pub fn lookup(&mut self, address: u64, modules_generation: u16) -> CacheResult<R> {
21 let slot = (address % (CACHE_ENTRY_COUNT as u64)) as u16;
22 match &self.entries[slot as usize] {
23 None => {
24 self.stats.miss_empty_slot_count += 1;
25 }
26 Some(entry) => {
27 if entry.modules_generation == modules_generation {
28 if entry.address == address {
29 self.stats.hit_count += 1;
30 return CacheResult::Hit(entry.unwind_rule);
31 } else {
32 self.stats.miss_wrong_address_count += 1;
33 }
34 } else {
35 self.stats.miss_wrong_modules_count += 1;
36 }
37 }
38 }
39 CacheResult::Miss(CacheHandle {
40 slot,
41 address,
42 modules_generation,
43 })
44 }
45
46 pub fn insert(&mut self, handle: CacheHandle, unwind_rule: R) {
47 let CacheHandle {
48 slot,
49 address,
50 modules_generation,
51 } = handle;
52 self.entries[slot as usize] = Some(CacheEntry {
53 address,
54 modules_generation,
55 unwind_rule,
56 });
57 }
58
59 pub fn stats(&self) -> CacheStats {
61 self.stats
62 }
63}
64
65pub enum CacheResult<R: UnwindRule> {
66 Miss(CacheHandle),
67 Hit(R),
68}
69
70pub struct CacheHandle {
71 slot: u16,
72 address: u64,
73 modules_generation: u16,
74}
75
76const _: () = assert!(
77 CACHE_ENTRY_COUNT as u64 <= u16::MAX as u64,
78 "u16 should be sufficient to store the cache slot index"
79);
80
81#[derive(Clone, Copy, Debug)]
82struct CacheEntry<R: UnwindRule> {
83 address: u64,
84 modules_generation: u16,
85 unwind_rule: R,
86}
87
88#[derive(Default, Debug, Clone, Copy)]
90pub struct CacheStats {
91 pub hit_count: u64,
93 pub miss_empty_slot_count: u64,
95 pub miss_wrong_modules_count: u64,
101 pub miss_wrong_address_count: u64,
104}
105
106impl CacheStats {
107 pub fn new() -> Self {
109 Default::default()
110 }
111
112 pub fn total(&self) -> u64 {
114 self.hits() + self.misses()
115 }
116
117 pub fn hits(&self) -> u64 {
119 self.hit_count
120 }
121
122 pub fn misses(&self) -> u64 {
124 self.miss_empty_slot_count + self.miss_wrong_modules_count + self.miss_wrong_address_count
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use crate::{aarch64::UnwindRuleAarch64, x86_64::UnwindRuleX86_64};
131
132 use super::*;
133
134 #[test]
136 fn test_cache_entry_size() {
137 assert_eq!(
138 core::mem::size_of::<Option<CacheEntry<UnwindRuleX86_64>>>(),
139 16
140 );
141 assert_eq!(
142 core::mem::size_of::<Option<CacheEntry<UnwindRuleAarch64>>>(),
143 24 );
145 }
146}