Do not collect functions from ELF for each new btf_encoder, and instead set a pointer to a shared elf_functions table, built beforehand by btf_encoder__pre_cus__load_module(). Change the algorithm of btf_encoder__add_saved_funcs(). Traverse elf_functions table once and for each function do the following: * check function states of each elf_function in a list to determine if it should be added to BTF * if yes, add the function to the BTF of the owner of the first elf_function in the list Do not call btf_encoder__add_saved_funcs() on every btf_encoder__add_encoder(). Instead, for non-reproducible multi-threaded case do that in pahole_threads_collect(), and for single-threaded or reproducible_build do that right before btf_encoder__encode(). Signed-off-by: Ihor Solodrai <ihor.solodrai@xxxxx> --- btf_encoder.c | 113 +++++++++++++++++++++++--------------------------- btf_encoder.h | 1 + pahole.c | 9 +++- 3 files changed, 61 insertions(+), 62 deletions(-) diff --git a/btf_encoder.c b/btf_encoder.c index 002358c..1ec39fe 100644 --- a/btf_encoder.c +++ b/btf_encoder.c @@ -80,7 +80,6 @@ struct btf_encoder_func_state { uint8_t optimized_parms:1; uint8_t unexpected_reg:1; uint8_t inconsistent_proto:1; - uint8_t processed:1; int ret_type_id; struct btf_encoder_func_parm *parms; struct btf_encoder_func_annot *annots; @@ -141,7 +140,7 @@ struct btf_encoder { struct elf_secinfo *secinfo; size_t seccnt; int encode_vars; - struct elf_functions functions; + struct elf_functions *functions; }; struct btf_func { @@ -163,6 +162,19 @@ struct btf_kfunc_set_range { */ static LIST_HEAD(elf_functions_list); +static struct elf_functions *elf_functions__get(Elf *elf) +{ + struct list_head *pos; + + list_for_each(pos, &elf_functions_list) { + struct elf_functions *funcs = list_entry(pos, struct elf_functions, node); + + if (funcs->elf == elf) + return funcs; + } + return NULL; +} + static void __elf_functions__delete(struct elf_functions *funcs) { struct elf_function *func, *next; @@ -238,8 +250,6 @@ out_delete: static LIST_HEAD(encoders); static pthread_mutex_t encoders__lock = PTHREAD_MUTEX_INITIALIZER; -static int btf_encoder__add_saved_funcs(struct btf_encoder *encoder); - /* mutex only needed for add/delete, as this can happen in multiple encoding * threads. Traversal of the list is currently confined to thread collection. */ @@ -891,8 +901,6 @@ int32_t btf_encoder__add_encoder(struct btf_encoder *encoder, struct btf_encoder if (encoder == other) return 0; - btf_encoder__add_saved_funcs(other); - for (shndx = 1; shndx < other->seccnt; shndx++) { struct gobuffer *var_secinfo_buf = &other->secinfo[shndx].secinfo; size_t sz = gobuffer__size(var_secinfo_buf); @@ -1335,61 +1343,52 @@ static inline struct elf_function *find_elf_function(struct elf_function *list_h 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 = find_elf_function(&encoder->functions.entries[i], encoder); - - if (!func) - continue; +/* Each element of elf_functions->entries is a list of a variable + * number of elf_function structs, because not all encoders saved a + * particular function. However each function saved by at least one + * encoder needs to be added to BTF _somewhere_. Here we traverse the + * entire elf_functions table, and for each list of elf_function + * structs a function is added to the BTF of the encoder of the first + * elf_function in the list. + */ +int btf_encoder__add_saved_funcs(struct btf_encoder *base_encoder) +{ + struct elf_functions *functions = base_encoder->functions; - struct btf_encoder_func_state *state = &func->state; - struct btf_encoder *other_encoder = NULL; + for (int i = 0; i < functions->cnt; i++) { + struct elf_function *list_head = &functions->entries[i]; + struct btf_encoder_func_state *state, *other_state; + struct elf_function *func; + bool skip_func = false; - if (!state->initialized || state->processed) - continue; - /* merge optimized-out status across encoders; since each - * encoder has the same elf symbol table we can use the - * same index to access the same elf symbol. + /* list_head without an encoder means no encoder has + * saved a function with this name */ - btf_encoders__for_each_encoder(other_encoder) { - struct elf_function *other_func; - struct btf_encoder_func_state *other_state; - uint8_t optimized, unexpected, inconsistent; - other_func = find_elf_function(&other_encoder->functions.entries[i], other_encoder); - - if (other_encoder == encoder || !other_func) - continue; + if (!list_head->encoder) + continue; + func = list_head; + state = &func->state; + for_each_elf_function(other_func, list_head) { other_state = &other_func->state; - if (!other_state->initialized) - continue; - optimized = state->optimized_parms | other_state->optimized_parms; - unexpected = state->unexpected_reg | other_state->unexpected_reg; - inconsistent = state->inconsistent_proto | other_state->inconsistent_proto; - if (!unexpected && !inconsistent && - !funcs__match(encoder, func, - encoder->btf, state, - other_encoder->btf, other_state)) - inconsistent = 1; - state->optimized_parms = other_state->optimized_parms = optimized; - state->unexpected_reg = other_state->unexpected_reg = unexpected; - state->inconsistent_proto = other_state->inconsistent_proto = inconsistent; - - other_state->processed = 1; + skip_func |= other_state->inconsistent_proto; + skip_func |= other_state->unexpected_reg; + skip_func |= !funcs__match(func->encoder, func, + func->encoder->btf, state, + other_func->encoder->btf, other_state); + if (skip_func) + break; } /* do not exclude functions with optimized-out parameters; they * may still be _called_ with the right parameter values, they * just do not _use_ them. Only exclude functions with * unexpected register use or multiple inconsistent prototypes. */ - if (!state->unexpected_reg && !state->inconsistent_proto) { - if (btf_encoder__add_func(encoder, NULL, func)) + if (!skip_func) { + if (btf_encoder__add_func(func->encoder, NULL, func)) return -1; } - state->processed = 1; } return 0; } @@ -1440,8 +1439,8 @@ static struct elf_function *btf_encoder__find_function(struct btf_encoder *encod struct elf_function *func, *head, *tmp; head = bsearch(&key, - encoder->functions.entries, - encoder->functions.cnt, + encoder->functions->entries, + encoder->functions->cnt, sizeof(key), functions_cmp); @@ -2166,9 +2165,6 @@ int btf_encoder__encode(struct btf_encoder *encoder) int err; size_t shndx; - /* for single-threaded case, saved funcs are added here */ - btf_encoder__add_saved_funcs(encoder); - for (shndx = 1; shndx < encoder->seccnt; shndx++) if (gobuffer__size(&encoder->secinfo[shndx].secinfo)) btf_encoder__add_datasec(encoder, shndx); @@ -2535,8 +2531,7 @@ struct btf_encoder *btf_encoder__new(struct cu *cu, const char *detached_filenam printf("%s: '%s' doesn't have symtab.\n", __func__, cu->filename); goto out; } - encoder->functions.symtab = encoder->symtab; - encoder->functions.elf = cu->elf; + encoder->functions = elf_functions__get(cu->elf); /* index the ELF sections for later lookup */ @@ -2575,9 +2570,6 @@ struct btf_encoder *btf_encoder__new(struct cu *cu, const char *detached_filenam if (!found_percpu && encoder->verbose) printf("%s: '%s' doesn't have '%s' section\n", __func__, cu->filename, PERCPU_SECTION); - if (elf_functions__collect(&encoder->functions)) - goto out_delete; - if (encoder->verbose) printf("File %s:\n", cu->filename); btf_encoders__add(encoder); @@ -2603,8 +2595,7 @@ void btf_encoder__delete(struct btf_encoder *encoder) zfree(&encoder->source_filename); btf__free(encoder->btf); encoder->btf = NULL; - - __elf_functions__delete(&encoder->functions); + elf_symtab__delete(encoder->symtab); free(encoder); } @@ -2703,7 +2694,7 @@ int btf_encoder__encode_cu(struct btf_encoder *encoder, struct cu *cu, struct co continue; if (!ftype__has_arg_names(&fn->proto)) continue; - if (encoder->functions.cnt) { + if (encoder->functions->cnt) { const char *name; name = function__name(fn); @@ -2719,7 +2710,7 @@ int btf_encoder__encode_cu(struct btf_encoder *encoder, struct cu *cu, struct co save = true; else func->generated = true; - } else if (encoder->functions.suffix_cnt && + } else if (encoder->functions->suffix_cnt && conf_load->btf_gen_optimized) { /* falling back to name.isra.0 match if no exact * match is found; only bother if we found any diff --git a/btf_encoder.h b/btf_encoder.h index 7debd67..29f652a 100644 --- a/btf_encoder.h +++ b/btf_encoder.h @@ -33,6 +33,7 @@ int btf_encoder__encode_cu(struct btf_encoder *encoder, struct cu *cu, struct co struct btf *btf_encoder__btf(struct btf_encoder *encoder); int btf_encoder__add_encoder(struct btf_encoder *encoder, struct btf_encoder *other); +int btf_encoder__add_saved_funcs(struct btf_encoder *base_encoder); int btf_encoder__pre_load_module(Dwfl_Module *mod, Elf *elf); diff --git a/pahole.c b/pahole.c index 891af3a..6f83636 100644 --- a/pahole.c +++ b/pahole.c @@ -3262,12 +3262,16 @@ static int pahole_threads_collect(struct conf_load *conf, int nr_threads, void * if (error) goto out; + err = btf_encoder__add_saved_funcs(btf_encoder); + if (err < 0) + goto out; + for (i = 0; i < nr_threads; i++) { /* * Merge content of the btf instances of worker threads to the btf * instance of the primary btf_encoder. */ - if (!threads[i]->btf) + if (!threads[i]->encoder || !threads[i]->btf) continue; err = btf_encoder__add_encoder(btf_encoder, threads[i]->encoder); if (err < 0) @@ -3915,6 +3919,9 @@ try_sole_arg_as_class_names: exit(1); } + if (conf_load.nr_jobs <= 1 || conf_load.reproducible_build) + btf_encoder__add_saved_funcs(btf_encoder); + err = btf_encoder__encode(btf_encoder); if (err) { fputs("Failed to encode BTF\n", stderr); -- 2.43.0