bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC dwarves 0/2] dwarves: detect BTF kinds supported by kernel
@ 2023-07-20 20:14 Alan Maguire
  2023-07-20 20:14 ` [RFC dwarves 1/2] dwarves: auto-detect maximum kind supported by vmlinux Alan Maguire
  2023-07-20 20:14 ` [RFC dwarves 2/2] btf_encoder: learn BTF_KIND_MAX value from base BTF when generating split BTF Alan Maguire
  0 siblings, 2 replies; 6+ messages in thread
From: Alan Maguire @ 2023-07-20 20:14 UTC (permalink / raw)
  To: acme
  Cc: andrii.nakryiko, ast, daniel, jolsa, martin.lau, song, yhs,
	john.fastabend, kpsingh, sdf, haoluo, mykolal, bpf, Alan Maguire

When a newer pahole is run on an older kernel, it often knows about BTF
kinds that the kernel does not support, and adds them to the BTF
representation.  This is a problem because the BTF generated is then
embedded in the kernel image.  When it is later read - possibly by
a different older toolchain or by the kernel directly - it is not usable.

The scripts/pahole-flags.sh script enumerates the various pahole options
available associated with various versions of pahole, but in the case
of an older kernel is the set of BTF kinds the kernel can handle that
is of more importance.

Because recent features such as BTF_KIND_ENUM64 are added by default
(and only skipped if --skip_encoding_btf_* is set), BTF will be
created with these newer kinds that the older kernel cannot read.
This can be fixed by stable-backporting --skip options, but this is
cumbersome and would have to be done every time a new BTF kind is
introduced.

So this series attempts to detect the BTF kinds supported by the
kernel/modules so that this can inform BTF encoding for older
kernels.  We look for BTF_KIND_MAX - either as an enumerated value
in vmlinux DWARF (patch 1) or as an enumerated value in base vmlinux
BTF (patch 2).

The aim is to minimize overhead on older stable kernels when new BTF
kinds are introduced.  Kind encoding [1] solves the parsing problem
with BTF, but this approach is intended to ensure generated BTF is
usable when newer pahole runs on older kernels.

This approach requires BTF kinds to be defined via an enumerated type,
which happened for 5.16 and later.  Older kernels than this used #defines
so the approach will only work for 5.16 stable kernels and later currently.

[1] https://lore.kernel.org/bpf/20230616171728.530116-1-alan.maguire@oracle.com/

Alan Maguire (2):
  dwarves: auto-detect maximum kind supported by vmlinux
  btf_encoder: learn BTF_KIND_MAX value from base BTF when generating
    split BTF

 btf_encoder.c  | 36 ++++++++++++++++++++++++++++++++++
 btf_encoder.h  |  2 ++
 dwarf_loader.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
 dwarves.h      |  2 ++
 pahole.c       |  2 ++
 5 files changed, 94 insertions(+)

-- 
2.39.3


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC dwarves 1/2] dwarves: auto-detect maximum kind supported by vmlinux
  2023-07-20 20:14 [RFC dwarves 0/2] dwarves: detect BTF kinds supported by kernel Alan Maguire
