Skip to content

Commit 47d5bb0

Browse files
committed
feat(heaptrack): allow enabling/disabling via IPC
1 parent b6f2d03 commit 47d5bb0

File tree

6 files changed

+202
-8
lines changed

6 files changed

+202
-8
lines changed

crates/heaptrack/src/bpf.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,38 @@ impl HeaptrackBpf {
5555
Ok(())
5656
}
5757

58+
/// Enable event tracking
59+
pub fn enable_tracking(&mut self) -> Result<()> {
60+
let key = 0u32;
61+
let value = true as u8;
62+
self.skel
63+
.maps
64+
.tracking_enabled
65+
.update(
66+
&key.to_le_bytes(),
67+
&value.to_le_bytes(),
68+
libbpf_rs::MapFlags::ANY,
69+
)
70+
.context("Failed to enable tracking")?;
71+
Ok(())
72+
}
73+
74+
/// Disable event tracking
75+
pub fn disable_tracking(&mut self) -> Result<()> {
76+
let key = 0u32;
77+
let value = false as u8;
78+
self.skel
79+
.maps
80+
.tracking_enabled
81+
.update(
82+
&key.to_le_bytes(),
83+
&value.to_le_bytes(),
84+
libbpf_rs::MapFlags::ANY,
85+
)
86+
.context("Failed to disable tracking")?;
87+
Ok(())
88+
}
89+
5890
pub fn attach_malloc(&mut self, libc_path: &Path) -> Result<()> {
5991
let malloc_opts = UprobeOpts {
6092
func_name: Some("malloc".to_string()),

crates/heaptrack/src/bpf/heaptrack.bpf.c

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,26 @@ struct {
2929
__uint(max_entries, 256 * 1024);
3030
} events SEC(".maps");
3131

32+
/* Map to control whether tracking is enabled (0 = disabled, 1 = enabled) */
33+
struct {
34+
__uint(type, BPF_MAP_TYPE_ARRAY);
35+
__uint(max_entries, 1);
36+
__type(key, __u32);
37+
__type(value, __u8);
38+
} tracking_enabled SEC(".maps");
39+
40+
/* Helper to check if tracking is currently enabled */
41+
static __always_inline int is_enabled(void) {
42+
__u32 key = 0;
43+
__u8 *enabled = bpf_map_lookup_elem(&tracking_enabled, &key);
44+
45+
/* Default to enabled if map not initialized */
46+
if (!enabled) {
47+
return 1;
48+
}
49+
50+
return *enabled;
51+
}
3252

3353
/* Helper to check if a PID or any of its ancestors should be tracked */
3454
static __always_inline int is_tracked(__u32 pid) {
@@ -76,14 +96,16 @@ int tracepoint_sched_fork(struct trace_event_raw_sched_process_fork *ctx) {
7696

7797
SEC("tracepoint/syscalls/sys_enter_execve")
7898
int tracepoint_sys_execve(struct trace_event_raw_sys_enter *ctx) {
79-
__u32 pid = bpf_get_current_pid_tgid() >> 32;
99+
__u64 tid = bpf_get_current_pid_tgid();
100+
__u32 pid = tid >> 32;
80101

81102
/* Check if this process or any parent is being tracked */
82-
if (is_tracked(pid)) {
103+
if (is_tracked(pid) && is_enabled()) {
83104
struct event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
84105
if (e) {
85106
e->timestamp = bpf_ktime_get_ns();
86107
e->pid = pid;
108+
e->tid = tid & 0xFFFFFFFF;
87109
e->event_type = EVENT_TYPE_EXECVE;
88110
e->addr = 0;
89111
e->size = 0;
@@ -123,7 +145,7 @@ int uretprobe_malloc(struct pt_regs *ctx) {
123145
__u32 pid = tid >> 32;
124146

125147
/* Check if this PID is being tracked */
126-
if (is_tracked(pid)) {
148+
if (is_tracked(pid) && is_enabled()) {
127149
__u64 *size_ptr = bpf_map_lookup_elem(&malloc_size, &tid);
128150
if (size_ptr) {
129151
__u64 addr = PT_REGS_RC(ctx);
@@ -135,6 +157,7 @@ int uretprobe_malloc(struct pt_regs *ctx) {
135157
if (e) {
136158
e->timestamp = bpf_ktime_get_ns();
137159
e->pid = pid;
160+
e->tid = tid & 0xFFFFFFFF;
138161
e->event_type = EVENT_TYPE_MALLOC;
139162
e->addr = addr;
140163
e->size = size;
@@ -151,10 +174,11 @@ int uretprobe_malloc(struct pt_regs *ctx) {
151174

152175
SEC("uprobe")
153176
int uprobe_free(struct pt_regs *ctx) {
154-
__u32 pid = bpf_get_current_pid_tgid() >> 32;
177+
__u64 tid = bpf_get_current_pid_tgid();
178+
__u32 pid = tid >> 32;
155179

156180
/* Check if this PID is being tracked */
157-
if (is_tracked(pid)) {
181+
if (is_tracked(pid) && is_enabled()) {
158182
__u64 addr = PT_REGS_PARM1(ctx);
159183

160184
/* Only track non-NULL frees */
@@ -163,6 +187,7 @@ int uprobe_free(struct pt_regs *ctx) {
163187
if (e) {
164188
e->timestamp = bpf_ktime_get_ns();
165189
e->pid = pid;
190+
e->tid = tid & 0xFFFFFFFF;
166191
e->event_type = EVENT_TYPE_FREE;
167192
e->addr = addr;
168193
e->size = 0; /* size unknown for free */
@@ -205,7 +230,7 @@ int uretprobe_calloc(struct pt_regs *ctx) {
205230
__u32 pid = tid >> 32;
206231

207232
/* Check if this PID is being tracked */
208-
if (is_tracked(pid)) {
233+
if (is_tracked(pid) && is_enabled()) {
209234
__u64 *size_ptr = bpf_map_lookup_elem(&calloc_size, &tid);
210235
if (size_ptr) {
211236
__u64 addr = PT_REGS_RC(ctx);
@@ -217,6 +242,7 @@ int uretprobe_calloc(struct pt_regs *ctx) {
217242
if (e) {
218243
e->timestamp = bpf_ktime_get_ns();
219244
e->pid = pid;
245+
e->tid = tid & 0xFFFFFFFF;
220246
e->event_type = EVENT_TYPE_CALLOC;
221247
e->addr = addr;
222248
e->size = size;
@@ -260,7 +286,7 @@ int uretprobe_realloc(struct pt_regs *ctx) {
260286
__u32 pid = tid >> 32;
261287

262288
/* Check if this PID is being tracked */
263-
if (is_tracked(pid)) {
289+
if (is_tracked(pid) && is_enabled()) {
264290
__u64 *size_ptr = bpf_map_lookup_elem(&realloc_size, &tid);
265291
if (size_ptr) {
266292
__u64 addr = PT_REGS_RC(ctx);
@@ -272,6 +298,7 @@ int uretprobe_realloc(struct pt_regs *ctx) {
272298
if (e) {
273299
e->timestamp = bpf_ktime_get_ns();
274300
e->pid = pid;
301+
e->tid = tid & 0xFFFFFFFF;
275302
e->event_type = EVENT_TYPE_REALLOC;
276303
e->addr = addr;
277304
e->size = size;
@@ -315,7 +342,7 @@ int uretprobe_aligned_alloc(struct pt_regs *ctx) {
315342
__u32 pid = tid >> 32;
316343

317344
/* Check if this PID is being tracked */
318-
if (is_tracked(pid)) {
345+
if (is_tracked(pid) && is_enabled()) {
319346
__u64 *size_ptr = bpf_map_lookup_elem(&aligned_alloc_size, &tid);
320347
if (size_ptr) {
321348
__u64 addr = PT_REGS_RC(ctx);
@@ -327,6 +354,7 @@ int uretprobe_aligned_alloc(struct pt_regs *ctx) {
327354
if (e) {
328355
e->timestamp = bpf_ktime_get_ns();
329356
e->pid = pid;
357+
e->tid = tid & 0xFFFFFFFF;
330358
e->event_type = EVENT_TYPE_ALIGNED_ALLOC;
331359
e->addr = addr;
332360
e->size = size;

crates/heaptrack/src/ipc.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use anyhow::{Context, Result};
2+
use ipc_channel::ipc::{self, IpcOneShotServer, IpcSender};
3+
use log::debug;
4+
use serde::{Deserialize, Serialize};
5+
use std::sync::{Arc, Mutex};
6+
7+
pub type HeaptrackIpcServer = IpcOneShotServer<IpcSender<IpcMessage>>;
8+
9+
use crate::Tracker;
10+
11+
/// Commands sent from the runner to control heaptrack
12+
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
13+
pub enum IpcCommand {
14+
Enable,
15+
Disable,
16+
Ping,
17+
}
18+
19+
/// Response sent back to runner
20+
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
21+
pub enum IpcResponse {
22+
Ack,
23+
Err,
24+
}
25+
26+
/// Message combining command and response channel
27+
#[derive(Debug, Serialize, Deserialize)]
28+
pub struct IpcMessage {
29+
pub command: IpcCommand,
30+
pub response_channel: IpcSender<IpcResponse>,
31+
}
32+
33+
/// Client for sending commands from runner to heaptrack
34+
pub struct HeaptrackIpcClient {
35+
sender: IpcSender<IpcMessage>,
36+
}
37+
38+
impl HeaptrackIpcClient {
39+
pub fn new(sender: IpcSender<IpcMessage>) -> Self {
40+
Self { sender }
41+
}
42+
43+
/// Create from the connection accepted by the runner
44+
pub fn from_accepted(sender: IpcSender<IpcMessage>) -> Self {
45+
Self { sender }
46+
}
47+
48+
fn send_command(&self, cmd: IpcCommand) -> Result<IpcResponse> {
49+
let (response_tx, response_rx) = ipc::channel::<IpcResponse>()?;
50+
51+
let msg = IpcMessage {
52+
command: cmd,
53+
response_channel: response_tx,
54+
};
55+
56+
self.sender.send(msg).context("Failed to send command")?;
57+
let response = response_rx.recv().context("Failed to receive response")?;
58+
59+
Ok(response)
60+
}
61+
62+
pub fn enable(&self) -> Result<()> {
63+
let response = self.send_command(IpcCommand::Enable)?;
64+
match response {
65+
IpcResponse::Ack => Ok(()),
66+
IpcResponse::Err => anyhow::bail!("Failed to enable tracking"),
67+
}
68+
}
69+
70+
pub fn disable(&self) -> Result<()> {
71+
let response = self.send_command(IpcCommand::Disable)?;
72+
match response {
73+
IpcResponse::Ack => Ok(()),
74+
IpcResponse::Err => anyhow::bail!("Failed to disable tracking"),
75+
}
76+
}
77+
78+
pub fn ping(&self) -> Result<()> {
79+
let response = self.send_command(IpcCommand::Ping)?;
80+
match response {
81+
IpcResponse::Ack => Ok(()),
82+
IpcResponse::Err => anyhow::bail!("Failed to ping heaptrack"),
83+
}
84+
}
85+
}
86+
87+
/// Handle incoming IPC messages in heaptrack
88+
pub fn handle_ipc_message(msg: IpcMessage, tracker: &Arc<Mutex<Tracker>>) {
89+
let response = match msg.command {
90+
IpcCommand::Enable => match tracker.lock() {
91+
Ok(mut t) => match t.enable() {
92+
Ok(_) => {
93+
debug!("Tracking enabled");
94+
IpcResponse::Ack
95+
}
96+
Err(_) => IpcResponse::Err,
97+
},
98+
Err(_) => IpcResponse::Err,
99+
},
100+
IpcCommand::Disable => match tracker.lock() {
101+
Ok(mut t) => match t.disable() {
102+
Ok(_) => {
103+
debug!("Tracking disabled");
104+
IpcResponse::Ack
105+
}
106+
Err(_) => IpcResponse::Err,
107+
},
108+
Err(_) => IpcResponse::Err,
109+
},
110+
IpcCommand::Ping => IpcResponse::Ack,
111+
};
112+
113+
// Send response back
114+
let _ = msg.response_channel.send(response);
115+
}

crates/heaptrack/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
mod bpf;
22
mod events;
3+
mod ipc;
34
mod libc;
45
mod poller;
56
mod tracker;
67

78
pub use events::{Event, EventType};
9+
pub use ipc::{
10+
HeaptrackIpcClient, HeaptrackIpcServer, IpcCommand as HeaptrackIpcCommand,
11+
IpcMessage as HeaptrackIpcMessage, IpcResponse as HeaptrackIpcResponse, handle_ipc_message,
12+
};
813
pub use poller::{EventHandler, RingBufferPoller};
914
pub use tracker::Tracker;

crates/heaptrack/src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ enum Commands {
2828
/// Output folder for the allocations data
2929
#[arg(short, long, default_value = ".")]
3030
output: PathBuf,
31+
32+
/// Optional IPC server name for receiving control commands
33+
#[arg(long)]
34+
ipc_server: Option<String>,
3135
},
3236
}
3337

crates/heaptrack/src/tracker.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,14 @@ impl Tracker {
8080

8181
Ok(())
8282
}
83+
84+
/// Enable event tracking in the BPF program
85+
pub fn enable(&mut self) -> anyhow::Result<()> {
86+
self.heaptrack.enable_tracking()
87+
}
88+
89+
/// Disable event tracking in the BPF program
90+
pub fn disable(&mut self) -> anyhow::Result<()> {
91+
self.heaptrack.disable_tracking()
92+
}
8393
}

0 commit comments

Comments
 (0)