aya_friday/maps/stack_trace.rs
1//! A hash map of kernel or user space stack traces.
2//!
3//! See [`StackTraceMap`] for documentation and examples.
4
5use std::{
6 borrow::{Borrow, BorrowMut},
7 os::fd::AsFd as _,
8};
9
10use aya_obj::generated::BPF_F_STACK_BUILD_ID;
11
12use crate::{
13 maps::{IterableMap, MapData, MapError, MapIter, MapKeys, hash_map},
14 sys::{SyscallError, bpf_map_lookup_elem_ptr},
15};
16
17/// A hash map of kernel or user space stack traces.
18///
19/// Stack trace maps can be used to store stack traces captured by eBPF programs, which can be
20/// useful for profiling, to associate a trace to an event, etc. You can capture traces calling
21/// `stack_id = bpf_get_stackid(ctx, map, flags)` from eBPF, and then you can retrieve the traces
22/// from their stack ids.
23///
24/// # Minimum kernel version
25///
26/// The minimum kernel version required to use this feature is 4.6.
27///
28/// # Examples
29///
30/// ```no_run
31/// # #[derive(thiserror::Error, Debug)]
32/// # enum Error {
33/// # #[error(transparent)]
34/// # IO(#[from] std::io::Error),
35/// # #[error(transparent)]
36/// # Map(#[from] aya::maps::MapError),
37/// # #[error(transparent)]
38/// # Ebpf(#[from] aya::EbpfError)
39/// # }
40/// # let bpf = aya::Ebpf::load(&[])?;
41/// use aya::maps::StackTraceMap;
42/// use aya::util::kernel_symbols;
43///
44/// let mut stack_traces = StackTraceMap::try_from(bpf.map("STACK_TRACES").unwrap())?;
45/// // load kernel symbols from /proc/kallsyms
46/// let ksyms = kernel_symbols()?;
47///
48/// // NOTE: you typically send stack_ids from eBPF to user space using other maps
49/// let stack_id = 1234;
50/// let mut stack_trace = stack_traces.get(&stack_id, 0)?;
51///
52/// // here we resolve symbol names using kernel symbols. If this was a user space stack (for
53/// // example captured from a uprobe), you'd have to load the symbols using some other mechanism
54/// // (eg loading the target binary debuginfo)
55/// for frame in stack_trace.frames() {
56/// if let Some(sym) = ksyms.range(..=frame.ip).next_back().map(|(_, s)| s) {
57/// println!(
58/// "{:#x} {}",
59/// frame.ip,
60/// sym
61/// );
62/// } else {
63/// println!(
64/// "{:#x}",
65/// frame.ip
66/// );
67/// }
68/// }
69///
70/// # Ok::<(), Error>(())
71/// ```
72///
73#[derive(Debug)]
74#[doc(alias = "BPF_MAP_TYPE_STACK_TRACE")]
75pub struct StackTraceMap<T> {
76 pub(crate) inner: T,
77 max_stack_depth: usize,
78}
79
80// A stack trace entry is a single `u64` instruction pointer, matching the
81// kernel layout for `BPF_MAP_TYPE_STACK_TRACE` values.
82type StackEntry = u64;
83
84impl<T: Borrow<MapData>> StackTraceMap<T> {
85 pub(crate) fn new(map: T) -> Result<Self, MapError> {
86 let data = map.borrow();
87
88 let key_size = data.obj.key_size() as usize;
89 let expected_key = size_of::<u32>();
90 if key_size != expected_key {
91 return Err(MapError::InvalidKeySize {
92 size: key_size,
93 expected: expected_key,
94 });
95 }
96
97 // BPF_F_STACK_BUILD_ID switches stack entries to
98 // `struct bpf_stack_build_id` (32 bytes), which `get` decodes as
99 // `[u64]` and would silently corrupt. Reject it.
100 let flags = data.obj.map_flags();
101 if flags & BPF_F_STACK_BUILD_ID != 0 {
102 return Err(MapError::UnsupportedMapFlags {
103 flags,
104 reason: "StackTraceMap does not support bpf_stack_build_id entries",
105 });
106 }
107
108 let value_size = data.obj.value_size() as usize;
109 let expected_stride = size_of::<StackEntry>();
110 if value_size == 0 || !value_size.is_multiple_of(expected_stride) {
111 return Err(MapError::InvalidValueStride {
112 size: value_size,
113 stride: expected_stride,
114 });
115 }
116 let max_stack_depth = value_size / expected_stride;
117
118 Ok(Self {
119 inner: map,
120 max_stack_depth,
121 })
122 }
123
124 /// Returns the stack trace with the given `stack_id`.
125 ///
126 /// # Errors
127 ///
128 /// Returns [`MapError::KeyNotFound`] if there is no stack trace with the
129 /// given `stack_id`, or [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails.
130 pub fn get(&self, stack_id: &u32, flags: u64) -> Result<StackTrace, MapError> {
131 let fd = self.inner.borrow().fd().as_fd();
132
133 let mut frames: Vec<StackEntry> = vec![0; self.max_stack_depth];
134 bpf_map_lookup_elem_ptr(fd, Some(stack_id), frames.as_mut_ptr(), flags)
135 .map_err(|io_error| SyscallError {
136 call: "bpf_map_lookup_elem",
137 io_error,
138 })?
139 .ok_or(MapError::KeyNotFound)?;
140
141 let frames = frames
142 .into_iter()
143 .take_while(|ip| *ip != 0)
144 .map(|ip| StackFrame { ip })
145 .collect::<Vec<_>>();
146
147 Ok(StackTrace {
148 id: *stack_id,
149 frames,
150 })
151 }
152
153 /// An iterator visiting all (`stack_id`, `stack_trace`) pairs in arbitrary order. The
154 /// iterator item type is `Result<(u32, StackTrace), MapError>`.
155 pub fn iter(&self) -> MapIter<'_, u32, StackTrace, Self> {
156 MapIter::new(self)
157 }
158
159 /// An iterator visiting all the `stack_ids` in arbitrary order. The iterator element
160 /// type is `Result<u32, MapError>`.
161 pub fn stack_ids(&self) -> MapKeys<'_, u32> {
162 MapKeys::new(self.inner.borrow())
163 }
164}
165
166impl<T: Borrow<MapData>> IterableMap<u32, StackTrace> for StackTraceMap<T> {
167 fn map(&self) -> &MapData {
168 self.inner.borrow()
169 }
170
171 fn get(&self, key: &u32) -> Result<StackTrace, MapError> {
172 self.get(key, 0)
173 }
174}
175
176impl<'a, T: Borrow<MapData>> IntoIterator for &'a StackTraceMap<T> {
177 type Item = Result<(u32, StackTrace), MapError>;
178 type IntoIter = MapIter<'a, u32, StackTrace, StackTraceMap<T>>;
179
180 fn into_iter(self) -> Self::IntoIter {
181 self.iter()
182 }
183}
184
185impl<T: BorrowMut<MapData>> StackTraceMap<T> {
186 /// Removes the stack trace with the given `stack_id`.
187 pub fn remove(&mut self, stack_id: &u32) -> Result<(), MapError> {
188 hash_map::remove(self.inner.borrow_mut(), stack_id)
189 }
190}
191
192/// A kernel or user space stack trace.
193///
194/// See the [`StackTraceMap`] documentation for examples.
195pub struct StackTrace {
196 /// The stack trace id as returned by `bpf_get_stackid()`.
197 pub id: u32,
198 frames: Vec<StackFrame>,
199}
200
201impl StackTrace {
202 /// Returns the frames in this stack trace.
203 pub fn frames(&self) -> &[StackFrame] {
204 &self.frames
205 }
206}
207
208/// A stack frame.
209pub struct StackFrame {
210 /// The instruction pointer of this frame.
211 pub ip: u64,
212}