Skip to content

Commit

Permalink
refine merge c and lua callstack
Browse files Browse the repository at this point in the history
  • Loading branch information
findstr committed Oct 21, 2023
1 parent 591d8b9 commit 5c1e56d
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 69 deletions.
6 changes: 6 additions & 0 deletions src/bpf/lua.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef _LUA_H
#define _LUA_H

#include "lua54.h"

#endif
22 changes: 12 additions & 10 deletions src/bpf/lstate.h → src/bpf/lua54.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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

41 changes: 30 additions & 11 deletions src/bpf/profile.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_tracing.h>
#include "lstate.h"
#include "lua.h"

char LICENSE[] SEC("license") = "GPL";

Expand Down Expand Up @@ -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 {
Expand All @@ -58,6 +60,7 @@ struct c_stk_ctx {
};

struct lua_stk_ctx {
int calln;
int lua_index;
StkIdRel stack;
CallInfo *ci;
Expand Down Expand Up @@ -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++;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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;
Expand Down
128 changes: 80 additions & 48 deletions src/perf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,13 @@ struct StackFrame {
lstack: Vec<u64>,
}

struct LuaCallChunk {
c_addr: u64,
lua_frame: Vec<String>,
#[derive(Debug)]
enum LuaFrame {
Lua(String),
C(u64),
}

#[derive(Clone)]
struct SymInfo {
addr: u64,
offset: u64,
Expand Down Expand Up @@ -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(
Expand All @@ -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
Expand Down Expand Up @@ -639,59 +643,87 @@ impl Perf {
None => String::from("None"),
}
}
fn split_lua_chunk(&self, skel: &ProfileSkel, frame: &StackFrame) -> Vec<Vec<LuaFrame>> {
let mut chunks: Vec<Vec<LuaFrame>> = Vec::new();
let mut chunk: Vec<LuaFrame> = 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<SymInfo>) -> Vec<Vec<SymInfo>> {
let mut chunks: Vec<Vec<SymInfo>> = Vec::new();
let mut chunk: Vec<SymInfo> = 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<String> {
let mut lua_call_chunk: Vec<LuaCallChunk> = 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<String> = ksyms.iter().map(|s| s.name.clone()).collect();
//split lua call chunk
let mut chunk: Vec<String> = 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<String> = 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<String> = 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<u64> = 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 {
Expand Down

0 comments on commit 5c1e56d

Please sign in to comment.