framehop/
cache.rs

1use alloc::boxed::Box;
2
3use crate::{rule_cache::RuleCache, unwind_rule::UnwindRule};
4
5pub use crate::rule_cache::CacheStats;
6
7/// A trait which lets you opt into allocation-free unwinding. The two implementations of
8/// this trait are [`MustNotAllocateDuringUnwind`] and [`MayAllocateDuringUnwind`].
9pub trait AllocationPolicy {
10    type GimliUnwindContextStorage<R: gimli::ReaderOffset>: gimli::UnwindContextStorage<R>;
11    type GimliEvaluationStorage<R: gimli::Reader>: gimli::EvaluationStorage<R>;
12}
13
14/// Require allocation-free unwinding. This is one of the two [`AllocationPolicy`]
15/// implementations.
16///
17/// Using this means that the unwinder cache takes up more memory, because it preallocates
18/// space for DWARF CFI unwind table row evaluation and for DWARF CFI expression evaluation.
19/// And because those preallocations are of a fixed size, it is possible that this fixed
20/// size is not large enough for certain DWARF unwinding tasks.
21pub struct MustNotAllocateDuringUnwind;
22
23/// This is only used in the implementation of [MustNotAllocateDuringUnwind] and
24/// is not intended to be used by the outside world.
25#[doc(hidden)]
26pub struct StoreOnStack;
27
28impl<RO: gimli::ReaderOffset> gimli::UnwindContextStorage<RO> for StoreOnStack {
29    type Rules = [(gimli::Register, gimli::RegisterRule<RO>); 192];
30    type Stack = [gimli::UnwindTableRow<RO, Self>; 4];
31}
32
33impl<R: gimli::Reader> gimli::EvaluationStorage<R> for StoreOnStack {
34    type Stack = [gimli::Value; 64];
35    type ExpressionStack = [(R, R); 4];
36    type Result = [gimli::Piece<R>; 1];
37}
38
39impl AllocationPolicy for MustNotAllocateDuringUnwind {
40    type GimliUnwindContextStorage<R: gimli::ReaderOffset> = StoreOnStack;
41    type GimliEvaluationStorage<R: gimli::Reader> = StoreOnStack;
42}
43
44/// Allow allocation during unwinding. This is one of the two [`AllocationPolicy`]
45/// implementations.
46///
47/// This is the preferred policy because it saves memory and places no limitations on
48/// DWARF CFI evaluation.
49pub struct MayAllocateDuringUnwind;
50impl AllocationPolicy for MayAllocateDuringUnwind {
51    type GimliUnwindContextStorage<R: gimli::ReaderOffset> = gimli::StoreOnHeap;
52    type GimliEvaluationStorage<R: gimli::Reader> = gimli::StoreOnHeap;
53}
54
55/// The unwinder cache. This needs to be created upfront before unwinding. During
56/// unwinding, the unwinder needs exclusive access to this cache.
57///
58/// A single unwinder cache can be used with multiple unwinders alternatingly.
59///
60/// The cache stores unwind rules for addresses it has seen before, and it stores the
61/// unwind context which gimli needs for DWARF CFI evaluation.
62pub struct Cache<R: UnwindRule, P: AllocationPolicy = MayAllocateDuringUnwind> {
63    pub(crate) gimli_unwind_context:
64        Box<gimli::UnwindContext<usize, P::GimliUnwindContextStorage<usize>>>,
65    pub(crate) rule_cache: RuleCache<R>,
66}
67
68impl<R: UnwindRule, P: AllocationPolicy> Cache<R, P> {
69    pub fn new() -> Self {
70        Self {
71            gimli_unwind_context: Box::new(gimli::UnwindContext::new_in()),
72            rule_cache: RuleCache::new(),
73        }
74    }
75}
76
77impl<R: UnwindRule, P: AllocationPolicy> Default for Cache<R, P> {
78    fn default() -> Self {
79        Self::new()
80    }
81}