Skip to main content

shuyi_platform/
heap.rs

1//! The heap counter's rows as a [`HeapSnapshot`] (ADR-0046 ยง6), with the
2//! `heap` feature: the one conversion a backend that counts the heap
3//! makes in its [`Platform::heap_snapshot`](crate::Platform::heap_snapshot),
4//! so `shuyi-app` and a game's tool program (`shuyi-cli`) give the same
5//! snapshot of the same counts. A backend reads its counter
6//! (`shuyi_alloc_count::global::reading` and `rows`, and
7//! `shuyi_alloc_count::tags::overflowed`) and hands the counts here.
8
9use crate::alloc_prelude::*;
10use alloc::borrow::Cow;
11
12use shuyi_alloc_count::{Counts, TagCounts};
13use shuyi_ui::perf::{HeapSnapshot, TagClass, TagRow};
14
15/// The snapshot of the heap counter's `counts` and its `rows` by tag, with
16/// `overflowed` names past its table (`tags::overflowed`), at frame 0: a
17/// row each, its class read from its number (an unknown one as
18/// [`TagClass::Other`]), in the snapshot's order.
19pub fn snapshot(counts: Counts, rows: &[TagCounts], overflowed: u32) -> HeapSnapshot {
20    let rows: Vec<TagRow> = rows.iter().map(row).collect();
21    let mut snapshot = HeapSnapshot::new(counts.live_bytes, counts.peak_bytes, rows);
22    snapshot.overflowed = overflowed;
23    snapshot
24}
25
26/// The counter's row `counts` as a snapshot's.
27fn row(counts: &TagCounts) -> TagRow {
28    let class = TagClass::from_u8(counts.class).unwrap_or(TagClass::Other);
29    let mut row = TagRow::new(
30        class,
31        Cow::Borrowed(counts.name),
32        counts.live_bytes,
33        counts.live_blocks,
34    );
35    row.allocated_bytes = counts.allocated_bytes;
36    row.peak_bytes = counts.peak_bytes;
37    row
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    /// The counter's row of tag `tag`, `name` of `class`, with `live`
45    /// bytes in `blocks`, `allocated` ever and `peak` at the most.
46    fn counts(tag: u16, class: u8, name: &'static str, live: u64, blocks: u32) -> TagCounts {
47        let mut row = TagCounts::new(tag, class, name);
48        row.live_bytes = live;
49        row.live_blocks = blocks;
50        row.allocated_bytes = live * 3;
51        row.peak_bytes = live * 2;
52        row
53    }
54
55    #[test]
56    fn the_counters_rows_become_the_snapshots_in_its_order() {
57        let rows = [
58            counts(0, 0, "other", 100, 1),
59            counts(1, 1, "update", 300, 2),
60            counts(2, 3, "image", 500, 4),
61            counts(3, 2, "ai", 0, 0),
62            counts(4, 9, "strange", 200, 1),
63        ];
64        let snapshot = snapshot(Counts::new(1100, 4000, 7), &rows, 2);
65        assert_eq!(
66            (snapshot.frame, snapshot.live_bytes, snapshot.peak_bytes),
67            (0, 1100, 4000)
68        );
69        assert_eq!(snapshot.overflowed, 2);
70        let read: Vec<(TagClass, &str, u64, u32)> = snapshot
71            .tags
72            .iter()
73            .map(|r| (r.class, &*r.name, r.live_bytes, r.live_blocks))
74            .collect();
75        assert_eq!(
76            read,
77            [
78                (TagClass::Asset, "image", 500, 4),
79                (TagClass::Phase, "update", 300, 2),
80                (TagClass::Other, "strange", 200, 1),
81                (TagClass::Other, "other", 100, 1),
82                (TagClass::Span, "ai", 0, 0),
83            ]
84        );
85        let image = &snapshot.tags[0];
86        assert_eq!((image.allocated_bytes, image.peak_bytes), (1500, 1000));
87    }
88
89    #[test]
90    fn no_rows_is_a_snapshot_of_the_totals_alone() {
91        let snapshot = snapshot(Counts::new(10, 20, 0), &[], 0);
92        assert_eq!(snapshot, HeapSnapshot::new(10, 20, Vec::new()));
93    }
94}