3
3
use crate :: {
4
4
AnyRef , ExnRef , ExternRef , Func , Instance , Module , Val , ValType ,
5
5
store:: { AutoAssertNoGc , StoreOpaque } ,
6
- vm:: { Backtrace , VMContext } ,
6
+ vm:: { CurrentActivationBacktrace , VMContext } ,
7
7
} ;
8
8
use alloc:: vec:: Vec ;
9
- use core:: { ffi:: c_void, ops :: ControlFlow , ptr:: NonNull } ;
9
+ use core:: { ffi:: c_void, ptr:: NonNull } ;
10
10
use wasmtime_environ:: {
11
11
DefinedFuncIndex , FrameInstPos , FrameStackShape , FrameStateSlot , FrameStateSlotOffset ,
12
12
FrameTableDescriptorIndex , FrameValType , FuncKey ,
@@ -33,18 +33,11 @@ impl StoreOpaque {
33
33
return None ;
34
34
}
35
35
36
- let mut frames = vec ! [ ] ;
37
- Backtrace :: trace ( self , |frame| {
38
- // `is_trapping_frame == false`: for now, we do not yet
39
- // support capturing stack values after a trap, so the PC
40
- // we use to look up metadata is always a "post-position"
41
- // PC, i.e., a call's return address.
42
- frames. extend ( VirtualFrame :: decode ( self , frame, false ) ) ;
43
- ControlFlow :: Continue ( ( ) )
44
- } ) ;
36
+ let iter = unsafe { CurrentActivationBacktrace :: new ( self ) } ;
45
37
Some ( StackView {
46
- store : self ,
47
- frames,
38
+ iter,
39
+ is_trapping_frame : false ,
40
+ frames : vec ! [ ] ,
48
41
} )
49
42
}
50
43
}
@@ -54,23 +47,53 @@ impl StoreOpaque {
54
47
/// See the documentation on `Store::stack_value` for more information
55
48
/// about which frames this view will show.
56
49
pub struct StackView < ' a > {
57
- /// Mutable borrow held to the store .
50
+ /// Iterator over frames .
58
51
///
59
- /// This both ensures that the stack does not mutate while we're
60
- /// observing it (any borrow would do), and lets us create
61
- /// host-API GC references as values that are references are read
62
- /// off of the stack (a mutable borrow is needed for this).
63
- store : & ' a mut StoreOpaque ,
52
+ /// This iterator owns the store while the view exists (accessible
53
+ /// as `iter.store`).
54
+ iter : CurrentActivationBacktrace < ' a > ,
64
55
65
- /// Pre-enumerated frames. We precompute this rather than walking
66
- /// a true iterator finger up the stack (e.g., current FP and
67
- /// current `CallThreadState`) because our existing unwinder logic
68
- /// is written in a visit-with-closure style; and users of this
69
- /// API are likely to visit every frame anyway, so
70
- /// sparseness/efficiency is not a main concern here.
56
+ /// Is the next frame to be visited by the iterator a trapping
57
+ /// frame?
58
+ ///
59
+ /// This alters how we interpret `pc`: for a trap, we look at the
60
+ /// instruction that *starts* at `pc`, while for all frames
61
+ /// further up the stack (i.e., at a callsite), we look at teh
62
+ /// instruction that *ends* at `pc`.
63
+ is_trapping_frame : bool ,
64
+
65
+ /// Virtual frame queue: decoded from `iter`, not yet
66
+ /// yielded. Innermost frame on top (last).
67
+ ///
68
+ /// This is only non-empty when there is more than one virtual
69
+ /// frame in a physical frame (i.e., for inlining); thus, its size
70
+ /// is bounded by our inlining depth.
71
71
frames : Vec < VirtualFrame > ,
72
72
}
73
73
74
+ impl < ' a > Iterator for StackView < ' a > {
75
+ type Item = FrameView ;
76
+ fn next ( & mut self ) -> Option < Self :: Item > {
77
+ // If there are no virtual frames to yield, take and decode
78
+ // the next physical frame.
79
+ //
80
+ // Note that `if` rather than `while` here, and the assert
81
+ // that we get some virtual frames back, enforce the invariant
82
+ // that each physical frame decodes to at least one virtual
83
+ // frame (i.e., there are no physical frames for interstitial
84
+ // functions or other things that we completely ignore). If
85
+ // this ever changes, we can remove the assert and convert
86
+ // this to a loop that polls until it finds virtual frames.
87
+ if self . frames . is_empty ( ) {
88
+ let next_frame = self . iter . next ( ) ?;
89
+ self . frames = VirtualFrame :: decode ( self . iter . store , next_frame, self . is_trapping_frame ) ;
90
+ self . is_trapping_frame = false ;
91
+ }
92
+
93
+ self . frames . pop ( ) . map ( move |vf| FrameView :: new ( vf) )
94
+ }
95
+ }
96
+
74
97
/// Internal data pre-computed for one stack frame.
75
98
///
76
99
/// This combines physical frame info (pc, fp) with the module this PC
@@ -93,32 +116,9 @@ struct VirtualFrame {
93
116
stack_shape : FrameStackShape ,
94
117
}
95
118
96
- /// A view of a frame that can decode values in that frame.
97
- pub struct FrameView < ' a > {
98
- frame_state_slot : FrameStateSlot < ' a > ,
99
- store : & ' a mut StoreOpaque ,
100
- slot_addr : usize ,
101
- wasm_pc : u32 ,
102
- stack : Vec < ( FrameStateSlotOffset , FrameValType ) > ,
103
- }
104
-
105
- impl < ' a > StackView < ' a > {
106
- /// Get a handle to a specific frame.
107
- ///
108
- /// # Panics
109
- ///
110
- /// Panics if the index is out of range.
111
- pub fn frame ( & mut self , index : usize ) -> FrameView < ' _ > {
112
- FrameView :: new ( self . store , & self . frames [ index] )
113
- }
114
-
115
- /// Get the number of frames viewable on this stack.
116
- pub fn len ( & self ) -> usize {
117
- self . frames . len ( )
118
- }
119
- }
120
-
121
119
impl VirtualFrame {
120
+ /// Return virtual frames corresponding to a physical frame, from
121
+ /// outermost to innermost.
122
122
fn decode ( store : & StoreOpaque , frame : Frame , is_trapping_frame : bool ) -> Vec < VirtualFrame > {
123
123
let module = store
124
124
. modules ( )
@@ -137,25 +137,43 @@ impl VirtualFrame {
137
137
return vec ! [ ] ;
138
138
} ;
139
139
140
- let mut frames : Vec < _ > = program_points
140
+ program_points
141
141
. map ( |( wasm_pc, frame_descriptor, stack_shape) | VirtualFrame {
142
142
fp : frame. fp ( ) ,
143
143
module : module. clone ( ) ,
144
144
wasm_pc,
145
145
frame_descriptor,
146
146
stack_shape,
147
147
} )
148
- . collect ( ) ;
149
-
150
- // Reverse the frames so we return them inside-out, matching
151
- // the bottom-up stack traversal order.
152
- frames. reverse ( ) ;
153
- frames
148
+ . collect ( )
154
149
}
155
150
}
156
151
157
- impl < ' a > FrameView < ' a > {
158
- fn new ( store : & ' a mut StoreOpaque , frame : & ' a VirtualFrame ) -> Self {
152
+ /// A view of a frame that can decode values in that frame.
153
+ pub struct FrameView {
154
+ slot_addr : usize ,
155
+ func_key : FuncKey ,
156
+ wasm_pc : u32 ,
157
+ /// Shape of locals in this frame.
158
+ ///
159
+ /// We need to store this locally because `FrameView` cannot
160
+ /// borrow the store: it needs a mut borrow, and an iterator
161
+ /// cannot yield the same mut borrow multiple times because it
162
+ /// cannot control the lifetime of the values it yields (the
163
+ /// signature of `next()` does not bound the return value to the
164
+ /// `&mut self` arg).
165
+ locals : Vec < ( FrameStateSlotOffset , FrameValType ) > ,
166
+ /// Shape of the stack slots at this program point in this frame.
167
+ ///
168
+ /// In addition to the borrowing-related reason above, we also
169
+ /// materialize this because we want to provide O(1) access to the
170
+ /// stack by depth, and the frame slot descriptor stores info in a
171
+ /// linked-list (actually DAG, with dedup'ing) way.
172
+ stack : Vec < ( FrameStateSlotOffset , FrameValType ) > ,
173
+ }
174
+
175
+ impl FrameView {
176
+ fn new ( frame : VirtualFrame ) -> Self {
159
177
let frame_table = frame. module . frame_table ( ) ;
160
178
// Parse the frame descriptor.
161
179
let ( data, slot_to_fp_offset) = frame_table
@@ -165,56 +183,68 @@ impl<'a> FrameView<'a> {
165
183
let slot_addr = frame
166
184
. fp
167
185
. wrapping_sub ( usize:: try_from ( slot_to_fp_offset) . unwrap ( ) ) ;
168
- // Materialize the stack shape so we have O(1) access to its elements.
186
+
187
+ // Materialize the stack shape so we have O(1) access to its
188
+ // elements, and so we don't need to keep the borrow to the
189
+ // module alive.
169
190
let mut stack = frame_state_slot
170
191
. stack ( frame. stack_shape )
171
192
. collect :: < Vec < _ > > ( ) ;
172
193
stack. reverse ( ) ; // Put top-of-stack last.
194
+
195
+ // Materialize the local offsets/types so we don't need to
196
+ // keep the borrow to the module alive.
197
+ let locals = frame_state_slot. locals ( ) . collect :: < Vec < _ > > ( ) ;
198
+
173
199
FrameView {
174
- store,
175
- frame_state_slot,
176
200
slot_addr,
201
+ func_key : frame_state_slot. func_key ( ) ,
177
202
wasm_pc : frame. wasm_pc ,
178
203
stack,
204
+ locals,
179
205
}
180
206
}
181
207
182
- fn raw_instance ( & mut self ) -> & ' a crate :: vm:: Instance {
208
+ fn raw_instance < ' a > ( & self , _store : & ' a mut StoreOpaque ) -> & ' a crate :: vm:: Instance {
183
209
// Read out the vmctx slot.
184
210
// SAFETY: vmctx is always at offset 0 in the slot.
185
211
let vmctx: * mut VMContext = unsafe { * ( self . slot_addr as * mut _ ) } ;
186
212
let vmctx = NonNull :: new ( vmctx) . expect ( "null vmctx in debug state slot" ) ;
187
213
// SAFETY: the stored vmctx value is a valid instance in this
188
- // store; we only visit frames from this store in teh backtrace.
214
+ // store; we only visit frames from this store in the
215
+ // backtrace.
189
216
let instance = unsafe { crate :: vm:: Instance :: from_vmctx ( vmctx) } ;
190
217
// SAFETY: the instance pointer read above is valid.
191
218
unsafe { instance. as_ref ( ) }
192
219
}
193
220
194
221
/// Get the instance associated with this frame.
195
- pub fn instance ( & mut self ) -> Instance {
196
- let instance = self . raw_instance ( ) ;
197
- Instance :: from_wasmtime ( instance. id ( ) , self . store )
222
+ pub fn instance ( & self , view : & mut StackView < ' _ > ) -> Instance {
223
+ let instance = self . raw_instance ( view . iter . store ) ;
224
+ Instance :: from_wasmtime ( instance. id ( ) , view . iter . store )
198
225
}
199
226
200
227
/// Get the module associated with this frame, if any (i.e., not a
201
228
/// container instance for a host-created entity).
202
- pub fn module ( & mut self ) -> Option < & Module > {
203
- let instance = self . raw_instance ( ) ;
229
+ pub fn module < ' a > ( & self , view : & ' a mut StackView < ' _ > ) -> Option < & ' a Module > {
230
+ let instance = self . raw_instance ( view . iter . store ) ;
204
231
instance. runtime_module ( )
205
232
}
206
233
207
234
/// Get the raw function index associated with this frame, and the
208
235
/// PC as an offset within its code section, if it is a Wasm
209
236
/// function directly from the given `Module` (rather than a
210
237
/// trampoline).
211
- pub fn wasm_function_index_and_pc ( & mut self ) -> Option < ( DefinedFuncIndex , u32 ) > {
212
- let FuncKey :: DefinedWasmFunction ( module, func) = self . frame_state_slot . func_key ( ) else {
238
+ pub fn wasm_function_index_and_pc (
239
+ & self ,
240
+ view : & mut StackView < ' _ > ,
241
+ ) -> Option < ( DefinedFuncIndex , u32 ) > {
242
+ let FuncKey :: DefinedWasmFunction ( module, func) = self . func_key else {
213
243
return None ;
214
244
} ;
215
245
debug_assert_eq ! (
216
246
module,
217
- self . module( )
247
+ self . module( view )
218
248
. expect( "module should be defined if this is a defined function" )
219
249
. env_module( )
220
250
. module_index
@@ -224,7 +254,7 @@ impl<'a> FrameView<'a> {
224
254
225
255
/// Get the number of locals in this frame.
226
256
pub fn num_locals ( & self ) -> usize {
227
- self . frame_state_slot . num_locals ( )
257
+ self . locals . len ( )
228
258
}
229
259
230
260
/// Get the depth of the operand stack in this frame.
@@ -238,11 +268,11 @@ impl<'a> FrameView<'a> {
238
268
///
239
269
/// Panics if the index is out-of-range (greater than
240
270
/// `num_locals()`).
241
- pub fn local ( & mut self , index : usize ) -> ( ValType , Val ) {
242
- let ( offset, ty) = self . frame_state_slot . local ( index) . unwrap ( ) ;
271
+ pub fn local ( & self , view : & mut StackView < ' _ > , index : usize ) -> ( ValType , Val ) {
272
+ let ( offset, ty) = self . locals [ index] ;
243
273
// SAFETY: compiler produced metadata to describe this local
244
274
// slot and stored a value of the correct type into it.
245
- unsafe { read_value ( self . store , self . slot_addr , offset, ty) }
275
+ unsafe { read_value ( view . iter . store , self . slot_addr , offset, ty) }
246
276
}
247
277
248
278
/// Get the type and value of the given operand-stack value in
@@ -252,12 +282,12 @@ impl<'a> FrameView<'a> {
252
282
/// from there are more recently pushed values. In other words,
253
283
/// index order reads the Wasm virtual machine's abstract stack
254
284
/// state left-to-right.
255
- pub fn stack ( & mut self , index : usize ) -> ( ValType , Val ) {
285
+ pub fn stack ( & self , view : & mut StackView < ' _ > , index : usize ) -> ( ValType , Val ) {
256
286
let ( offset, ty) = self . stack [ index] ;
257
287
// SAFETY: compiler produced metadata to describe this
258
288
// operand-stack slot and stored a value of the correct type
259
289
// into it.
260
- unsafe { read_value ( self . store , self . slot_addr , offset, ty) }
290
+ unsafe { read_value ( view . iter . store , self . slot_addr , offset, ty) }
261
291
}
262
292
}
263
293
0 commit comments