Reputation: 23
I'm playing around with eBPF, specifically using the bcc package to try to trace when a function enters and exits. This is the program:
typedef struct FunctionEvent_t {
u32 pid;
u64 timestamp;
u64 func_addr;
u8 entry;
} FunctionEvent;
BPF_PERF_OUTPUT(traceevents);
static void fill_and_submit(struct pt_regs *ctx, FunctionEvent *event) {
event->pid = bpf_get_current_pid_tgid();
event->timestamp = bpf_ktime_get_ns();
event->func_addr = PT_REGS_IP(ctx);
traceevents.perf_submit(ctx, event, sizeof(FunctionEvent));
}
int do_entry_point(struct pt_regs *ctx) {
FunctionEvent event = {.entry = 1};
fill_and_submit(ctx, &event);
return 0;
}
int do_exit_point(struct pt_regs *ctx) {
FunctionEvent event = {.entry = 0};
fill_and_submit(ctx, &event);
return 0;
}
The condensed python code is:
from bcc import BPF
def print_trace_event(self, _cpu, data, _size):
"""
Print a new lock event.
"""
event = self.bpf_instance["traceevents"].event(data)
if self.args.pids and event.pid not in self.args.pids:
return
output = f"{event.timestamp} [Pid {event.pid}] {event.func_addr} {event.entry} {bpf_instance.sym(event.func_addr, event.pid)}"
print(output)
bpf_instance = BPF(text=bpf_program)
bpf_instance.attach_uprobe(name="path/to/exe", sym="my_func_1", fn_name="do_entry_point")
bpf_instance.attach_uretprobe(name="path/to/exe", sym="my_func_1", fn_name="do_exit_point")
bpf_instance["traceevents"].open_perf_buffer(print_trace_event)
while True:
try:
bpf_instance.perf_buffer_poll()
except KeyboardInterrupt:
sys.exit(0)
The output though is giving the correct entry function but its giving the address of the caller of my_func_1
so I can't map the entry and exit together.
1123110348467323 [Pid 74585] 7990672 1 b'my_func_1'
1123110348515011 [Pid 74585] 12766685 0 b'my_other_func'
1123110348534593 [Pid 74585] 7990672 1 b'my_func_1'
1123110348552326 [Pid 74585] 12767556 0 b'my_other_func'
I'm getting the function address using PT_REGS_IP(ctx)
but it looks like this is giving the address of the caller function on the return probe. Is there a way to retrieve the function name on the return probe so I can map entry and exits together? The plan is to have this work on any function so I need a programmable way to do this, I can't just hard code the function names in.
Upvotes: 0
Views: 47
Reputation: 13133
When there's data that can be obtained on function entry but not on exit, what we do a lot is save it in a map to retrieve it at the exit point. For example, bcc tools do this a lot.
In your case, it could look like:
BPF_HASH(curr_func, u64, u64);
static void fill_and_submit(struct pt_regs *ctx, FunctionEvent *event, u64 pid_tgid) {
event->pid = (u32)pid_tgid;
event->timestamp = bpf_ktime_get_ns();
traceevents.perf_submit(ctx, event, sizeof(FunctionEvent));
}
int do_entry_point(struct pt_regs *ctx) {
FunctionEvent event = {.entry = 1};
u64 pid_tgid = bpf_get_current_pid_tgid();
event.func_addr = PT_REGS_IP(ctx);
fill_and_submit(ctx, &event, pid_tgid);
curr_func.update(&pid_tgid, &event.func_addr);
return 0;
}
int do_exit_point(struct pt_regs *ctx) {
FunctionEvent event = {.entry = 0};
u64 pid_tgid = bpf_get_current_pid_tgid();
u64 *ptr = curr_func.lookup(&pid_tgid);
if (!ptr) {
return 0;
}
event->func_addr = *ptr;
fill_and_submit(ctx, &event, pid_tgid);
return 0;
}
Upvotes: 0