From 5c1e56d40c9b9296dd4dc917a4d96d87a259ade7 Mon Sep 17 00:00:00 2001 From: findstr Date: Sat, 21 Oct 2023 18:19:21 +0800 Subject: [PATCH] refine merge c and lua callstack --- src/bpf/lua.h | 6 ++ src/bpf/{lstate.h => lua54.h} | 22 +++--- src/bpf/profile.bpf.c | 41 ++++++++--- src/perf.rs | 128 +++++++++++++++++++++------------- 4 files changed, 128 insertions(+), 69 deletions(-) create mode 100644 src/bpf/lua.h rename src/bpf/{lstate.h => lua54.h} (95%) diff --git a/src/bpf/lua.h b/src/bpf/lua.h new file mode 100644 index 0000000..709e354 --- /dev/null +++ b/src/bpf/lua.h @@ -0,0 +1,6 @@ +#ifndef _LUA_H +#define _LUA_H + +#include "lua54.h" + +#endif \ No newline at end of file diff --git a/src/bpf/lstate.h b/src/bpf/lua54.h similarity index 95% rename from src/bpf/lstate.h rename to src/bpf/lua54.h index 387c28d..9b94727 100644 --- a/src/bpf/lstate.h +++ b/src/bpf/lua54.h @@ -40,16 +40,16 @@ typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); /* ** Bits in CallInfo status */ -#define CIST_OAH (1<<0) /* original value of 'allowhook' */ -#define CIST_C (1<<1) /* call is running a C function */ -#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */ -#define CIST_HOOKED (1<<3) /* call is running a debug hook */ -#define CIST_YPCALL (1<<4) /* doing a yieldable protected call */ -#define CIST_TAIL (1<<5) /* call was tail called */ -#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ -#define CIST_FIN (1<<7) /* function "called" a finalizer */ -#define CIST_TRAN (1<<8) /* 'ci' has transfer information */ -#define CIST_CLSRET (1<<9) /* function is closing tbc variables */ +#define CIST_OAH (1<<0) /* original value of 'allowhook' */ +#define CIST_C (1<<1) /* call is running a C function */ +#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */ +#define CIST_HOOKED (1<<3) /* call is running a debug hook */ +#define CIST_YPCAL (1<<4) /* doing a yieldable protected call */ +#define CIST_TAIL (1<<5) /* call was tail called */ +#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ +#define CIST_FIN (1<<7) /* function "called" a finalizer */ +#define CIST_TRAN (1<<8) /* 'ci' has transfer information */ +#define CIST_CLSRET (1<<9) /* function is closing tbc variables */ /* Bits 10-12 are used for CIST_RECST (see below) */ #define CIST_RECST 10 #if defined(LUA_COMPAT_LT_LE) @@ -523,5 +523,7 @@ static __always_inline void *lua_func_addr(StkIdRel stk, CallInfo *ci) { return MARK_LUA_ADDR((uintptr_t)source.linedefined << 32 | file_id); } +#define lua_ci_is_fresh(ci) ((ci->callstatus & CIST_FRESH) == CIST_FRESH) + #endif diff --git a/src/bpf/profile.bpf.c b/src/bpf/profile.bpf.c index 0530ada..439f501 100644 --- a/src/bpf/profile.bpf.c +++ b/src/bpf/profile.bpf.c @@ -4,7 +4,7 @@ #include #include #include -#include "lstate.h" +#include "lua.h" char LICENSE[] SEC("license") = "GPL"; @@ -45,6 +45,8 @@ struct lua_stack { u32 count; lua_State *L; lua_State *buf[MAX_STACK_DEPTH]; + u8 L_cnt; + u8 cnt[MAX_STACK_DEPTH]; }; struct c_stk_ctx { @@ -58,6 +60,7 @@ struct c_stk_ctx { }; struct lua_stk_ctx { + int calln; int lua_index; StkIdRel stack; CallInfo *ci; @@ -125,15 +128,20 @@ unwind_c(u32 i, void *ud) lua_State *L = (lua_State *)ctx->rbx; if (lua->L == NULL) { ctx->lua->L = L; + ctx->lua->L_cnt = 1; } else if (lua->L != L) { size_t x = (size_t)lua->count & 0xffff; if (x < MAX_STACK_DEPTH) { lua->buf[x] = lua->L; + lua->cnt[x] = lua->L_cnt; lua->L = L; + lua->L_cnt = 1; lua->count++; } DEBUG("unwind_c lua stack change:%lx", L); - } + } else { + ctx->lua->L_cnt++; + } } event->ustack[i] = ctx->rip; event->ustack_sz++; @@ -192,6 +200,7 @@ struct { __uint(max_entries, 1); } tmp_call_info SEC(".maps"); +DECLARE_TMP_VAR(CallInfo, ci); static __always_inline int lua_ci_stk(void *ptr, CallInfo **ci, StkIdRel *stk) { lua_State L; @@ -211,12 +220,9 @@ unwind_lua(u32 i, void *ud) int err; void *addr; u32 zero = 0; - DEBUG("----unwind_lua"); - CallInfo *ci = bpf_map_lookup_elem(&tmp_call_info, &zero); - if (ci == NULL) { - return LOOP_BREAK; - } + FETCH_TMP_VAR(CallInfo, ci, LOOP_BREAK); struct lua_stk_ctx *ctx = (struct lua_stk_ctx *)ud; + DEBUG("----unwind_lua ci:%lx", ctx->ci); if ((ctx->event->lstack_sz & 0xffff) >= ARRAY_SIZE(ctx->event->lstack)) { return LOOP_BREAK; } @@ -227,6 +233,8 @@ unwind_lua(u32 i, void *ud) return LOOP_BREAK; } lua_State *L = ctx->lua->buf[i]; + ctx->calln = ctx->lua->cnt[i]; + DEBUG("unwind_lua begin ci:%d calln:%d", ctx->lua_index, ctx->calln); int err = lua_ci_stk(L, &ctx->ci, &ctx->stack); if (err != 0) { DEBUG("unwind_lua read L error:%d", err); @@ -245,13 +253,21 @@ unwind_lua(u32 i, void *ud) DEBUG("unwind_lua lua_func_addr fail:%lx", ctx->ci); return LOOP_CONTINUE; } - DEBUG("unwind_lua i:%d luaV_execute :%lx prev:%lx", i, addr, ci->previous); + DEBUG("unwind_lua i:%d luaV_execute :%lx calln:%d prev:%lx", i, addr, ctx->calln, ci->previous); size_t j = (size_t)ctx->event->lstack_sz & 0xffff; if (j < ARRAY_SIZE(ctx->event->lstack)) { ctx->event->lstack[j] = (u64)addr; ctx->event->lstack_sz++; } - ctx->ci = ci->previous; + j = (size_t)ctx->event->lstack_sz & 0xffff; + if (j < ARRAY_SIZE(ctx->event->lstack) && lua_ci_is_fresh(ci)) { + ctx->event->lstack[j] = 0; + ctx->event->lstack_sz++; + if (--ctx->calln <= 0) { + ci->previous = NULL; + } + } + ctx->ci = ci->previous; return LOOP_CONTINUE; } @@ -296,6 +312,7 @@ int profile(struct bpf_perf_event_data *perf_ctx) ctx.c.event = stack_event; stack_event->ustack_sz = 0; lua_stack->L = NULL; + lua_stack->L_cnt = 0; lua_stack->count = 0; //unwind user space DEBUG("---------profile unwind start:%lx", ctx.c.rip); @@ -308,7 +325,9 @@ int profile(struct bpf_perf_event_data *perf_ctx) } //unwind lua_State if (ctx.c.lua->L != NULL && (ctx.c.lua->count & 0xffff) < MAX_STACK_DEPTH) { - ctx.c.lua->buf[ctx.c.lua->count & 0xffff] = ctx.c.lua->L; + int i = ctx.c.lua->count & 0xffff; + ctx.c.lua->buf[i] = ctx.c.lua->L; + ctx.c.lua->cnt[i] = ctx.c.lua->L_cnt; ctx.c.lua->count++; } stack_event->lstack_sz = 0; @@ -322,7 +341,7 @@ int profile(struct bpf_perf_event_data *perf_ctx) stack_event->lstack_sz *= sizeof(u64); } stack_event->stk_id = get_stack_id(stack_event); - #if 0 + #if LOG_LEVEL <= LOG_DEBUG struct stack_event *new_event = bpf_ringbuf_reserve(&events, sizeof(*stack_event), 0); if (!new_event) return 1; diff --git a/src/perf.rs b/src/perf.rs index 108a122..4caf125 100644 --- a/src/perf.rs +++ b/src/perf.rs @@ -58,10 +58,13 @@ struct StackFrame { lstack: Vec, } -struct LuaCallChunk { - c_addr: u64, - lua_frame: Vec, +#[derive(Debug)] +enum LuaFrame { + Lua(String), + C(u64), } + +#[derive(Clone)] struct SymInfo { addr: u64, offset: u64, @@ -505,7 +508,7 @@ impl Perf { } } } else { - println!("Lua Stack:{:X}", *addr); + println!("Lua Stack:{:X}", *addr); let mut stk = Vec::new(); stk.push(*addr); show_stack_trace( @@ -521,6 +524,7 @@ impl Perf { let stack = StackFrame::from_event(event); let stack_strs = self.combine_stack(skel, &stack); + println!("combined:"); println!("{}", stack_strs.join("\n")); println!("============="); 0 @@ -639,59 +643,87 @@ impl Perf { None => String::from("None"), } } + fn split_lua_chunk(&self, skel: &ProfileSkel, frame: &StackFrame) -> Vec> { + let mut chunks: Vec> = Vec::new(); + let mut chunk: Vec = Vec::new(); + for addr in frame.lstack.iter() { + if *addr == 0 { //CIST_FRESH + chunks.push(chunk); + chunk = Vec::new(); + continue + } + if *addr & LUA_MASK == 0 { + chunk.push(LuaFrame::C(*addr)) + } else { + let addrv = *addr & !LUA_MASK; + let file_id = addrv as u32; + let line = (addrv >> 32) as u32; + let str = format!("{}:{}", self.id_to_str(skel, file_id), line); + chunk.push(LuaFrame::Lua(str)); + } + } + if chunk.len() > 0 { + chunks.push(chunk); + } + return chunks + } + + fn split_c_chunk(&self, usym: &Vec) -> Vec> { + let mut chunks: Vec> = Vec::new(); + let mut chunk: Vec = Vec::new(); + for sym in usym.iter() { + if sym.name.contains("luaV_execute") { + chunk.push(sym.clone()); + chunks.push(chunk); + chunk = Vec::new(); + } else { + chunk.push(sym.clone()); + } + } + if chunk.len() > 0 { + chunks.push(chunk); + } + return chunks + } fn combine_stack(&self, skel: &ProfileSkel, frame: &StackFrame) -> Vec { - let mut lua_call_chunk: Vec = Vec::new(); let ksyms = self.syms_of_stack(0, &frame.kstack); let usyms = self.syms_of_stack(self.pid, &frame.ustack); - let mut frame_strs:Vec = ksyms.iter().map(|s| s.name.clone()).collect(); //split lua call chunk - let mut chunk: Vec = Vec::new(); - for addr in frame.lstack.iter() { - if addr & LUA_MASK == 0 { //c addr - lua_call_chunk.push(LuaCallChunk { c_addr: *addr, lua_frame: chunk }); - chunk = Vec::new(); - continue - } - let addrv = *addr & !LUA_MASK; - let file_id = addrv as u32; - let line = (addrv >> 32) as u32; - let str = format!("{}:{}", self.id_to_str(skel, file_id), line); - chunk.push(str); - } - if !chunk.is_empty() { - lua_call_chunk.push(LuaCallChunk { c_addr: 0, lua_frame: chunk }) - } - let mut buf: Vec = Vec::new(); - for sym in usyms.iter() { - buf.push(sym.name.clone()); - let mut match_index: usize = 0; - for (i, chunk) in lua_call_chunk.iter().enumerate() { - let match_c_addr = (sym.addr - sym.offset) == chunk.c_addr || - (chunk.c_addr == 0 && sym.name.contains("luaV_execute")); - if match_c_addr { - match_index = i+1; - break; - } - } - if match_index <= 0 { - continue; + let mut lua_chunks = self.split_lua_chunk(skel, frame); + let mut c_chunks = self.split_c_chunk(&usyms); + let mut frame_strs:Vec = ksyms.iter().map(|s| s.name.clone()).collect(); + for c_chunk in c_chunks.iter_mut() { + for i in 0..(c_chunk.len() - 1) { + frame_strs.push(c_chunk[i].name.clone()); } - //find luaV_execute - for name in buf.iter() { - if name.contains("luaV_execute") { - for chunk in lua_call_chunk.drain(0..match_index as usize).into_iter() { - for name in chunk.lua_frame.iter() { - frame_strs.push(name.clone()); - } + if lua_chunks.len() > 0 { //has lua stack left + let lua_chunk = lua_chunks.remove(0); + let lua_c_func: Vec = lua_chunk.iter().map( + |f| match f { + LuaFrame::Lua(_) => 0, + LuaFrame::C(addr) => *addr, } - } - frame_strs.push(name.clone()); + ).collect(); + let lua_c_syms = self.syms_of_stack(self.pid, &lua_c_func); + for (frame, sym) in lua_chunk.iter().zip(lua_c_syms) { + match frame { + LuaFrame::Lua(str) => { + frame_strs.push(str.clone()) + }, + LuaFrame::C(addr) => + if c_chunk.iter().find( + |&x| return x.addr - x.offset == *addr + ).is_none() { + frame_strs.push(sym.name.clone()) + } + } + }; } - buf.clear(); + //push c chunk + frame_strs.push(c_chunk.last().unwrap().name.clone()); } - frame_strs.extend(buf); - frame_strs + return frame_strs; } fn flame_entry(&self, skel: &ProfileSkel, frame: &StackFrame) -> String {