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}