@ 2023-07-20 20:14 ` Alan Maguire
  2023-07-26 10:39   ` Jiri Olsa
  2023-07-20 20:14 ` [RFC dwarves 2/2] btf_encoder: learn BTF_KIND_MAX value from base BTF when generating split BTF Alan Maguire
  1 sibling, 1 reply; 6+ messages in thread
From: Alan Maguire @ 2023-07-20 20:14 UTC (permalink / raw)
  To: acme
  Cc: andrii.nakryiko, ast, daniel, jolsa, martin.lau, song, yhs,
	john.fastabend, kpsingh, sdf, haoluo, mykolal, bpf, Alan Maguire

When a newer pahole is run on an older kernel, it often knows about BTF
kinds that the kernel does not support.  This is a problem because the BTF
generated is then embedded in the kernel image and read, and if unknown
kinds are found, BTF handling fails and core BPF functionality is
unavailable.

The scripts/pahole-flags.sh script enumerates the various pahole options
available associated with various versions of pahole, but the problem is
what matters in the case of an older kernel is the set of kinds the kernel
understands.  Because recent features such as BTF_KIND_ENUM64 are added
by default (and only skipped if --skip_encoding_btf_* is set), BTF will
be created with these newer kinds that the older kernel cannot read.
This can be fixed by stable-backporting --skip options, but this is
cumbersome and would have to be done every time a new BTF kind is
introduced.

Here instead we pre-process the DWARF information associated with the
target for BTF generation; if we find an enum with a BTF_KIND_MAX
value in the DWARF associated with the object, we use that to
determine the maximum BTF kind supported.  Note that the enum
representation of BTF kinds starts for the 5.16 kernel; prior to this
The benefit of auto-detection is that no work is required for older
kernels when new kinds are added, and --skip_encoding options are
less needed.

[1] https://github.com/oracle-samples/bpftune/issues/35

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 btf_encoder.c  | 12 ++++++++++++
 dwarf_loader.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
 dwarves.h      |  2 ++
 3 files changed, 66 insertions(+)

diff --git a/btf_encoder.c b/btf_encoder.c
index 65f6e71..98c7529 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -1889,3 +1889,15 @@ struct btf *btf_encoder__btf(struct btf_encoder *encoder)
 {
 	return encoder->btf;
 }
+
+void dwarves__set_btf_kind_max(struct conf_load *conf_load, int btf_kind_max)
+{
+	if (btf_kind_max < 0 || btf_kind_max >= BTF_KIND_MAX)
+		return;
+	if (btf_kind_max < BTF_KIND_DECL_TAG)
+		conf_load->skip_encoding_btf_decl_tag = true;
+	if (btf_kind_max < BTF_KIND_TYPE_TAG)
+		conf_load->skip_encoding_btf_type_tag = true;
+	if (btf_kind_max < BTF_KIND_ENUM64)
+		conf_load->skip_encoding_btf_enum64 = true;
+}
diff --git a/dwarf_loader.c b/dwarf_loader.c
index ccf3194..8984043 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -3358,8 +3358,60 @@ static int __dwarf_cus__process_cus(struct dwarf_cus *dcus)
 	return 0;
 }
 
+/* Find enumeration value for BTF_KIND_MAX; replace conf_load->btf_kind_max with
+ * this value if found since it indicates that the target object does not know
+ * about kinds > its BTF_KIND_MAX value.  This is valuable for kernel/module
+ * BTF where a newer pahole/libbpf operate on an older kernel which cannot
+ * parse some of the newer kinds pahole can generate.
+ */
+static void dwarf__find_btf_kind_max(struct dwarf_cus *dcus)
+{
+	struct conf_load *conf = dcus->conf;
+	uint8_t pointer_size, offset_size;
+	Dwarf_Off off = 0, noff;
+	size_t cuhl;
+
+	while (dwarf_nextcu(dcus->dw, off, &noff, &cuhl, NULL, &pointer_size, &offset_size) == 0) {
+		Dwarf_Die die_mem;
+		Dwarf_Die *cu_die = dwarf_offdie(dcus->dw, off + cuhl, &die_mem);
+		Dwarf_Die child;
+
+		if (cu_die == NULL)
+			break;
+		if (dwarf_child(cu_die, &child) == 0) {
+			Dwarf_Die *die = &child;
+
+			do {
+				Dwarf_Die echild, *edie;
+
+				if (dwarf_tag(die) != DW_TAG_enumeration_type ||
+				    !dwarf_haschildren(die) ||
+				    dwarf_child(die, &echild) != 0)
+					continue;
+				edie = &echild;
+				do {
+					const char *ename;
+					int btf_kind_max;
+
+					if (dwarf_tag(edie) != DW_TAG_enumerator)
+						continue;
+					ename = attr_string(edie, DW_AT_name, conf);
+					if (!ename || strcmp(ename, "BTF_KIND_MAX") != 0)
+						continue;
+					btf_kind_max = attr_numeric(edie, DW_AT_const_value);
+					dwarves__set_btf_kind_max(conf, btf_kind_max);
+					return;
+				} while (dwarf_siblingof(edie, edie) == 0);
+			} while (dwarf_siblingof(die, die) == 0);
+		}
+		off = noff;
+	}
+}
+
 static int dwarf_cus__process_cus(struct dwarf_cus *dcus)
 {
+	dwarf__find_btf_kind_max(dcus);
+
 	if (dcus->conf->nr_jobs > 1)
 		return dwarf_cus__threaded_process_cus(dcus);
 
diff --git a/dwarves.h b/dwarves.h
index eb1a6df..f4d9347 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -1480,4 +1480,6 @@ extern const char tabs[];
 #define DW_TAG_skeleton_unit 0x4a
 #endif
 
+void dwarves__set_btf_kind_max(struct conf_load *conf_load, int btf_kind_max);
+
 #endif /* _DWARVES_H_ */
-- 
2.39.3


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC dwarves 2/2] btf_encoder: learn BTF_KIND_MAX value from base BTF when generating split BTF
  2023-07-20 20:14 [RFC dwarves 0/2] dwarves: detect BTF kinds supported by kernel Alan Maguire
  2023-07-20 20:14 ` [RFC dwarves 1/2] dwarves: auto-detect maximum kind supported by vmlinux Alan Maguire
