TreeWater
TreeWater

Reputation: 23

How to get callee function name in eBPF on both entry and return probes

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

Answers (1)

pchaigno
pchaigno

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

Related Questions