perf_event/events/
breakpoint.rs

1use bitflags::bitflags;
2use perf_event_open_sys::bindings;
3
4use crate::events::Event;
5
6bitflags! {
7    /// Memory access mask for a hardware data breakpoint.
8    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
9    pub struct BreakpointAccess : u32 {
10        /// Count when we read the memory location.
11        const READ = bindings::HW_BREAKPOINT_R;
12
13        /// Count when we write the memory location.
14        const WRITE = bindings::HW_BREAKPOINT_W;
15
16        /// Count when we read or write the memory location.
17        const READ_WRITE = Self::READ.union(Self::WRITE).bits();
18    }
19}
20
21/// A hardware breakpoint.
22///
23/// A hardware breakpoint watches a region of memory for accesses. It has three
24/// parameters:
25/// - the address that is being watched (`addr`)
26/// - the number of bytes that breakpoint covers (`len`)
27/// - which type of memory accesses we care about (`ty`)
28///
29/// Note that both number of bytes that can be watched as well as the number of
30/// breakpoints that is allowed to be active at any given time is limited.
31///
32/// # Execute Breakpoint
33/// We can use a breakpoint to count the number of times that a function gets
34/// called, as long as the compiler does not optimize the function away.
35///
36/// ```
37/// # use perf_event::Builder;
38/// # use perf_event::events::Breakpoint;
39/// #[inline(never)]
40/// fn do_some_things() {
41///     // ...
42///     # println!("test println so the function doesn't get removed")
43/// }
44///
45/// let fnptr = do_some_things as fn() as usize;
46/// let mut counter = Builder::new(Breakpoint::execute(fnptr as u64)).build()?;
47/// counter.enable()?;
48///
49/// for _ in 0..500 {
50///     do_some_things();
51/// }
52///
53/// counter.disable()?;
54/// assert_eq!(counter.read()?, 500);
55/// # std::io::Result::Ok(())
56/// ```
57///
58/// # Data Breakpoint
59/// We can also use a breakpoint to count the number of times that a memory
60/// location is accessed.
61/// ```
62/// # use perf_event::Builder;
63/// # use perf_event::events::Breakpoint;
64/// #
65/// let mut data: Vec<u64> = (0..1024).rev().collect();
66///
67/// let breakpoint = Breakpoint::read_write(&data[20] as *const _ as usize as u64, 8);
68/// let mut counter = Builder::new(breakpoint).build()?;
69/// counter.enable()?;
70/// data.sort();
71/// counter.disable()?;
72///
73/// println!("Position 20 accessed {} times", counter.read()?);
74/// # std::io::Result::Ok(())
75/// ```
76///
77/// # Usage Notes
78/// - Some systems do not support creating read-only or write-only breakpoints.
79///   If you are getting `EINVAL` errors while trying to build such a counter
80///   using a read-write breakpoint might work instead.
81///
82/// - The valid values of len are quite limited. The [`perf_event_open`][man]
83///   manpage indicates that the only valid values for `bp_len` are 1, 2, 4, and
84///   8.
85///
86/// [man]: https://www.mankier.com/2/perf_event_open
87#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
88pub enum Breakpoint {
89    /// Data breakpoint. Triggers when code reads or writes to the memory area
90    /// as configured by the parameters below.
91    Data {
92        /// Bitfield containing the types of accesses we want the breakpoint to
93        /// trigger on.
94        access: BreakpointAccess,
95
96        /// The address of the memory location on which the breakpoint should
97        /// trigger.
98        addr: u64,
99
100        /// The length of the breakpoint being measured.
101        ///
102        /// There are a limited number of valid values for this field.
103        /// Basically, the options are 1, 2, 4, and 8. Setting this
104        /// field to anything else will cause counter creation to fail
105        /// with an error.
106        len: u64,
107    },
108
109    /// Code breakpoint. Triggers when the code at the address is executed.
110    Code {
111        /// The address that the breakpoint is monitoring.
112        addr: u64,
113    },
114}
115
116impl Breakpoint {
117    /// Create a code execution breakpoint, that counts the number of
118    /// times the instruction at the provided address was executed.
119    pub const fn execute(addr: u64) -> Self {
120        Self::Code { addr }
121    }
122
123    /// Create a memory read breakpoint, that counts the number of
124    /// times we read from the provided memory location.
125    ///
126    /// See the struct field docs for valid values of `len`.
127    pub const fn read(addr: u64, len: u64) -> Self {
128        Self::Data {
129            access: BreakpointAccess::READ,
130            addr,
131            len,
132        }
133    }
134
135    /// Create a memory write breakpoint, that counts the number of
136    /// times we write to the provided memory location.
137    ///
138    /// See the struct field docs for valid values of `len`.
139    pub const fn write(addr: u64, len: u64) -> Self {
140        Self::Data {
141            access: BreakpointAccess::WRITE,
142            addr,
143            len,
144        }
145    }
146
147    /// Create a memory access breakpoint, that counts the number of
148    /// times we either read from or write to the provided memory
149    /// location.
150    ///
151    /// See the struct field docs for valid values of `len`.
152    pub const fn read_write(addr: u64, len: u64) -> Self {
153        Self::Data {
154            access: BreakpointAccess::READ_WRITE,
155            addr,
156            len,
157        }
158    }
159}
160
161impl Event for Breakpoint {
162    fn update_attrs(self, attr: &mut bindings::perf_event_attr) {
163        attr.type_ = bindings::PERF_TYPE_BREAKPOINT;
164        attr.config = 0;
165
166        match self {
167            Self::Data { access, addr, len } => {
168                attr.bp_type = access.bits();
169                attr.bp_addr = addr;
170                attr.bp_len = len;
171            }
172            Self::Code { addr } => {
173                attr.bp_type = bindings::HW_BREAKPOINT_X;
174                attr.bp_addr = addr;
175                // According to the perf_event_open man page, execute breakpoints
176                // should set len to sizeof(long).
177                attr.bp_len = std::mem::size_of::<libc::c_long>() as _;
178            }
179        }
180    }
181}