@ 2023-07-20 20:14 ` Alan Maguire
  1 sibling, 0 replies; 6+ messages in thread
From: Alan Maguire @ 2023-07-20 20:14 UTC (permalink / raw)
  To: acme
  Cc: andrii.nakryiko, ast, daniel, jolsa, martin.lau, song, yhs,
	john.fastabend, kpsingh, sdf, haoluo, mykolal, bpf, Alan Maguire

When creating module BTF, the module likely will not have a DWARF
specificiation of BTF_KIND_MAX, so look for it in the base BTF.  For
vmlinux base BTF, the enumeration value is present, so the base BTF
can be checked to limit BTF kind representation.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 btf_encoder.c | 24 ++++++++++++++++++++++++
 btf_encoder.h |  2 ++
 pahole.c      |  2 ++
 3 files changed, 28 insertions(+)

diff --git a/btf_encoder.c b/btf_encoder.c
index 98c7529..269ed48 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -1901,3 +1901,27 @@ void dwarves__set_btf_kind_max(struct conf_load *conf_load, int btf_kind_max)
 	if (btf_kind_max < BTF_KIND_ENUM64)
 		conf_load->skip_encoding_btf_enum64 = true;
 }
+
+void btf__set_btf_kind_max(struct conf_load *conf_load, struct btf *btf)
+{
+	__u32 id, type_cnt = btf__type_cnt(btf);
+
+	for (id = 1; id < type_cnt; id++) {
+		const struct btf_type *t = btf__type_by_id(btf, id);
+		const struct btf_enum *e;
+		__u16 vlen, i;
+
+		if (!t || !btf_is_enum(t))
+			continue;
+		vlen = btf_vlen(t);
+		e = btf_enum(t);
+		for (i = 0; i < vlen; e++, i++) {
+			const char *name = btf__name_by_offset(btf, e->name_off);
+
+			if (!name || strcmp(name, "BTF_KIND_MAX"))
+				continue;
+			dwarves__set_btf_kind_max(conf_load, e->val);
+			return;
+		}
+	}
+}
diff --git a/btf_encoder.h b/btf_encoder.h
index 34516bb..e5e12ef 100644
--- a/btf_encoder.h
+++ b/btf_encoder.h
@@ -27,4 +27,6 @@ struct btf *btf_encoder__btf(struct btf_encoder *encoder);
 
 int btf_encoder__add_encoder(struct btf_encoder *encoder, struct btf_encoder *other);
 
+void btf__set_btf_kind_max(struct conf_load *conf_load, struct btf *btf);
+
 #endif /* _BTF_ENCODER_H_ */
diff --git a/pahole.c b/pahole.c
index 6fc4ed6..542921a 100644
--- a/pahole.c
+++ b/pahole.c
@@ -3462,6 +3462,7 @@ int main(int argc, char *argv[])
 				base_btf_file, libbpf_get_error(conf_load.base_btf));
 			goto out;
 		}
+		btf__set_btf_kind_max(&conf_load, conf_load.base_btf);
 		if (!btf_encode && !ctf_encode) {
 			// Force "btf" since a btf_base is being informed
 			conf_load.format_path = "btf";
@@ -3505,6 +3506,7 @@ try_sole_arg_as_class_names:
 					base_btf_file, libbpf_get_error(conf_load.base_btf));
 				goto out;
 			}
+			btf__set_btf_kind_max(&conf_load, conf_load.base_btf);
 		}
 	}
 
-- 
2.39.3


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [RFC dwarves 1/2] dwarves: auto-detect maximum kind supported by vmlinux
  2023-07-20 20:14 ` [RFC dwarves 1/2] dwarves: auto-detect maximum kind supported by vmlinux Alan Maguire
@ 2023-07-26 10:39   ` Jiri Olsa
  2023-07-26 15:28     ` Alan Maguire
  0 siblings, 1 reply; 6+ messages in thread
From: Jiri Olsa @ 2023-07-26 10:39 UTC (permalink / raw)
  To: Alan Maguire
  Cc: acme, andrii.nakryiko, ast, daniel, martin.lau, song, yhs,
	john.fastabend, kpsingh, sdf, haoluo, mykolal, bpf

