Skip to main content

profile_inspect/types/
heap.rs

1use serde::Deserialize;
2
3/// Raw V8 heap profile (allocation sampling)
4#[derive(Debug, Deserialize)]
5#[serde(rename_all = "camelCase")]
6pub struct HeapProfile {
7    /// Root node of the allocation tree
8    pub head: HeapProfileNode,
9
10    /// Allocation samples
11    #[serde(default)]
12    pub samples: Vec<HeapSample>,
13}
14
15/// A node in the heap profile allocation tree
16#[derive(Debug, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct HeapProfileNode {
19    /// Call frame information
20    pub call_frame: HeapCallFrame,
21
22    /// Self size (bytes allocated directly by this frame)
23    #[serde(default)]
24    pub self_size: u64,
25
26    /// Unique node ID
27    #[serde(default)]
28    pub id: u32,
29
30    /// Child nodes
31    #[serde(default)]
32    pub children: Vec<HeapProfileNode>,
33}
34
35/// Call frame for heap profile nodes
36#[derive(Debug, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub struct HeapCallFrame {
39    /// Function name
40    pub function_name: String,
41
42    /// Script ID
43    #[serde(default)]
44    pub script_id: String,
45
46    /// Script URL
47    #[serde(default)]
48    pub url: String,
49
50    /// Line number (1-based in heap profiles)
51    #[serde(default)]
52    pub line_number: i32,
53
54    /// Column number (1-based in heap profiles)
55    #[serde(default)]
56    pub column_number: i32,
57}
58
59/// Heap allocation sample
60#[derive(Debug, Deserialize)]
61#[serde(rename_all = "camelCase")]
62pub struct HeapSample {
63    /// Size of allocation in bytes
64    pub size: u64,
65
66    /// Node ID where allocation occurred
67    pub node_id: u32,
68
69    /// Ordinal (sample order)
70    #[serde(default)]
71    pub ordinal: u32,
72}
73
74impl HeapProfile {
75    /// Parse a heap profile from JSON
76    ///
77    /// # Errors
78    /// Returns an error if the JSON is invalid.
79    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
80        // Heap profiles can be deeply nested, so we need to disable recursion limit
81        // and use serde_stacker to dynamically grow the stack
82        let mut deserializer = serde_json::Deserializer::from_str(json);
83        deserializer.disable_recursion_limit();
84        let deserializer = serde_stacker::Deserializer::new(&mut deserializer);
85        HeapProfile::deserialize(deserializer)
86    }
87
88    /// Calculate total allocated bytes
89    pub fn total_bytes(&self) -> u64 {
90        self.samples.iter().map(|s| s.size).sum()
91    }
92}