diff --git a/agent/src/config/config.rs b/agent/src/config/config.rs index f4cd99e04df..cf7a4c4fe2f 100644 --- a/agent/src/config/config.rs +++ b/agent/src/config/config.rs @@ -1153,6 +1153,7 @@ pub struct EbpfProfileLanguages { pub python_disabled: bool, pub php_disabled: bool, pub nodejs_disabled: bool, + pub lua_disabled: bool, } impl Default for EbpfProfileLanguages { @@ -1161,6 +1162,7 @@ impl Default for EbpfProfileLanguages { python_disabled: false, php_disabled: false, nodejs_disabled: false, + lua_disabled: false, } } } diff --git a/agent/src/ebpf/kernel/perf_profiler.bpf.c b/agent/src/ebpf/kernel/perf_profiler.bpf.c index 803cdafed43..b48a9409bea 100644 --- a/agent/src/ebpf/kernel/perf_profiler.bpf.c +++ b/agent/src/ebpf/kernel/perf_profiler.bpf.c @@ -99,15 +99,17 @@ typedef struct { } stack_t; /* - * Stack map for interpreter stacks (Python, PHP, V8). + * Stack map for interpreter stacks (Python, PHP, V8, Lua). * Now uses stack_t to store frame_types and extra_data. * Due to the limitation of the number of eBPF instruction in kernel, this * feature is suitable for Linux5.2+ * * Map sizes are configured in user space program */ -MAP_HASH(custom_stack_map_a, __u32, stack_t, 1, FEATURE_FLAG_DWARF_UNWINDING) -MAP_HASH(custom_stack_map_b, __u32, stack_t, 1, FEATURE_FLAG_DWARF_UNWINDING) +MAP_HASH(custom_stack_map_a, __u32, stack_t, 1, + FEATURE_FLAG_DWARF_UNWINDING | FEATURE_FLAG_PROFILE_LUA) +MAP_HASH(custom_stack_map_b, __u32, stack_t, 1, + FEATURE_FLAG_DWARF_UNWINDING | FEATURE_FLAG_PROFILE_LUA) /* * The following maps are used for DWARF based unwinding @@ -312,7 +314,7 @@ MAP_HASH(lua_tstate_map, __u32, struct lua_state_cache_t, LUA_TSTATE_ENTRIES, FE /* Records which Lua runtime a process uses (key: tgid, value: LANG_* bitmask). * Memory: LUA_TSTATE_ENTRIES(65536) * (key 4B + value 4B + hash header) -> roughly sub‑MB. */ -MAP_HASH(lang_flags_map, __u32, __u32, LUA_TSTATE_ENTRIES, FEATURE_FLAG_PROFILE_ONCPU) +MAP_HASH(lua_lang_flags_map, __u32, __u32, LUA_TSTATE_ENTRIES, FEATURE_FLAG_PROFILE_ONCPU) /* Per-process Lua unwinding metadata (key: tgid, value: lua_unwind_info_t). * Memory: LUA_TSTATE_ENTRIES(65536) * (key 4B + value 16B + hash header) -> roughly low MB. */ @@ -909,7 +911,7 @@ PERF_EVENT_PROG(oncpu_profile) (struct bpf_perf_event_data * ctx) { pre_python_unwind(ctx, state, &oncpu_maps, PROG_PYTHON_UNWIND_PE_IDX); } - __u32 *flags = lang_flags_map__lookup(&key->tgid); + __u32 *flags = lua_lang_flags_map__lookup(&key->tgid); if (flags && (*flags & (LANG_LUA | LANG_LUAJIT))) { state->lua_is_jit = (*flags & LANG_LUAJIT) ? 1 : 0; diff --git a/agent/src/ebpf/mod.rs b/agent/src/ebpf/mod.rs index f70df95393a..f7baed017a7 100644 --- a/agent/src/ebpf/mod.rs +++ b/agent/src/ebpf/mod.rs @@ -128,6 +128,8 @@ pub const FEATURE_PROFILE_PYTHON: c_int = 9; pub const FEATURE_PROFILE_PHP: c_int = 10; #[allow(dead_code)] pub const FEATURE_PROFILE_V8: c_int = 11; +#[allow(dead_code)] +pub const FEATURE_PROFILE_LUA: c_int = 12; //追踪器当前状态 #[allow(dead_code)] diff --git a/agent/src/ebpf/user/config.h b/agent/src/ebpf/user/config.h index 3c4eb476cc3..8128fe518fd 100644 --- a/agent/src/ebpf/user/config.h +++ b/agent/src/ebpf/user/config.h @@ -89,7 +89,7 @@ #define PROG_ONCPU_OUTPUT_FOR_PE "df_PE_oncpu_output" // lua related maps -#define MAP_LUA_LANG_FLAGS_NAME "__lang_flags_map" +#define MAP_LUA_LANG_FLAGS_NAME "__lua_lang_flags_map" #define MAP_LUA_UNWIND_INFO_NAME "__lua_unwind_info_map" #define MAP_LUA_OFFSETS_NAME "__lua_offsets_map" #define MAP_LUAJIT_OFFSETS_NAME "__luajit_offsets_map" @@ -170,6 +170,7 @@ enum cfg_feature_idx { FEATURE_PROFILE_PYTHON, FEATURE_PROFILE_PHP, FEATURE_PROFILE_V8, + FEATURE_PROFILE_LUA, FEATURE_MAX, }; @@ -184,6 +185,7 @@ enum cfg_feature_idx { #define FEATURE_FLAG_PROFILE_PYTHON (1 << FEATURE_PROFILE_PYTHON) #define FEATURE_FLAG_PROFILE_PHP (1 << FEATURE_PROFILE_PHP) #define FEATURE_FLAG_PROFILE_V8 (1 << FEATURE_PROFILE_V8) +#define FEATURE_FLAG_PROFILE_LUA (1 << FEATURE_PROFILE_LUA) #define FEATURE_FLAG_PROFILE (FEATURE_FLAG_PROFILE_ONCPU | FEATURE_FLAG_PROFILE_OFFCPU | FEATURE_FLAG_PROFILE_MEMORY) diff --git a/agent/src/ebpf/user/load.c b/agent/src/ebpf/user/load.c index 36570f51722..7dbb2e060e3 100644 --- a/agent/src/ebpf/user/load.c +++ b/agent/src/ebpf/user/load.c @@ -970,6 +970,9 @@ int ebpf_obj_load(struct ebpf_object *obj) if (!python_profiler_enabled()) { enabled_feats &= ~FEATURE_FLAG_PROFILE_PYTHON; } + if (!lua_profiler_enabled()) { + enabled_feats &= ~FEATURE_FLAG_PROFILE_LUA; + } enabled_feats &= ~extended_feature_flags(map); if (enabled_feats == 0 && map->def.type != BPF_MAP_TYPE_PROG_ARRAY && @@ -991,7 +994,8 @@ int ebpf_obj_load(struct ebpf_object *obj) } // Log language profiler map creation with max_entries for verification if (strstr(map->name, "php_") || strstr(map->name, "v8_") || - strstr(map->name, "python_")) { + strstr(map->name, "python_") || strstr(map->name, "lua_") || + strstr(map->name, "luajit_")) { ebpf_info ("Language profiler map created: name=%s, max_entries=%d (1 means disabled)\n", map->name, map->def.max_entries); diff --git a/agent/src/ebpf/user/profile/perf_profiler.c b/agent/src/ebpf/user/profile/perf_profiler.c index db47a3fafc3..c34a6f5daa0 100644 --- a/agent/src/ebpf/user/profile/perf_profiler.c +++ b/agent/src/ebpf/user/profile/perf_profiler.c @@ -327,7 +327,11 @@ static int create_profiler(struct bpf_tracer *tracer) if ((ret = maps_config(tracer, MAP_STACK_B_NAME, cap))) return ret; - if (get_dwarf_enabled() && (major > 5 || (major == 5 && minor >= 2))) { + bool kernel_supports_custom_stack_maps = + (major > 5 || (major == 5 && minor >= 2)); + bool need_custom_stack_maps = + get_dwarf_enabled() || lua_profiler_enabled(); + if (kernel_supports_custom_stack_maps && need_custom_stack_maps) { if ((ret = maps_config(tracer, MAP_CUSTOM_STACK_A_NAME, cap))) { return ret; } @@ -335,7 +339,9 @@ static int create_profiler(struct bpf_tracer *tracer) if ((ret = maps_config(tracer, MAP_CUSTOM_STACK_B_NAME, cap))) { return ret; } + } + if (get_dwarf_enabled() && kernel_supports_custom_stack_maps) { if ((ret = maps_config(tracer, MAP_PROCESS_SHARD_LIST_NAME, get_dwarf_process_map_size()))) { diff --git a/agent/src/ebpf/user/tracer.c b/agent/src/ebpf/user/tracer.c index e696c6acf6e..680a08098b0 100644 --- a/agent/src/ebpf/user/tracer.c +++ b/agent/src/ebpf/user/tracer.c @@ -2018,6 +2018,11 @@ bool python_profiler_enabled(void) return is_feature_regex_set(FEATURE_PROFILE_PYTHON); } +bool lua_profiler_enabled(void) +{ + return is_feature_regex_set(FEATURE_PROFILE_LUA); +} + static void init_thread_ids(void) { thread_ids.entries = NULL; diff --git a/agent/src/ebpf/user/tracer.h b/agent/src/ebpf/user/tracer.h index d341e9cf10e..202f114837f 100644 --- a/agent/src/ebpf/user/tracer.h +++ b/agent/src/ebpf/user/tracer.h @@ -627,6 +627,7 @@ bool is_feature_regex_set(int feature); bool php_profiler_enabled(void); bool v8_profiler_enabled(void); bool python_profiler_enabled(void); +bool lua_profiler_enabled(void); int bpf_tracer_init(const char *log_file, bool is_stdout); int tracer_bpf_load(struct bpf_tracer *tracer); int tracer_probes_init(struct bpf_tracer *tracer); diff --git a/agent/src/ebpf/user/unwind_tracer.c b/agent/src/ebpf/user/unwind_tracer.c index b7bab0c1d4e..69225ba158b 100644 --- a/agent/src/ebpf/user/unwind_tracer.c +++ b/agent/src/ebpf/user/unwind_tracer.c @@ -307,32 +307,34 @@ int unwind_tracer_init(struct bpf_tracer *tracer) { pthread_mutex_unlock(&g_v8_unwind_table_lock); } - int lua_lang_fd = bpf_table_get_fd(tracer, MAP_LUA_LANG_FLAGS_NAME); - int lua_unwind_info_fd = bpf_table_get_fd(tracer, MAP_LUA_UNWIND_INFO_NAME); - int lua_offsets_fd = bpf_table_get_fd(tracer, MAP_LUA_OFFSETS_NAME); - int luajit_offsets_fd = bpf_table_get_fd(tracer, MAP_LUAJIT_OFFSETS_NAME); - - if (lua_lang_fd < 0 || lua_unwind_info_fd < 0 || lua_offsets_fd < 0 || luajit_offsets_fd < 0) { - ebpf_warning("Failed to get lua profiling map fds (lang:%d unwind:%d lua_ofs:%d lj_ofs:%d)\n", - lua_lang_fd, lua_unwind_info_fd, lua_offsets_fd, luajit_offsets_fd); - return -1; - } + if (lua_profiler_enabled()) { + int lua_lang_fd = bpf_table_get_fd(tracer, MAP_LUA_LANG_FLAGS_NAME); + int lua_unwind_info_fd = bpf_table_get_fd(tracer, MAP_LUA_UNWIND_INFO_NAME); + int lua_offsets_fd = bpf_table_get_fd(tracer, MAP_LUA_OFFSETS_NAME); + int luajit_offsets_fd = bpf_table_get_fd(tracer, MAP_LUAJIT_OFFSETS_NAME); + + if (lua_lang_fd < 0 || lua_unwind_info_fd < 0 || lua_offsets_fd < 0 || luajit_offsets_fd < 0) { + ebpf_warning("Failed to get lua profiling map fds (lang:%d unwind:%d lua_ofs:%d lj_ofs:%d)\n", + lua_lang_fd, lua_unwind_info_fd, lua_offsets_fd, luajit_offsets_fd); + return -1; + } - lua_unwind_table_t *lua_table = - lua_unwind_table_create(lua_lang_fd, lua_unwind_info_fd, lua_offsets_fd, luajit_offsets_fd); - if (lua_table == NULL) { - ebpf_warning("Failed to create lua unwind table\n"); - return -1; - } - lua_set_map_fds(lua_lang_fd, lua_unwind_info_fd, lua_offsets_fd, luajit_offsets_fd); + lua_unwind_table_t *lua_table = + lua_unwind_table_create(lua_lang_fd, lua_unwind_info_fd, lua_offsets_fd, luajit_offsets_fd); + if (lua_table == NULL) { + ebpf_warning("Failed to create lua unwind table\n"); + return -1; + } + lua_set_map_fds(lua_lang_fd, lua_unwind_info_fd, lua_offsets_fd, luajit_offsets_fd); - pthread_mutex_lock(&g_lua_unwind_table_lock); - g_lua_unwind_table = lua_table; - pthread_mutex_unlock(&g_lua_unwind_table_lock); + pthread_mutex_lock(&g_lua_unwind_table_lock); + g_lua_unwind_table = lua_table; + pthread_mutex_unlock(&g_lua_unwind_table_lock); - if (dwarf_available() && get_dwarf_enabled() && tracer) { - lua_queue_existing_processes(tracer); - } + if (dwarf_available() && tracer) { + lua_queue_existing_processes(tracer); + } + } return 0; } @@ -422,7 +424,7 @@ static void lua_queue_existing_processes(struct bpf_tracer *tracer) { DIR *dir = NULL; struct dirent *entry = NULL; - if (tracer == NULL || tracer->state != TRACER_RUNNING) { + if (tracer == NULL) { return; } @@ -529,7 +531,11 @@ void unwind_tracer_drop() { } void unwind_process_exec(int pid) { - if (!dwarf_available() || !get_dwarf_enabled()) { + if (!dwarf_available()) { + return; + } + + if (!get_dwarf_enabled() && !lua_profiler_enabled()) { return; } @@ -543,7 +549,11 @@ void unwind_process_exec(int pid) { // Process events in the queue void unwind_events_handle(void) { - if (!dwarf_available() || !get_dwarf_enabled()) { + if (!dwarf_available()) { + return; + } + + if (!get_dwarf_enabled() && !lua_profiler_enabled()) { return; } @@ -565,7 +575,7 @@ void unwind_events_handle(void) { } tracer = event->tracer; - if (tracer && python_profiler_enabled() && is_python_process(event->pid)) { + if (tracer && get_dwarf_enabled() && python_profiler_enabled() && is_python_process(event->pid)) { python_unwind_table_load(g_python_unwind_table, event->pid); pthread_mutex_lock(&tracer->mutex_probes_lock); python_parse_and_register(event->pid, tracer->tps); @@ -574,17 +584,17 @@ void unwind_events_handle(void) { pthread_mutex_unlock(&tracer->mutex_probes_lock); } - if (tracer && php_profiler_enabled() && is_php_process(event->pid)) { + if (tracer && get_dwarf_enabled() && php_profiler_enabled() && is_php_process(event->pid)) { php_unwind_table_load(g_php_unwind_table, event->pid); // Note: PHP profiling doesn't require uprobe registration like Python } - if (tracer && v8_profiler_enabled() && is_v8_process(event->pid)) { + if (tracer && get_dwarf_enabled() && v8_profiler_enabled() && is_v8_process(event->pid)) { v8_unwind_table_load(g_v8_unwind_table, event->pid); // Note: V8 profiling doesn't require uprobe registration like Python } - if (tracer && is_lua_process(event->pid)) { + if (tracer && lua_profiler_enabled() && is_lua_process(event->pid)) { if (g_lua_unwind_table) { lua_unwind_table_load(g_lua_unwind_table, event->pid); } @@ -612,7 +622,11 @@ void unwind_events_handle(void) { // Process exit, reclaim resources void unwind_process_exit(int pid) { - if (!dwarf_available() || !get_dwarf_enabled()) { + if (!dwarf_available()) { + return; + } + + if (!get_dwarf_enabled() && !lua_profiler_enabled()) { return; } diff --git a/agent/src/ebpf_dispatcher.rs b/agent/src/ebpf_dispatcher.rs index 382cd5f06b3..a1e6fa233c5 100644 --- a/agent/src/ebpf_dispatcher.rs +++ b/agent/src/ebpf_dispatcher.rs @@ -1071,6 +1071,12 @@ impl EbpfCollector { CString::new(".*").unwrap().as_c_str().as_ptr(), ); } + if !languages.lua_disabled { + ebpf::set_feature_regex( + ebpf::FEATURE_PROFILE_LUA, + CString::new(".*").unwrap().as_c_str().as_ptr(), + ); + } #[cfg(feature = "extended_observability")] { diff --git a/server/agent_config/README-CH.md b/server/agent_config/README-CH.md index 7ffc6c3108d..1a6147ec727 100644 --- a/server/agent_config/README-CH.md +++ b/server/agent_config/README-CH.md @@ -5026,8 +5026,76 @@ inputs: **详细描述**: -禁用 Node.js(V8)解释器剖析。禁用后将不采集 Node.js 进程的函数调用栈, -可节省约 6.4 MB 内核内存(v8_unwind_info_map)。 +禁用 Node.js (V8 引擎) 剖析功能。禁用后将不会采集 Node.js 进程的函数调用栈,可节省约 6.4 MB 的内核内存。 + +此配置项控制以下 eBPF map 的创建: +- `v8_unwind_info_map`:存储进程级 unwinding 信息,包括 V8 内部结构偏移量 (~6.4 MB) + +适用场景: +- 环境中确定不运行 Node.js 应用 +- 仅关注其他语言的性能分析 +- 内存受限的环境需要优化资源使用 + +**重要提示**:修改此配置将自动触发 deepflow-agent 重启,因为 eBPF maps 无法在运行时动态创建或销毁。 + +##### 禁用 Lua 剖析 {#inputs.ebpf.profile.languages.lua_disabled} + +**标签**: + +agent_restart + +**FQCN**: + +`inputs.ebpf.profile.languages.lua_disabled` + +**默认值**: +```yaml +inputs: + ebpf: + profile: + languages: + lua_disabled: false +``` + +**模式**: +| Key | Value | +| ---- | ---------------------------- | +| Type | bool | + +**详细描述**: + +禁用 Lua 解释器剖析功能。禁用后将不会采集 Lua 进程的函数调用栈,可节省约 13 MB 的内核内存。 + +此配置项控制以下 eBPF maps 的创建: +- `lua_tstate_map`:缓存每线程 lua_State 栈(按线程,容量较大,约 7 MB) +- `lua_lang_flags_map`:记录进程 Lua/LuaJIT 类型标记(约 2.5 MB) +- `lua_unwind_info_map`:存储进程级 unwinding 元信息(约 3 MB) +- `lua_offsets_map`、`luajit_offsets_map`:存储 Lua/LuaJIT 结构偏移表(总计 < 2 KB) + +适用场景: +- 环境中确定不运行 Lua/LuaJIT 应用(如 OpenResty/nginx-lua) +- 仅关注其他语言的性能分析 +- 内存受限的环境需要优化资源使用 + +**重要提示**:修改此配置将自动触发 deepflow-agent 重启,因为 eBPF maps 无法在运行时动态创建或销毁。 + +**内存节省效果总结**: + +| 配置方式 | Python | PHP | Node.js | Lua | 总计内存占用 | 节省内存 | +| -------- | ------ | --- | ------- | --- | ----------- | -------- | +| 全部启用(默认) | 6.1 MB | 5.2 MB | 6.4 MB | ~13 MB | ~30-33 MB | 0 MB | +| 仅 Python | 6.1 MB | 0 MB | 0 MB | 0 MB | ~6.1 MB | ~24-27 MB | +| 仅 PHP | 0 MB | 5.2 MB | 0 MB | 0 MB | ~5.2 MB | ~25-28 MB | +| 仅 Node.js | 0 MB | 0 MB | 6.4 MB | 0 MB | ~6.4 MB | ~23-26 MB | +| 仅 Lua | 0 MB | 0 MB | 0 MB | ~13 MB | ~13 MB | ~17-20 MB | +| 全部禁用 | 0 MB | 0 MB | 0 MB | 0 MB | ~0 MB | ~30-33 MB | + +**注意事项**: +- 修改语言开关需要重启 deepflow-agent 才能生效 +- eBPF maps 使用预分配机制,空载和满载占用相同的内核内存 +- 禁用时,对应语言的 eBPF maps 会被创建但 max_entries 设为 1(最小化内存占用) +- 禁用时,不会创建对应语言的 unwind table,也不会加载进程的 unwinding 信息 +- 禁用不需要的语言除了节省内存,还能减少 CPU 开销 ### 调优 {#inputs.ebpf.tunning} diff --git a/server/agent_config/README.md b/server/agent_config/README.md index 57865a50364..143f514f655 100644 --- a/server/agent_config/README.md +++ b/server/agent_config/README.md @@ -5159,6 +5159,53 @@ inputs: Disable Node.js (V8) interpreter profiling. When disabled, Node.js process stack traces will not be collected, saving approximately 6.4 MB of kernel memory (v8_unwind_info_map). +**Important**: Changing this configuration will automatically trigger deepflow-agent restart, as eBPF maps cannot be dynamically created or destroyed at runtime. + +##### Lua profiling disabled {#inputs.ebpf.profile.languages.lua_disabled} + +**Tags**: + +agent_restart + +**FQCN**: + +`inputs.ebpf.profile.languages.lua_disabled` + +**Default value**: +```yaml +inputs: + ebpf: + profile: + languages: + lua_disabled: false +``` + +**Schema**: +| Key | Value | +| ---- | ---------------------------- | +| Type | bool | + +**Description**: + +Disable Lua interpreter profiling. When disabled, Lua process stack traces will not be collected, saving approximately 13 MB of kernel memory (lua_tstate_map, lua_lang_flags_map, lua_unwind_info_map, lua_offsets_map, luajit_offsets_map). + +**Important**: Changing this configuration will automatically trigger deepflow-agent restart, as eBPF maps cannot be dynamically created or destroyed at runtime. + +**Memory saving summary**: +- All enabled (default): ~30-33 MB +- Python only: ~6.1 MB (saves ~24-27 MB) +- PHP only: ~5.2 MB (saves ~25-28 MB) +- Node.js only: ~6.4 MB (saves ~23-26 MB) +- Lua only: ~13 MB (saves ~17-20 MB) +- All disabled: ~0 MB (saves ~30-33 MB) + +**Notes**: +- Changing language switches requires deepflow-agent restart +- eBPF maps use pre-allocation mechanism (same memory usage whether empty or full) +- When disabled, language-specific eBPF maps are created with max_entries=1 (minimized memory) +- When disabled, unwind tables are not created and process unwinding info is not loaded +- Disabling unused languages saves memory and reduces CPU overhead + ### Tunning {#inputs.ebpf.tunning} #### Collector Queue Size {#inputs.ebpf.tunning.collector_queue_size} diff --git a/server/agent_config/template.yaml b/server/agent_config/template.yaml index 65dff610bb7..f12a000e785 100644 --- a/server/agent_config/template.yaml +++ b/server/agent_config/template.yaml @@ -3757,6 +3757,23 @@ inputs: # 禁用 Node.js(V8)解释器剖析。禁用后将不采集 Node.js 进程的函数调用栈, # 可节省约 6.4 MB 内核内存(v8_unwind_info_map)。 nodejs_disabled: false + # type: bool + # name: + # en: Lua profiling disabled + # ch: 禁用 Lua 剖析 + # unit: + # range: [] + # enum_options: [] + # modification: agent_restart + # ee_feature: false + # description: + # en: |- + # Disable Lua interpreter profiling. When disabled, Lua process stack traces will not be collected, + # saving approximately 13 MB of kernel memory (lua_tstate_map, lua_lang_flags_map, lua_unwind_info_map, lua_offsets_map, luajit_offsets_map). + # ch: |- + # 禁用 Lua 解释器剖析。禁用后将不采集 Lua 进程的函数调用栈, + # 可节省约 13 MB 内核内存(lua_tstate_map、lua_lang_flags_map、lua_unwind_info_map、lua_offsets_map、luajit_offsets_map)。 + lua_disabled: false # type: section # name: # en: Tunning