On Thu, Jul 20, 2023 at 09:14:42PM +0100, Alan Maguire wrote:
> When a newer pahole is run on an older kernel, it often knows about BTF
> kinds that the kernel does not support.  This is a problem because the BTF
> generated is then embedded in the kernel image and read, and if unknown
> kinds are found, BTF handling fails and core BPF functionality is
> unavailable.
> 
> The scripts/pahole-flags.sh script enumerates the various pahole options
> available associated with various versions of pahole, but the problem is
> what matters in the case of an older kernel is the set of kinds the kernel
> understands.  Because recent features such as BTF_KIND_ENUM64 are added
> by default (and only skipped if --skip_encoding_btf_* is set), BTF will
> be created with these newer kinds that the older kernel cannot read.
> This can be fixed by stable-backporting --skip options, but this is
> cumbersome and would have to be done every time a new BTF kind is
> introduced.
> 
> Here instead we pre-process the DWARF information associated with the
> target for BTF generation; if we find an enum with a BTF_KIND_MAX
> value in the DWARF associated with the object, we use that to
> determine the maximum BTF kind supported.  Note that the enum
> representation of BTF kinds starts for the 5.16 kernel; prior to this
> The benefit of auto-detection is that no work is required for older
> kernels when new kinds are added, and --skip_encoding options are
> less needed.
> 
> [1] https://github.com/oracle-samples/bpftune/issues/35
> 
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
>  btf_encoder.c  | 12 ++++++++++++
>  dwarf_loader.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  dwarves.h      |  2 ++
>  3 files changed, 66 insertions(+)
> 
> diff --git a/btf_encoder.c b/btf_encoder.c
> index 65f6e71..98c7529 100644
> --- a/btf_encoder.c
> +++ b/btf_encoder.c
> @@ -1889,3 +1889,15 @@ struct btf *btf_encoder__btf(struct btf_encoder *encoder)
>  {
>  	return encoder->btf;
>  }
> +
> +void dwarves__set_btf_kind_max(struct conf_load *conf_load, int btf_kind_max)
> +{
> +	if (btf_kind_max < 0 || btf_kind_max >= BTF_KIND_MAX)
> +		return;
> +	if (btf_kind_max < BTF_KIND_DECL_TAG)
> +		conf_load->skip_encoding_btf_decl_tag = true;
> +	if (btf_kind_max < BTF_KIND_TYPE_TAG)
> +		conf_load->skip_encoding_btf_type_tag = true;
> +	if (btf_kind_max < BTF_KIND_ENUM64)
> +		conf_load->skip_encoding_btf_enum64 = true;
> +}

hi,
so there are some older kernels other than stable that would use this feature
right? because stable already have proper setup for pahole options

or it's just there to be complete and we'd eventually add new rules in here?
wouldn't that be covered by the BTF kind layout stuff you work on? is there
some overlap?

