btf_encoder__save_func() accumulates observations of DWARF functions, maintaining an elf_function per function name. Part of this routine is a funcs_match() call, which requires access to BTF implicitly referenced by btf_encoder_func_state. In case when elf_functions table is maintained for each btf_encoder this is not an issue, because every btf_encoder_func_state available to this encoder can only reference its own BTF. However, if elf_functions becomes shared, existing elf_function/btf_encoder_function_state structs can be produced by other encoders with their own BTFs. In this case funcs_match() can not be called unless BTFs are available and writes to them are synchronized in some manner (which is not desirable). Accumulated states are later merged between encoders in btf_encoder__add_saved_funcs(). To enable sharing elf_functions table between encoders it is changed from an array of elf_function structs into an array of elf_function_entry. Each entry stores a linked listed of elf_function structs, one for each encoder that searched for this name at least once. To be able to identify a btf_encoder owner of the elf_function, a const pointer to btf_encoder field is added, as well as next pointer to enable a list. At this point each btf_encoder is still maintaining it's own elf_functions table, but the updated implementation is used. Suggested-by: Eduard Zingerman <eddyz87@xxxxxxxxx> Signed-off-by: Ihor Solodrai <ihor.solodrai@xxxxx> --- btf_encoder.c | 113 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 29 deletions(-) diff --git a/btf_encoder.c b/btf_encoder.c index 0d4a4fe..33cd9eb 100644 --- a/btf_encoder.c +++ b/btf_encoder.c @@ -91,6 +91,8 @@ struct elf_function { bool generated; size_t prefixlen; struct btf_encoder_func_state state; + const struct btf_encoder *owner; + struct elf_function *next; }; struct var_info { @@ -105,11 +107,17 @@ struct elf_secinfo { uint64_t sz; }; +struct elf_functions_entry { + const char *name; + size_t prefixlen; + struct elf_function *funcs; // list of elf_function +}; + struct elf_functions { struct list_head node; // for elf_functions_list Elf *elf; // source ELF struct elf_symtab *symtab; - struct elf_function *entries; + struct elf_functions_entry *entries; int allocated; int cnt; int suffix_cnt; @@ -181,10 +189,30 @@ static struct elf_functions *elf_functions__get(Elf *elf) return NULL; } -static inline void elf_functions__delete(struct elf_functions *funcs) +static void elf_functions_entry__delete(struct elf_functions_entry *entry) +{ + struct elf_function *func = entry->funcs; + while (func) { + struct elf_function *next = func->next; + free(func->alias); + zfree(&func->state.annots); + zfree(&func->state.parms); + free(func); + func = next; + } +} + +static inline void __elf_functions__delete(struct elf_functions *funcs) { + for (int i = 0; i < funcs->cnt; i++) + elf_functions_entry__delete(&funcs->entries[i]); free(funcs->entries); elf_symtab__delete(funcs->symtab); +} + +static inline void elf_functions__delete(struct elf_functions *funcs) +{ + __elf_functions__delete(funcs); list_del(&funcs->node); free(funcs); } @@ -216,6 +244,7 @@ out_error: return NULL; } + int btf_encoder__pre_cus__load_module(struct process_dwflmod_parms *parms, Dwfl_Module *mod, Dwarf *dw, Elf *elf) { struct elf_functions *funcs = elf_functions__new(elf); @@ -1303,12 +1332,30 @@ static int32_t btf_encoder__add_func(struct btf_encoder *encoder, struct functio return 0; } +#define for_each_elf_function(func, entry) \ + for (struct elf_function *func = entry->funcs; func; func = func->next) + +static inline struct elf_function *find_elf_function_in_entry(struct elf_functions_entry *entry, + const struct btf_encoder *encoder) +{ + for_each_elf_function(f, entry) { + if (f->owner == encoder) + return f; + } + + return NULL; +} + static int btf_encoder__add_saved_funcs(struct btf_encoder *encoder) { int i; for (i = 0; i < encoder->functions.cnt; i++) { - struct elf_function *func = &encoder->functions.entries[i]; + struct elf_function *func = find_elf_function_in_entry(&encoder->functions.entries[i], encoder); + + if (!func) + continue; + struct btf_encoder_func_state *state = &func->state; struct btf_encoder *other_encoder = NULL; @@ -1322,11 +1369,11 @@ static int btf_encoder__add_saved_funcs(struct btf_encoder *encoder) struct elf_function *other_func; struct btf_encoder_func_state *other_state; uint8_t optimized, unexpected, inconsistent; + other_func = find_elf_function_in_entry(&other_encoder->functions.entries[i], other_encoder); - if (other_encoder == encoder) + if (other_encoder == encoder || !other_func) continue; - other_func = &other_encoder->functions.entries[i]; other_state = &other_func->state; if (!other_state->initialized) continue; @@ -1360,8 +1407,8 @@ static int btf_encoder__add_saved_funcs(struct btf_encoder *encoder) static int functions_cmp(const void *_a, const void *_b) { - const struct elf_function *a = _a; - const struct elf_function *b = _b; + const struct elf_functions_entry *a = _a; + const struct elf_functions_entry *b = _b; /* if search key allows prefix match, verify target has matching * prefix len and prefix matches. @@ -1387,7 +1434,7 @@ static void *reallocarray_grow(void *ptr, int *nmemb, size_t size) static int elf_functions__collect_function(struct elf_functions *functions, GElf_Sym *sym) { - struct elf_function *new; + struct elf_functions_entry *new; const char *name; if (elf_sym__type(sym) != STT_FUNC) @@ -1410,14 +1457,14 @@ static int elf_functions__collect_function(struct elf_functions *functions, GElf functions->entries = new; } - struct elf_function *func = &functions->entries[functions->cnt]; + struct elf_functions_entry *entry = &functions->entries[functions->cnt]; - memset(func, 0, sizeof(*func)); - func->name = name; + memset(entry, 0, sizeof(*entry)); + entry->name = name; if (strchr(name, '.')) { const char *suffix = strchr(name, '.'); functions->suffix_cnt++; - func->prefixlen = suffix - name; + entry->prefixlen = suffix - name; } functions->cnt++; @@ -1427,9 +1474,30 @@ static int elf_functions__collect_function(struct elf_functions *functions, GElf static struct elf_function *btf_encoder__find_function(const struct btf_encoder *encoder, const char *name, size_t prefixlen) { - struct elf_function key = { .name = name, .prefixlen = prefixlen }; + struct elf_function *func = NULL; + struct elf_functions_entry key = { .name = name, .prefixlen = prefixlen }; + struct elf_functions_entry *entry = bsearch(&key, + encoder->functions.entries, + encoder->functions.cnt, + sizeof(key), + functions_cmp); + if (entry) { + // If entry is found then there is a name match. + func = find_elf_function_in_entry(entry, encoder); + // However if there is no elf_function for this encoder in the entry, + // it means the encoder encountered this name for the first time. + // In such case allocate space for new elf_function and add it to the entry. + if (!func) { + func = zalloc(sizeof(*func)); + func->name = entry->name; + func->prefixlen = entry->prefixlen; + func->owner = encoder; + func->next = entry->funcs; + entry->funcs = func; + } + } - return bsearch(&key, encoder->functions.entries, encoder->functions.cnt, sizeof(key), functions_cmp); + return func; } static bool btf_name_char_ok(char c, bool first) @@ -2563,17 +2631,8 @@ out_delete: return NULL; } -void btf_encoder__delete_func(struct elf_function *func) -{ - free(func->alias); - zfree(&func->state.annots); - zfree(&func->state.parms); -} - void btf_encoder__delete(struct btf_encoder *encoder) { - int i; - if (encoder == NULL) return; @@ -2583,13 +2642,9 @@ void btf_encoder__delete(struct btf_encoder *encoder) zfree(&encoder->source_filename); btf__free(encoder->btf); encoder->btf = NULL; - elf_symtab__delete(encoder->symtab); - for (i = 0; i < encoder->functions.cnt; i++) - btf_encoder__delete_func(&encoder->functions.entries[i]); - encoder->functions.allocated = encoder->functions.cnt = 0; - free(encoder->functions.entries); - encoder->functions.entries = NULL; + __elf_functions__delete(&encoder->functions); + encoder->percpu.allocated = encoder->percpu.var_cnt = 0; free(encoder->percpu.vars); encoder->percpu.vars = NULL; -- 2.34.1