wasmtime/runtime/vm/instance/allocator/pooling/
metrics.rs

1use core::sync::atomic::Ordering;
2
3use crate::{Engine, vm::PoolingInstanceAllocator};
4
5/// `PoolingAllocatorMetrics` provides access to runtime metrics of a pooling
6/// allocator configured with [`crate::InstanceAllocationStrategy::Pooling`].
7///
8/// This is a cheap cloneable handle which can be obtained with
9/// [`Engine::pooling_allocator_metrics`].
10#[derive(Clone)]
11pub struct PoolingAllocatorMetrics {
12    engine: Engine,
13}
14
15impl PoolingAllocatorMetrics {
16    pub(crate) fn new(engine: &Engine) -> Option<Self> {
17        engine.allocator().as_pooling().map(|_| Self {
18            engine: engine.clone(),
19        })
20    }
21
22    /// Returns the number of core (module) instances currently allocated.
23    pub fn core_instances(&self) -> u64 {
24        self.allocator().live_core_instances.load(Ordering::Relaxed)
25    }
26
27    /// Returns the number of component instances currently allocated.
28    pub fn component_instances(&self) -> u64 {
29        self.allocator()
30            .live_component_instances
31            .load(Ordering::Relaxed)
32    }
33
34    /// Returns the number of WebAssembly memories currently allocated.
35    pub fn memories(&self) -> usize {
36        self.allocator().live_memories.load(Ordering::Relaxed)
37    }
38
39    /// Returns the number of WebAssembly tables currently allocated.
40    pub fn tables(&self) -> usize {
41        self.allocator().live_tables.load(Ordering::Relaxed)
42    }
43
44    fn allocator(&self) -> &PoolingInstanceAllocator {
45        self.engine
46            .allocator()
47            .as_pooling()
48            .expect("engine should have pooling allocator")
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use crate::{
55        Config, InstanceAllocationStrategy, Store,
56        component::{Component, Linker},
57    };
58
59    use super::*;
60
61    // A component with 1 core instance, 1 memory, 1 table
62    const TEST_COMPONENT: &[u8] = b"
63        (component
64            (core module $m
65                (memory 1)
66                (table 1 funcref)
67            )
68            (core instance (instantiate (module $m)))
69        )
70    ";
71
72    #[test]
73    #[cfg_attr(miri, ignore)]
74    fn smoke_test() {
75        // Start with nothing
76        let engine =
77            Engine::new(&Config::new().allocation_strategy(InstanceAllocationStrategy::pooling()))
78                .unwrap();
79        let metrics = engine.pooling_allocator_metrics().unwrap();
80
81        assert_eq!(metrics.core_instances(), 0);
82        assert_eq!(metrics.component_instances(), 0);
83        assert_eq!(metrics.memories(), 0);
84        assert_eq!(metrics.tables(), 0);
85
86        // Instantiate one of each
87        let mut store = Store::new(&engine, ());
88        let component = Component::new(&engine, TEST_COMPONENT).unwrap();
89        let linker = Linker::new(&engine);
90        let instance = linker.instantiate(&mut store, &component).unwrap();
91
92        assert_eq!(metrics.core_instances(), 1);
93        assert_eq!(metrics.component_instances(), 1);
94        assert_eq!(metrics.memories(), 1);
95        assert_eq!(metrics.tables(), 1);
96
97        // Back to nothing
98        let _ = (instance, store);
99
100        assert_eq!(metrics.core_instances(), 0);
101        assert_eq!(metrics.component_instances(), 0);
102        assert_eq!(metrics.memories(), 0);
103        assert_eq!(metrics.tables(), 0);
104    }
105
106    #[test]
107    fn test_non_pooling_allocator() {
108        let engine =
109            Engine::new(&Config::new().allocation_strategy(InstanceAllocationStrategy::OnDemand))
110                .unwrap();
111
112        let maybe_metrics = engine.pooling_allocator_metrics();
113        assert!(maybe_metrics.is_none());
114    }
115}