Skip to content

Commit 0789e0e

Browse files
feat: migrate interpreter symbolization from agent to server
Move interpreter frame formatting (PHP/V8/Python/Lua) from agent-side string generation to server-side structured symbolization. Agent changes: - Send structured interp_symbol_info_t via protobuf InterpreterStack - Remove agent-side symbol formatting (fold_interpreter_frames_*) - Add RawInterpreterData/NativeStackTrace fields to Profile message - Agent C code extracts raw symbols, Rust converts to protobuf Server changes: - Add symbolizer stub package (enterprise provides full implementation) - decoder.go detects RawInterpreterData flag, calls symbolizer.Symbolize() - Backward compatible: old agents still send pre-formatted Data field Protobuf changes: - Add InterpreterFrameSymbol, InterpreterStack messages to metric.proto - Add interpreter_stack, native_stack_trace, raw_interpreter_data fields Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1956e66 commit 0789e0e

15 files changed

Lines changed: 391 additions & 418 deletions

File tree

agent/crates/trace-utils/src/lib.rs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -575,15 +575,6 @@ extern "C" {
575575
pub fn is_v8_process(pid: u32) -> bool;
576576

577577
pub fn lua_detect(pid: u32, out: *mut LuaRuntimeInfo) -> i32;
578-
pub fn lua_format_folded_stack_trace(
579-
tracer: *mut libc::c_void,
580-
pid: u32,
581-
frames: *const u64,
582-
frame_count: u32,
583-
new_cache: bool,
584-
info_p: *mut libc::c_void,
585-
err_tag: *const libc::c_char,
586-
) -> *mut libc::c_char;
587578
pub fn lua_set_map_fds(
588579
lang_flags_fd: i32,
589580
unwind_info_fd: i32,
@@ -622,31 +613,6 @@ extern "C" {
622613
pub fn v8_unwind_table_load(table: *mut V8UnwindTable, pid: u32);
623614
pub fn v8_unwind_table_unload(table: *mut V8UnwindTable, pid: u32);
624615

625-
pub fn merge_lua_stacks(
626-
trace_str: *mut libc::c_void,
627-
len: usize,
628-
u_trace: *const libc::c_void,
629-
i_trace: *const libc::c_void,
630-
) -> usize;
631-
pub fn merge_python_stacks(
632-
trace_str: *mut libc::c_void,
633-
len: usize,
634-
i_trace: *const libc::c_void,
635-
u_trace: *const libc::c_void,
636-
) -> usize;
637-
pub fn merge_php_stacks(
638-
trace_str: *mut libc::c_void,
639-
len: usize,
640-
i_trace: *const libc::c_void,
641-
u_trace: *const libc::c_void,
642-
) -> usize;
643-
pub fn merge_v8_stacks(
644-
trace_str: *mut libc::c_void,
645-
len: usize,
646-
i_trace: *const libc::c_void,
647-
u_trace: *const libc::c_void,
648-
) -> usize;
649-
650616
pub fn resolve_php_frame(
651617
pid: u32,
652618
zend_function_ptr: u64,

agent/crates/trace-utils/src/trace_utils.h

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -608,16 +608,6 @@ extern bool is_v8_process(uint32_t pid);
608608
extern int32_t lua_detect(uint32_t pid, lua_runtime_info_t *out);
609609
#endif
610610

611-
#if defined(DF_ENTERPRISE)
612-
extern char *lua_format_folded_stack_trace(void *tracer,
613-
uint32_t pid,
614-
const uint64_t *frames,
615-
uint32_t frame_count,
616-
bool new_cache,
617-
void *info_p,
618-
const char *err_tag);
619-
#endif
620-
621611
#if defined(DF_ENTERPRISE)
622612
extern void lua_set_map_fds(int32_t lang_flags_fd,
623613
int32_t unwind_info_fd,
@@ -644,34 +634,6 @@ extern void lua_unwind_table_load(lua_unwind_table_t *table, uint32_t pid);
644634
extern void lua_unwind_table_unload(lua_unwind_table_t *table, uint32_t pid);
645635
#endif
646636

647-
#if defined(DF_ENTERPRISE)
648-
extern size_t merge_lua_stacks(void *trace_str,
649-
size_t len,
650-
const void *u_trace,
651-
const void *i_trace);
652-
#endif
653-
654-
#if defined(DF_ENTERPRISE)
655-
extern size_t merge_php_stacks(void *trace_str,
656-
size_t len,
657-
const void *i_trace,
658-
const void *u_trace);
659-
#endif
660-
661-
#if defined(DF_ENTERPRISE)
662-
extern size_t merge_python_stacks(void *trace_str,
663-
size_t len,
664-
const void *i_trace,
665-
const void *u_trace);
666-
#endif
667-
668-
#if defined(DF_ENTERPRISE)
669-
extern size_t merge_v8_stacks(void *trace_str,
670-
size_t len,
671-
const void *i_trace,
672-
const void *u_trace);
673-
#endif
674-
675637
#if defined(DF_ENTERPRISE)
676638
extern php_unwind_table_t *php_unwind_table_create(int32_t unwind_info_map_fd,
677639
int32_t offsets_map_fd);

agent/src/ebpf/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,9 @@ pub struct stack_profile_data {
544544
pub comm: [u8; PACKET_KNAME_MAX_PADDING + 1],
545545
pub process_name: [u8; PACKET_KNAME_MAX_PADDING + 1], // process name
546546
pub container_id: [u8; CONTAINER_ID_SIZE], // container id
547+
pub interp_frame_count: u32, // number of structured interpreter frames
548+
pub interp_frames_ptr: u64, // pointer to CSymbolInfo array
549+
pub raw_interpreter_data: u8, // 1 = has structured interpreter data
547550
pub stack_data_len: u32, // stack data length
548551

549552
/*
@@ -558,6 +561,22 @@ pub struct stack_profile_data {
558561
pub stack_data: *mut c_char,
559562
}
560563

564+
/// C-compatible interpreter frame info struct.
565+
/// Must match the C `interp_symbol_info_t` layout defined in extended.h.
566+
#[repr(C)]
567+
#[derive(Debug, Copy, Clone)]
568+
pub struct CInterpreterFrameInfo {
569+
pub frame_type: u32,
570+
pub function_name: *mut c_char,
571+
pub class_name: *mut c_char,
572+
pub lineno: u32,
573+
pub file_name: *mut c_char,
574+
pub sub_type: u32,
575+
pub is_jit: u8,
576+
pub raw_addr: u64,
577+
pub resolve_failed: u8,
578+
}
579+
561580
extern "C" {
562581
/*
563582
* Set maximum amount of data passed to the agent by eBPF program.

agent/src/ebpf/user/extended/extended.c

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "../tracer.h"
2727
#include "../socket.h"
2828
#include "../proc.h"
29+
#include "extended.h"
2930

3031
int __attribute__ ((weak)) extended_reader_create(struct bpf_tracer *tracer)
3132
{
@@ -91,19 +92,31 @@ int __attribute__ ((weak)) print_extra_pkt_info(bool datadump_enable,
9192
return 0;
9293
}
9394

94-
char * __attribute__ ((weak)) extended_resolve_frame(int pid, u64 addr, u8 frame_type, u64 extra_a, u64 extra_b)
95+
int __attribute__ ((weak)) extended_extract_interpreter_frames(int pid,
96+
const u8 *frame_types,
97+
const u64 *addrs,
98+
const u64 *extra_data_a,
99+
const u64 *extra_data_b,
100+
int frame_count,
101+
void *tracer,
102+
bool new_cache,
103+
void *info_p,
104+
interp_symbol_info_t *out_frames,
105+
int max_out)
95106
{
96-
return NULL;
107+
return 0;
97108
}
98109

99-
int __attribute__ ((weak)) extended_merge_stacks(char *dst, int len, const char *i_trace, const char *u_trace, int pid)
110+
void __attribute__ ((weak)) extended_free_interp_frames(interp_symbol_info_t *frames, int count)
100111
{
101-
return 0;
102112
}
103113

104-
char * __attribute__ ((weak)) extended_format_lua_stack(void *tracer, int pid, int stack_id,
105-
const char *stack_map_name, void *h,
106-
bool new_cache, void *info_p)
114+
int __attribute__ ((weak)) extended_extract_structured_frames(void *tracer, int tgid,
115+
int user_stack_id, int interp_stack_id,
116+
const char *custom_stack_map_name,
117+
bool new_cache, void *info_p,
118+
interp_symbol_info_t *out_frames,
119+
int max_out)
107120
{
108-
return NULL;
121+
return 0;
109122
}

agent/src/ebpf/user/extended/extended.h

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -110,40 +110,80 @@ int print_extra_pkt_info(bool datadump_enable, const char *pkt_data, int len,
110110
char *buf, int buf_len, u8 direction);
111111

112112
/**
113-
* @brief **extended_resolve_frame()** Resolve a custom/interpreter frame
114-
* @param pid Process ID
115-
* @param addr Frame address/ID
116-
* @param frame_type Frame type identifier
117-
* @param extra_a Extra data A from stack map
118-
* @param extra_b Extra data B from stack map
119-
* @return Resolved symbol string (must be freed) or NULL
113+
* @brief Structured interpreter symbol info for per-frame extraction.
114+
* Matches the Rust CSymbolInfo layout (#[repr(C)]).
120115
*/
121-
char *extended_resolve_frame(int pid, u64 addr, u8 frame_type, u64 extra_a, u64 extra_b);
116+
#ifndef INTERP_SYMBOL_INFO_DEFINED
117+
#define INTERP_SYMBOL_INFO_DEFINED
118+
typedef struct {
119+
u32 frame_type; // FRAME_TYPE_PHP/V8/LUA/PYTHON
120+
char *function_name; // allocated via clib_mem_alloc
121+
char *class_name; // allocated via clib_mem_alloc (or NULL)
122+
u32 lineno;
123+
char *file_name; // allocated via clib_mem_alloc (or NULL)
124+
u32 sub_type; // language-specific sub-type
125+
u8 is_jit; // 1 = JIT-compiled frame
126+
u64 raw_addr; // original address
127+
u8 resolve_failed; // 1 = resolution failed
128+
} interp_symbol_info_t;
129+
#endif
122130

123131
/**
124-
* @brief **extended_merge_stacks()** Merge interpreter and user stacks
125-
* @param dst Destination buffer
126-
* @param len Buffer length
127-
* @param i_trace Interpreter stack string
128-
* @param u_trace User stack string
132+
* @brief **extended_extract_interpreter_frames()** Extract structured interpreter frames
129133
* @param pid Process ID
130-
* @return Bytes written
134+
* @param frame_types Array of frame types from BPF map
135+
* @param addrs Array of frame addresses
136+
* @param extra_data_a Array of extra data A values
137+
* @param extra_data_b Array of extra data B values
138+
* @param frame_count Number of frames in arrays
139+
* @param tracer BPF tracer handle (for Lua)
140+
* @param new_cache Whether this is a new cache entry (for Lua)
141+
* @param info_p Process info pointer (for Lua)
142+
* @param out_frames Output array of interp_symbol_info_t (caller-allocated)
143+
* @param max_out Maximum output frames
144+
* @return Number of frames written to out_frames
145+
*/
146+
int extended_extract_interpreter_frames(int pid,
147+
const u8 *frame_types,
148+
const u64 *addrs,
149+
const u64 *extra_data_a,
150+
const u64 *extra_data_b,
151+
int frame_count,
152+
void *tracer,
153+
bool new_cache,
154+
void *info_p,
155+
interp_symbol_info_t *out_frames,
156+
int max_out);
157+
158+
/**
159+
* @brief **extended_free_interp_frames()** Free memory owned by interp_symbol_info_t array
160+
* @param frames Array of interp_symbol_info_t
161+
* @param count Number of entries
131162
*/
132-
int extended_merge_stacks(char *dst, int len, const char *i_trace, const char *u_trace, int pid);
163+
void extended_free_interp_frames(interp_symbol_info_t *frames, int count);
133164

134165
/**
135-
* @brief **extended_format_lua_stack()** Format Lua interpreter stack frames
166+
* @brief **extended_extract_structured_frames()** High-level extraction for a stack trace
167+
*
168+
* Reads user and interpreter BPF stack maps, extracts structured interpreter
169+
* frame symbols via per-symbol cache + extract functions.
170+
*
136171
* @param tracer BPF tracer handle
137-
* @param pid Process ID
138-
* @param stack_id Interpreter stack ID from BPF map
139-
* @param stack_map_name Name of the stack map
140-
* @param h Stack string hash table
141-
* @param new_cache Whether to create new cache entry
172+
* @param tgid Process ID (for cache lookup and process type detection)
173+
* @param user_stack_id User stack ID from BPF map (-1 if none)
174+
* @param interp_stack_id Interpreter stack ID from BPF map (-1 if none)
175+
* @param custom_stack_map_name Name of the custom stack map
176+
* @param new_cache Whether cache entry is new
142177
* @param info_p Process info pointer
143-
* @return Formatted stack string (caller must free) or NULL
178+
* @param out_frames Caller-allocated output array
179+
* @param max_out Maximum output frame count
180+
* @return Number of frames written to out_frames
144181
*/
145-
char *extended_format_lua_stack(void *tracer, int pid, int stack_id,
146-
const char *stack_map_name, void *h,
147-
bool new_cache, void *info_p);
182+
int extended_extract_structured_frames(void *tracer, int tgid,
183+
int user_stack_id, int interp_stack_id,
184+
const char *custom_stack_map_name,
185+
bool new_cache, void *info_p,
186+
interp_symbol_info_t *out_frames,
187+
int max_out);
148188

149189
#endif /* DF_EXTENDED_H */

agent/src/ebpf/user/profile/perf_profiler.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -257,14 +257,8 @@ static void oncpu_reader_work(void *arg)
257257
exit:
258258
print_cp_tracer_status();
259259

260-
print_hash_stack_str(&oncpu_ctx.stack_str_hash);
261-
/* free stack_str_hash */
262-
if (likely(oncpu_ctx.stack_str_hash.buckets != NULL)) {
263-
release_stack_str_hash(&oncpu_ctx.stack_str_hash);
264-
}
265-
266260
print_hash_stack_trace_msg(&oncpu_ctx.msg_hash);
267-
/* free stack_str_hash */
261+
/* free msg_hash */
268262
if (likely(oncpu_ctx.msg_hash.buckets != NULL)) {
269263
/* Ensure that all elements are released properly/cleanly */
270264
push_and_release_stack_trace_msg(&oncpu_ctx,

agent/src/ebpf/user/profile/perf_profiler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ typedef struct {
186186
u8 comm[TASK_COMM_LEN];
187187
u8 process_name[TASK_COMM_LEN];
188188
u8 container_id[CONTAINER_ID_SIZE];
189+
u32 interp_frame_count; // number of structured interpreter frames
190+
u64 interp_frames_ptr; // pointer to interp_symbol_info_t array
191+
u8 raw_interpreter_data; // 1 = has structured interpreter data
189192
u32 data_len;
190193
u64 data_ptr;
191194
u8 data[0];

0 commit comments

Comments
 (0)