profile_bee/
trace_handler.rs1use crate::ebpf::{FramePointersPod, StackInfoPod};
2use crate::{cache::PointerStackFramesCache, types::StackFrameInfo, types::StackInfoExt};
3use aya::maps::MapData;
4use aya::maps::StackTraceMap;
5use blazesym::symbolize::source::Kernel;
6use blazesym::symbolize::source::Process;
7use blazesym::symbolize::source::Source;
8use blazesym::symbolize::Input;
9use blazesym::symbolize::Symbolized;
10use blazesym::symbolize::Symbolizer;
11use blazesym::Addr;
12use blazesym::Pid;
13use profile_bee_common::StackInfo;
14
15pub struct SymbolFormatter;
16
17impl SymbolFormatter {
18 fn map_kernel_sym_to_stack(sym: Symbolized) -> StackFrameInfo {
20 let sym = match sym {
21 Symbolized::Sym(sym) => sym,
22 Symbolized::Unknown(_reason) => {
23 return StackFrameInfo {
24 symbol: Some(format!("[unknown]")), ..Default::default()
26 };
27 }
28 };
29
30 StackFrameInfo {
31 symbol: Some(format!("{}_k", sym.name)),
32 ..Default::default()
33 }
34 }
35
36 fn map_user_sym_to_stack(sym: Symbolized) -> StackFrameInfo {
38 let sym = match sym {
39 Symbolized::Sym(sym) => sym,
40 Symbolized::Unknown(_reason) => {
41 return StackFrameInfo {
42 symbol: Some(format!("[unknown]")), ..Default::default()
44 };
45 }
46 };
47
48 StackFrameInfo {
49 symbol: Some(format!("{}", sym.name)),
50 ..Default::default()
51 }
52 }
53}
54
55pub struct TraceHandler {
60 symbolizer: Symbolizer,
62 cache: PointerStackFramesCache,
64}
65
66impl TraceHandler {
67 pub fn new() -> Self {
68 TraceHandler {
69 symbolizer: Symbolizer::new(),
70 cache: Default::default(),
71 }
72 }
73
74 pub fn print_stats(&self) {
75 println!("{}", self.cache.stats());
76 }
77
78 fn symbolize_kernel_stack(&self, addrs: &[Addr]) -> Result<Vec<StackFrameInfo>, &str> {
80 let src = Source::Kernel(Kernel::default());
81 let syms = self
82 .symbolizer
83 .symbolize(&src, Input::AbsAddr(&addrs))
84 .map_err(|e| {
85 tracing::error!("Failed to symbolize {:?}", e);
86 "failed to run symbolize"
87 })?
88 .into_iter()
89 .map(SymbolFormatter::map_kernel_sym_to_stack)
90 .collect::<Vec<_>>();
91
92 Ok(syms)
93 }
94
95 fn symbolize_user_stack(&self, pid: u32, addrs: &[Addr]) -> Result<Vec<StackFrameInfo>, &str> {
97 let src: Source<'_> = Source::Process(Process::new(Pid::from(pid)));
98
99 let syms = self
100 .symbolizer
101 .symbolize(&src, Input::AbsAddr(addrs))
102 .map_err(|e| {
103 tracing::trace!("Failed to symbolize {:?}", e);
104 "failed to run symbolize"
105 })?
106 .into_iter()
107 .map(SymbolFormatter::map_user_sym_to_stack)
108 .collect::<Vec<_>>();
109
110 Ok(syms)
111 }
112
113 pub fn get_exp_stacked_frames(
116 &mut self,
117 stack_info: &StackInfo,
118 stack_traces: &StackTraceMap<MapData>,
119 group_by_cpu: bool,
120 stacked_pointers: &aya::maps::HashMap<MapData, StackInfoPod, FramePointersPod>,
121 ) -> Vec<StackFrameInfo> {
122 let (kernel_stack, fp_user_stack) = self.get_instruction_pointers(stack_info, stack_traces);
123
124 let key = StackInfoPod(stack_info.clone());
125
126 let user_stack = if let Ok(pointers) = stacked_pointers.get(&key, 0) {
128 let pointers = pointers.0;
129 let len = (pointers.len as usize).min(pointers.pointers.len());
130 let fp_len = fp_user_stack.as_ref().map_or(0, |v| v.len());
131 if len > fp_len {
132 let addrs: Vec<u64> = pointers.pointers[..len].to_vec();
133 tracing::debug!(
134 "Using DWARF-unwound stack ({} frames) for pid {}",
135 addrs.len(),
136 stack_info.tgid,
137 );
138 Some(addrs)
139 } else {
140 fp_user_stack
141 }
142 } else {
143 fp_user_stack
144 };
145
146 let stacks = self.format_stack_trace(stack_info, kernel_stack, user_stack, group_by_cpu);
147
148 stacks
149 }
150
151 pub fn get_stacked_frames(
153 &mut self,
154 stack_info: &StackInfo,
155 stack_traces: &StackTraceMap<MapData>,
156 group_by_cpu: bool,
157 ) -> Vec<StackFrameInfo> {
158 let (kernel_stack, user_stack) = self.get_instruction_pointers(stack_info, stack_traces);
159 let stacks = self.format_stack_trace(stack_info, kernel_stack, user_stack, group_by_cpu);
160 stacks
161 }
162
163 pub fn get_instruction_pointers(
185 &mut self,
186 stack_info: &StackInfo,
187 stack_traces: &StackTraceMap<MapData>,
188 ) -> (Option<Vec<u64>>, Option<Vec<u64>>) {
189 let ktrace_id = stack_info.kernel_stack_id;
190 let utrace_id = stack_info.user_stack_id;
191
192 let kernel_stack = if ktrace_id > -1 {
193 stack_traces.get(&(ktrace_id as u32), 0).ok().map(|stack| {
194 let addrs: Vec<Addr> = stack
195 .frames()
196 .iter()
197 .map(|frame| {
198 let instruction_pointer = frame.ip;
199 instruction_pointer
200 })
201 .collect();
202
203 addrs
204 })
205 } else {
206 None
207 };
208
209 let user_stack = if utrace_id > -1 {
210 stack_traces.get(&(utrace_id as u32), 0).ok().map(|stack| {
211 let addrs: Vec<Addr> = stack
212 .frames()
213 .iter()
214 .map(|frame| frame.ip)
215 .collect();
216 addrs
217 })
218 } else {
219 None
220 };
221
222 (kernel_stack, user_stack)
223 }
224
225 fn format_stack_trace(
229 &self,
230 stack_info: &StackInfo,
231 kernel_stack: Option<Vec<u64>>,
232 user_stack: Option<Vec<u64>>,
233 group_by_cpu: bool,
235 ) -> Vec<StackFrameInfo> {
236 if stack_info.tgid == 0 {
237 let mut idle = StackFrameInfo::prepare(stack_info);
238 idle.symbol = Some("idle".into());
239 let mut idle_cpu = StackFrameInfo::process_only(stack_info);
240
241 if let Some(cpu_id) = stack_info.get_cpu_id() {
242 idle_cpu.symbol = Some(format!("cpu_{:02}", cpu_id));
243 } else {
244 idle_cpu.symbol = idle_cpu.symbol.map(|s| s.replace("swapper/", "cpu_"));
245 }
246
247 if group_by_cpu {
248 if let Some(cpu_id) = stack_info.get_cpu_id() {
249 idle_cpu.symbol = Some(format!("cpu_{:02}", cpu_id));
250 return vec![idle_cpu, idle];
251 }
252 }
253
254 return vec![idle, idle_cpu];
255 }
256
257 let pid = stack_info.tgid;
258
259 let addrs = user_stack.unwrap_or_default();
260 let user_syms = self
261 .symbolize_user_stack(pid, &addrs)
262 .ok()
263 .unwrap_or_default();
264
265 let kernel_addrs = kernel_stack.unwrap_or_default();
266 let kernel_syms = self
267 .symbolize_kernel_stack(&kernel_addrs)
268 .ok()
269 .unwrap_or_default();
270
271 let mut combined = kernel_syms
272 .into_iter()
273 .chain(user_syms.into_iter())
274 .collect::<Vec<_>>();
275
276 let pid_info = StackFrameInfo::process_only(stack_info);
277 combined.push(pid_info);
278
279 if group_by_cpu {
280 if let Some(cpu_id) = stack_info.get_cpu_id() {
281 let frame = StackFrameInfo {
282 symbol: Some(format!("cpu_{:02}", cpu_id)),
283 ..Default::default()
284 };
285
286 combined.push(frame);
287 }
288 }
289
290 combined.reverse();
291
292 combined
293 }
294}