diff --git a/agent/src/ebpf/kernel/include/perf_profiler.h b/agent/src/ebpf/kernel/include/perf_profiler.h index 366e864dd88..ac8bf25a90d 100644 --- a/agent/src/ebpf/kernel/include/perf_profiler.h +++ b/agent/src/ebpf/kernel/include/perf_profiler.h @@ -41,6 +41,14 @@ typedef enum { PROFILER_CNT } profiler_idx; +#define JAVA_SYMBOL_MAX_LENGTH 128 +#define MAP_MEMORY_JAVA_SYMBOL_MAP_NAME "__memory_java_symbol_map" + +struct java_symbol_map_key { + __u32 tgid; + __u64 class_id; +}; + struct stack_trace_key_t { __u32 pid; // processID or threadID __u32 tgid; // processID @@ -56,7 +64,7 @@ struct stack_trace_key_t { } off_cpu; struct { __u64 size; - char class_name[32]; // TODO: change to a symbol id + __u64 class_id; // Use symbol address as class_id } memory; } ext_data; }; diff --git a/agent/src/ebpf/user/profile/profile_common.c b/agent/src/ebpf/user/profile/profile_common.c index 68e55dbba40..189729aacab 100644 --- a/agent/src/ebpf/user/profile/profile_common.c +++ b/agent/src/ebpf/user/profile/profile_common.c @@ -714,6 +714,27 @@ static inline void update_matched_process_in_total(struct profiler_context *ctx, } } +#define UNKNOWN_JAVA_SYMBOL_STR "Unknown" + +static char *get_java_symbol(struct bpf_tracer *t, struct java_symbol_map_key *key) { + char value[JAVA_SYMBOL_MAX_LENGTH]; + memset(value, 0, JAVA_SYMBOL_MAX_LENGTH * sizeof(char)); + if (!bpf_table_get(t, MAP_MEMORY_JAVA_SYMBOL_MAP_NAME, key, value)) { + return NULL; + } + char *ret = rewrite_java_symbol(value); + if (!ret) { + int len = strlen(value); + ret = clib_mem_alloc_aligned("symbol_str", len + 1, 0, NULL); + if (ret == NULL) { + return NULL; + } + memcpy(ret, value, len); + ret[len] = '\0'; + } + return ret; +} + static void aggregate_stack_traces(struct profiler_context *ctx, struct bpf_tracer *t, const char *stack_map_name, @@ -913,9 +934,18 @@ static void aggregate_stack_traces(struct profiler_context *ctx, if (matched) str_len += strlen(v->comm) + sizeof(pre_tag); - if (ctx->type == PROFILER_TYPE_MEMORY) { - rewrite_java_symbol(v->ext_data.memory.class_name); - str_len += strlen(v->ext_data.memory.class_name) + 1; + char *class_name = NULL; + + if (ctx->type == PROFILER_TYPE_MEMORY && v->ext_data.memory.class_id != 0) { + struct java_symbol_map_key key = { 0 }; + key.tgid = v->tgid; + key.class_id = v->ext_data.memory.class_id; + class_name = get_java_symbol(t, &key); + if (class_name) { + str_len += strlen(class_name) + 1; + } else { + str_len += strlen(UNKNOWN_JAVA_SYMBOL_STR) + 1; + } } int len = sizeof(stack_trace_msg_t) + str_len; @@ -924,6 +954,9 @@ static void aggregate_stack_traces(struct profiler_context *ctx, clib_mem_free(trace_str); if (__info_p) AO_DEC(&__info_p->use); + if (class_name) { + clib_mem_free(class_name); + } continue; } @@ -943,7 +976,13 @@ static void aggregate_stack_traces(struct profiler_context *ctx, } offset += snprintf(msg_str + offset, str_len - offset, "%s", trace_str); if (ctx->type == PROFILER_TYPE_MEMORY) { - offset += snprintf(msg_str + offset, str_len - offset, ";%s", v->ext_data.memory.class_name); + if (class_name) { + offset += snprintf(msg_str + offset, str_len - offset, ";%s", class_name); + clib_mem_free(class_name); + class_name = NULL; + } else { + offset += snprintf(msg_str + offset, str_len - offset, ";%s", UNKNOWN_JAVA_SYMBOL_STR); + } } msg->data_len = strlen((char *)msg->data); diff --git a/agent/src/ebpf/user/profile/stringifier.c b/agent/src/ebpf/user/profile/stringifier.c index 429ea558e81..527b73b2d19 100644 --- a/agent/src/ebpf/user/profile/stringifier.c +++ b/agent/src/ebpf/user/profile/stringifier.c @@ -194,42 +194,90 @@ static char *proc_symbol_name_fetch(pid_t pid, struct bcc_symbol *sym) return ptr; } -void rewrite_java_symbol(char *sym) { +// Demangle with +// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3 +char *rewrite_java_symbol(char *sym) { int len = strlen(sym); if (len == 0) { - return; + return NULL; } - int i, cls_start = 1; - bool is_array = false; + int i = 0, j = 0; + for (i = 0; i < len && sym[i] == '['; i++) {} + int array_dims = i; - switch (sym[0]) { - case '[': - if (len <= 1 || sym[1] != 'L') { - break; - } - is_array = true; - cls_start++; - // fallthrough to handle "[LClassname;::methodName" + // make room for array ']'s and base type name expension + int new_len = len + array_dims + 16; + char *dst = clib_mem_alloc_aligned("symbol_str", new_len, 0, NULL); + if (dst == NULL) { + return dst; + } + memset(dst, 0, new_len); + int offset = 0; + + switch (sym[i]) { + case 'B': + offset += snprintf(dst + offset, new_len - offset, "byte"); + i++; + break; + case 'C': + offset += snprintf(dst + offset, new_len - offset, "char"); + i++; + break; + case 'D': + offset += snprintf(dst + offset, new_len - offset, "double"); + i++; + break; + case 'F': + offset += snprintf(dst + offset, new_len - offset, "float"); + i++; + break; + case 'I': + offset += snprintf(dst + offset, new_len - offset, "int"); + i++; + break; + case 'J': + offset += snprintf(dst + offset, new_len - offset, "long"); + i++; + break; + case 'S': + offset += snprintf(dst + offset, new_len - offset, "short"); + i++; + break; + case 'Z': + offset += snprintf(dst + offset, new_len - offset, "boolean"); + i++; + break; case 'L': // LClassName;::methodName - for (i = cls_start; i < len; i++) { - if (sym[i] == ';') { + for (j = i + 1; j < len; j++) { + if (sym[j] == ';') { break; } } - if (i != len) { - memcpy(sym, sym + cls_start, i - cls_start); - memcpy(sym + i - cls_start, sym + i + 1, len - i - 1); - memset(sym + len - cls_start - 1, '\0', cls_start + 1); - if (is_array) { - memcpy(sym + len - cls_start - 1, "[]", 2); - } + if (j == len) { + goto failed; } + memcpy(dst + offset, sym + i + 1, j - (i + 1)); + offset += j - i; + i = j + 1; break; default: - break; + goto failed; } + + for (int j = 0; j < array_dims; j++) { + offset += snprintf(dst + offset, new_len - offset, "[]"); + } + + // rest + snprintf(dst + offset, new_len - offset, sym + i); + + return dst; + +failed: + clib_mem_free(dst); + return NULL; } static inline int symcache_resolve(pid_t pid, void *resolver, u64 address, @@ -255,7 +303,11 @@ static inline int symcache_resolve(pid_t pid, void *resolver, u64 address, *sym_ptr = proc_symbol_name_fetch(pid, sym); if (p->is_java) { // handle java encoded symbols - rewrite_java_symbol(*sym_ptr); + char *new_sym = rewrite_java_symbol(*sym_ptr); + if (new_sym != NULL) { + clib_mem_free(*sym_ptr); + *sym_ptr = new_sym; + } } pthread_mutex_unlock(&p->mutex); return ret; diff --git a/agent/src/ebpf/user/profile/stringifier.h b/agent/src/ebpf/user/profile/stringifier.h index d081e7e6ea5..eb20e08531d 100644 --- a/agent/src/ebpf/user/profile/stringifier.h +++ b/agent/src/ebpf/user/profile/stringifier.h @@ -47,7 +47,7 @@ char *resolve_and_gen_stack_trace_str(struct bpf_tracer *t, stack_str_hash_t *h, bool new_cache, char *process_name, void *info_p, bool ignore_libs); -void rewrite_java_symbol(char *sym); +char *rewrite_java_symbol(char *sym); #endif /* AARCH64_MUSL */ #endif /* DF_USER_STRINGIFIER_H */ diff --git a/agent/src/ebpf/user/table.c b/agent/src/ebpf/user/table.c index 4162f1d19f1..0246cb4310c 100644 --- a/agent/src/ebpf/user/table.c +++ b/agent/src/ebpf/user/table.c @@ -27,6 +27,21 @@ #include "table.h" #include "log.h" +bool bpf_table_get(struct bpf_tracer *tracer, + const char *tb_name, void *key, void *val) +{ + struct ebpf_map *map = ebpf_obj__get_map_by_name(tracer->obj, tb_name); + if (map == NULL) { + ebpf_warning("[%s] map name \"%s\" map is NULL.\n", __func__, + tb_name); + return false; + } + + int map_fd = map->fd; + + return bpf_lookup_elem(map_fd, key, val) == 0; +} + bool bpf_table_get_value(struct bpf_tracer *tracer, const char *tb_name, uint64_t key, void *val_buf) { diff --git a/agent/src/ebpf/user/table.h b/agent/src/ebpf/user/table.h index ee3b6bb629b..2be5e3ee50e 100644 --- a/agent/src/ebpf/user/table.h +++ b/agent/src/ebpf/user/table.h @@ -18,6 +18,9 @@ #define DF_BPF_TABLE_H #include "tracer.h" +bool bpf_table_get(struct bpf_tracer *tracer, + const char *tb_name, void *key, void *val); + bool bpf_table_get_value(struct bpf_tracer *tracer, const char *tb_name, uint64_t key,