1use std::fmt;
7
8use super::Heap;
9use super::arena::{Arena, Record};
10use super::gc::GcStats;
11
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
14pub struct ArenaReport {
15 pub live: usize,
17 pub capacity: usize,
19 pub used_bytes: usize,
21 pub reserved_bytes: usize,
23}
24
25impl ArenaReport {
26 fn of<T: Record>(arena: &Arena<T>) -> Self {
27 ArenaReport {
28 live: arena.live_count(),
29 capacity: arena.slot_count(),
30 used_bytes: arena.used_bytes(),
31 reserved_bytes: arena.bytes(),
32 }
33 }
34}
35
36#[derive(Clone, Debug, Default, PartialEq, Eq)]
39pub struct ValueHeapReport {
40 pub used_slots: usize,
42 pub free_by_class: Vec<usize>,
45 pub free_runs: usize,
47 pub free_run_slots: usize,
48 pub capacity_slots: usize,
50 pub reserved_bytes: usize,
51}
52
53#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
58pub struct CodeReport {
59 pub units: usize,
61 pub static_units: usize,
62 pub dynamic_units: usize,
63 pub dynamic_bytes: usize,
65 pub instructions: usize,
66 pub instruction_bytes: usize,
67 pub position_bytes: usize,
69 pub source_bytes: usize,
71 pub regexp_programs: usize,
73 pub regexp_bytes: usize,
74}
75
76#[derive(Clone, Debug, Default, PartialEq)]
78pub struct MemoryReport {
79 pub objects: ArenaReport,
80 pub shapes: ArenaReport,
81 pub strings: ArenaReport,
82 pub symbols: ArenaReport,
83 pub bigints: ArenaReport,
84 pub cells: ArenaReport,
85 pub functions: ArenaReport,
86 pub bytecode: ArenaReport,
87 pub internals: ArenaReport,
88 pub values: ValueHeapReport,
89 pub rope_bytes: usize,
91 pub atoms: usize,
93 pub pinned_atoms: usize,
95 pub code: CodeReport,
96 pub register_capacity: usize,
98 pub register_high_water: usize,
101 pub ic_entries: usize,
103 pub ic_free: usize,
105 pub gc: GcStats,
106 pub allocated_since_gc: usize,
109 pub threshold: usize,
110 pub budget: usize,
112 pub table_bytes: usize,
115 pub cap: usize,
117}
118
119impl MemoryReport {
120 pub fn arenas(&self) -> [(&'static str, &ArenaReport); 9] {
122 [
123 ("objects", &self.objects),
124 ("shapes", &self.shapes),
125 ("strings", &self.strings),
126 ("symbols", &self.symbols),
127 ("bigints", &self.bigints),
128 ("cells", &self.cells),
129 ("functions", &self.functions),
130 ("bytecode", &self.bytecode),
131 ("internals", &self.internals),
132 ]
133 }
134
135 pub fn reserved_bytes(&self) -> usize {
137 self.arenas()
138 .iter()
139 .map(|(_, arena)| arena.reserved_bytes)
140 .sum::<usize>()
141 + self.values.reserved_bytes
142 + self.rope_bytes
143 + self.code.dynamic_bytes
144 + self.code.regexp_bytes
145 + self.table_bytes
146 }
147}
148
149impl Heap {
150 pub(crate) fn report(&self) -> MemoryReport {
151 let mut free_by_class = Vec::new();
152 for range in self.values.free_blocks() {
153 let class = super::values::class_of(range.len) as usize;
154 if free_by_class.len() <= class {
155 free_by_class.resize(class + 1, 0);
156 }
157 free_by_class[class] += 1;
158 }
159 MemoryReport {
160 objects: ArenaReport::of(&self.objects),
161 shapes: ArenaReport::of(&self.shapes),
162 strings: ArenaReport::of(&self.strings),
163 symbols: ArenaReport::of(&self.symbols),
164 bigints: ArenaReport::of(&self.bigints),
165 cells: ArenaReport::of(&self.cells),
166 functions: ArenaReport::of(&self.payloads.functions),
167 bytecode: ArenaReport::of(&self.payloads.bytecode),
168 internals: ArenaReport::of(&self.payloads.internals),
169 values: ValueHeapReport {
170 used_slots: self.values.used_slots(),
171 free_by_class,
172 free_runs: self.values.free_runs().count(),
173 free_run_slots: self
174 .values
175 .free_runs()
176 .map(|(start, end)| (end - start) as usize)
177 .sum(),
178 capacity_slots: self.values.used_slots() + self.values.free_slots(),
179 reserved_bytes: self.values.bytes(),
180 },
181 rope_bytes: self.rope_bytes.get(),
182 atoms: self.atoms.len(),
183 pinned_atoms: self.pinned_atoms.len(),
184 code: CodeReport {
185 dynamic_units: self.code.len(),
186 dynamic_bytes: self.code.live_bytes(),
187 regexp_programs: self.regexps.len(),
188 regexp_bytes: self.regexps.bytes(),
189 ..CodeReport::default()
190 },
191 register_capacity: self.vm.registers.capacity(),
192 register_high_water: self.vm.registers.len(),
193 ic_entries: self.ics.len(),
194 ic_free: self.ics.free_len(),
195 gc: self.gc.stats,
196 allocated_since_gc: self.gc.allocated,
197 threshold: self.gc.threshold(),
198 budget: self.gc.budget(),
199 table_bytes: self.table_bytes(),
200 cap: self.gc.limits.max_bytes,
201 }
202 }
203}
204
205fn kib(bytes: usize) -> String {
206 format!("{:.1} KiB", bytes as f64 / 1024.0)
207}
208
209impl fmt::Display for MemoryReport {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212 writeln!(
213 f,
214 "{:<12}{:>10}{:>10}{:>14}{:>14}",
215 "memory", "live", "capacity", "used", "reserved"
216 )?;
217 for (name, arena) in self.arenas() {
218 writeln!(
219 f,
220 "{name:<12}{:>10}{:>10}{:>14}{:>14}",
221 arena.live,
222 arena.capacity,
223 kib(arena.used_bytes),
224 kib(arena.reserved_bytes)
225 )?;
226 }
227 let free: usize = self.values.capacity_slots - self.values.used_slots;
228 writeln!(
229 f,
230 "{:<12}{:>10}{:>10}{:>14}{:>14}",
231 "values",
232 self.values.used_slots,
233 self.values.capacity_slots,
234 kib(self.values.used_slots * 8),
235 kib(self.values.reserved_bytes)
236 )?;
237 writeln!(
238 f,
239 " free value slots: {free} in {:?} by class and {} runs of {} slots",
240 self.values.free_by_class, self.values.free_runs, self.values.free_run_slots
241 )?;
242 writeln!(
243 f,
244 "ropes: {}; atoms: {} ({} pinned); tables {}",
245 kib(self.rope_bytes),
246 self.atoms,
247 self.pinned_atoms,
248 kib(self.table_bytes)
249 )?;
250 writeln!(
251 f,
252 "heap reserved: {}; threshold {}; budget {}; cap {}",
253 kib(self.reserved_bytes()),
254 kib(self.threshold),
255 kib(self.budget),
256 if self.cap == usize::MAX {
257 "none".to_owned()
258 } else {
259 kib(self.cap)
260 }
261 )?;
262 writeln!(
263 f,
264 "code: {} units ({} static, {} dynamic holding {}), {} instructions ({}), \
265 positions {}, source {}, {} regexp programs ({})",
266 self.code.units,
267 self.code.static_units,
268 self.code.dynamic_units,
269 kib(self.code.dynamic_bytes),
270 self.code.instructions,
271 kib(self.code.instruction_bytes),
272 kib(self.code.position_bytes),
273 kib(self.code.source_bytes),
274 self.code.regexp_programs,
275 kib(self.code.regexp_bytes)
276 )?;
277 writeln!(
278 f,
279 "registers: {} high-water, {} capacity; inline caches: {} entries, {} free",
280 self.register_high_water, self.register_capacity, self.ic_entries, self.ic_free
281 )?;
282 writeln!(
283 f,
284 "gc: {} collections, {} records and {} freed, live {}",
285 self.gc.collections,
286 self.gc.freed_records,
287 kib(self.gc.freed_bytes as usize),
288 kib(self.gc.live_bytes)
289 )
290 }
291}