profiler/profile.bpf.c (213 lines of code) (raw):

/* SPDX-License-Identifier: BSD-2-Clause */ /* Copyright (c) 2022 LG Electronics */ #include "lua_state.h" #include "profile.h" #include "maps.bpf.h" const volatile bool kernel_stacks_only = false; const volatile bool user_stacks_only = false; const volatile bool disable_lua_user_trace = false; const volatile bool include_idle = false; const volatile pid_t targ_pid = -1; const volatile pid_t targ_tid = -1; const volatile int frame_depth = 15; struct { __uint(type, BPF_MAP_TYPE_STACK_TRACE); __type(key, u32); } stackmap SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, struct stack_key); __type(value, sizeof(u64)); __uint(max_entries, MAX_ENTRIES); } counts SEC(".maps"); #define MAX_ENTRIES 10240 // for collecting lua stack trace function name // and pass the pointer of Lua_state to perf event struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, MAX_ENTRIES); __type(key, __u32); __type(value, struct lua_stack_event); } lua_events SEC(".maps"); // output the lua stack to user space because we cannot keep all of them in // ebpf maps struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __uint(key_size, sizeof(__u32)); __uint(value_size, sizeof(__u32)); } lua_event_output SEC(".maps"); /* * If PAGE_OFFSET macro is not available in vmlinux.h, determine ip whose MSB * (Most Significant Bit) is 1 as the kernel address. * TODO: use end address of user space to determine the address space of ip */ #if defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) #define BITS_PER_ADDR (64) #define MSB_SET_ULONG (1UL << (BITS_PER_ADDR - 1)) static __always_inline bool is_kernel_addr(u64 addr) { return !!(addr & MSB_SET_ULONG); } #else static __always_inline bool is_kernel_addr(u64 addr) { return false; } #endif /* __TARGET_ARCH_arm64 || __TARGET_ARCH_x86 */ static inline int lua_get_funcdata(struct bpf_perf_event_data *ctx, cTValue *frame, struct lua_stack_event *eventp, int level) { if (!frame) return -1; GCfunc *fn = frame_func(frame); if (!fn) return -1; if (isluafunc(fn)) { eventp->type = FUNC_TYPE_LUA; GCproto *pt = funcproto(fn); if (!pt) return -1; eventp->ffid = BPF_PROBE_READ_USER(pt, firstline); GCstr *name = proto_chunkname(pt); /* GCstr *name */ const char *src = strdata(name); if (!src) return -1; bpf_probe_read_user_str(eventp->name, sizeof(eventp->name), src); bpf_printk("level= %d, fn_name=%s\n", level, eventp->name); } else if (iscfunc(fn)) { eventp->type = FUNC_TYPE_C; eventp->funcp = BPF_PROBE_READ_USER(fn, c.f); } else if (isffunc(fn)) { eventp->type = FUNC_TYPE_F; eventp->ffid = BPF_PROBE_READ_USER(fn, c.ffid); } eventp->level = level; bpf_perf_event_output(ctx, &lua_event_output, BPF_F_CURRENT_CPU, eventp, sizeof(*eventp)); return 0; } static int fix_lua_stack(struct bpf_perf_event_data *ctx, __u32 tid, int stack_id) { if (stack_id == 0) { return 0; } struct lua_stack_event *eventp; eventp = bpf_map_lookup_elem(&lua_events, &tid); if (!eventp) return 0; eventp->user_stack_id = stack_id; lua_State *L = eventp->L; if (!L) return 0; // start from the top of the stack and trace back // count the number of function calls founded int level = 1, count = 0; cTValue *frame, *nextframe, *bot = tvref(BPF_PROBE_READ_USER(L, stack)) + LJ_FR2; int i = 0; frame = nextframe = BPF_PROBE_READ_USER(L, base) - 1; /* Traverse frames backwards. */ // for the ebpf verifier insns (limit 1000000), we need to limit the max loop times to 13 for (; i < frame_depth && frame > bot; i++) { if (frame_gc(frame) == obj2gco(L)) { level++; /* Skip dummy frames. See lj_err_optype_call(). */ } if (level-- == 0) { level++; /* Level found. */ if (lua_get_funcdata(ctx, frame, eventp, count) != 0) { continue; } count++; } nextframe = frame; if (frame_islua(frame)) { frame = frame_prevl(frame); } else { if (frame_isvarg(frame)) level++; /* Skip vararg pseudo-frame. */ frame = frame_prevd(frame); } } return 0; } SEC("perf_event") int do_perf_event(struct bpf_perf_event_data *ctx) { __u64 id = bpf_get_current_pid_tgid(); __u32 pid = id >> 32; __u32 tid = id; __u64 *valp; static const __u64 zero; struct stack_key key = {}; if (!include_idle && tid == 0) return 0; if (targ_pid != -1 && targ_pid != pid) return 0; if (targ_tid != -1 && targ_tid != tid) return 0; key.pid = pid; bpf_get_current_comm(&key.name, sizeof(key.name)); if (user_stacks_only) key.kern_stack_id = -1; else key.kern_stack_id = bpf_get_stackid(&ctx->regs, &stackmap, 0); if (kernel_stacks_only) key.user_stack_id = -1; else key.user_stack_id = bpf_get_stackid(&ctx->regs, &stackmap, BPF_F_USER_STACK); if (key.kern_stack_id >= 0) { // populate extras to fix the kernel stack __u64 ip = PT_REGS_IP(&ctx->regs); if (is_kernel_addr(ip)) { key.kernel_ip = ip; } } valp = bpf_map_lookup_or_try_init(&counts, &key, &zero); if (valp) __sync_fetch_and_add(valp, 1); if (!disable_lua_user_trace && (!valp || *valp <= 1)) { // only get lua stack the first time we found a new stack id fix_lua_stack(ctx, tid, key.user_stack_id); } return 0; } static int probe_entry_lua_cancel(struct pt_regs *ctx) { if (!PT_REGS_PARM2(ctx)) return 0; if (!PT_REGS_PARM4(ctx)) return 0; __u64 pid_tgid = bpf_get_current_pid_tgid(); __u32 pid = pid_tgid >> 32; __u32 tid = (__u32)pid_tgid; if (targ_pid != -1 && targ_pid != pid) return 0; bpf_map_delete_elem(&lua_events, &tid); return 0; } SEC("uprobe/handle_entry_lua_cancel") int handle_entry_lua_cancel(struct pt_regs *ctx) { return probe_entry_lua_cancel(ctx); } static int probe_entry_lua(struct pt_regs *ctx) { if (!PT_REGS_PARM1(ctx)) return 0; __u64 pid_tgid = bpf_get_current_pid_tgid(); __u32 pid = pid_tgid >> 32; __u32 tid = (__u32)pid_tgid; struct lua_stack_event event = {}; if (targ_pid != -1 && targ_pid != pid) return 0; event.pid = pid; event.L = (void *)PT_REGS_PARM1(ctx); bpf_map_update_elem(&lua_events, &tid, &event, BPF_ANY); return 0; } SEC("uprobe/handle_entry_lua") int handle_entry_lua(struct pt_regs *ctx) { return probe_entry_lua(ctx); } char LICENSE[] SEC("license") = "GPL";