Skip to content

Commit 68bbe2c

Browse files
authored
Merge pull request #23 from multikernel/perf-event-fold-type
Simplify and tighten the @perf_event API
2 parents 6f9e6e8 + 1011bd6 commit 68bbe2c

11 files changed

Lines changed: 180 additions & 633 deletions

BUILTINS.md

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ fn main() -> i32 {
8585

8686
#### `attach(handle, target, flags)` / `attach(handle, opts, flags)`
8787
**Signature:** `attach(handle: ProgramHandle, target: str(128), flags: u32) -> u32`
88-
**Signature:** `attach(handle: ProgramHandle, opts: perf_options, flags: u32) -> PerfAttachment`
88+
**Signature:** `attach(handle: ProgramHandle, opts: perf_options, flags: u32) -> perf_attachment`
8989
**Variadic:** No
9090
**Context:** Userspace only
9191

@@ -98,12 +98,12 @@ fn main() -> i32 {
9898
- `flags`: Attachment flags (context-dependent)
9999
- Perf event form:
100100
- `handle`: Program handle returned from `load()`
101-
- `opts`: `perf_options` value — only `perf_type` and `perf_config` are required; all other fields have defaults, including no group (`group` invalid and `group_fd=-1`)
101+
- `opts`: `perf_options` value — only `event` is required; all other fields have defaults, including no group (`group` defaults to an invalid attachment)
102102
- `flags`: Must be `0` for perf attaches; nonzero values are rejected
103103

104104
**Return Value:**
105105
- Standard form returns `0` on success and an error code on failure
106-
- Perf event form returns a `PerfAttachment` value with the open counter/link identity and an internal stale-handle token
106+
- Perf event form returns a `perf_attachment` value with the open counter/link identity and an internal stale-handle token
107107

108108
**Examples:**
109109
```kernelscript
@@ -113,26 +113,25 @@ if (result != 0) {
113113
print("Failed to attach program")
114114
}
115115
116-
// Minimal perf attach — all non-perf_type/perf_config fields use defaults:
116+
// Minimal perf attach — all non-event fields use defaults:
117117
// pid=-1 (all procs), cpu=0, period=1_000_000, wakeup=1; perf attach flags must be 0
118118
var perf_prog = load(on_branch_miss)
119-
var perf_att = attach(perf_prog, perf_options { perf_type: perf_type_hardware, perf_config: branch_misses }, 0)
119+
var perf_att = attach(perf_prog, perf_options { event: branch_misses }, 0)
120120
var count = read(perf_att).scaled
121121
detach(perf_att)
122122
detach(perf_prog)
123123
124124
// Grouped perf events: branch joins cache's leader group. Adding a member restarts the group.
125-
var cache = attach(perf_prog, perf_options { perf_type: perf_type_hardware, perf_config: cache_misses }, 0)
125+
var cache = attach(perf_prog, perf_options { event: cache_misses }, 0)
126126
var branch = attach(perf_prog, perf_options {
127-
perf_type: perf_type_hardware,
128-
perf_config: branch_misses,
127+
event: branch_misses,
129128
group: cache,
130129
}, 0)
131130
detach(branch)
132131
detach(cache)
133132
```
134133

135-
Grouped events are scheduled as one atomic PMU unit. Separate events and separate groups may be multiplexed, but members inside one group cannot be independently multiplexed. Static groups that exceed the target PMU counter limit are rejected at compile time; override the detected/default limit with `KERNELSCRIPT_PERF_GROUP_MAX_EVENTS` when compiling for a different target. The effective limit is capped at 16 to match `PerfRead`.
134+
Grouped events are scheduled as one atomic PMU unit. Separate events and separate groups may be multiplexed, but members inside one group cannot be independently multiplexed. A group that needs more hardware PMU counters than the running host provides is rejected by the kernel at `perf_event_open(2)`, and `attach()` reports the error at runtime. `read(att)` exposes up to 16 group entries (`perf_read`).
136135

137136
**Context-specific implementations:**
138137
- **eBPF:** Not available
@@ -143,14 +142,14 @@ Grouped events are scheduled as one atomic PMU unit. Separate events and separat
143142

144143
#### `detach(handle)`
145144
**Signature:** `detach(handle: ProgramHandle) -> void`
146-
**Signature:** `detach(handle: PerfAttachment) -> void`
145+
**Signature:** `detach(handle: perf_attachment) -> void`
147146
**Variadic:** No
148147
**Context:** Userspace only
149148

150149
**Description:** Detach a loaded eBPF program from its current attachment point, or tear down one perf attachment.
151150

152151
**Parameters:**
153-
- `handle`: Program handle returned from `load()`, or a `PerfAttachment` returned from perf `attach()`
152+
- `handle`: Program handle returned from `load()`, or a `perf_attachment` returned from perf `attach()`
154153

155154
**Return Value:**
156155
- No return value (void)
@@ -171,7 +170,7 @@ detach(prog) // Clean up
171170
---
172171

173172
#### `read(handle)`
174-
**Signature:** `read(handle: PerfAttachment) -> PerfRead`
173+
**Signature:** `read(handle: perf_attachment) -> perf_read`
175174
**Variadic:** No
176175
**Context:** Userspace only
177176

README.md

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ fn main() -> i32 {
294294

295295
### Hardware Performance Counter Programs
296296

297-
Use `@perf_event` to attach eBPF programs to hardware or software performance counters. `perf_options` keeps the kernel's tagged `perf_type + perf_config` model, so adding new perf event families does not require flattening everything into one enum. Only `perf_type` and `perf_config` are required; all other fields have sensible defaults. Perf attaches return a first-class attachment value, so if you need the current count in userspace, call `read(att).scaled`:
297+
Use `@perf_event` to attach eBPF programs to hardware or software performance counters. Each event is named by a single `event` value that carries both the kernel `perf_event_attr.type` tag and its config, so the type can never be mismatched with the config. Only `event` is required; all other fields have sensible defaults. Perf attaches return a first-class attachment value, so if you need the current count in userspace, call `read(att).scaled`:
298298

299299
```kernelscript
300300
// eBPF program fires on every hardware branch-miss sample
@@ -308,7 +308,7 @@ fn main() -> i32 {
308308
309309
// Minimal form — defaults: pid=-1 (all procs), cpu=0, no group,
310310
// period=1_000_000, wakeup=1; perf attach flags must be 0
311-
var att = attach(prog, perf_options { perf_type: perf_type_hardware, perf_config: branch_misses }, 0)
311+
var att = attach(prog, perf_options { event: branch_misses }, 0)
312312
var count = read(att).scaled
313313
print("branch misses: %lld", count)
314314
@@ -318,48 +318,35 @@ fn main() -> i32 {
318318
}
319319
```
320320

321-
Perf events can share a kernel scheduling group by passing the leader attachment directly with `group`.
322-
The lower-level `group_fd: cache.perf_fd` form is still supported for compatibility:
321+
Perf events can share a kernel scheduling group by passing the leader attachment with `group`:
323322

324323
```kernelscript
325-
var cache = attach(prog, perf_options { perf_type: perf_type_hardware, perf_config: cache_misses }, 0)
324+
var cache = attach(prog, perf_options { event: cache_misses }, 0)
326325
var branch = attach(prog, perf_options {
327-
perf_type: perf_type_hardware,
328-
perf_config: branch_misses,
326+
event: branch_misses,
329327
group: cache,
330328
}, 0)
331329
```
332330

333-
Adding a member restarts the whole group from zero. Detaching a leader cascades to any live members. A group competes for PMU counters as one atomic unit: different groups can be multiplexed over time, but members inside one group are not independently multiplexed. For statically visible groups, the compiler rejects groups that need more PMU counter slots than the target limit. The limit is read from known sysfs PMU caps when available, defaults to 4, can be overridden with `KERNELSCRIPT_PERF_GROUP_MAX_EVENTS`, and is capped at 16 to match `PerfRead`.
331+
Adding a member restarts the whole group from zero. Detaching a leader cascades to any live members. A group competes for PMU counters as one atomic unit: different groups can be multiplexed over time, but members inside one group are not independently multiplexed. A group that needs more hardware PMU counters than the running host provides is rejected by the kernel at `perf_event_open(2)`, and `attach()` surfaces the error at runtime — on the actual deployment host, where the real counter count is known. `read(att)` exposes up to 16 group entries (`perf_read`).
334332

335-
`read(att)` returns a `PerfRead` snapshot with raw, multiplex-scaled, timing, and group fields. Use `read(att).scaled` for that attachment's counter value, `read(att).raw` for its unscaled value, and `read(att).values` / `read(att).ids` for a same-time group snapshot.
333+
`read(att)` returns a `perf_read` snapshot with raw, multiplex-scaled, timing, and group fields. Use `read(att).scaled` for that attachment's counter value, `read(att).raw` for its unscaled value, and `read(att).values` / `read(att).ids` for a same-time group snapshot.
336334

337-
**Available `perf_type` values:**
335+
**Available `event` constants:**
338336

339-
| Enum value | Hardware/software event |
340-
|---|---|
341-
| `perf_type_hardware` | `PERF_TYPE_HARDWARE` |
342-
| `perf_type_software` | `PERF_TYPE_SOFTWARE` |
343-
| `perf_type_tracepoint` | `PERF_TYPE_TRACEPOINT` |
344-
| `perf_type_hw_cache` | `PERF_TYPE_HW_CACHE` |
345-
| `perf_type_raw` | `PERF_TYPE_RAW` |
346-
| `perf_type_breakpoint` | `PERF_TYPE_BREAKPOINT` |
337+
Each constant packs its `perf_event_attr.type` tag in the high 32 bits and its config in the low 32 bits, so naming the event fixes both.
347338

348-
**Common `perf_config` constants:**
349-
350-
| Constant | Intended `perf_type` | Linux config |
339+
| Constant | Kernel type | Linux config |
351340
|---|---|---|
352-
| `cpu_cycles` | `perf_type_hardware` | `PERF_COUNT_HW_CPU_CYCLES` |
353-
| `instructions` | `perf_type_hardware` | `PERF_COUNT_HW_INSTRUCTIONS` |
354-
| `cache_references` | `perf_type_hardware` | `PERF_COUNT_HW_CACHE_REFERENCES` |
355-
| `cache_misses` | `perf_type_hardware` | `PERF_COUNT_HW_CACHE_MISSES` |
356-
| `branch_instructions` | `perf_type_hardware` | `PERF_COUNT_HW_BRANCH_INSTRUCTIONS` |
357-
| `branch_misses` | `perf_type_hardware` | `PERF_COUNT_HW_BRANCH_MISSES` |
358-
| `page_faults` | `perf_type_software` | `PERF_COUNT_SW_PAGE_FAULTS` |
359-
| `context_switches` | `perf_type_software` | `PERF_COUNT_SW_CONTEXT_SWITCHES` |
360-
| `cpu_migrations` | `perf_type_software` | `PERF_COUNT_SW_CPU_MIGRATIONS` |
361-
362-
For newer families such as `perf_type_hw_cache`, pass the kernel-compatible encoded `perf_config` value directly.
341+
| `cpu_cycles` | `PERF_TYPE_HARDWARE` | `PERF_COUNT_HW_CPU_CYCLES` |
342+
| `instructions` | `PERF_TYPE_HARDWARE` | `PERF_COUNT_HW_INSTRUCTIONS` |
343+
| `cache_references` | `PERF_TYPE_HARDWARE` | `PERF_COUNT_HW_CACHE_REFERENCES` |
344+
| `cache_misses` | `PERF_TYPE_HARDWARE` | `PERF_COUNT_HW_CACHE_MISSES` |
345+
| `branch_instructions` | `PERF_TYPE_HARDWARE` | `PERF_COUNT_HW_BRANCH_INSTRUCTIONS` |
346+
| `branch_misses` | `PERF_TYPE_HARDWARE` | `PERF_COUNT_HW_BRANCH_MISSES` |
347+
| `page_faults` | `PERF_TYPE_SOFTWARE` | `PERF_COUNT_SW_PAGE_FAULTS` |
348+
| `context_switches` | `PERF_TYPE_SOFTWARE` | `PERF_COUNT_SW_CONTEXT_SWITCHES` |
349+
| `cpu_migrations` | `PERF_TYPE_SOFTWARE` | `PERF_COUNT_SW_CPU_MIGRATIONS` |
363350

364351
📖 **For detailed language specification, syntax reference, and advanced features, please read [`SPEC.md`](SPEC.md).**
365352

0 commit comments

Comments
 (0)