mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-03-20 11:56:22 +08:00
fix: Correct tutorial 12 profile folded stack format (#202)
- Reorder stack processing to place kernel frames at bottom and user frames on top - Match expected flamegraph.pl input format - Simplify output to standard folded format: "comm;stack1;stack2 count" - Remove timestamp and CPU metadata
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
use std::mem;
|
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
use blazesym::symbolize;
|
use blazesym::symbolize;
|
||||||
use nix::sys::sysinfo;
|
use nix::sys::sysinfo;
|
||||||
|
use std::mem;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
pub const MAX_STACK_DEPTH: usize = 128;
|
pub const MAX_STACK_DEPTH: usize = 128;
|
||||||
pub const TASK_COMM_LEN: usize = 16;
|
pub const TASK_COMM_LEN: usize = 16;
|
||||||
@@ -35,7 +35,7 @@ impl EventHandler {
|
|||||||
pub fn new(format: OutputFormat) -> Self {
|
pub fn new(format: OutputFormat) -> Self {
|
||||||
// Get system uptime to calculate boot time
|
// Get system uptime to calculate boot time
|
||||||
let boot_time_ns = Self::get_boot_time_ns();
|
let boot_time_ns = Self::get_boot_time_ns();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
symbolizer: symbolize::Symbolizer::new(),
|
symbolizer: symbolize::Symbolizer::new(),
|
||||||
format,
|
format,
|
||||||
@@ -49,11 +49,11 @@ impl EventHandler {
|
|||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
.expect("System time before Unix epoch");
|
.expect("System time before Unix epoch");
|
||||||
let now_ns = now.as_nanos() as u64;
|
let now_ns = now.as_nanos() as u64;
|
||||||
|
|
||||||
// Get system uptime in nanoseconds
|
// Get system uptime in nanoseconds
|
||||||
let info = sysinfo::sysinfo().expect("Failed to get sysinfo");
|
let info = sysinfo::sysinfo().expect("Failed to get sysinfo");
|
||||||
let uptime_ns = (info.uptime().as_secs_f64() * 1_000_000_000.0) as u64;
|
let uptime_ns = (info.uptime().as_secs_f64() * 1_000_000_000.0) as u64;
|
||||||
|
|
||||||
// Boot time = current time - uptime
|
// Boot time = current time - uptime
|
||||||
now_ns - uptime_ns
|
now_ns - uptime_ns
|
||||||
}
|
}
|
||||||
@@ -104,8 +104,10 @@ impl EventHandler {
|
|||||||
let unix_timestamp_ns = event.timestamp + self.boot_time_ns;
|
let unix_timestamp_ns = event.timestamp + self.boot_time_ns;
|
||||||
let timestamp_sec = unix_timestamp_ns / 1_000_000_000;
|
let timestamp_sec = unix_timestamp_ns / 1_000_000_000;
|
||||||
let timestamp_nsec = unix_timestamp_ns % 1_000_000_000;
|
let timestamp_nsec = unix_timestamp_ns % 1_000_000_000;
|
||||||
println!("[{}.{:09}] COMM: {} (pid={}) @ CPU {}",
|
println!(
|
||||||
timestamp_sec, timestamp_nsec, comm, event.pid, event.cpu_id);
|
"[{}.{:09}] COMM: {} (pid={}) @ CPU {}",
|
||||||
|
timestamp_sec, timestamp_nsec, comm, event.pid, event.cpu_id
|
||||||
|
);
|
||||||
|
|
||||||
if event.kstack_size > 0 {
|
if event.kstack_size > 0 {
|
||||||
println!("Kernel:");
|
println!("Kernel:");
|
||||||
@@ -128,48 +130,43 @@ impl EventHandler {
|
|||||||
|
|
||||||
fn handle_folded_extended(&self, event: &StacktraceEvent) {
|
fn handle_folded_extended(&self, event: &StacktraceEvent) {
|
||||||
let comm = Self::get_comm_str(&event.comm);
|
let comm = Self::get_comm_str(&event.comm);
|
||||||
let tid = event.pid; // For single-threaded processes, TID = PID
|
|
||||||
|
|
||||||
let mut stack_frames = Vec::new();
|
let mut stack_frames = Vec::new();
|
||||||
|
|
||||||
// Process user stack (if present)
|
// Process kernel stack first (bottom of stack, closer to root)
|
||||||
if event.ustack_size > 0 {
|
|
||||||
let ustack = Self::get_stack_slice(&event.ustack, event.ustack_size);
|
|
||||||
let user_frames = symbolize_stack_to_vec(&self.symbolizer, ustack, event.pid);
|
|
||||||
|
|
||||||
// Add user frames in reverse order (top to bottom)
|
|
||||||
for frame in user_frames.iter().rev() {
|
|
||||||
stack_frames.push(frame.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process kernel stack (if present)
|
|
||||||
if event.kstack_size > 0 {
|
if event.kstack_size > 0 {
|
||||||
let kstack = Self::get_stack_slice(&event.kstack, event.kstack_size);
|
let kstack = Self::get_stack_slice(&event.kstack, event.kstack_size);
|
||||||
let kernel_frames = symbolize_stack_to_vec(&self.symbolizer, kstack, 0);
|
let kernel_frames = symbolize_stack_to_vec(&self.symbolizer, kstack, 0);
|
||||||
|
|
||||||
// Add kernel frames with [k] suffix in reverse order (top to bottom)
|
// Add kernel frames with [k] suffix (from bottom to top)
|
||||||
for frame in kernel_frames.iter().rev() {
|
for frame in kernel_frames.iter() {
|
||||||
stack_frames.push(format!("{}_[k]", frame));
|
stack_frames.push(format!("{}_[k]", frame));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format: timestamp_ns comm pid tid cpu stack1;stack2;stack3
|
// Process user stack (on top of kernel stack)
|
||||||
// Convert kernel timestamp to Unix timestamp
|
if event.ustack_size > 0 {
|
||||||
let unix_timestamp_ns = event.timestamp + self.boot_time_ns;
|
let ustack = Self::get_stack_slice(&event.ustack, event.ustack_size);
|
||||||
println!(
|
let user_frames = symbolize_stack_to_vec(&self.symbolizer, ustack, event.pid);
|
||||||
"{} {} {} {} {} {}",
|
|
||||||
unix_timestamp_ns,
|
// Add user frames (from bottom to top)
|
||||||
comm,
|
for frame in user_frames.iter() {
|
||||||
event.pid,
|
stack_frames.push(frame.clone());
|
||||||
tid,
|
}
|
||||||
event.cpu_id,
|
}
|
||||||
stack_frames.join(";")
|
|
||||||
);
|
// If no frames, skip this event
|
||||||
|
if stack_frames.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format for flamegraph.pl folded format:
|
||||||
|
// comm;stack_frame1;stack_frame2;stack_frame3 count
|
||||||
|
// The count is 1 for each sample
|
||||||
|
println!("{};{} 1", comm, stack_frames.join(";"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn print_frame(
|
fn print_frame(
|
||||||
name: &str,
|
name: &str,
|
||||||
addr_info: Option<(blazesym::Addr, blazesym::Addr, usize)>,
|
addr_info: Option<(blazesym::Addr, blazesym::Addr, usize)>,
|
||||||
@@ -217,7 +214,11 @@ fn convert_stack_addresses(stack: &[u64]) -> Vec<blazesym::Addr> {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
} else {
|
} else {
|
||||||
// For same-sized types, still need to return owned data for consistency
|
// For same-sized types, still need to return owned data for consistency
|
||||||
stack.iter().copied().map(|addr| addr as blazesym::Addr).collect()
|
stack
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.map(|addr| addr as blazesym::Addr)
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,26 +243,30 @@ fn get_symbolize_source(pid: u32) -> symbolize::source::Source<'static> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Symbolize stack and return as vector of strings for folded format
|
// Symbolize stack and return as vector of strings for folded format
|
||||||
fn symbolize_stack_to_vec(symbolizer: &symbolize::Symbolizer, stack: &[u64], pid: u32) -> Vec<String> {
|
fn symbolize_stack_to_vec(
|
||||||
|
symbolizer: &symbolize::Symbolizer,
|
||||||
|
stack: &[u64],
|
||||||
|
pid: u32,
|
||||||
|
) -> Vec<String> {
|
||||||
let converted = convert_stack_addresses(stack);
|
let converted = convert_stack_addresses(stack);
|
||||||
let stack_addrs = get_stack_slice(stack, &converted);
|
let stack_addrs = get_stack_slice(stack, &converted);
|
||||||
let src = get_symbolize_source(pid);
|
let src = get_symbolize_source(pid);
|
||||||
|
|
||||||
let syms = match symbolizer.symbolize(&src, symbolize::Input::AbsAddr(stack_addrs)) {
|
let syms = match symbolizer.symbolize(&src, symbolize::Input::AbsAddr(stack_addrs)) {
|
||||||
Ok(syms) => syms,
|
Ok(syms) => syms,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// Return addresses if symbolization fails
|
// Return addresses if symbolization fails
|
||||||
return stack_addrs.iter().map(|addr| format!("{:#x}", addr)).collect();
|
return stack_addrs
|
||||||
|
.iter()
|
||||||
|
.map(|addr| format!("{:#x}", addr))
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for (addr, sym) in stack_addrs.iter().copied().zip(syms) {
|
for (addr, sym) in stack_addrs.iter().copied().zip(syms) {
|
||||||
match sym {
|
match sym {
|
||||||
symbolize::Symbolized::Sym(symbolize::Sym {
|
symbolize::Symbolized::Sym(symbolize::Sym { name, .. }) => {
|
||||||
name,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
result.push(name.to_string());
|
result.push(name.to_string());
|
||||||
}
|
}
|
||||||
symbolize::Symbolized::Unknown(..) => {
|
symbolize::Symbolized::Unknown(..) => {
|
||||||
@@ -306,4 +311,4 @@ fn show_stack_trace(stack: &[u64], symbolizer: &symbolize::Symbolizer, pid: u32)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user