profile-inspect 0.1.3

Analyze V8 CPU and heap profiles from Node.js/Chrome DevTools
Documentation
use serde::Deserialize;

/// Raw V8 heap profile (allocation sampling)
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HeapProfile {
    /// Root node of the allocation tree
    pub head: HeapProfileNode,

    /// Allocation samples
    #[serde(default)]
    pub samples: Vec<HeapSample>,
}

/// A node in the heap profile allocation tree
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HeapProfileNode {
    /// Call frame information
    pub call_frame: HeapCallFrame,

    /// Self size (bytes allocated directly by this frame)
    #[serde(default)]
    pub self_size: u64,

    /// Unique node ID
    #[serde(default)]
    pub id: u32,

    /// Child nodes
    #[serde(default)]
    pub children: Vec<HeapProfileNode>,
}

/// Call frame for heap profile nodes
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HeapCallFrame {
    /// Function name
    pub function_name: String,

    /// Script ID
    #[serde(default)]
    pub script_id: String,

    /// Script URL
    #[serde(default)]
    pub url: String,

    /// Line number (1-based in heap profiles)
    #[serde(default)]
    pub line_number: i32,

    /// Column number (1-based in heap profiles)
    #[serde(default)]
    pub column_number: i32,
}

/// Heap allocation sample
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HeapSample {
    /// Size of allocation in bytes
    pub size: u64,

    /// Node ID where allocation occurred
    pub node_id: u32,

    /// Ordinal (sample order)
    #[serde(default)]
    pub ordinal: u32,
}

impl HeapProfile {
    /// Parse a heap profile from JSON
    ///
    /// # Errors
    /// Returns an error if the JSON is invalid.
    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
        // Heap profiles can be deeply nested, so we need to disable recursion limit
        // and use serde_stacker to dynamically grow the stack
        let mut deserializer = serde_json::Deserializer::from_str(json);
        deserializer.disable_recursion_limit();
        let deserializer = serde_stacker::Deserializer::new(&mut deserializer);
        HeapProfile::deserialize(deserializer)
    }

    /// Calculate total allocated bytes
    pub fn total_bytes(&self) -> u64 {
        self.samples.iter().map(|s| s.size).sum()
    }
}