perf_event_open/sample/record/ctx.rs
1use super::{RecordId, Task};
2
3/// Process context switched.
4///
5/// # Examples
6///
7/// ```rust
8/// # #[cfg(not(feature = "linux-4.3"))]
9/// # return;
10/// #
11/// use std::thread;
12/// use std::time::Duration;
13///
14/// use perf_event_open::config::{Cpu, Opts, Proc};
15/// use perf_event_open::count::Counter;
16/// use perf_event_open::event::sw::Software;
17/// # use perf_event_open::sample::record::ctx::Switch;
18/// # use perf_event_open::sample::record::Record;
19///
20/// let event = Software::Dummy;
21/// let target = (Proc::CURRENT, Cpu::ALL);
22///
23/// let mut opts = Opts::default();
24/// opts.extra_record.ctx_switch = true;
25///
26/// let counter = Counter::new(event, target, opts).unwrap();
27/// let sampler = counter.sampler(5).unwrap();
28///
29/// counter.enable().unwrap();
30///
31/// // Triggers a context switch.
32/// thread::sleep(Duration::from_millis(1));
33///
34/// # let mut switch_out = false;
35/// # let mut switch_in = false;
36/// for it in sampler.iter() {
37/// println!("{:-?}", it);
38/// # if let Record::CtxSwitch(it) = it.1 {
39/// # match it.info {
40/// # Switch::OutTo { .. } => switch_out = true,
41/// # Switch::InFrom(_) => switch_in = true,
42/// # }
43/// # }
44/// }
45/// # assert!(switch_out);
46/// # assert!(switch_in);
47/// ```
48///
49/// Since `linux-4.3`: <https://github.com/torvalds/linux/commit/45ac1403f564f411c6a383a2448688ba8dd705a4>
50#[derive(Clone)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct CtxSwitch {
53 /// Record IDs.
54 pub record_id: Option<RecordId>,
55
56 /// Context switch info.
57 pub info: Switch,
58}
59
60impl CtxSwitch {
61 #[cfg(feature = "linux-4.3")]
62 pub(crate) unsafe fn from_ptr(
63 mut ptr: *const u8,
64 cpu_wide: bool,
65 misc: u16,
66 sample_id_all: Option<super::SampleType>,
67 ) -> Self {
68 use super::SampleType;
69 use crate::ffi::{bindings as b, deref_offset};
70
71 // PERF_RECORD_SWITCH
72 // https://github.com/torvalds/linux/blob/v6.13/include/uapi/linux/perf_event.h#L1119
73 // struct {
74 // struct perf_event_header header;
75 // struct sample_id sample_id;
76 // };
77 //
78 // PERF_RECORD_SWITCH_CPU_WIDE
79 // https://github.com/torvalds/linux/blob/v6.13/include/uapi/linux/perf_event.h#L1131
80 // struct {
81 // struct perf_event_header header;
82 // u32 next_prev_pid;
83 // u32 next_prev_tid;
84 // struct sample_id sample_id;
85 // };
86
87 let task = cpu_wide.then(|| Task {
88 pid: deref_offset(&mut ptr),
89 tid: deref_offset(&mut ptr),
90 });
91 let info = if misc as u32 & b::PERF_RECORD_MISC_SWITCH_OUT > 0 {
92 #[cfg(feature = "linux-4.17")]
93 let preempt = misc as u32 & b::PERF_RECORD_MISC_SWITCH_OUT_PREEMPT > 0;
94 #[cfg(not(feature = "linux-4.17"))]
95 let preempt = false;
96 Switch::OutTo { task, preempt }
97 } else {
98 Switch::InFrom(task)
99 };
100 let record_id = sample_id_all.map(|SampleType(ty)| RecordId::from_ptr(ptr, ty));
101
102 Self { record_id, info }
103 }
104}
105
106super::from!(CtxSwitch);
107
108super::debug!(CtxSwitch {
109 {record_id?},
110 {info},
111});
112
113// Some(task) if PERF_RECORD_SWITCH_CPU_WIDE
114/// Context switch info.
115#[derive(Clone, Debug)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub enum Switch {
118 // PERF_RECORD_MISC_SWITCH_OUT
119 /// Switched out.
120 OutTo {
121 /// Info of the task being switched to.
122 task: Option<Task>,
123 // PERF_RECORD_MISC_SWITCH_OUT_PREEMPT
124 // https://github.com/torvalds/linux/blob/v6.13/kernel/events/core.c#L9298
125 // https://github.com/torvalds/linux/blob/v6.13/tools/perf/util/scripting-engines/trace-event-python.c#L1571
126 /// Indicates whether the context switch was triggered by preemption.
127 ///
128 /// Since `linux-4.17`: <https://github.com/torvalds/linux/commit/101592b4904ecf6b8ed2a4784d41d180319d95a1>
129 preempt: bool,
130 },
131 // !PERF_RECORD_MISC_SWITCH_OUT
132 /// Switched in.
133 ///
134 /// Contains info of the task being switched from if possible.
135 InFrom(Option<Task>),
136}