hydro_deploy/localhost/
samply.rs
1use std::collections::BTreeMap;
2use std::path::PathBuf;
3use std::str::FromStr;
4
5use futures::future::join_all;
6use itertools::Itertools;
7use serde::{Deserialize, Serialize};
8use serde_json::Number;
9use wholesym::debugid::DebugId;
10use wholesym::{LookupAddress, MultiArchDisambiguator, SymbolManager, SymbolManagerConfig};
11
12#[derive(Serialize, Deserialize, Debug)]
13pub struct FxProfile {
14 threads: Vec<Thread>,
15 libs: Vec<Lib>,
16}
17
18#[derive(Serialize, Deserialize, Debug)]
19pub struct Lib {
20 pub path: String,
21 #[serde(rename = "breakpadId")]
22 pub breakpad_id: String,
23}
24
25#[derive(Serialize, Deserialize, Debug)]
26pub struct Thread {
27 #[serde(rename = "stackTable")]
28 pub stack_table: StackTable,
29 #[serde(rename = "frameTable")]
30 pub frame_table: FrameTable,
31 #[serde(rename = "funcTable")]
32 pub func_table: FuncTable,
33 pub samples: Samples,
34 #[serde(rename = "isMainThread")]
35 pub is_main_thread: bool,
36}
37
38#[derive(Serialize, Deserialize, Debug)]
39pub struct Samples {
40 pub stack: Vec<Option<usize>>,
41 pub weight: Vec<u64>,
42}
43
44#[derive(Serialize, Deserialize, Debug)]
45pub struct StackTable {
46 pub prefix: Vec<Option<usize>>,
47 pub frame: Vec<usize>,
48}
49
50#[derive(Serialize, Deserialize, Debug)]
51pub struct FrameTable {
52 pub address: Vec<Number>,
54 pub func: Vec<usize>,
55}
56
57#[derive(Serialize, Deserialize, Debug)]
58pub struct FuncTable {
59 pub resource: Vec<Number>,
61}
62
63pub async fn samply_to_folded(loaded: FxProfile) -> String {
64 let symbol_manager = SymbolManager::with_config(SymbolManagerConfig::default());
65
66 let mut symbol_maps = vec![];
67 for lib in &loaded.libs {
68 symbol_maps.push(
69 symbol_manager
70 .load_symbol_map_for_binary_at_path(
71 &PathBuf::from_str(&lib.path).unwrap(),
72 Some(MultiArchDisambiguator::DebugId(
73 DebugId::from_breakpad(&lib.breakpad_id).unwrap(),
74 )),
75 )
76 .await
77 .ok(),
78 );
79 }
80
81 let mut folded_frames: BTreeMap<Vec<Option<String>>, u64> = BTreeMap::new();
82 for thread in loaded.threads.into_iter().filter(|t| t.is_main_thread) {
83 let frame_lookuped = join_all((0..thread.frame_table.address.len()).map(|frame_id| {
84 let fr_address = &thread.frame_table.address;
85 let fr_func = &thread.frame_table.func;
86 let fn_resource = &thread.func_table.resource;
87 let symbol_maps = &symbol_maps;
88 async move {
89 let address = fr_address[frame_id].as_u64()?;
90 let func_id = fr_func[frame_id];
91 let resource_id = fn_resource[func_id].as_u64()?;
92 let symbol_map = symbol_maps[resource_id as usize].as_ref()?;
93 let lookuped = symbol_map
94 .lookup(LookupAddress::Relative(address as u32))
95 .await?;
96
97 if let Some(inline_frames) = lookuped.frames {
98 Some(
99 inline_frames
100 .into_iter()
101 .rev()
102 .map(|inline| inline.function.unwrap_or_else(|| "unknown".to_string()))
103 .join(";"),
104 )
105 } else {
106 Some(lookuped.symbol.name)
107 }
108 }
109 }))
110 .await;
111
112 let all_leaves_grouped = thread
113 .samples
114 .stack
115 .iter()
116 .enumerate()
117 .filter_map(|(idx, s)| s.map(|s| (idx, s)))
118 .map(|(idx, leaf)| (leaf, thread.samples.weight[idx]))
119 .chunk_by(|&(leaf, _)| leaf)
120 .into_iter()
121 .map(|(leaf, group)| {
122 let weight = group.map(|(_leaf, weight)| weight).sum();
123 (leaf, weight)
124 })
125 .collect::<Vec<(usize, u64)>>();
126
127 for (leaf, weight) in all_leaves_grouped {
128 let mut cur_stack = Some(leaf);
129 let mut stack = vec![];
130 while let Some(sample) = cur_stack {
131 let frame_id = thread.stack_table.frame[sample];
132 stack.push(frame_lookuped[frame_id].clone());
133 cur_stack = thread.stack_table.prefix[sample];
134 }
135
136 *folded_frames.entry(stack).or_default() += weight;
137 }
138 }
139
140 let mut output = String::new();
141 for (stack, weight) in folded_frames {
142 for (i, s) in stack.iter().rev().enumerate() {
143 if i != 0 {
144 output.push(';');
145 }
146 output.push_str(s.as_deref().unwrap_or("unknown"));
147 }
148
149 output.push_str(&format!(" {}\n", weight));
150 }
151
152 output
153}