samply-api 0.24.0

JSON API for querying symbol information, uses samply-symbols.
Documentation
use std::collections::HashMap;
use std::num::NonZeroU32;

use serde_derive::Serialize;

#[derive(Serialize, Debug)]
pub struct Response {
    pub results: Vec<Result>,
}

#[derive(Serialize, Debug)]
pub struct Result {
    pub stacks: Vec<Stack>,
    pub found_modules: HashMap<String, bool>,

    #[serde(skip_serializing_if = "HashMap::is_empty")]
    pub module_errors: HashMap<String, Vec<Error>>,
}

#[derive(Serialize, Debug)]
pub struct Stack(pub Vec<StackFrame>);

#[derive(Serialize, Debug)]
pub struct StackFrame {
    /// index of this StackFrame in its parent Stack
    pub frame: u32,

    #[serde(serialize_with = "crate::hex::as_hex_string")]
    pub module_offset: u32,

    pub module: String,

    #[serde(flatten)]
    pub symbol: Option<Symbol>,
}

#[derive(Serialize, Debug)]
pub struct Symbol {
    pub function: String,

    #[serde(serialize_with = "crate::hex::as_hex_string")]
    pub function_offset: u32,

    #[serde(
        skip_serializing_if = "Option::is_none",
        serialize_with = "crate::hex::as_optional_hex_string"
    )]
    pub function_size: Option<u32>,

    #[serde(flatten)]
    pub debug_info: Option<DebugInfo>,
}

#[derive(Serialize, Debug)]
pub struct DebugInfo {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub file: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub line: Option<NonZeroU32>,

    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub inlines: Vec<FrameDebugInfo>,
}

#[derive(Serialize, Debug)]
pub struct FrameDebugInfo {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub function: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub file: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub line: Option<NonZeroU32>,
}

#[derive(Serialize, Debug)]
pub struct Error {
    pub name: String,
    pub message: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub filename: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub line: Option<u32>,
}

impl From<&samply_symbols::Error> for Error {
    fn from(err: &samply_symbols::Error) -> Self {
        Self {
            name: err.enum_as_string().to_string(),
            message: err.to_string(),
            filename: None,
            line: None,
        }
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;

    use serde_json::Result;

    use super::super::response_json;

    #[test]
    fn serialize_correctly() -> Result<()> {
        let response = response_json::Response {
            results: vec![response_json::Result {
                stacks: vec![response_json::Stack(vec![
                    response_json::StackFrame {
                        frame: 0,
                        module_offset: 0xb2e3f7,
                        module: String::from("xul.pdb"),
                        symbol: Some(response_json::Symbol {
                            function: String::from("sctp_send_initiate"),
                            function_offset: 0x4ca,
                            function_size: None,
                            debug_info: None,
                        }),
                    },
                    response_json::StackFrame {
                        frame: 1,
                        module_offset: 0x1010a,
                        module: String::from("wntdll.pdb"),
                        symbol: None,
                    },
                ])],
                found_modules: [(
                    String::from("xul.pdb/44E4EC8C2F41492B9369D6B9A059577C2"),
                    true,
                )]
                .iter()
                .cloned()
                .collect(),
                module_errors: HashMap::new(),
            }],
        };
        let response = serde_json::to_string_pretty(&response)?;
        let expected = r#"{
  "results": [
    {
      "stacks": [
        [
          {
            "frame": 0,
            "module_offset": "0xb2e3f7",
            "module": "xul.pdb",
            "function": "sctp_send_initiate",
            "function_offset": "0x4ca"
          },
          {
            "frame": 1,
            "module_offset": "0x1010a",
            "module": "wntdll.pdb"
          }
        ]
      ],
      "found_modules": {
        "xul.pdb/44E4EC8C2F41492B9369D6B9A059577C2": true
      }
    }
  ]
}"#;
        eprintln!("{response}");
        assert_eq!(response, expected);
        Ok(())
    }
}