1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! Per-task hardware-PMU `perf` counting (`perf stat -- cmd`).
//!
//! Where [`super::hw`] can bind a system-wide event to an explicit CPU context,
//! this module counts a *specific task*: the counter is
//! programmed onto hardware only while the target task is the running task, and
//! its per-slice deltas are accumulated across context switches. That is what
//! makes `perf stat -- /bin/true` attribute events to the workload rather than
//! to whatever happened to run on the CPU.
//!
//! ## Ownership and lifetime
//!
//! A [`PerTaskCounter`] is shared (`Arc`) between two places:
//!
//! * the target [`Thread`]'s perf context, walked by the scheduler
//! hooks ([`perf_sched_in`] / [`perf_sched_out`]) and the exec/exit hooks, and
//! * the [`super::hw::HwPerfEvent`] behind the perf fd, which serves
//! `read(perf_fd)` / `ioctl(ENABLE/DISABLE/RESET)` and frees the HW counter on
//! `Drop`.
//!
//! Both can outlive the other (the fd can be `close`d while the task runs, or
//! the task can exit while the fd is still open), so the HW counter is freed via
//! the idempotent [`free_hw`] from whichever side reaches end-of-life first
//! ([`HwPerfEvent::drop`] or [`on_task_exit`]).
//!
//! ## Hot-path cost
//!
//! The scheduler hooks run inside `switch_to` with IRQs disabled and preemption
//! off: no allocation, no sleeping locks. They early-return on a single relaxed
//! load of [`PERF_TASK_ACTIVE`] when no per-task counter exists anywhere, so the
//! common (perf-unused) case is one atomic load per switch.
//!
//! ## Per-task sampling (`perf record -- cmd`, M3-pt-rec)
//!
//! A task-bound event opened with a nonzero sampling period and a supported
//! scalar `sample_type` behaves like an [M2 sampling
//! event](super::sampling) *while the attached task is running*, and fires no
//! samples while it is not — so the samples are attributed to the task.
//!
//! This reuses the M2 IRQ backend wholesale. The mechanism is:
//!
//! * `mmap(perf_fd)` allocates the ring (in [`super::hw::HwPerfEvent::device_mmap`])
//! and publishes the ring plus page/notify anchors through the fd-owned
//! [`PerfInheritanceFamily`]. Existing and future descendants receive the same
//! output.
//! * [`perf_sched_in`] arms the slice: `preload` the counter to overflow after
//! `sample_period` events, `register` a [`SampleSlot`](super::sampling::SampleSlot)
//! pointing at the ptc's ring + notify, and `enable_irq` the overflow line.
//! * [`perf_sched_out`] disarms the slice: stop the counter, `disable_irq`, and
//! `unregister` the slot — so the next time some *other* task runs, an overflow
//! on this counter cannot fire a sample into our ring.
//!
//! The IRQ-half (the overflow handler writing `PERF_RECORD_SAMPLE` and re-arming)
//! is exactly the M2 [`super::sampling::pmu_overflow_handler`] — nothing here
//! runs in IRQ context except via the registered slot.
//!
//! ## Scope / deferrals
//!
//! There is no counter multiplexing (so `time_running == time_enabled`).
//! Generation-bearing owner leases follow task migration across CPUs, and an
//! optional CPU filter limits eligibility. Sampling supports fixed-period
//! (`-c <period>`) and frequency mode (`-F`, `sample_freq`); inherited child
//! events share the root output through the same owned redirect boundary.
use Arc;
use ;
use PhysAddr;
pub use on_clone_inherit;
pub use PERF_TASK_ACTIVE;
pub use ;
use ;
use crate::;
pub use attach;
pub use detach_unpublished;
use now_ns;
pub use ;
pub use ;
pub use ;
pub use ;
pub use PerTaskConfig;
pub use PerTaskCounter;
pub use SamplingAnchors;
pub use ;
pub use stop_requested_on_owner;
pub use ;