From: Eduard Zingerman <eddyz87@gmail.com>
To: Alan Maguire <alan.maguire@oracle.com>, acme@kernel.org
Cc: dwarves@vger.kernel.org, olsajiri@gmail.com,
shung-hsi.yu@suse.com, jirislaby@kernel.org
Subject: Re: [PATCH v2 dwarves 1/3] btf_encoder: record BTF-centric function state instead of DWARF-centric
Date: Fri, 20 Sep 2024 12:18:37 -0700 [thread overview]
Message-ID: <2be35785218e6df18d3e05ddcaf0406227e05109.camel@gmail.com> (raw)
In-Reply-To: <20240916134946.3893204-2-alan.maguire@oracle.com>
On Mon, 2024-09-16 at 14:49 +0100, Alan Maguire wrote:
[...]
> When this is done, memory consumption peaks at 1Gb rather than
> ~4Gb for vmlinux generation. Time taken appears to be approximately
> the same for -j1, but slightly faster for multiple threads;
> for example:
Hi Alan,
Sorry for delayed reply.
I tried this patch with selftests vmlinux and see huge reduction indeed:
$ /usr/bin/time -v \
pahole -j1 --btf_features=consistent_func --btf_encode_detached=old.btf vmlinux
...
Maximum resident set size (kbytes): 3123660
$ /usr/bin/time -v \
pahole -j1 --btf_features=consistent_func --btf_encode_detached=new.btf vmlinux
...
Maximum resident set size (kbytes): 464400
[...]
> In terms of functions encoded, 360 fewer functions make it into
> BTF due to the different approach in consistency checking, but after
> examining these cases, they do appear to be legitimately inconsistent
> functions where the optimized versions have parameter mismatches
> with the non-optimized expectations.
>
> Mileage may vary of course, and any testing folks could do would
> be greatly appreciated!
Interestingly, I don't see any difference in generated "consistent"
funcs (BPF selftests kernel config, compiled using mainline clang):
$ comm -3 <(bpftool btf dump file new.btf | grep ' FUNC ' | sed "s/.*'\(.*\)'.*/\1/" | sort) \
<(bpftool btf dump file old.btf | grep ' FUNC ' | sed "s/.*'\(.*\)'.*/\1/" | sort) \
| tee | wc -l
0
A few nitpicks below, but overall looks great.
Reviewed-by: Eduard Zingerman <eddyz87@gmail.com>
[...]
> @@ -669,18 +692,25 @@ static int32_t btf_encoder__tag_type(struct btf_encoder *encoder, uint32_t tag_t
> return encoder->type_id_off + tag_type;
> }
>
> -static int32_t btf_encoder__add_func_proto(struct btf_encoder *encoder, struct ftype *ftype)
> +static int32_t btf_encoder__add_func_proto(struct btf_encoder *encoder, struct ftype *ftype, struct elf_function *func)
It is a bit unfortunate that we now have this "doubled" representation
and both ftype and elf_function pointers have to be passed through
several layers.
Ihor (in CC) is now working on having single btf_encoder for each
processed CU (in order to be able to sort btf's by CU names before
merging to a single BTF for dedup, thus making reproducible builds
cheaper). I wonder if this could be further extended to have a
separate BTF for functions and merge only consistent functions from
this BTF to a final BTF.
[...]
> @@ -694,20 +724,34 @@ static int32_t btf_encoder__add_func_proto(struct btf_encoder *encoder, struct f
>
> /* add parameters */
> param_idx = 0;
> - ftype__for_each_parameter(ftype, param) {
> - const char *name = parameter__name(param);
> + if (ftype) {
> + ftype__for_each_parameter(ftype, param) {
> + const char *name = parameter__name(param);
> +
> + type_id = param->tag.type == 0 ? 0 : encoder->type_id_off + param->tag.type;
> + ++param_idx;
> + if (btf_encoder__add_func_param(encoder, name, type_id, param_idx == nr_params))
> + return -1;
> + }
>
> - type_id = param->tag.type == 0 ? 0 : encoder->type_id_off + param->tag.type;
> ++param_idx;
> - if (btf_encoder__add_func_param(encoder, name, type_id, param_idx == nr_params))
> - return -1;
> - }
> + if (ftype->unspec_parms)
> + if (btf_encoder__add_func_param(encoder, NULL, 0, param_idx == nr_params))
> + return -1;
> + } else {
> + for (param_idx = 0; param_idx < nr_params; param_idx++) {
> + struct btf_encoder_func_parm *p = &state->parms[param_idx];
> + name = btf__name_by_offset(btf, p->name_off);
>
> - ++param_idx;
> - if (ftype->unspec_parms)
> - if (btf_encoder__add_func_param(encoder, NULL, 0, param_idx == nr_params))
> - return -1;
> + /* adding BTF data may result in a move of the
> + * name string memory, so make a temporary copy.
> + */
> + strncpy(tmp_name, name, sizeof(tmp_name));
When compiling with fresh gcc (14.2.1) there is a warning reported at this line:
btf_encoder.c:749:25: warning: ‘strncpy’ specified bound 128 equals destination size [-Wstringop-truncation]
749 | strncpy(tmp_name, name, sizeof(tmp_name));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Which seems to be a legit warning, given section CAVEATS from strncpy man page.
There is also a second warning like this (see below).
(when compiled with BUILD_TYPE=Release).
>
> + if (btf_encoder__add_func_param(encoder, tmp_name, p->type_id, param_idx == nr_params))
> + return -1;
> + }
> + }
> return id;
> }
>
> @@ -831,53 +875,170 @@ static int32_t btf_encoder__add_decl_tag(struct btf_encoder *encoder, const char
[...]
> -static bool funcs__match(struct btf_encoder *encoder, struct elf_function *func, struct function *f2)
> +static bool names__match(struct btf *btf1, const struct btf_type *t1,
> + struct btf *btf2, const struct btf_type *t2)
> {
[...]
> +static bool types__match(struct btf_encoder *encoder,
> + struct btf *btf1, int type_id1,
> + struct btf *btf2, int type_id2)
> +{
[...]
> + switch (btf_kind(t1)) {
> + case BTF_KIND_INT:
Nit: ints have a meaningful .size field and are followed by a u32 word,
both should be compared.
> + case BTF_KIND_FLOAT:
> + case BTF_KIND_FWD:
Nit: BTF_KIND_FWD should go to default, fwd__kind steps over it.
> + case BTF_KIND_TYPEDEF:
> + case BTF_KIND_STRUCT:
> + case BTF_KIND_UNION:
> + case BTF_KIND_ENUM:
> + case BTF_KIND_ENUM64:
> + return names__match(btf1, t1, btf2, t2);
> + case BTF_KIND_PTR:
> + case BTF_KIND_VOLATILE:
> + case BTF_KIND_CONST:
> + case BTF_KIND_RESTRICT:
> + case BTF_KIND_TYPE_TAG:
> + id1 = t1->type;
> + id2 = t2->type;
> + break;
> + case BTF_KIND_ARRAY: {
> + const struct btf_array *a1 = btf_array(t1);
> + const struct btf_array *a2 = btf_array(t2);
> +
> + if (a1->nelems != a2->nelems)
> + return false;
> + id1 = a1->type;
> + id2 = a2->type;
> + break;
> + }
> + case BTF_KIND_FUNC_PROTO: {
> + const struct btf_param *p1 = btf_params(t1);
> + const struct btf_param *p2 = btf_params(t2);
> + int i, vlen = btf_vlen(t1);
> +
> + if (vlen != btf_vlen(t2))
> + return false;
> + if (!types__match(encoder, btf1, t1->type,
> + btf2, t2->type))
> + return false;
> + for (i = 0; i < vlen; i++, p1++, p2++) {
> + if (!types__match(encoder, btf1, t1->type,
> + btf2, t2->type))
> + return false;
> + }
> + return true;
> + }
> + default:
> + return false;
> + }
> + } while (1);
> +
> + return false;
> +}
> +
> +static bool funcs__match(struct btf_encoder *encoder, struct elf_function *func,
> + struct btf *btf1, struct btf_encoder_func_state *s1,
> + struct btf *btf2, struct btf_encoder_func_state *s2)
> +{
> + uint8_t i;
> +
> + if (!s1->initialized || !s2->initialized)
> return true;
Nit: is it even possible to get here when !s1->initialized || !s2->initialized?
Maybe log something about inconsistent state?
>
> - if (!func->state.got_proto)
> - func->state.got_proto = proto__get(f1, func->state.proto, sizeof(func->state.proto));
> + if (s1->nr_parms != s2->nr_parms) {
> + btf_encoder__log_func_skip(encoder, func,
> + "param count mismatch; %d params != %d params\n",
> + s1->nr_parms, s2->nr_parms);
> + return false;
> + }
> + if (!types__match(encoder, btf1, s1->ret_type_id, btf2, s2->ret_type_id)) {
> + btf_encoder__log_func_skip(encoder, func, "return type mismatch\n");
> + return false;
> + }
> + if (s1->nr_parms == 0)
> + return true;
>
> - if (proto__get(f2, proto, sizeof(proto))) {
> - if (strcmp(func->state.proto, proto) != 0) {
> - if (encoder->verbose)
> - printf("function mismatch for '%s'('%s'): '%s' != '%s'\n",
> - name, f1->alias ?: name,
> - func->state.proto, proto);
> + for (i = 0; i < s1->nr_parms; i++) {
> + if (!types__match(encoder, btf1, s1->parms[i].type_id,
> + btf2, s2->parms[i].type_id)) {
> + if (encoder->verbose) {
> + const char *p1 = btf__name_by_offset(btf1, s1->parms[i].name_off);
> + const char *p2 = btf__name_by_offset(btf2, s2->parms[i].name_off);
> +
> + btf_encoder__log_func_skip(encoder, func,
> + "param type mismatch for param#%d %s %s %s\n",
> + i + 1,
> + p1 ?: "",
> + p1 && p2 ? "!=" : "",
> + p2 ?: "");
> + }
> return false;
> }
> }
> @@ -886,119 +1047,212 @@ static bool funcs__match(struct btf_encoder *encoder, struct elf_function *func,
[...]
> -static int32_t btf_encoder__add_func(struct btf_encoder *encoder, struct function *fn)
> +static int32_t btf_encoder__add_func(struct btf_encoder *encoder, struct function *fn, struct elf_function *func)
> {
[...]
> + if (!fn) {
> + struct btf_encoder_func_state *state = &func->state;
> + uint16_t idx;
> +
> + if (!state || state->nr_annots == 0)
> + return 0;
> +
> + for (idx = 0; idx < state->nr_annots; idx++) {
> + struct btf_encoder_func_annot *a = &state->annots[idx];
> +
> + value = btf__str_by_offset(encoder->btf, a->value);
> + /* adding BTF data may result in a mode of the
> + * value string memory, so make a temporary copy.
> + */
> + strncpy(tmp_value, value, sizeof(tmp_value));
(Second warning from gcc 14.2.1 is reported here)
> + component_idx = a->component_idx;
> +
> + tag_type_id = btf_encoder__add_decl_tag(encoder, tmp_value, btf_fn_id, component_idx);
> + if (tag_type_id < 0)
> + break;
> + }
[...]
next prev parent reply other threads:[~2024-09-20 19:18 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-09-16 13:49 [PATCH v2 dwarves 0/3] reduce memory overhead of BTF encoding Alan Maguire
2024-09-16 13:49 ` [PATCH v2 dwarves 1/3] btf_encoder: record BTF-centric function state instead of DWARF-centric Alan Maguire
2024-09-20 19:18 ` Eduard Zingerman [this message]
2024-09-24 14:31 ` Alan Maguire
2024-09-24 19:02 ` Ihor Solodrai
2024-09-24 19:10 ` Eduard Zingerman
2024-09-23 14:19 ` Jiri Olsa
2024-09-24 14:40 ` Alan Maguire
2024-09-16 13:49 ` [PATCH v2 dwarves 2/3] pfunct: show all functions that match filter criteria Alan Maguire
2024-09-16 13:49 ` [PATCH v2 dwarves 3/3] tests: add test validating BTF encoding, reasons we skip functions Alan Maguire
2024-09-18 8:41 ` Arnaldo Carvalho de Melo
2024-09-25 9:27 ` Alan Maguire
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2be35785218e6df18d3e05ddcaf0406227e05109.camel@gmail.com \
--to=eddyz87@gmail.com \
--cc=acme@kernel.org \
--cc=alan.maguire@oracle.com \
--cc=dwarves@vger.kernel.org \
--cc=jirislaby@kernel.org \
--cc=olsajiri@gmail.com \
--cc=shung-hsi.yu@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox