Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions BUILTINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,22 @@ fn main() -> i32 {

---

#### `attach(handle, target, flags)`
#### `attach(handle, target, flags)` / `attach(handle, attr)`
**Signature:** `attach(handle: ProgramHandle, target: str(128), flags: u32) -> u32`
**Signature:** `attach(handle: ProgramHandle, attr: perf_event_attr) -> u32`
**Variadic:** No
**Context:** Userspace only

**Description:** Attach a loaded eBPF program to a target interface or attachment point.
**Description:** Attach a loaded eBPF program to a target interface or attachment point, or attach it to a perf event described by `perf_event_attr`.

**Parameters:**
- `handle`: Program handle returned from `load()`
- `target`: Target interface name (e.g., "eth0", "lo") or attachment point
- `flags`: Attachment flags (context-dependent)
- Standard form:
- `handle`: Program handle returned from `load()`
- `target`: Target interface name (e.g., "eth0", "lo") or attachment point
- `flags`: Attachment flags (context-dependent)
- Perf event form:
- `handle`: Program handle returned from `load()`
- `attr`: `perf_event_attr` value describing counter, pid, cpu, period, and filter flags

**Return Value:**
- Returns `0` on success
Expand All @@ -106,11 +111,25 @@ var result = attach(prog, "eth0", 0)
if (result != 0) {
print("Failed to attach program")
}

var perf_attr = perf_event_attr {
counter: branch_misses,
pid: -1,
cpu: 0,
period: 1000000,
wakeup: 1,
inherit: false,
exclude_kernel: false,
exclude_user: false
}

var perf_prog = load(on_branch_miss)
attach(perf_prog, perf_attr)
```

**Context-specific implementations:**
- **eBPF:** Not available
- **Userspace:** Uses `bpf_prog_attach` system call
- **Userspace:** Uses `attach_bpf_program_by_fd` for standard targets and `ks_open_perf_event` for perf events
- **Kernel Module:** Not available

---
Expand Down Expand Up @@ -340,7 +359,7 @@ fn main() -> i32 {
|----------|------|-----------|---------------|-------|
| `print()` | ✅ | ✅ | ✅ | Different output destinations |
| `load()` | ❌ | ✅ | ❌ | Program management only |
| `attach()` | ❌ | ✅ | ❌ | Program management only |
| `attach()` | ❌ | ✅ | ❌ | Standard attach and perf_event_attr attach |
| `detach()` | ❌ | ✅ | ❌ | Program management only |
| `register()` | ❌ | ✅ | ❌ | struct_ops registration |
| `test()` | ❌ | ✅ | ❌ | Testing framework only |
Expand Down
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ fn traffic_shaper(ctx: *__sk_buff) -> i32 {
// Trace system call entry
return 0
}

// Perf event program for hardware counter sampling
@perf_event
fn on_branch_miss(ctx: *bpf_perf_event_data) -> i32 {
// Runs on every hardware branch-miss event
return 0
}
```

### Type System
Expand Down Expand Up @@ -261,6 +268,50 @@ fn main() -> i32 {
}
```

### Hardware Performance Counter Programs

Use `@perf_event` to attach eBPF programs to hardware or software performance counters. The userspace side describes the counter via a `perf_event_attr` struct literal and calls `attach(prog, attr)`:

```kernelscript
// eBPF program fires on every hardware branch-miss sample
@perf_event
fn on_branch_miss(ctx: *bpf_perf_event_data) -> i32 {
return 0
}

fn main() -> i32 {
var attr = perf_event_attr {
counter: branch_misses, // hardware counter (see perf_counter enum)
pid: -1, // all processes
cpu: 0, // CPU 0
period: 1000000, // sample every 1 million events
wakeup: 1,
inherit: false,
exclude_kernel: false,
exclude_user: false
}

var prog = load(on_branch_miss)
attach(prog, attr) // opens perf_event_open fd, resets, attaches BPF, enables
detach(prog) // disables counter, destroys BPF link, closes fd
return 0
}
```

**Available `perf_counter` values:**

| Enum value | Hardware/software event |
|---|---|
| `cpu_cycles` | `PERF_COUNT_HW_CPU_CYCLES` |
| `instructions` | `PERF_COUNT_HW_INSTRUCTIONS` |
| `cache_references` | `PERF_COUNT_HW_CACHE_REFERENCES` |
| `cache_misses` | `PERF_COUNT_HW_CACHE_MISSES` |
| `branch_instructions` | `PERF_COUNT_HW_BRANCH_INSTRUCTIONS` |
| `branch_misses` | `PERF_COUNT_HW_BRANCH_MISSES` |
| `page_faults` | `PERF_COUNT_SW_PAGE_FAULTS` |
| `context_switches` | `PERF_COUNT_SW_CONTEXT_SWITCHES` |
| `cpu_migrations` | `PERF_COUNT_SW_CPU_MIGRATIONS` |

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

🔧 **For complete builtin functions reference, see [`BUILTINS.md`](BUILTINS.md).**
Expand Down Expand Up @@ -304,6 +355,7 @@ my_project/
- `tc` - Traffic control programs
- `probe` - Kernel function probing
- `tracepoint` - Kernel tracepoint programs
- `perf_event` - Hardware/software performance counter programs

**Available struct_ops:**
- `tcp_congestion_ops` - TCP congestion control
Expand Down
94 changes: 93 additions & 1 deletion SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var flows : hash<IpAddress, PacketStats>(1024)
KernelScript uses a simple and clear scoping model that eliminates ambiguity:

- **`@helper` functions**: Kernel-shared functions - accessible by all eBPF programs, compile to eBPF bytecode
- **Attributed functions** (e.g., `@xdp`, `@tc`, `@tracepoint`): eBPF program entry points - compile to eBPF bytecode
- **Attributed functions** (e.g., `@xdp`, `@tc`, `@tracepoint`, `@perf_event`): eBPF program entry points - compile to eBPF bytecode
- **Regular functions**: User space - functions and data structures compile to native executable
- **Maps and global configs**: Shared resources accessible from both kernel and user space
- **No wrapper syntax**: Direct, flat structure without unnecessary nesting
Expand Down Expand Up @@ -440,6 +440,98 @@ kernelscript init tracepoint/syscalls/sys_enter_read my_syscall_tracer
# appropriate KernelScript templates with correct context types
```

#### 3.1.3 Perf Event Programs

`@perf_event` programs attach eBPF logic to hardware or software performance counters via `perf_event_open(2)`. The eBPF function is invoked for every counter sample; the userspace side controls which counter to monitor through a `perf_event_attr` struct literal passed to `attach()`.

**Syntax:**
```kernelscript
@perf_event
fn <handler_name>(ctx: *bpf_perf_event_data) -> i32 {
// runs on every sample
return 0
}
```

The context type is always `*bpf_perf_event_data` (from `vmlinux.h`).

**Userspace lifecycle:**
```kernelscript
fn main() -> i32 {
var attr = perf_event_attr {
counter: branch_misses, // perf_counter enum value
pid: -1, // -1 = all processes; ≥0 = specific PID
cpu: 0, // ≥0 = specific CPU; -1 = any CPU (pid must be ≥0)
period: 1000000, // sample after this many events (0 → default 1000000)
wakeup: 1, // wake userspace after N samples (0 → default 1)
inherit: false, // inherit to forked children
exclude_kernel: false, // exclude kernel-mode samples
exclude_user: false // exclude user-mode samples
}

var prog = load(my_handler)
attach(prog, attr) // perf_event_open → IOC_RESET → attach BPF → IOC_ENABLE
// ... run workload ...
detach(prog) // IOC_DISABLE → bpf_link__destroy → close(perf_fd)
return 0
}
```

**`pid` / `cpu` rules enforced at runtime:**

| `pid` | `cpu` | Meaning |
|---|---|---|
| ≥ 0 | ≥ 0 | Specific process on specific CPU |
| ≥ 0 | -1 | Specific process on any CPU |
| -1 | ≥ 0 | All processes on specific CPU (system-wide) |
| -1 | -1 | **Invalid** — rejected with error |

**`perf_counter` enum:**

| Value | Linux constant |
|---|---|
| `cpu_cycles` | `PERF_COUNT_HW_CPU_CYCLES` |
| `instructions` | `PERF_COUNT_HW_INSTRUCTIONS` |
| `cache_references` | `PERF_COUNT_HW_CACHE_REFERENCES` |
| `cache_misses` | `PERF_COUNT_HW_CACHE_MISSES` |
| `branch_instructions` | `PERF_COUNT_HW_BRANCH_INSTRUCTIONS` |
| `branch_misses` | `PERF_COUNT_HW_BRANCH_MISSES` |
| `page_faults` | `PERF_COUNT_SW_PAGE_FAULTS` |
| `context_switches` | `PERF_COUNT_SW_CONTEXT_SWITCHES` |
| `cpu_migrations` | `PERF_COUNT_SW_CPU_MIGRATIONS` |

**Generated C helpers (emitted when `attach(prog, attr)` is used):**

| Function | Signature | Description |
|---|---|---|
| `ks_open_perf_event` | `int (ks_perf_event_attr)` | Calls `perf_event_open(2)`, returns fd |
| `ks_read_perf_count` | `int64_t (int perf_fd)` | Reads current 64-bit counter via `read()` |
| `ks_print_perf_count` | `void (int perf_fd, const char*)` | Prints `[perf] <name>: <count>` to stdout |

**Attach sequence (compiler-generated):**
1. `ks_attr.attr.disabled = 1` — open counter without starting it
2. `syscall(SYS_perf_event_open, ...)` → `perf_fd`
3. `ioctl(perf_fd, PERF_EVENT_IOC_RESET, 0)` — zero the counter
4. `bpf_program__attach_perf_event(prog, perf_fd)` — link BPF program
5. `ioctl(perf_fd, PERF_EVENT_IOC_ENABLE, 0)` — **start counting**

**Detach sequence (compiler-generated):**
1. `ioctl(perf_fd, PERF_EVENT_IOC_DISABLE, 0)` — stop counting
2. `bpf_link__destroy(link)` — unlink BPF program
3. `close(perf_fd)` — release the kernel perf event

**Compiler implementation:**
- Detects `attach(prog, perf_event_attr_value)` call (two-argument form) and emits `ks_open_perf_event` + `attach_bpf_program_by_fd` sequence
- Validates `pid ≥ -1`, `cpu ≥ -1`, and rejects `pid == -1 && cpu == -1` at runtime
- Emits `PERF_FLAG_FD_CLOEXEC` for safe fd inheritance
- BPF program section is `SEC("perf_event")`

**Project Initialization:**
```bash
# Initialize a perf_event project
kernelscript init perf_event my_perf_monitor
```

### 3.2 Named Configuration Blocks
```kernelscript
// Named configuration blocks - globally accessible
Expand Down
28 changes: 28 additions & 0 deletions examples/perf_branch_miss.ks
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// perf_branch_miss.ks
// Demonstrates @perf_event program type in KernelScript.
// The eBPF program runs on every hardware branch-miss event.
// The userspace side opens the perf event and attaches the BPF program.

@perf_event
fn on_branch_miss(ctx: *bpf_perf_event_data) -> i32 {
return 0
}

fn main() -> i32 {
var attr = perf_event_attr {
counter: branch_misses,
pid: -1,
cpu: 0,
period: 1000000,
wakeup: 1,
inherit: false,
exclude_kernel: false,
exclude_user: false
}

var prog = load(on_branch_miss)
attach(prog, attr)
detach(prog)

return 0
}
3 changes: 2 additions & 1 deletion src/ast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type probe_type =

(** Program types supported by KernelScript *)
type program_type =
| Xdp | Tc | Probe of probe_type | Tracepoint | StructOps
| Xdp | Tc | Probe of probe_type | Tracepoint | StructOps | PerfEvent

(** Map types for eBPF maps *)
type map_type =
Expand Down Expand Up @@ -658,6 +658,7 @@ let string_of_program_type = function
| Probe Kprobe -> "kprobe"
| Tracepoint -> "tracepoint"
| StructOps -> "struct_ops"
| PerfEvent -> "perf_event"

let string_of_map_type = function
| Hash -> "hash"
Expand Down
40 changes: 40 additions & 0 deletions src/btf_parser.ml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ let get_program_template prog_type btf_path =
| "tc" -> ("*__sk_buff", "i32", [
"__sk_buff"
])
| "perf_event" -> ("*bpf_perf_event_data", "i32", [
"bpf_perf_event_data"
])
| _ -> failwith (sprintf "Unsupported program type '%s' for generic template. Use specific template functions for kprobe/tracepoint." prog_type)
in

Expand Down Expand Up @@ -364,6 +367,7 @@ let generate_kernelscript_source ?extra_param ?include_kfuncs template project_n
Kernelscript_context.Kprobe_codegen.register ();
Kernelscript_context.Tracepoint_codegen.register ();
Kernelscript_context.Fprobe_codegen.register ();
Kernelscript_context.Perf_event_codegen.register ();

(* Get program description from context codegen system *)
let context_comment = "// " ^ (Kernelscript_context.Context_codegen.get_context_program_description template.program_type) in
Expand Down Expand Up @@ -502,6 +506,39 @@ let generate_kernelscript_source ?extra_param ?include_kfuncs template project_n
| None -> ""
in

(* perf_event programs use a completely different main() with attach(prog, attr) *)
if template.program_type = "perf_event" then
sprintf {|%s
// Generated by KernelScript compiler with direct BTF parsing%s

%s
%s {
// TODO: Implement your perf_event logic here

return %s
}

fn main() -> i32 {
var attr = perf_event_attr {
counter: branch_misses,
pid: -1,
cpu: 0,
period: 1000000,
wakeup: 1,
inherit: false,
exclude_kernel: false,
exclude_user: false
}

var prog = load(%s)
attach(prog, attr)
detach(prog)

return 0
}
|} context_comment include_line attribute_line function_definition sample_return function_name
else

sprintf {|%s
// Generated by KernelScript compiler with direct BTF parsing%s
%s
Expand Down Expand Up @@ -549,6 +586,9 @@ let get_program_btf_types prog_type =
| "tracepoint" -> [
("trace_entry", "struct");
]
| "perf_event" -> [
("bpf_perf_event_data", "struct");
]
| _ -> []

(* Program-type specific kfunc names to extract from BTF *)
Expand Down
1 change: 1 addition & 0 deletions src/codegen_common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ let rec ir_type_to_c target = function
| UserspaceStd -> "char") (* Base type for userspace string - size handled in declaration *)
| IRPointer (inner_type, _) -> sprintf "%s*" (ir_type_to_c target inner_type)
| IRArray (inner_type, size, _) -> sprintf "%s[%d]" (ir_type_to_c target inner_type) size
| IRStruct ("perf_event_attr", _) -> "ks_perf_event_attr" (* Avoid conflict with linux/perf_event.h *)
| IRStruct (name, _) -> sprintf "struct %s" name
| IREnum (name, _) -> sprintf "enum %s" name
| IRResult (ok_type, _err_type) -> ir_type_to_c target ok_type (* simplified to ok type *)
Expand Down
2 changes: 1 addition & 1 deletion src/context/dune
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
(library
(public_name kernelscript.context)
(name kernelscript_context)
(modules context_codegen xdp_codegen tc_codegen kprobe_codegen tracepoint_codegen fprobe_codegen)
(modules context_codegen xdp_codegen tc_codegen kprobe_codegen tracepoint_codegen fprobe_codegen perf_event_codegen)
(libraries unix str))
Loading