Enables counting for the configured events. If counting is already enabled, this is a no-op. # Errors Returns [`SamplerError`] if the kernel rejects the counting request.
(&mut self)
| 62 | /// |
| 63 | /// Returns [`SamplerError`] if the kernel rejects the counting request. |
| 64 | pub fn start(&mut self) -> Result<(), SamplerError> { |
| 65 | if self.running { |
| 66 | return Ok(()); |
| 67 | } |
| 68 | |
| 69 | let kpc_vt = self.sampler.kperf.vtable(); |
| 70 | |
| 71 | try_kpc( |
| 72 | // SAFETY: kpc_set_counting is a sysctl write; classes was obtained from |
| 73 | // a valid kpep_config. Passing 0 on failure is always safe. |
| 74 | unsafe { (kpc_vt.kpc_set_counting)(self.classes) }, |
| 75 | SamplerError::UnableToStartCounting, |
| 76 | )?; |
| 77 | |
| 78 | let counting_guard = DropGuard::new((), |()| { |
| 79 | // SAFETY: Disable counting by writing 0 to the sysctl. The function |
| 80 | // pointer is valid, and 0 is a valid argument. |
| 81 | let _res = unsafe { (kpc_vt.kpc_set_counting)(0) }; |
| 82 | }); |
| 83 | |
| 84 | try_kpc( |
| 85 | // SAFETY: same as kpc_set_counting, sysctl write with valid classes. |
| 86 | unsafe { (kpc_vt.kpc_set_thread_counting)(self.classes) }, |
| 87 | SamplerError::UnableToStartThreadCounting, |
| 88 | )?; |
| 89 | |
| 90 | self.running = true; |
| 91 | |
| 92 | // NOTE: On some macOS versions, configurable counters can return stale |
| 93 | // thread samples immediately after start when reconfiguring rapidly. |
| 94 | // Callers can force an all-CPU read via kpc_get_cpu_counters(true, ...) |
| 95 | // between start and the first sample to flush counters. |
| 96 | // TODO: consider providing a built-in flush or stabilized sample API. |
| 97 | |
| 98 | DropGuard::dismiss(counting_guard); |
| 99 | |
| 100 | Ok(()) |
| 101 | } |
| 102 | |
| 103 | /// Reads the current raw counter values for the configured events. |
| 104 | /// |