> diff --git a/dwarf_loader.c b/dwarf_loader.c
> index ccf3194..8984043 100644
> --- a/dwarf_loader.c
> +++ b/dwarf_loader.c
> @@ -3358,8 +3358,60 @@ static int __dwarf_cus__process_cus(struct dwarf_cus *dcus)
>  	return 0;
>  }
>  
> +/* Find enumeration value for BTF_KIND_MAX; replace conf_load->btf_kind_max with
> + * this value if found since it indicates that the target object does not know
> + * about kinds > its BTF_KIND_MAX value.  This is valuable for kernel/module
> + * BTF where a newer pahole/libbpf operate on an older kernel which cannot
> + * parse some of the newer kinds pahole can generate.
> + */
> +static void dwarf__find_btf_kind_max(struct dwarf_cus *dcus)
> +{
> +	struct conf_load *conf = dcus->conf;
> +	uint8_t pointer_size, offset_size;
> +	Dwarf_Off off = 0, noff;
> +	size_t cuhl;
> +
> +	while (dwarf_nextcu(dcus->dw, off, &noff, &cuhl, NULL, &pointer_size, &offset_size) == 0) {
> +		Dwarf_Die die_mem;
> +		Dwarf_Die *cu_die = dwarf_offdie(dcus->dw, off + cuhl, &die_mem);
> +		Dwarf_Die child;
> +
> +		if (cu_die == NULL)
> +			break;
> +		if (dwarf_child(cu_die, &child) == 0) {
> +			Dwarf_Die *die = &child;
> +
> +			do {
> +				Dwarf_Die echild, *edie;
> +
> +				if (dwarf_tag(die) != DW_TAG_enumeration_type ||
> +				    !dwarf_haschildren(die) ||
> +				    dwarf_child(die, &echild) != 0)
> +					continue;
> +				edie = &echild;
> +				do {
> +					const char *ename;
> +					int btf_kind_max;
> +
> +					if (dwarf_tag(edie) != DW_TAG_enumerator)
> +						continue;
> +					ename = attr_string(edie, DW_AT_name, conf);
> +					if (!ename || strcmp(ename, "BTF_KIND_MAX") != 0)
> +						continue;
> +					btf_kind_max = attr_numeric(edie, DW_AT_const_value);
> +					dwarves__set_btf_kind_max(conf, btf_kind_max);
> +					return;
> +				} while (dwarf_siblingof(edie, edie) == 0);
> +			} while (dwarf_siblingof(die, die) == 0);
> +		}
> +		off = noff;
> +	}
> +}
> +
>  static int dwarf_cus__process_cus(struct dwarf_cus *dcus)
>  {
> +	dwarf__find_btf_kind_max(dcus);

first I though this should be enabled by some (detect) option.. but that
would probably beat the main purpose.. also I think we don't need kernel
with BTF that it can't process

jirka

> +
>  	if (dcus->conf->nr_jobs > 1)
>  		return dwarf_cus__threaded_process_cus(dcus);
>  
> diff --git a/dwarves.h b/dwarves.h
> index eb1a6df..f4d9347 100644
> --- a/dwarves.h
> +++ b/dwarves.h
> @@ -1480,4 +1480,6 @@ extern const char tabs[];
>  #define DW_TAG_skeleton_unit 0x4a
>  #endif
>  
> +void dwarves__set_btf_kind_max(struct conf_load *conf_load, int btf_kind_max);
> +
>  #endif /* _DWARVES_H_ */
> -- 
> 2.39.3
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC dwarves 1/2] dwarves: auto-detect maximum kind supported by vmlinux
  2023-07-26 10:39   ` Jiri Olsa
@ 2023-07-26 15:28     ` Alan Maguire
  2023-07-27 15:47       ` Alexei Starovoitov
  0 siblings, 1 reply; 6+ messages in thread
From: Alan Maguire @ 2023-07-26 15:28 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: acme, andrii.nakryiko, ast, daniel, martin.lau, song, yhs,
	john.fastabend, kpsingh, sdf, haoluo, mykolal, bpf

On 26/07/2023 11:39, Jiri Olsa wrote:
> On Thu, Jul 20, 2023 at 09:14:42PM +0100, Alan Maguire wrote:
>> When a newer pahole is run on an older kernel, it often knows about BTF
>> kinds that the kernel does not support.  This is a problem because the BTF
>> generated is then embedded in the kernel image and read, and if unknown
>> kinds are found, BTF handling fails and core BPF functionality is
>> unavailable.
>>
>> The scripts/pahole-flags.sh script enumerates the various pahole options
>> available associated with various versions of pahole, but the problem is
>> what matters in the case of an older kernel is the set of kinds the kernel
>> understands.  Because recent features such as BTF_KIND_ENUM64 are added
>> by default (and only skipped if --skip_encoding_btf_* is set), BTF will
>> be created with these newer kinds that the older kernel cannot read.
>> This can be fixed by stable-backporting --skip options, but this is
>> cumbersome and would have to be done every time a new BTF kind is
>> introduced.
>>
>> Here instead we pre-process the DWARF information associated with the
>> target for BTF generation; if we find an enum with a BTF_KIND_MAX
>> value in the DWARF associated with the object, we use that to
>> determine the maximum BTF kind supported.  Note that the enum
>> representation of BTF kinds starts for the 5.16 kernel; prior to this
>> The benefit of auto-detection is that no work is required for older
>> kernels when new kinds are added, and --skip_encoding options are
>> less needed.
>>
>> [1] https://github.com/oracle-samples/bpftune/issues/35
>>
>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>> ---
>>  btf_encoder.c  | 12 ++++++++++++
>>  dwarf_loader.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>  dwarves.h      |  2 ++
>>  3 files changed, 66 insertions(+)
>>
>> diff --git a/btf_encoder.c b/btf_encoder.c
>> index 65f6e71..98c7529 100644
>> --- a/btf_encoder.c
>> +++ b/btf_encoder.c
>> @@ -1889,3 +1889,15 @@ struct btf *btf_encoder__btf(struct btf_encoder *encoder)
>>  {
>>  	return encoder->btf;
>>  }
>> +
>> +void dwarves__set_btf_kind_max(struct conf_load *conf_load, int btf_kind_max)
>> +{
>> +	if (btf_kind_max < 0 || btf_kind_max >= BTF_KIND_MAX)
>> +		return;
>> +	if (btf_kind_max < BTF_KIND_DECL_TAG)
>> +		conf_load->skip_encoding_btf_decl_tag = true;
>> +	if (btf_kind_max < BTF_KIND_TYPE_TAG)
>> +		conf_load->skip_encoding_btf_type_tag = true;
>> +	if (btf_kind_max < BTF_KIND_ENUM64)
>> +		conf_load->skip_encoding_btf_enum64 = true;
>> +}
> 
> hi,
> so there are some older kernels other than stable that would use this feature
> right? because stable already have proper setup for pahole options
> 
> or it's just there to be complete and we'd eventually add new rules in here?
> wouldn't that be covered by the BTF kind layout stuff you work on? is there
> some overlap?
> 

Yeah, the idea is to minimize the complexity when adding new kinds. The
approach explored here does this because when adding a new kind we have
to either

- make it a pahole opt-in parameter, which means more --btf_encode_*
parameters for dwarves; or
- make it an opt-out parameter, which means more stable backports to set
the opt-out.

My original hope was BTF kind layout encoding would solve the problem,
but the problem is that new kinds are often entwined tightly in the
representation of structures, functions etc. When that happens, even
knowing the kind layout won't help an older kernel interpret what the
BTF actually means when it was generated by a newer pahole. Kind layout
still has value - it means BTF can always be dumped for example - but it
can't really help the kernel to reliabily _use_ kernel/module BTF
information. So the approach here is to try to streamline the process by
not requiring new options when a new kind is added; we can simply detect
if the kernel knows about the kind and skip it if not. Since this
detection is all internal to pahole, nothing needs to be exposed to the
user.


>> diff --git a/dwarf_loader.c b/dwarf_loader.c
>> index ccf3194..8984043 100644
>> --- a/dwarf_loader.c
>> +++ b/dwarf_loader.c
>> @@ -3358,8 +3358,60 @@ static int __dwarf_cus__process_cus(struct dwarf_cus *dcus)
>>  	return 0;
>>  }
>>  
>> +/* Find enumeration value for BTF_KIND_MAX; replace conf_load->btf_kind_max with
>> + * this value if found since it indicates that the target object does not know
>> + * about kinds > its BTF_KIND_MAX value.  This is valuable for kernel/module
>> + * BTF where a newer pahole/libbpf operate on an older kernel which cannot
>> + * parse some of the newer kinds pahole can generate.
>> + */
>> +static void dwarf__find_btf_kind_max(struct dwarf_cus *dcus)
>> +{
>> +	struct conf_load *conf = dcus->conf;
>> +	uint8_t pointer_size, offset_size;
>> +	Dwarf_Off off = 0, noff;
>> +	size_t cuhl;
>> +
>> +	while (dwarf_nextcu(dcus->dw, off, &noff, &cuhl, NULL, &pointer_size, &offset_size) == 0) {
>> +		Dwarf_Die die_mem;
>> +		Dwarf_Die *cu_die = dwarf_offdie(dcus->dw, off + cuhl, &die_mem);
>> +		Dwarf_Die child;
>> +
>> +		if (cu_die == NULL)
>> +			break;
>> +		if (dwarf_child(cu_die, &child) == 0) {
>> +			Dwarf_Die *die = &child;
>> +
>> +			do {
>> +				Dwarf_Die echild, *edie;
>> +
>> +				if (dwarf_tag(die) != DW_TAG_enumeration_type ||
>> +				    !dwarf_haschildren(die) ||
>> +				    dwarf_child(die, &echild) != 0)
>> +					continue;
>> +				edie = &echild;
>> +				do {
>> +					const char *ename;
>> +					int btf_kind_max;
>> +
>> +					if (dwarf_tag(edie) != DW_TAG_enumerator)
>> +						continue;
>> +					ename = attr_string(edie, DW_AT_name, conf);
>> +					if (!ename || strcmp(ename, "BTF_KIND_MAX") != 0)
>> +						continue;
>> +					btf_kind_max = attr_numeric(edie, DW_AT_const_value);
>> +					dwarves__set_btf_kind_max(conf, btf_kind_max);
>> +					return;
>> +				} while (dwarf_siblingof(edie, edie) == 0);
>> +			} while (dwarf_siblingof(die, die) == 0);
>> +		}
>> +		off = noff;
>> +	}
>> +}
>> +
>>  static int dwarf_cus__process_cus(struct dwarf_cus *dcus)
>>  {
>> +	dwarf__find_btf_kind_max(dcus);
> 
> first I though this should be enabled by some (detect) option.. but that
> would probably beat the main purpose.. also I think we don't need kernel
> with BTF that it can't process
> 

Hmm, maybe it might be good to have it associated with --btf_gen_all in
case we ever want to disable it for debugging purposes? What do you
think? Thanks!

Alan

> jirka
> 
>> +
>>  	if (dcus->conf->nr_jobs > 1)
>>  		return dwarf_cus__threaded_process_cus(dcus);
>>  
>> diff --git a/dwarves.h b/dwarves.h
>> index eb1a6df..f4d9347 100644
>> --- a/dwarves.h
>> +++ b/dwarves.h
>> @@ -1480,4 +1480,6 @@ extern const char tabs[];
>>  #define DW_TAG_skeleton_unit 0x4a
>>  #endif
>>  
>> +void dwarves__set_btf_kind_max(struct conf_load *conf_load, int btf_kind_max);
>> +
>>  #endif /* _DWARVES_H_ */
>> -- 
>> 2.39.3
>>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC dwarves 1/2] dwarves: auto-detect maximum kind supported by vmlinux
  2023-07-26 15:28     ` Alan Maguire
