1use std::borrow::Borrow;
2use std::fmt;
3use std::fmt::{Debug, Formatter};
4
5use auxiliary::{Aux, AuxOutputHwId};
6use bpf::BpfEvent;
7use cgroup::Cgroup;
8use comm::Comm;
9use ctx::CtxSwitch;
10use itrace::ItraceStart;
11use ksymbol::Ksymbol;
12use lost::{LostRecords, LostSamples};
13use mmap::Mmap;
14use ns::Namespaces;
15use read::Read;
16use sample::Sample;
17use task::{Exit, Fork};
18use text_poke::TextPoke;
19use throttle::{Throttle, Unthrottle};
20
21use super::rb::CowChunk;
22use crate::ffi::{bindings as b, deref_offset, Attr};
23
24pub mod auxiliary;
25pub mod bpf;
26pub mod cgroup;
27pub mod comm;
28pub mod ctx;
29pub mod itrace;
30pub mod ksymbol;
31pub mod lost;
32pub mod mmap;
33pub mod ns;
34pub mod read;
35pub mod sample;
36pub mod task;
37pub mod text_poke;
38pub mod throttle;
39
40#[derive(Clone)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub enum Record {
48 Sample(Box<Sample>),
50
51 Mmap(Box<Mmap>),
53 Read(Box<Read>),
55 Cgroup(Box<Cgroup>),
58 Ksymbol(Box<Ksymbol>),
61 TextPoke(Box<TextPoke>),
64 BpfEvent(Box<BpfEvent>),
67 CtxSwitch(Box<CtxSwitch>),
70 Namespaces(Box<Namespaces>),
73 ItraceStart(Box<ItraceStart>),
76
77 Aux(Box<Aux>),
80 AuxOutputHwId(Box<AuxOutputHwId>),
83
84 Comm(Box<Comm>),
86 Exit(Box<Exit>),
88 Fork(Box<Fork>),
90
91 Throttle(Box<Throttle>),
93 Unthrottle(Box<Unthrottle>),
95
96 LostRecords(Box<LostRecords>),
98 LostSamples(Box<LostSamples>),
101
102 Unknown(Vec<u8>),
106}
107
108impl Debug for Record {
109 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
110 macro_rules! debug {
111 ($($varient:ident,)+) => {
112 match self {
113 $(Self::$varient(it) => {
114 if f.alternate() {
115 return write!(f, "{:#?}", it)
116 }
117 if f.sign_minus(){
118 return write!(f, "{:-?}", it)
119 }
120 write!(f, "{:?}", it)
121 })+
122 }
123 };
124 }
125
126 debug![
127 Sample,
128 Mmap,
129 Read,
130 Cgroup,
131 Ksymbol,
132 TextPoke,
133 BpfEvent,
134 CtxSwitch,
135 Namespaces,
136 ItraceStart,
137 Aux,
138 AuxOutputHwId,
139 Comm,
140 Exit,
141 Fork,
142 Throttle,
143 Unthrottle,
144 LostRecords,
145 LostSamples,
146 Unknown,
147 ]
148 }
149}
150
151#[derive(Clone, Debug)]
153#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
154pub struct Task {
155 pub pid: u32,
157 pub tid: u32,
159}
160
161#[derive(Clone, Debug)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
164pub enum Priv {
165 User,
168 Kernel,
171 Hv,
174 GuestUser,
177 GuestKernel,
180 Unknown,
183}
184
185impl Priv {
186 pub(crate) fn from_misc(misc: u16) -> Self {
187 match misc as u32 & b::PERF_RECORD_MISC_CPUMODE_MASK {
189 b::PERF_RECORD_MISC_USER => Self::User,
190 b::PERF_RECORD_MISC_KERNEL => Self::Kernel,
191 b::PERF_RECORD_MISC_HYPERVISOR => Self::Hv,
192 b::PERF_RECORD_MISC_GUEST_USER => Self::GuestUser,
193 b::PERF_RECORD_MISC_GUEST_KERNEL => Self::GuestKernel,
194 b::PERF_RECORD_MISC_CPUMODE_UNKNOWN => Self::Unknown,
195 _ => Self::Unknown, }
197 }
198}
199
200#[derive(Clone)]
202#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
203pub struct RecordId {
204 pub id: Option<u64>,
206
207 pub stream_id: Option<u64>,
213
214 pub cpu: Option<u32>,
216
217 pub task: Option<Task>,
219
220 pub time: Option<u64>,
224}
225
226debug!(RecordId {
227 {id?},
228 {stream_id?},
229 {cpu?},
230 {task?},
231 {time?},
232});
233
234pub(crate) struct SampleType(pub u64);
235
236impl RecordId {
237 pub(crate) unsafe fn from_ptr(mut ptr: *const u8, sample_type: u64) -> Self {
238 macro_rules! when {
249 ($flag:ident, $ty:ty) => {
250 (sample_type & (b::$flag as u64) > 0).then(|| deref_offset::<$ty>(&mut ptr))
251 };
252 ($flag:ident, $then:expr) => {
253 (sample_type & (b::$flag as u64) > 0).then(|| $then)
254 };
255 }
256
257 let task = when!(PERF_SAMPLE_TID, {
258 let pid = deref_offset(&mut ptr);
259 let tid = deref_offset(&mut ptr);
260 Task { pid, tid }
261 });
262 let time = when!(PERF_SAMPLE_TIME, u64);
263 let id = when!(PERF_SAMPLE_ID, u64);
264 let stream_id = when!(PERF_SAMPLE_STREAM_ID, u64);
265 let cpu = when!(PERF_SAMPLE_CPU, u32);
266
267 Self {
278 id,
279 stream_id,
280 cpu,
281 task,
282 time,
283 }
284 }
285}
286
287macro_rules! from {
288 ($ty:ident) => {
289 impl From<Box<$ty>> for super::Record {
290 fn from(value: Box<$ty>) -> Self {
291 Self::$ty(value)
292 }
293 }
294 };
295}
296use from;
297
298macro_rules! debug {
299 ($ty:ty { $first_field:tt, $($field:tt,)* }) => {
300 impl std::fmt::Debug for $ty {
301 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302 use crate::sample::record::debug;
303
304 if f.sign_minus() {
306 let has_none = debug!(is_none, self, $first_field) $(|| debug!(is_none, self, $field))+;
307 write!(f, "{} {{ ", stringify!($ty))?;
308 if has_none {
309 debug!({:-?}, self, f, "{}: {:-?}, ", $first_field);
310 $(debug!({:-?}, self, f, "{}: {:-?}, ", $field);)+
311 write!(f, "..")?;
312 } else {
313 debug!({:-?}, self, f, "{}: {:-?}", $first_field);
314 $(debug!({:-?}, self, f, ", {}: {:-?}", $field);)+
315 }
316 return write!(f, " }}")
317 }
318
319 if f.alternate() {
321 let has_none = debug!(is_none, self, $first_field) $(|| debug!(is_none, self, $field))+;
322 let mut ds = f.debug_struct(stringify!($ty));
323 debug!({:#?}, self, ds, $first_field);
324 $(debug!({:#?}, self, ds, $field);)*
325 return if has_none {
326 ds.finish_non_exhaustive()
327 } else {
328 ds.finish()
329 }
330 }
331
332 let mut ds = f.debug_struct(stringify!($ty));
334 debug!({:?}, self, ds, $first_field);
335 $(debug!({:?}, self, ds, $field);)*
336 ds.finish()
337 }
338 }
339 };
340 (is_none, $self:ident, {$field:ident}) => {
342 false
343 };
344 (is_none, $self:ident, {$field:ident?}) => {
345 $self.$field.is_none()
346 };
347 ({:?}, $self:ident, $ds:ident, {$field:ident$(?)?}) => {
348 $ds.field(stringify!($field), &$self.$field);
349 };
350 ({:#?}, $self:ident, $ds:ident, {$field:ident}) => {
351 $ds.field(stringify!($field), &$self.$field);
352 };
353 ({:#?}, $self:ident, $ds:ident, {$field:ident?}) => {
354 if let Some(it) = &$self.$field {
355 $ds.field(stringify!($field), it);
356 }
357 };
358 ({:-?}, $self:ident, $f:ident, $fmt:literal, {$field:ident}) => {
359 write!($f, $fmt, stringify!($field), &$self.$field)?;
360 };
361 ({:-?}, $self:ident, $f:ident, $fmt:literal, {$field:ident?}) => {
362 if let Some(it) = &$self.$field {
363 write!($f, $fmt, stringify!($field), it)?;
364 }
365 };
366}
367pub(crate) use debug;
368
369#[derive(Clone, Debug)]
373#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
374pub struct UnsafeParser {
375 pub sample_id_all: bool,
376 pub sample_type: u64,
377 pub read_format: u64,
378 pub user_regs: usize,
379 pub intr_regs: usize,
380 pub branch_sample_type: u64,
381}
382
383impl UnsafeParser {
384 pub(crate) fn from_attr(attr: &Attr) -> Self {
385 Self {
386 sample_id_all: attr.sample_id_all() > 0,
387 sample_type: attr.sample_type,
388 user_regs: attr.sample_regs_user.count_ones() as _,
389 intr_regs: attr.sample_regs_intr.count_ones() as _,
390 branch_sample_type: attr.branch_sample_type,
391 read_format: attr.read_format,
392 }
393 }
394
395 pub unsafe fn parse<T>(&self, bytes: T) -> (Priv, Record)
403 where
404 T: Borrow<[u8]>,
405 {
406 let bytes = bytes.borrow();
407 let ptr = &mut bytes.as_ptr();
408
409 let ty: u32 = deref_offset(ptr);
417 let misc: u16 = deref_offset(ptr);
418 let record_priv = Priv::from_misc(misc);
419
420 let ptr = ptr.add(size_of::<u16>()); let sample_id_all = self.sample_id_all.then_some(SampleType(self.sample_type));
422
423 fn from<T>(t: T) -> Record
424 where
425 Box<T>: Into<Record>,
426 {
427 Box::new(t).into()
428 }
429
430 let record = match ty {
431 b::PERF_RECORD_SAMPLE => from(Sample::from_ptr(
432 ptr,
433 misc,
434 self.read_format,
435 self.sample_type,
436 self.user_regs,
437 self.intr_regs,
438 self.branch_sample_type,
439 )),
440 b::PERF_RECORD_MMAP => from(Mmap::from_ptr(ptr, misc, false, sample_id_all)),
441 b::PERF_RECORD_MMAP2 => from(Mmap::from_ptr(ptr, misc, true, sample_id_all)),
442 b::PERF_RECORD_READ => from(Read::from_ptr(ptr, self.read_format, sample_id_all)),
443 #[cfg(feature = "linux-5.7")]
444 b::PERF_RECORD_CGROUP => from(Cgroup::from_ptr(ptr, sample_id_all)),
445 #[cfg(feature = "linux-5.1")]
446 b::PERF_RECORD_KSYMBOL => from(Ksymbol::from_ptr(ptr, sample_id_all)),
447 #[cfg(feature = "linux-5.9")]
448 b::PERF_RECORD_TEXT_POKE => from(TextPoke::from_ptr(ptr, sample_id_all)),
449 #[cfg(feature = "linux-5.1")]
450 b::PERF_RECORD_BPF_EVENT => from(BpfEvent::from_ptr(ptr, sample_id_all)),
451 #[cfg(feature = "linux-4.3")]
452 b::PERF_RECORD_SWITCH => from(CtxSwitch::from_ptr(ptr, false, misc, sample_id_all)),
453 #[cfg(feature = "linux-4.3")]
454 b::PERF_RECORD_SWITCH_CPU_WIDE => {
455 from(CtxSwitch::from_ptr(ptr, true, misc, sample_id_all))
456 }
457 #[cfg(feature = "linux-4.12")]
458 b::PERF_RECORD_NAMESPACES => from(Namespaces::from_ptr(ptr, sample_id_all)),
459 #[cfg(feature = "linux-4.1")]
460 b::PERF_RECORD_ITRACE_START => from(ItraceStart::from_ptr(ptr, sample_id_all)),
461 #[cfg(feature = "linux-4.1")]
462 b::PERF_RECORD_AUX => from(Aux::from_ptr(ptr, sample_id_all)),
463 #[cfg(feature = "linux-5.16")]
464 b::PERF_RECORD_AUX_OUTPUT_HW_ID => from(AuxOutputHwId::from_ptr(ptr, sample_id_all)),
465 b::PERF_RECORD_COMM => from(Comm::from_ptr(ptr, misc, sample_id_all)),
466 b::PERF_RECORD_EXIT => from(Exit::from_ptr(ptr, sample_id_all)),
467 b::PERF_RECORD_FORK => from(Fork::from_ptr(ptr, sample_id_all)),
468 b::PERF_RECORD_THROTTLE => from(Throttle::from_ptr(ptr, sample_id_all)),
469 b::PERF_RECORD_UNTHROTTLE => from(Unthrottle::from_ptr(ptr, sample_id_all)),
470 b::PERF_RECORD_LOST => from(LostRecords::from_ptr(ptr, sample_id_all)),
471 #[cfg(feature = "linux-4.2")]
472 b::PERF_RECORD_LOST_SAMPLES => from(LostSamples::from_ptr(ptr, sample_id_all)),
473 _ => Record::Unknown(bytes.to_vec()), };
475
476 (record_priv, record)
477 }
478}
479
480#[derive(Debug)]
487pub struct Parser(pub(in crate::sample) UnsafeParser);
488
489impl Parser {
490 pub fn parse(&self, chunk: CowChunk<'_>) -> (Priv, Record) {
492 let bytes = chunk.as_bytes();
493 unsafe { self.0.parse(bytes) }
494 }
495
496 pub fn as_unsafe(&self) -> &UnsafeParser {
498 &self.0
499 }
500}