From patchwork Thu Nov 28 01:24:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ihor Solodrai X-Patchwork-Id: 13887520 Received: from mail-0301.mail-europe.com (mail-0301.mail-europe.com [188.165.51.139]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DED0924B34 for ; Thu, 28 Nov 2024 01:24:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=188.165.51.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732757077; cv=none; b=dtIU71CsePGSrXK/F6WNJGkHwm62OYhxa42W/oGfaEK+7q2ryRlpkzrPP7p1slPW6t3GI1ujCLXguywFc2Z1fGYgw9cCQ6pc7QiFyIeUdojZNnPQxDL8BUe2eIKadxwZ7OCK4ZmA4KCBBARQkxmiHQ+i6Z31SGa2UDHSq3Q8I8U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732757077; c=relaxed/simple; bh=hTezoi4Y4xzFdPY93sZrzt/0tWwClRnM9FDioJqDyQk=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=TIBOXrXk5PLkTNIB1mkBjGWeK5Wnd4q/JxGbLt7Z6+HXl9D0jQLffUbE1y9OBNSbuTm269xUwXMHR/7iGoCvuYfCbT9Sbu1H/BOE2NIVKwKa2WXZzsJbxPUM9eyPvWiaDrNszksV/GB9Gd6GLJ5z0YpE0KwuKtvla9gj5JAZZyc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me; spf=pass smtp.mailfrom=pm.me; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b=O3wckkW/; arc=none smtp.client-ip=188.165.51.139 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pm.me Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b="O3wckkW/" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pm.me; s=protonmail3; t=1732757060; x=1733016260; bh=xFpiKjWglhVTpZuSM5IwuY0WaHeiv9m4wTkxcr93whQ=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector:List-Unsubscribe:List-Unsubscribe-Post; b=O3wckkW/66vcKuCDlj8xh63RSg+s8NoRomS2S/Z+YdqlvQS5WVXURswZx17E3FBUj DWZ+ylPxTu+TEa2GUIdreqQsIkKmPLhvYd0bppeSFz/LheZPVTJn6eU/DOS0hwys0f Cwjax/ftWFgDbAj/QjBq/gmTZYPP7p0rGM9gXzROi8yInF3MQl+JB8ms04Fda0p+dS 57vQw/PLEYZ0kCk6jFjLh9AVBGmBCaf1tdQagHzyUJzif91hmv/BtsL65VOn28w3sf VwkzjHm5U3TTYpM006SGz/qLbesN6n269uLP/lwsxlSG4KQm31OE87TacBrfPbNPK4 C+aM4Gpudl8zQ== Date: Thu, 28 Nov 2024 01:24:17 +0000 To: dwarves@vger.kernel.org, acme@kernel.org From: Ihor Solodrai Cc: bpf@vger.kernel.org, alan.maguire@oracle.com, eddyz87@gmail.com, andrii@kernel.org, mykolal@fb.com Subject: [RFC PATCH 8/9] btf_encoder: introduce btf_encoding_context Message-ID: <20241128012341.4081072-9-ihor.solodrai@pm.me> In-Reply-To: <20241128012341.4081072-1-ihor.solodrai@pm.me> References: <20241128012341.4081072-1-ihor.solodrai@pm.me> Feedback-ID: 27520582:user:proton X-Pm-Message-ID: af392d2d22b21d9dc7cba46a810445e6823d3dee Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-State: RFC Introduce a static struct holding global data necessary for BTF encoding: elf_functions tables and btf_encoder structs. The context has init()/exit() interface that should be used to indicate when BTF encoding work has started and ended. I considered freeing everything contained in the context exclusively on exit(), however it turns out this unnecessarily increases max RSS. Probably because the work done in btf_encoder__encode() requires relatively more memory, and if encoders and tables are freed earlier, that space is reused. Compare: -j4: Maximum resident set size (kbytes): 868484 -j8: Maximum resident set size (kbytes): 1003040 -j16: Maximum resident set size (kbytes): 1039416 -j32: Maximum resident set size (kbytes): 1145312 vs -j4: Maximum resident set size (kbytes): 972692 -j8: Maximum resident set size (kbytes): 1043184 -j16: Maximum resident set size (kbytes): 1081156 -j32: Maximum resident set size (kbytes): 1218184 Signed-off-by: Ihor Solodrai --- btf_encoder.c | 117 +++++++++++++++++++++++++++++++++++++------------- btf_encoder.h | 3 ++ pahole.c | 10 ++++- 3 files changed, 98 insertions(+), 32 deletions(-) diff --git a/btf_encoder.c b/btf_encoder.c index 778481d..bd5a370 100644 --- a/btf_encoder.c +++ b/btf_encoder.c @@ -151,19 +151,73 @@ struct btf_kfunc_set_range { uint64_t end; }; +static struct { + bool initialized; + + /* In principle, multiple ELFs can be processed in one pahole + * run, so we have to store elf_functions table per ELF. + * An elf_functions struct is added to the list in + * btf_encoder__pre_load_module(). + * The list is cleared at the end of btf_encoder__add_saved_funcs(). + */ + struct list_head elf_functions_list; + + /* The mutex only needed for add/delete, as this can happen in + * multiple encoding threads. A btf_encoder is added to this + * list in btf_encoder__new(), and removed in btf_encoder__delete(). + * All encoders except the main one (`btf_encoder` in pahole.c) + * are deleted in pahole_threads_collect(). + */ + pthread_mutex_t btf_encoder_list_lock; + struct list_head btf_encoder_list; + +} btf_encoding_context; + +int btf_encoding_context__init(void) +{ + int err = 0; + + if (btf_encoding_context.initialized) { + fprintf(stderr, "%s was called while context is already initialized\n", __func__); + err = -1; + goto out; + } + + INIT_LIST_HEAD(&btf_encoding_context.elf_functions_list); + INIT_LIST_HEAD(&btf_encoding_context.btf_encoder_list); + pthread_mutex_init(&btf_encoding_context.btf_encoder_list_lock, NULL); + btf_encoding_context.initialized = true; + +out: + return err; +} + +static inline void btf_encoder__delete_all(void); +static inline void elf_functions__delete_all(void); + +void btf_encoding_context__exit(void) +{ + if (!btf_encoding_context.initialized) { + fprintf(stderr, "%s was called while context is not initialized\n", __func__); + return; + } + + if (!list_empty(&btf_encoding_context.elf_functions_list)) + elf_functions__delete_all(); + + if (!list_empty(&btf_encoding_context.btf_encoder_list)) + btf_encoder__delete_all(); + + pthread_mutex_destroy(&btf_encoding_context.btf_encoder_list_lock); + btf_encoding_context.initialized = false; +} -/* In principle, multiple ELFs can be processed in one pahole run, - * so we have to store elf_functions table per ELF. - * An element is added to the list on btf_encoder__pre_load_module, - * and removed after btf_encoder__encode is done. - */ -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) { + list_for_each(pos, &btf_encoding_context.elf_functions_list) { struct elf_functions *funcs = list_entry(pos, struct elf_functions, node); if (funcs->elf == elf) @@ -184,9 +238,8 @@ static inline void elf_functions__delete_all(void) { struct list_head *pos, *tmp; - list_for_each_safe(pos, tmp, &elf_functions_list) { + list_for_each_safe(pos, tmp, &btf_encoding_context.elf_functions_list) { struct elf_functions *funcs = list_entry(pos, struct elf_functions, node); - elf_functions__delete(funcs); } } @@ -215,7 +268,7 @@ int btf_encoder__pre_load_module(Dwfl_Module *mod, Elf *elf) if (err) goto out_delete; - list_add_tail(&funcs->node, &elf_functions_list); + list_add_tail(&funcs->node, &btf_encoding_context.elf_functions_list); return 0; @@ -224,29 +277,21 @@ out_delete: return err; } - -static LIST_HEAD(encoders); -static pthread_mutex_t encoders__lock = PTHREAD_MUTEX_INITIALIZER; - -/* mutex only needed for add/delete, as this can happen in multiple encoding - * threads. Traversal of the list is currently confined to thread collection. - */ - #define btf_encoders__for_each_encoder(encoder) \ - list_for_each_entry(encoder, &encoders, node) + list_for_each_entry(encoder, &btf_encoding_context.btf_encoder_list, node) static void btf_encoders__add(struct btf_encoder *encoder) { - pthread_mutex_lock(&encoders__lock); - list_add_tail(&encoder->node, &encoders); - pthread_mutex_unlock(&encoders__lock); + pthread_mutex_lock(&btf_encoding_context.btf_encoder_list_lock); + list_add_tail(&encoder->node, &btf_encoding_context.btf_encoder_list); + pthread_mutex_unlock(&btf_encoding_context.btf_encoder_list_lock); } static void btf_encoders__delete(struct btf_encoder *encoder) { struct btf_encoder *existing = NULL; - pthread_mutex_lock(&encoders__lock); + pthread_mutex_lock(&btf_encoding_context.btf_encoder_list_lock); /* encoder may not have been added to list yet; check. */ btf_encoders__for_each_encoder(existing) { if (encoder == existing) @@ -254,7 +299,7 @@ static void btf_encoders__delete(struct btf_encoder *encoder) } if (encoder == existing) list_del(&encoder->node); - pthread_mutex_unlock(&encoders__lock); + pthread_mutex_unlock(&btf_encoding_context.btf_encoder_list_lock); } #define PERCPU_SECTION ".data..percpu" @@ -1355,19 +1400,15 @@ int btf_encoder__add_saved_funcs(struct btf_encoder *encoder) list_for_each_entry(s, &e->func_states, node) nr_saved_fns++; } - /* Another thread already did this work */ - if (nr_saved_fns == 0) { - printf("nothing to do for encoder...\n"); + + if (nr_saved_fns == 0) return 0; - } - printf("got %d saved functions...\n", nr_saved_fns); saved_fns = calloc(nr_saved_fns, sizeof(*saved_fns)); btf_encoders__for_each_encoder(e) { list_for_each_entry(s, &e->func_states, node) saved_fns[i++] = s; } - printf("added %d saved fns\n", i); qsort(saved_fns, nr_saved_fns, sizeof(*saved_fns), saved_functions_cmp); for (i = 0; i < nr_saved_fns; i = j) { @@ -1399,6 +1440,9 @@ int btf_encoder__add_saved_funcs(struct btf_encoder *encoder) free(saved_fns); btf_encoders__for_each_encoder(e) btf_encoder__delete_saved_funcs(e); + + elf_functions__delete_all(); + return 0; } @@ -2177,7 +2221,6 @@ int btf_encoder__encode(struct btf_encoder *encoder) err = btf_encoder__write_elf(encoder, encoder->btf, BTF_ELF_SEC); } - elf_functions__delete_all(); return err; } @@ -2564,6 +2607,18 @@ void btf_encoder__delete(struct btf_encoder *encoder) free(encoder); } +static inline void btf_encoder__delete_all(void) +{ + struct btf_encoder *encoder; + struct list_head *pos, *tmp; + + list_for_each_safe(pos, tmp, &btf_encoding_context.btf_encoder_list) { + encoder = list_entry(pos, struct btf_encoder, node); + btf_encoder__delete(encoder); + } +} + + int btf_encoder__encode_cu(struct btf_encoder *encoder, struct cu *cu, struct conf_load *conf_load) { struct llvm_annotation *annot; diff --git a/btf_encoder.h b/btf_encoder.h index 29f652a..73d6e13 100644 --- a/btf_encoder.h +++ b/btf_encoder.h @@ -23,6 +23,9 @@ enum btf_var_option { BTF_VAR_GLOBAL = 2, }; +int btf_encoding_context__init(void); +void btf_encoding_context__exit(void); + struct btf_encoder *btf_encoder__new(struct cu *cu, const char *detached_filename, struct btf *base_btf, bool verbose, struct conf_load *conf_load); void btf_encoder__delete(struct btf_encoder *encoder); diff --git a/pahole.c b/pahole.c index b5aea56..6b46399 100644 --- a/pahole.c +++ b/pahole.c @@ -3741,8 +3741,12 @@ int main(int argc, char *argv[]) conf_load.threads_collect = pahole_threads_collect; } - if (btf_encode) + if (btf_encode) { conf_load.pre_load_module = btf_encoder__pre_load_module; + err = btf_encoding_context__init(); + if (err < 0) + goto out; + } // Make 'pahole --header type < file' a shorter form of 'pahole -C type --count 1 < file' if (conf.header_type && !class_name && prettify_input) { @@ -3858,7 +3862,11 @@ try_sole_arg_as_class_names: goto out_cus_delete; } } + out_ok: + if (btf_encode) + btf_encoding_context__exit(); + if (stats_formatter != NULL) print_stats();