Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/debug.rs
3052 views
1
//! Tests for instrumentation-based debugging.
2
3
use std::sync::Arc;
4
use std::sync::atomic::{AtomicUsize, Ordering};
5
use wasmtime::{
6
AsContextMut, Caller, Config, DebugEvent, DebugHandler, Engine, Extern, FrameParentResult,
7
Func, Global, GlobalType, Instance, Module, Mutability, Store, StoreContextMut, Val, ValType,
8
};
9
10
#[test]
11
fn debugging_does_not_work_with_signal_based_traps() {
12
let mut config = Config::default();
13
config.guest_debug(true).signals_based_traps(true);
14
let err = Engine::new(&config).expect_err("invalid config should produce an error");
15
assert!(format!("{err:?}").contains("cannot use signals-based traps"));
16
}
17
18
#[test]
19
fn debugging_apis_are_denied_without_debugging() -> wasmtime::Result<()> {
20
let mut config = Config::default();
21
config.guest_debug(false);
22
let engine = Engine::new(&config)?;
23
let module = Module::new(&engine, "(module (global $g (mut i32) (i32.const 0)))")?;
24
let mut store = Store::new(&engine, ());
25
let instance = Instance::new(&mut store, &module, &[])?;
26
27
assert!(store.debug_frames().is_none());
28
assert!(instance.debug_global(&mut store, 0).is_none());
29
30
Ok(())
31
}
32
33
fn get_module_and_store<C: Fn(&mut Config)>(
34
c: C,
35
wat: &str,
36
) -> wasmtime::Result<(Module, Store<()>)> {
37
let mut config = Config::default();
38
config.guest_debug(true);
39
config.wasm_exceptions(true);
40
c(&mut config);
41
let engine = Engine::new(&config)?;
42
let module = Module::new(&engine, wat)?;
43
Ok((module, Store::new(&engine, ())))
44
}
45
46
fn test_stack_values<C: Fn(&mut Config), F: Fn(Caller<'_, ()>) + Send + Sync + 'static>(
47
wat: &str,
48
c: C,
49
f: F,
50
) -> wasmtime::Result<()> {
51
let (module, mut store) = get_module_and_store(c, wat)?;
52
let func = Func::wrap(&mut store, move |caller: Caller<'_, ()>| {
53
f(caller);
54
});
55
let instance = Instance::new(&mut store, &module, &[Extern::Func(func)])?;
56
let mut results = [];
57
instance
58
.get_func(&mut store, "main")
59
.unwrap()
60
.call(&mut store, &[], &mut results)?;
61
62
Ok(())
63
}
64
65
#[test]
66
#[cfg_attr(miri, ignore)]
67
fn stack_values_two_frames() -> wasmtime::Result<()> {
68
let _ = env_logger::try_init();
69
70
for inlining in [false, true] {
71
test_stack_values(
72
r#"
73
(module
74
(import "" "host" (func))
75
(func (export "main")
76
i32.const 1
77
i32.const 2
78
call 2
79
drop)
80
(func (param i32 i32) (result i32)
81
local.get 0
82
local.get 1
83
call 0
84
i32.add))
85
"#,
86
|config| {
87
config.compiler_inlining(inlining);
88
if inlining {
89
unsafe {
90
config.cranelift_flag_set("wasmtime_inlining_intra_module", "true");
91
}
92
}
93
},
94
|mut caller: Caller<'_, ()>| {
95
let mut stack = caller.debug_frames().unwrap();
96
assert!(!stack.done());
97
assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 1);
98
assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 65);
99
100
assert_eq!(stack.num_locals(), 2);
101
assert_eq!(stack.num_stacks(), 2);
102
assert_eq!(stack.local(0).unwrap_i32(), 1);
103
assert_eq!(stack.local(1).unwrap_i32(), 2);
104
assert_eq!(stack.stack(0).unwrap_i32(), 1);
105
assert_eq!(stack.stack(1).unwrap_i32(), 2);
106
107
assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
108
assert!(!stack.done());
109
assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
110
assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 55);
111
112
assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
113
assert!(stack.done());
114
},
115
)?;
116
}
117
Ok(())
118
}
119
120
#[test]
121
#[cfg_attr(miri, ignore)]
122
fn stack_values_exceptions() -> wasmtime::Result<()> {
123
test_stack_values(
124
r#"
125
(module
126
(tag $t (param i32))
127
(import "" "host" (func))
128
(func (export "main")
129
(block $b (result i32)
130
(try_table (catch $t $b)
131
(throw $t (i32.const 42)))
132
i32.const 0)
133
(call 0)
134
(drop)))
135
"#,
136
|_config| {},
137
|mut caller: Caller<'_, ()>| {
138
let mut stack = caller.debug_frames().unwrap();
139
assert!(!stack.done());
140
assert_eq!(stack.num_stacks(), 1);
141
assert_eq!(stack.stack(0).unwrap_i32(), 42);
142
assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
143
assert!(stack.done());
144
},
145
)
146
}
147
148
#[test]
149
#[cfg_attr(miri, ignore)]
150
fn stack_values_dead_gc_ref() -> wasmtime::Result<()> {
151
test_stack_values(
152
r#"
153
(module
154
(type $s (struct))
155
(import "" "host" (func))
156
(func (export "main")
157
(struct.new $s)
158
(call 0)
159
(drop)))
160
"#,
161
|config| {
162
config.wasm_gc(true);
163
},
164
|mut caller: Caller<'_, ()>| {
165
let mut stack = caller.debug_frames().unwrap();
166
assert!(!stack.done());
167
assert_eq!(stack.num_stacks(), 1);
168
assert!(stack.stack(0).unwrap_anyref().is_some());
169
assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
170
assert!(stack.done());
171
},
172
)
173
}
174
175
#[test]
176
#[cfg_attr(miri, ignore)]
177
fn gc_access_during_call() -> wasmtime::Result<()> {
178
test_stack_values(
179
r#"
180
(module
181
(type $s (struct (field i32)))
182
(import "" "host" (func))
183
(func (export "main")
184
(local $l (ref null $s))
185
(local.set $l (struct.new $s (i32.const 42)))
186
(call 0)))
187
"#,
188
|config| {
189
config.wasm_gc(true);
190
},
191
|mut caller: Caller<'_, ()>| {
192
let mut stack = caller.debug_frames().unwrap();
193
194
// Do a GC while we hold the stack cursor.
195
stack.as_context_mut().gc(None).unwrap();
196
197
assert!(!stack.done());
198
assert_eq!(stack.num_stacks(), 0);
199
assert_eq!(stack.num_locals(), 1);
200
// Note that this struct is dead during the call, and the
201
// ref could otherwise be optimized away (no longer in the
202
// stackmap at this point); but we verify it is still
203
// alive here because it is rooted in the
204
// debug-instrumentation slot.
205
let s = stack
206
.local(0)
207
.unwrap_any_ref()
208
.unwrap()
209
.unwrap_struct(&stack)
210
.unwrap();
211
assert_eq!(s.field(&mut stack, 0).unwrap().unwrap_i32(), 42);
212
assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
213
assert!(stack.done());
214
},
215
)
216
}
217
218
#[test]
219
#[cfg_attr(miri, ignore)]
220
fn stack_values_two_activations() -> wasmtime::Result<()> {
221
let _ = env_logger::try_init();
222
223
let mut config = Config::default();
224
config.guest_debug(true);
225
config.wasm_exceptions(true);
226
let engine = Engine::new(&config)?;
227
let module1 = Module::new(
228
&engine,
229
r#"
230
(module
231
(import "" "host1" (func (param i32 i32) (result i32)))
232
(func (export "main") (result i32)
233
i32.const 1
234
i32.const 2
235
call 0))
236
"#,
237
)?;
238
let module2 = Module::new(
239
&engine,
240
r#"
241
(module
242
(import "" "host2" (func))
243
(func (export "inner") (param i32 i32) (result i32)
244
local.get 0
245
local.get 1
246
call 0
247
i32.add))
248
"#,
249
)?;
250
let mut store = Store::new(&engine, ());
251
252
let module1_clone = module1.clone();
253
let module2_clone = module2.clone();
254
let host2 = Func::wrap(&mut store, move |mut caller: Caller<'_, ()>| {
255
let mut stack = caller.debug_frames().unwrap();
256
assert!(!stack.done());
257
assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
258
assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 56);
259
assert!(Module::same(stack.module().unwrap(), &module2_clone));
260
assert_eq!(stack.num_locals(), 2);
261
assert_eq!(stack.num_stacks(), 2);
262
assert_eq!(stack.local(0).unwrap_i32(), 1);
263
assert_eq!(stack.local(1).unwrap_i32(), 2);
264
assert_eq!(stack.stack(0).unwrap_i32(), 1);
265
assert_eq!(stack.stack(1).unwrap_i32(), 2);
266
let inner_instance = stack.instance();
267
268
assert_eq!(stack.move_to_parent(), FrameParentResult::NewActivation);
269
assert!(!stack.done());
270
271
assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
272
assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 56);
273
assert!(Module::same(stack.module().unwrap(), &module1_clone));
274
assert_eq!(stack.num_locals(), 0);
275
assert_eq!(stack.num_stacks(), 2);
276
assert_eq!(stack.stack(0).unwrap_i32(), 1);
277
assert_eq!(stack.stack(1).unwrap_i32(), 2);
278
let outer_instance = stack.instance();
279
280
assert_ne!(inner_instance, outer_instance);
281
282
assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
283
assert!(stack.done());
284
});
285
286
let instance2 = Instance::new(&mut store, &module2, &[Extern::Func(host2)])?;
287
let inner = instance2.get_func(&mut store, "inner").unwrap();
288
289
let host1 = Func::wrap(
290
&mut store,
291
move |mut caller: Caller<'_, ()>, a: i32, b: i32| -> i32 {
292
let mut results = [Val::I32(0)];
293
inner
294
.call(&mut caller, &[Val::I32(a), Val::I32(b)], &mut results[..])
295
.unwrap();
296
results[0].unwrap_i32()
297
},
298
);
299
300
let instance1 = Instance::new(&mut store, &module1, &[Extern::Func(host1)])?;
301
let main = instance1.get_func(&mut store, "main").unwrap();
302
303
let mut results = [Val::I32(0)];
304
main.call(&mut store, &[], &mut results)?;
305
assert_eq!(results[0].unwrap_i32(), 3);
306
Ok(())
307
}
308
309
#[test]
310
#[cfg_attr(miri, ignore)]
311
fn debug_frames_on_store_with_no_wasm_activation() -> wasmtime::Result<()> {
312
let mut config = Config::default();
313
config.guest_debug(true);
314
let engine = Engine::new(&config)?;
315
let mut store = Store::new(&engine, ());
316
let frames = store
317
.debug_frames()
318
.expect("Debug frames should be available");
319
assert!(frames.done());
320
Ok(())
321
}
322
323
#[test]
324
#[cfg_attr(miri, ignore)]
325
fn private_entity_access() -> wasmtime::Result<()> {
326
let mut config = Config::default();
327
config.guest_debug(true);
328
config.wasm_gc(true);
329
config.gc_support(true);
330
config.wasm_exceptions(true);
331
let engine = Engine::new(&config)?;
332
let mut store = Store::new(&engine, ());
333
let module = Module::new(
334
&engine,
335
r#"
336
(module
337
(import "" "i" (global (mut i32)))
338
(import "" "f" (func (result i32)))
339
(global $g (mut i32) (i32.const 0))
340
(memory $m 1 1)
341
(table $t 10 10 i31ref)
342
(tag $tag (param f64))
343
(func (export "main")
344
;; $g := 42
345
i32.const 42
346
global.set $g
347
;; $m[1024] := 1
348
i32.const 1024
349
i32.const 1
350
i32.store8 $m
351
;; $t[1] := (ref.i31 (i32.const 100))
352
i32.const 1
353
i32.const 100
354
ref.i31
355
table.set $t)
356
357
(func (param i32)
358
local.get 0
359
global.set $g))
360
"#,
361
)?;
362
363
let host_global = Global::new(
364
&mut store,
365
GlobalType::new(ValType::I32, Mutability::Var),
366
Val::I32(1000),
367
)?;
368
let host_func = Func::wrap(&mut store, |_caller: Caller<'_, ()>| -> i32 { 7 });
369
370
let instance = Instance::new(
371
&mut store,
372
&module,
373
&[Extern::Global(host_global), Extern::Func(host_func)],
374
)?;
375
let func = instance.get_func(&mut store, "main").unwrap();
376
func.call(&mut store, &[], &mut [])?;
377
378
// Nothing is exported except for `main`, yet we can still access
379
// (below).
380
let exports = instance.exports(&mut store).collect::<Vec<_>>();
381
assert_eq!(exports.len(), 1);
382
assert!(exports.into_iter().next().unwrap().into_func().is_some());
383
384
// We can call a non-exported function.
385
let f = instance.debug_function(&mut store, 2).unwrap();
386
f.call(&mut store, &[Val::I32(1234)], &mut [])?;
387
388
let g = instance.debug_global(&mut store, 1).unwrap();
389
assert_eq!(g.get(&mut store).unwrap_i32(), 1234);
390
391
let m = instance.debug_memory(&mut store, 0).unwrap();
392
assert_eq!(m.data(&mut store)[1024], 1);
393
394
let t = instance.debug_table(&mut store, 0).unwrap();
395
let t_val = t.get(&mut store, 1).unwrap();
396
let t_val = t_val.as_any().unwrap().unwrap().unwrap_i31(&store).unwrap();
397
assert_eq!(t_val.get_u32(), 100);
398
399
let tag = instance.debug_tag(&mut store, 0).unwrap();
400
assert!(matches!(
401
tag.ty(&store).ty().param(0).unwrap(),
402
ValType::F64
403
));
404
405
// Check that we can access an imported global in the instance's
406
// index space.
407
let host_global_import = instance.debug_global(&mut store, 0).unwrap();
408
assert_eq!(host_global_import.get(&mut store).unwrap_i32(), 1000);
409
410
// Check that we can call an imported function in the instance's
411
// index space.
412
let host_func_import = instance.debug_function(&mut store, 0).unwrap();
413
let mut results = [Val::I32(0)];
414
host_func_import.call(&mut store, &[], &mut results[..])?;
415
assert_eq!(results[0].unwrap_i32(), 7);
416
417
// Check that out-of-bounds returns `None` rather than panic'ing.
418
assert!(instance.debug_global(&mut store, 2).is_none());
419
420
Ok(())
421
}
422
423
#[test]
424
#[cfg_attr(miri, ignore)]
425
#[cfg(target_pointer_width = "64")] // Threads not supported on 32-bit systems.
426
fn private_entity_access_shared_memory() -> wasmtime::Result<()> {
427
let mut config = Config::default();
428
config.guest_debug(true);
429
config.shared_memory(true);
430
config.wasm_threads(true);
431
let engine = Engine::new(&config)?;
432
let mut store = Store::new(&engine, ());
433
let module = Module::new(
434
&engine,
435
r#"
436
(module
437
(memory 1 1 shared))
438
"#,
439
)?;
440
441
let instance = Instance::new(&mut store, &module, &[])?;
442
443
let m = instance.debug_shared_memory(&mut store, 0).unwrap();
444
let unsafe_cell = &m.data()[1024];
445
assert_eq!(unsafe { *unsafe_cell.get() }, 0);
446
447
Ok(())
448
}
449
450
macro_rules! debug_event_checker {
451
($ty:tt,
452
$store:tt,
453
$(
454
{ $i:expr ; $pat:pat => $body:tt }
455
),*)
456
=>
457
{
458
#[derive(Clone)]
459
struct $ty(Arc<AtomicUsize>);
460
impl $ty {
461
fn new_and_counter() -> (Self, Arc<AtomicUsize>) {
462
let counter = Arc::new(AtomicUsize::new(0));
463
let counter_clone = counter.clone();
464
($ty(counter), counter_clone)
465
}
466
}
467
impl DebugHandler for $ty {
468
type Data = ();
469
fn handle(
470
&self,
471
#[allow(unused_variables, reason = "macro rules")]
472
#[allow(unused_mut, reason = "macro rules")]
473
mut $store: StoreContextMut<'_, ()>,
474
event: DebugEvent<'_>,
475
) -> impl Future<Output = ()> + Send {
476
let step = self.0.fetch_add(1, Ordering::Relaxed);
477
async move {
478
if false {}
479
$(
480
else if step == $i {
481
match event {
482
$pat => {
483
$body;
484
}
485
_ => panic!("Incorrect event"),
486
}
487
}
488
)*
489
else {
490
panic!("Too many steps");
491
}
492
}
493
}
494
}
495
}
496
}
497
498
#[tokio::test]
499
#[cfg_attr(miri, ignore)]
500
async fn uncaught_exception_events() -> wasmtime::Result<()> {
501
let _ = env_logger::try_init();
502
503
let (module, mut store) = get_module_and_store(
504
|config| {
505
config.wasm_exceptions(true);
506
},
507
r#"
508
(module
509
(tag $t (param i32))
510
(func (export "main")
511
call 1)
512
(func
513
(local $i i32)
514
(local.set $i (i32.const 100))
515
(throw $t (i32.const 42))))
516
"#,
517
)?;
518
519
debug_event_checker!(
520
D, store,
521
{ 0 ;
522
wasmtime::DebugEvent::UncaughtExceptionThrown(e) => {
523
assert_eq!(e.field(&mut store, 0).unwrap().unwrap_i32(), 42);
524
let mut stack = store.debug_frames().expect("frame cursor must be available");
525
assert!(!stack.done());
526
assert_eq!(stack.num_locals(), 1);
527
assert_eq!(stack.local(0).unwrap_i32(), 100);
528
assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
529
assert!(!stack.done());
530
assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
531
assert!(stack.done());
532
}
533
}
534
);
535
536
let (handler, counter) = D::new_and_counter();
537
store.set_debug_handler(handler);
538
539
let instance = Instance::new_async(&mut store, &module, &[]).await?;
540
let func = instance.get_func(&mut store, "main").unwrap();
541
let mut results = [];
542
let result = func.call_async(&mut store, &[], &mut results).await;
543
assert!(result.is_err()); // Uncaught exception.
544
assert_eq!(counter.load(Ordering::Relaxed), 1);
545
546
Ok(())
547
}
548
549
#[tokio::test]
550
#[cfg_attr(miri, ignore)]
551
async fn caught_exception_events() -> wasmtime::Result<()> {
552
let _ = env_logger::try_init();
553
554
let (module, mut store) = get_module_and_store(
555
|config| {
556
config.wasm_exceptions(true);
557
},
558
r#"
559
(module
560
(tag $t (param i32))
561
(func (export "main")
562
(block $b (result i32)
563
(try_table (catch $t $b)
564
call 1)
565
i32.const 0)
566
drop)
567
(func
568
(local $i i32)
569
(local.set $i (i32.const 100))
570
(throw $t (i32.const 42))))
571
"#,
572
)?;
573
574
debug_event_checker!(
575
D, store,
576
{ 0 ;
577
wasmtime::DebugEvent::CaughtExceptionThrown(e) => {
578
assert_eq!(e.field(&mut store, 0).unwrap().unwrap_i32(), 42);
579
let mut stack = store.debug_frames().expect("frame cursor must be available");
580
assert!(!stack.done());
581
assert_eq!(stack.num_locals(), 1);
582
assert_eq!(stack.local(0).unwrap_i32(), 100);
583
assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
584
assert!(!stack.done());
585
assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
586
assert!(stack.done());
587
}
588
}
589
);
590
591
let (handler, counter) = D::new_and_counter();
592
store.set_debug_handler(handler);
593
594
let instance = Instance::new_async(&mut store, &module, &[]).await?;
595
let func = instance.get_func(&mut store, "main").unwrap();
596
let mut results = [];
597
func.call_async(&mut store, &[], &mut results).await?;
598
assert_eq!(counter.load(Ordering::Relaxed), 1);
599
600
Ok(())
601
}
602
603
#[tokio::test]
604
#[cfg_attr(miri, ignore)]
605
async fn hostcall_trap_events() -> wasmtime::Result<()> {
606
let _ = env_logger::try_init();
607
608
let (module, mut store) = get_module_and_store(
609
|config| {
610
config.wasm_exceptions(true);
611
},
612
r#"
613
(module
614
(func (export "main")
615
i32.const 0
616
i32.const 0
617
i32.div_u
618
drop))
619
"#,
620
)?;
621
622
debug_event_checker!(
623
D, store,
624
{ 0 ;
625
wasmtime::DebugEvent::Trap(wasmtime_environ::Trap::IntegerDivisionByZero) => {}
626
}
627
);
628
629
let (handler, counter) = D::new_and_counter();
630
store.set_debug_handler(handler);
631
632
let instance = Instance::new_async(&mut store, &module, &[]).await?;
633
let func = instance.get_func(&mut store, "main").unwrap();
634
let mut results = [];
635
let result = func.call_async(&mut store, &[], &mut results).await;
636
assert!(result.is_err()); // Uncaught trap.
637
assert_eq!(counter.load(Ordering::Relaxed), 1);
638
639
Ok(())
640
}
641
642
#[tokio::test]
643
#[cfg_attr(miri, ignore)]
644
async fn hostcall_error_events() -> wasmtime::Result<()> {
645
let _ = env_logger::try_init();
646
647
let (module, mut store) = get_module_and_store(
648
|config| {
649
config.wasm_exceptions(true);
650
},
651
r#"
652
(module
653
(import "" "do_a_trap" (func))
654
(func (export "main")
655
call 0))
656
"#,
657
)?;
658
659
debug_event_checker!(
660
D, store,
661
{ 0 ;
662
wasmtime::DebugEvent::HostcallError(e) => {
663
assert!(format!("{e:?}").contains("secret error message"));
664
}
665
}
666
);
667
668
let (handler, counter) = D::new_and_counter();
669
store.set_debug_handler(handler);
670
671
let do_a_trap = Func::wrap(
672
&mut store,
673
|_caller: Caller<'_, ()>| -> wasmtime::Result<()> {
674
Err(wasmtime::format_err!("secret error message"))
675
},
676
);
677
let instance = Instance::new_async(&mut store, &module, &[Extern::Func(do_a_trap)]).await?;
678
let func = instance.get_func(&mut store, "main").unwrap();
679
let mut results = [];
680
let result = func.call_async(&mut store, &[], &mut results).await;
681
assert!(result.is_err()); // Uncaught trap.
682
assert_eq!(counter.load(Ordering::Relaxed), 1);
683
Ok(())
684
}
685
686
#[tokio::test]
687
#[cfg_attr(miri, ignore)]
688
async fn breakpoint_events() -> wasmtime::Result<()> {
689
let _ = env_logger::try_init();
690
691
let (module, mut store) = get_module_and_store(
692
|config| {
693
config.wasm_exceptions(true);
694
},
695
r#"
696
(module
697
(func (export "main") (param i32 i32) (result i32)
698
local.get 0
699
local.get 1
700
i32.add))
701
"#,
702
)?;
703
704
debug_event_checker!(
705
D, store,
706
{ 0 ;
707
wasmtime::DebugEvent::Breakpoint => {
708
let mut stack = store.debug_frames().expect("frame cursor must be available");
709
assert!(!stack.done());
710
assert_eq!(stack.num_locals(), 2);
711
assert_eq!(stack.local(0).unwrap_i32(), 1);
712
assert_eq!(stack.local(1).unwrap_i32(), 2);
713
let (func, pc) = stack.wasm_function_index_and_pc().unwrap();
714
assert_eq!(func.as_u32(), 0);
715
assert_eq!(pc, 0x28);
716
assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
717
assert!(stack.done());
718
}
719
}
720
);
721
722
let (handler, counter) = D::new_and_counter();
723
store.set_debug_handler(handler);
724
store
725
.edit_breakpoints()
726
.unwrap()
727
.add_breakpoint(&module, 0x28)?;
728
729
let instance = Instance::new_async(&mut store, &module, &[]).await?;
730
let func = instance.get_func(&mut store, "main").unwrap();
731
let mut results = [Val::I32(0)];
732
func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
733
.await?;
734
assert_eq!(counter.load(Ordering::Relaxed), 1);
735
assert_eq!(results[0].unwrap_i32(), 3);
736
737
let breakpoints = store.breakpoints().unwrap().collect::<Vec<_>>();
738
assert_eq!(breakpoints.len(), 1);
739
assert!(Module::same(&breakpoints[0].module, &module));
740
assert_eq!(breakpoints[0].pc, 0x28);
741
742
store
743
.edit_breakpoints()
744
.unwrap()
745
.remove_breakpoint(&module, 0x28)?;
746
func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
747
.await?;
748
assert_eq!(counter.load(Ordering::Relaxed), 1); // Should not have incremented from above.
749
assert_eq!(results[0].unwrap_i32(), 3);
750
751
// Enable single-step mode (on top of the breakpoint already enabled).
752
assert!(!store.is_single_step());
753
store.edit_breakpoints().unwrap().single_step(true).unwrap();
754
assert!(store.is_single_step());
755
756
debug_event_checker!(
757
D2, store,
758
{ 0 ;
759
wasmtime::DebugEvent::Breakpoint => {
760
let stack = store.debug_frames().unwrap();
761
assert!(!stack.done());
762
let (_, pc) = stack.wasm_function_index_and_pc().unwrap();
763
assert_eq!(pc, 0x24);
764
}
765
},
766
{
767
1 ;
768
wasmtime::DebugEvent::Breakpoint => {
769
let stack = store.debug_frames().unwrap();
770
assert!(!stack.done());
771
let (_, pc) = stack.wasm_function_index_and_pc().unwrap();
772
assert_eq!(pc, 0x26);
773
}
774
},
775
{
776
2 ;
777
wasmtime::DebugEvent::Breakpoint => {
778
let stack = store.debug_frames().unwrap();
779
assert!(!stack.done());
780
let (_, pc) = stack.wasm_function_index_and_pc().unwrap();
781
assert_eq!(pc, 0x28);
782
}
783
},
784
{
785
3 ;
786
wasmtime::DebugEvent::Breakpoint => {
787
let stack = store.debug_frames().unwrap();
788
assert!(!stack.done());
789
let (_, pc) = stack.wasm_function_index_and_pc().unwrap();
790
assert_eq!(pc, 0x29);
791
}
792
}
793
);
794
795
let (handler, counter) = D2::new_and_counter();
796
store.set_debug_handler(handler);
797
798
func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
799
.await?;
800
assert_eq!(counter.load(Ordering::Relaxed), 4);
801
802
// Re-enable individual breakpoint.
803
store
804
.edit_breakpoints()
805
.unwrap()
806
.add_breakpoint(&module, 0x28)
807
.unwrap();
808
809
// Now disable single-stepping. The single breakpoint set above
810
// should still remain.
811
store
812
.edit_breakpoints()
813
.unwrap()
814
.single_step(false)
815
.unwrap();
816
817
let (handler, counter) = D::new_and_counter();
818
store.set_debug_handler(handler);
819
820
func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
821
.await?;
822
assert_eq!(counter.load(Ordering::Relaxed), 1);
823
824
Ok(())
825
}
826
827
#[tokio::test]
828
#[cfg_attr(miri, ignore)]
829
async fn breakpoints_in_inlined_code() -> wasmtime::Result<()> {
830
let _ = env_logger::try_init();
831
832
let (module, mut store) = get_module_and_store(
833
|config| {
834
config.wasm_exceptions(true);
835
config.compiler_inlining(true);
836
unsafe {
837
config.cranelift_flag_set("wasmtime_inlining_intra_module", "true");
838
}
839
},
840
r#"
841
(module
842
(func $f (export "f") (param i32 i32) (result i32)
843
local.get 0
844
local.get 1
845
i32.add)
846
847
(func (export "main") (param i32 i32) (result i32)
848
local.get 0
849
local.get 1
850
call $f))
851
"#,
852
)?;
853
854
debug_event_checker!(
855
D, store,
856
{ 0 ;
857
wasmtime::DebugEvent::Breakpoint => {}
858
},
859
{ 1 ;
860
wasmtime::DebugEvent::Breakpoint => {}
861
}
862
);
863
864
let (handler, counter) = D::new_and_counter();
865
store.set_debug_handler(handler);
866
store
867
.edit_breakpoints()
868
.unwrap()
869
.add_breakpoint(&module, 0x2d)?; // `i32.add` in `$f`.
870
871
let instance = Instance::new_async(&mut store, &module, &[]).await?;
872
let func_main = instance.get_func(&mut store, "main").unwrap();
873
let func_f = instance.get_func(&mut store, "f").unwrap();
874
let mut results = [Val::I32(0)];
875
// Breakpoint in `$f` should have been hit in `main` even if it
876
// was inlined.
877
func_main
878
.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
879
.await?;
880
assert_eq!(counter.load(Ordering::Relaxed), 1);
881
assert_eq!(results[0].unwrap_i32(), 3);
882
883
// Breakpoint in `$f` should be hit when called directly, too.
884
func_f
885
.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
886
.await?;
887
assert_eq!(counter.load(Ordering::Relaxed), 2);
888
assert_eq!(results[0].unwrap_i32(), 3);
889
890
Ok(())
891
}
892
893
#[tokio::test]
894
#[cfg_attr(miri, ignore)]
895
async fn epoch_events() -> wasmtime::Result<()> {
896
let _ = env_logger::try_init();
897
898
let (module, mut store) = get_module_and_store(
899
|config| {
900
config.epoch_interruption(true);
901
},
902
r#"
903
(module
904
(func $f (export "f") (param i32 i32) (result i32)
905
local.get 0
906
local.get 1
907
i32.add))
908
"#,
909
)?;
910
911
debug_event_checker!(
912
D, store,
913
{ 0 ;
914
wasmtime::DebugEvent::EpochYield => {}
915
}
916
);
917
918
let (handler, counter) = D::new_and_counter();
919
store.set_debug_handler(handler);
920
921
store.set_epoch_deadline(1);
922
store.epoch_deadline_async_yield_and_update(1);
923
store.engine().increment_epoch();
924
925
let instance = Instance::new_async(&mut store, &module, &[]).await?;
926
let func_f = instance.get_func(&mut store, "f").unwrap();
927
let mut results = [Val::I32(0)];
928
func_f
929
.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
930
.await?;
931
assert_eq!(counter.load(Ordering::Relaxed), 1);
932
assert_eq!(results[0].unwrap_i32(), 3);
933
934
Ok(())
935
}
936
937