@ 2023-07-27 15:47       ` Alexei Starovoitov
  0 siblings, 0 replies; 6+ messages in thread
From: Alexei Starovoitov @ 2023-07-27 15:47 UTC (permalink / raw)
  To: Alan Maguire
  Cc: Jiri Olsa, Arnaldo Carvalho de Melo, Andrii Nakryiko,
	Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
	Hao Luo, Mykola Lysenko, bpf

On Wed, Jul 26, 2023 at 8:29 AM Alan Maguire <alan.maguire@oracle.com> wrote:
>
> On 26/07/2023 11:39, Jiri Olsa wrote:
> > On Thu, Jul 20, 2023 at 09:14:42PM +0100, Alan Maguire wrote:
> >> When a newer pahole is run on an older kernel, it often knows about BTF
> >> kinds that the kernel does not support.  This is a problem because the BTF
> >> generated is then embedded in the kernel image and read, and if unknown
> >> kinds are found, BTF handling fails and core BPF functionality is
> >> unavailable.
> >>
> >> The scripts/pahole-flags.sh script enumerates the various pahole options
> >> available associated with various versions of pahole, but the problem is
> >> what matters in the case of an older kernel is the set of kinds the kernel
> >> understands.  Because recent features such as BTF_KIND_ENUM64 are added
> >> by default (and only skipped if --skip_encoding_btf_* is set), BTF will
> >> be created with these newer kinds that the older kernel cannot read.
> >> This can be fixed by stable-backporting --skip options, but this is
> >> cumbersome and would have to be done every time a new BTF kind is
> >> introduced.
> >>
> >> Here instead we pre-process the DWARF information associated with the
> >> target for BTF generation; if we find an enum with a BTF_KIND_MAX
> >> value in the DWARF associated with the object, we use that to
> >> determine the maximum BTF kind supported.  Note that the enum
> >> representation of BTF kinds starts for the 5.16 kernel; prior to this
> >> The benefit of auto-detection is that no work is required for older
> >> kernels when new kinds are added, and --skip_encoding options are
> >> less needed.
> >>
> >> [1] https://github.com/oracle-samples/bpftune/issues/35
> >>
> >> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> >> ---
> >>  btf_encoder.c  | 12 ++++++++++++
> >>  dwarf_loader.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
> >>  dwarves.h      |  2 ++
> >>  3 files changed, 66 insertions(+)
> >>
> >> diff --git a/btf_encoder.c b/btf_encoder.c
> >> index 65f6e71..98c7529 100644
> >> --- a/btf_encoder.c
> >> +++ b/btf_encoder.c
> >> @@ -1889,3 +1889,15 @@ struct btf *btf_encoder__btf(struct btf_encoder *encoder)
> >>  {
> >>      return encoder->btf;
> >>  }
> >> +
> >> +void dwarves__set_btf_kind_max(struct conf_load *conf_load, int btf_kind_max)
> >> +{
> >> +    if (btf_kind_max < 0 || btf_kind_max >= BTF_KIND_MAX)
> >> +            return;
> >> +    if (btf_kind_max < BTF_KIND_DECL_TAG)
> >> +            conf_load->skip_encoding_btf_decl_tag = true;
> >> +    if (btf_kind_max < BTF_KIND_TYPE_TAG)
> >> +            conf_load->skip_encoding_btf_type_tag = true;
> >> +    if (btf_kind_max < BTF_KIND_ENUM64)
> >> +            conf_load->skip_encoding_btf_enum64 = true;
> >> +}
> >
> > hi,
> > so there are some older kernels other than stable that would use this feature
> > right? because stable already have proper setup for pahole options
> >
> > or it's just there to be complete and we'd eventually add new rules in here?
> > wouldn't that be covered by the BTF kind layout stuff you work on? is there
> > some overlap?
> >
>
> Yeah, the idea is to minimize the complexity when adding new kinds. The
> approach explored here does this because when adding a new kind we have
> to either
>
> - make it a pahole opt-in parameter, which means more --btf_encode_*
> parameters for dwarves; or
> - make it an opt-out parameter, which means more stable backports to set
> the opt-out.
>
> My original hope was BTF kind layout encoding would solve the problem,
> but the problem is that new kinds are often entwined tightly in the
> representation of structures, functions etc. When that happens, even
> knowing the kind layout won't help an older kernel interpret what the
> BTF actually means when it was generated by a newer pahole. Kind layout
> still has value - it means BTF can always be dumped for example - but it
> can't really help the kernel to reliabily _use_ kernel/module BTF
> information. So the approach here is to try to streamline the process by
> not requiring new options when a new kind is added; we can simply detect
> if the kernel knows about the kind and skip it if not. Since this
> detection is all internal to pahole, nothing needs to be exposed to the
> user.
>
>
> >> diff --git a/dwarf_loader.c b/dwarf_loader.c
> >> index ccf3194..8984043 100644
> >> --- a/dwarf_loader.c
> >> +++ b/dwarf_loader.c
> >> @@ -3358,8 +3358,60 @@ static int __dwarf_cus__process_cus(struct dwarf_cus *dcus)
> >>      return 0;
> >>  }
> >>
> >> +/* Find enumeration value for BTF_KIND_MAX; replace conf_load->btf_kind_max with
> >> + * this value if found since it indicates that the target object does not know
> >> + * about kinds > its BTF_KIND_MAX value.  This is valuable for kernel/module
> >> + * BTF where a newer pahole/libbpf operate on an older kernel which cannot
> >> + * parse some of the newer kinds pahole can generate.
> >> + */
> >> +static void dwarf__find_btf_kind_max(struct dwarf_cus *dcus)
> >> +{
> >> +    struct conf_load *conf = dcus->conf;
> >> +    uint8_t pointer_size, offset_size;
> >> +    Dwarf_Off off = 0, noff;
> >> +    size_t cuhl;
> >> +
> >> +    while (dwarf_nextcu(dcus->dw, off, &noff, &cuhl, NULL, &pointer_size, &offset_size) == 0) {
> >> +            Dwarf_Die die_mem;
> >> +            Dwarf_Die *cu_die = dwarf_offdie(dcus->dw, off + cuhl, &die_mem);
> >> +            Dwarf_Die child;
> >> +
> >> +            if (cu_die == NULL)
> >> +                    break;
> >> +            if (dwarf_child(cu_die, &child) == 0) {
> >> +                    Dwarf_Die *die = &child;
> >> +
> >> +                    do {
> >> +                            Dwarf_Die echild, *edie;
> >> +
> >> +                            if (dwarf_tag(die) != DW_TAG_enumeration_type ||
> >> +                                !dwarf_haschildren(die) ||
> >> +                                dwarf_child(die, &echild) != 0)
> >> +                                    continue;
> >> +                            edie = &echild;
> >> +                            do {
> >> +                                    const char *ename;
> >> +                                    int btf_kind_max;
> >> +
> >> +                                    if (dwarf_tag(edie) != DW_TAG_enumerator)
> >> +                                            continue;
> >> +                                    ename = attr_string(edie, DW_AT_name, conf);
> >> +                                    if (!ename || strcmp(ename, "BTF_KIND_MAX") != 0)
> >> +                                            continue;
> >> +                                    btf_kind_max = attr_numeric(edie, DW_AT_const_value);
> >> +                                    dwarves__set_btf_kind_max(conf, btf_kind_max);
> >> +                                    return;
> >> +                            } while (dwarf_siblingof(edie, edie) == 0);
> >> +                    } while (dwarf_siblingof(die, die) == 0);
> >> +            }
> >> +            off = noff;
> >> +    }
> >> +}
> >> +
> >>  static int dwarf_cus__process_cus(struct dwarf_cus *dcus)
> >>  {
> >> +    dwarf__find_btf_kind_max(dcus);
> >
> > first I though this should be enabled by some (detect) option.. but that
> > would probably beat the main purpose.. also I think we don't need kernel
> > with BTF that it can't process
> >
>
> Hmm, maybe it might be good to have it associated with --btf_gen_all in
> case we ever want to disable it for debugging purposes? What do you
> think? Thanks!

Overall approach makes sense to me and an extra flag like --btf_gen_all
to disable this smartness also makes sense.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-07-27 15:48 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-20 20:14 [RFC dwarves 0/2] dwarves: detect BTF kinds supported by kernel Alan Maguire
2023-07-20 20:14 ` [RFC dwarves 1/2] dwarves: auto-detect maximum kind supported by vmlinux Alan Maguire
2023-07-26 10:39   ` Jiri Olsa
2023-07-26 15:28     ` Alan Maguire
2023-07-27 15:47       ` Alexei Starovoitov
2023-07-20 20:14 ` [RFC dwarves 2/2] btf_encoder: learn BTF_KIND_MAX value from base BTF when generating split BTF Alan Maguire

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).