Add @perf_event program type with full attach/detach/count support#18
Add @perf_event program type with full attach/detach/count support#18SiyuanSun0736 wants to merge 2 commits intomultikernel:mainfrom
@perf_event program type with full attach/detach/count support#18Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds first-class @perf_event support to KernelScript, enabling eBPF programs to attach to hardware/software performance counters via a perf_event_attr literal and a dedicated attach(handle, attr) workflow with compiler-generated userspace lifecycle helpers.
Changes:
- Introduces
PerfEventprogram type end-to-end (AST/type-checking/IR/codegen) withSEC("perf_event")and*bpf_perf_event_datacontext. - Extends
attach()to support a 2-arg perf-event form, generating userspaceperf_event_open+ attach/enable/disable + read/print helpers. - Adds tests, example, and documentation for perf event programs, counters, and lifecycle semantics.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_program_ref.ml | Updates stdlib integration expectations for attach() custom validation and error assertion. |
| tests/test_perf_event_attach.ml | Adds unit tests covering perf attach/detach sequencing, pid/cpu validation, defaults, and helper generation. |
| tests/test_ir.ml | Extends IR program type pretty-printing for PerfEvent. |
| tests/dune | Registers new perf-event attach test executable and runtest rule. |
| src/userspace_codegen.ml | Adds perf-event attach detection, conditional headers/types, perf fd lifecycle helpers, and perf attach/detach support. |
| src/type_checker.ml | Adds @perf_event program type recognition and partial signature validation. |
| src/stdlib.ml | Adds perf_counter / perf_event_attr builtins and custom attach() validation for 2-arg/3-arg forms. |
| src/multi_program_analyzer.ml | Adds execution context and discovery logic for PerfEvent programs. |
| src/main.ml | Adds perf_event to init program-type whitelist and project description text. |
| src/ir_generator.ml | Adjusts lowering for non-void function calls used as statements (emit IRCall with discarded return). |
| src/ir_function_system.ml | Adds perf-event entrypoint signature validation at IR validation stage. |
| src/ebpf_c_codegen.ml | Registers perf-event context codegen and emits SEC("perf_event") / perf-event context detection. |
| src/context/perf_event_codegen.ml | New perf-event context codegen module (includes/field access/section name). |
| src/context/dune | Adds perf_event_codegen module to the context library build. |
| src/codegen_common.ml | Maps perf_event_attr IR struct to ks_perf_event_attr in generated C. |
| src/btf_parser.ml | Adds perf-event program template generation and BTF type list updates. |
| src/ast.ml | Adds PerfEvent to program_type and string conversion. |
| examples/perf_branch_miss.ks | New example demonstrating @perf_event attach/detach with perf_event_attr. |
| SPEC.md | Documents perf-event syntax, pid/cpu rules, counters, helper generation, and attach/detach sequences. |
| README.md | Adds perf-event overview, lifecycle example, and counter table. |
| BUILTINS.md | Documents attach(handle, attr) overload and usage example. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
## Language changes
- New `@perf_event` program attribute; context type `*bpf_perf_event_data`
- New `perf_event_attr` struct literal with counter, pid, cpu, period, wakeup,
inherit, exclude_kernel, exclude_user fields
- New `perf_counter` enum: cpu_cycles, instructions, cache_references,
cache_misses, branch_instructions, branch_misses, page_faults,
context_switches, cpu_migrations
- `attach(prog, attr)` two-argument form for perf_event programs
- `detach(prog)` cleans up BPF link, disables and closes perf fd
## Compiler / codegen
- AST: PerfEvent program type, perf_event_attr struct, perf_counter enum
- Type checker: validates @perf_event function signatures and attr fields
- IR generator: recognises PerfEvent program type
- ebpf_c_codegen: emits SEC("perf_event") and bpf_perf_event_data context
- userspace_codegen:
- ks_open_perf_event(): maps perf_counter enum to PERF_TYPE/PERF_COUNT
constants, validates pid/cpu rules, calls perf_event_open(2)
- attach sequence: disabled=1 → IOC_RESET → attach_perf_event → IOC_ENABLE
- detach sequence: IOC_DISABLE → bpf_link__destroy → close(perf_fd)
- ks_read_perf_count(): reads raw 64-bit counter via read()
- ks_print_perf_count(): prints "[perf] <name>: <count>" with PRId64
## Tests
- tests/test_perf_event_attach.ml (6 test cases):
- pid/cpu validation rules enforced
- counting startup ordering (RESET before ENABLE, attach before ENABLE)
- period/wakeup default values when 0
- custom period/wakeup runtime expressions
- ks_read/print_perf_count helpers generated with correct logic
- standard attach branches use libbpf_get_error
## Example
- examples/perf_branch_miss.ks: minimal @perf_event example (branch misses)
- examples/perf_branch_miss/: pre-built reference C output
## Docs
- README.md: @perf_event in program types overview; perf_counter table;
Hardware Performance Counter Programs section with full lifecycle example
- SPEC.md: section 3.1.3 Perf Event Programs — syntax, pid/cpu rules,
perf_counter enum, generated C helpers, attach/detach sequence steps
|
Reviewed the implementation; the codegen and tests are solid. Wanted to raise a higher-level design concern about the user-facing API before this lands, since changing it after merge would be a breaking change. Concerns with the current API
Proposed designCPU-pinned + period override: Four moves
Semantic story
What this preserves
PrerequisitePartial struct literals (omitted fields use declared defaults). This is a generally useful language feature, not perf-specific — anything with options bags benefits. Happy to discuss tradeoffs. The codegen and tests in this PR are sound; this is purely a user-facing surface concern. |
Summary
This PR adds first-class support for
@perf_eventeBPF programs in KernelScript. Users can now attach eBPF logic to hardware and software performance counters (branch misses, CPU cycles, cache misses, etc.) with a clean, safe, single-file workflow — no manualperf_event_open(2)boilerplate required.New language features
@perf_eventprogram attributeperf_event_attrstruct literal — passed directly toattach():perf_counterenum — 9 hardware/software counters:cpu_cycles,instructions,cache_references,cache_misses,branch_instructions,branch_misses,page_faults,context_switches,cpu_migrationsCompiler & codegen changes
ast.mlPerfEventprogram type,perf_event_attrstruct,perf_counterenumtype_checker.ml@perf_eventsignature andperf_event_attrfield typesir_generator.mlPerfEventprogram typeebpf_c_codegen.mlSEC("perf_event")andbpf_perf_event_datacontextcontext/perf_event_codegen.mlstdlib.mlattach/detachbuiltin resolution for perf_event formbtf_parser.mlperf_event_attrBTF type extractionGenerated userspace C functions (emitted when
attach(prog, attr)is used):ks_open_perf_event(ks_perf_event_attr)— mapsperf_counterenum →PERF_TYPE_*/PERF_COUNT_*, validatespid/cpurules, callssyscall(SYS_perf_event_open, ...)ks_read_perf_count(int perf_fd)→int64_t— reads raw counter value viaread()ks_print_perf_count(int perf_fd, const char *name)— prints[perf] <name>: <count>withPRId64Attach sequence (compiler-generated, in order):
attr.disabled = 1— open without startingperf_event_open(2)→perf_fdIOC_RESET— zero the counter before BPF attachmentbpf_program__attach_perf_event(prog, perf_fd)— link BPF programIOC_ENABLE— start countingDetach sequence:
IOC_DISABLE→bpf_link__destroy→close(perf_fd)pid/cpuvalidation (enforced at runtime):pidcpuTests
New file test_perf_event_attach.ml — 6 test cases:
perf_event_codegen_enforces_pid_cpu_rulesIOC_ENABLE/DISABLE, success messagesperf_event_counting_starts_correctlydisabled=1→IOC_RESET→attach_perf_event→IOC_ENABLEorderingperf_event_period_and_wakeup_defaults1000000/1) substituted whenperiod/wakeup= 0perf_event_period_and_wakeup_customperf_read_count_function_generatedks_read_perf_countandks_print_perf_countgenerated correctlystandard_attach_uses_libbpf_error_checkslibbpf_get_errorAll 6 tests pass. perf_branch_miss.ks compiles successfully end-to-end.
Documentation
@perf_eventadded to program types overview; new Hardware Performance Counter Programs section with full lifecycle example andperf_countertablepid/cpurules,perf_counterenum, generated C helpers table, attach/detach sequences, compiler implementation notesTesting checklist
dune build— passesdune build @tests— all OCaml unit tests passtest_all_examples.sh— perf_branch_miss.ks compiles KS→C→binary successfully (remaining failures are pre-existing and unrelated to this PR)