diff mbox series

[RFC,dwarves,3/4] pahole: add --btf_features=feature1[,feature2...] support

Message ID 20231011091732.93254-4-alan.maguire@oracle.com (mailing list archive)
State Superseded
Headers show
Series pahole, btf_encoder: support --btf_features | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Alan Maguire Oct. 11, 2023, 9:17 a.m. UTC
This allows consumers to specify an opt-in set of features
they want to use in BTF encoding.

Supported features are

	encode_force  Ignore invalid symbols when encoding BTF.
	var           Encode variables using BTF_KIND_VAR in BTF.
	float         Encode floating-point types in BTF.
	decl_tag      Encode declaration tags using BTF_KIND_DECL_TAG.
	type_tag      Encode type tags using BTF_KIND_TYPE_TAG.
	enum64        Encode enum64 values with BTF_KIND_ENUM64.
	optimized     Encode representations of optimized functions
	              with suffixes like ".isra.0" etc
	consistent    Avoid encoding inconsistent static functions.
	              These occur when a parameter is optimized out
	              in some CUs and not others, or when the same
	              function name has inconsistent BTF descriptions
	              in different CUs.

Specifying "--btf_features=all" is the equivalent to setting
all of the above.  If pahole does not know about a feature
it silently ignores it.  These properties allow us to use
the --btf_features option in the kernel pahole_flags.sh
script to specify the desired set of features.  If a new
feature is not present in pahole but requested, pahole
BTF encoding will not complain (but will not encode the
feature).

Suggested-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 man-pages/pahole.1 | 20 +++++++++++
 pahole.c           | 87 +++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 106 insertions(+), 1 deletion(-)

Comments

Eduard Zingerman Oct. 11, 2023, 4:28 p.m. UTC | #1
On Wed, 2023-10-11 at 10:17 +0100, Alan Maguire wrote:
> This allows consumers to specify an opt-in set of features
> they want to use in BTF encoding.
> 
> Supported features are
> 
> 	encode_force  Ignore invalid symbols when encoding BTF.
> 	var           Encode variables using BTF_KIND_VAR in BTF.
> 	float         Encode floating-point types in BTF.
> 	decl_tag      Encode declaration tags using BTF_KIND_DECL_TAG.
> 	type_tag      Encode type tags using BTF_KIND_TYPE_TAG.
> 	enum64        Encode enum64 values with BTF_KIND_ENUM64.
> 	optimized     Encode representations of optimized functions
> 	              with suffixes like ".isra.0" etc
> 	consistent    Avoid encoding inconsistent static functions.
> 	              These occur when a parameter is optimized out
> 	              in some CUs and not others, or when the same
> 	              function name has inconsistent BTF descriptions
> 	              in different CUs.
> 
> Specifying "--btf_features=all" is the equivalent to setting
> all of the above.  If pahole does not know about a feature
> it silently ignores it.  These properties allow us to use
> the --btf_features option in the kernel pahole_flags.sh
> script to specify the desired set of features.  If a new
> feature is not present in pahole but requested, pahole
> BTF encoding will not complain (but will not encode the
> feature).
> 
> Suggested-by: Andrii Nakryiko <andrii@kernel.org>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
>  man-pages/pahole.1 | 20 +++++++++++
>  pahole.c           | 87 +++++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 106 insertions(+), 1 deletion(-)
> 
> diff --git a/man-pages/pahole.1 b/man-pages/pahole.1
> index c1b48de..7c072dc 100644
> --- a/man-pages/pahole.1
> +++ b/man-pages/pahole.1
> @@ -273,6 +273,26 @@ Generate BTF for functions with optimization-related suffixes (.isra, .constprop
>  .B \-\-btf_gen_all
>  Allow using all the BTF features supported by pahole.
>  
> +.TP
> +.B \-\-btf_features=FEATURE_LIST
> +Encode BTF using the specified feature list, or specify 'all' for all features supported.  This single parameter value can be used as an alternative to unsing multiple BTF-related options. Supported features are
> +
> +.nf
> +	encode_force  Ignore invalid symbols when encoding BTF.
> +	var           Encode variables using BTF_KIND_VAR in BTF.
> +	float         Encode floating-point types in BTF.
> +	decl_tag      Encode declaration tags using BTF_KIND_DECL_TAG.
> +	type_tag      Encode type tags using BTF_KIND_TYPE_TAG.
> +	enum64        Encode enum64 values with BTF_KIND_ENUM64.
> +	optimized     Encode representations of optimized functions
> +	              with suffixes like ".isra.0" etc
> +	consistent    Avoid encoding inconsistent static functions.
> +	              These occur when a parameter is optimized out
> +	              in some CUs and not others, or when the same
> +	              function name has inconsistent BTF descriptions
> +	              in different CUs.
> +.fi
> +
>  .TP
>  .B \-l, \-\-show_first_biggest_size_base_type_member
>  Show first biggest size base_type member.
> diff --git a/pahole.c b/pahole.c
> index 7a41dc3..4f00b08 100644
> --- a/pahole.c
> +++ b/pahole.c
> @@ -1229,6 +1229,83 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
>  #define ARGP_skip_emitting_atomic_typedefs 338
>  #define ARGP_btf_gen_optimized  339
>  #define ARGP_skip_encoding_btf_inconsistent_proto 340
> +#define ARGP_btf_features	341
> +
> +/* --btf_features=feature1[,feature2,..] option allows us to specify
> + * opt-in features (or "all"); these are translated into conf_load
> + * values by specifying the associated bool offset and whether it
> + * is a skip option or not; btf_features is for opting _into_ features
> + * so for skip options we have to reverse the logic.  For example
> + * "--skip_encoding_btf_type_tag --btf_gen_floats" translate to
> + * "--btf_features=type_tag,float"
> + */
> +#define BTF_FEATURE(name, alias, skip)				\
> +	{ #name, #alias, offsetof(struct conf_load, alias), skip }
> +
> +struct btf_feature {
> +	const char      *name;
> +	const char      *option_alias;
> +	size_t          conf_load_offset;
> +	bool		skip;
> +} btf_features[] = {
> +	BTF_FEATURE(encode_force, btf_encode_force, false),
> +	BTF_FEATURE(var, skip_encoding_btf_vars, true),
> +	BTF_FEATURE(float, btf_gen_floats, false),
> +	BTF_FEATURE(decl_tag, skip_encoding_btf_decl_tag, true),
> +	BTF_FEATURE(type_tag, skip_encoding_btf_type_tag, true),
> +	BTF_FEATURE(enum64, skip_encoding_btf_enum64, true),
> +	BTF_FEATURE(optimized, btf_gen_optimized, false),
> +	/* the "skip" in skip_encoding_btf_inconsistent_proto is misleading
> +	 * here; this is a positive feature to ensure consistency of
> +	 * representation rather than a negative option which we want
> +	 * to invert.  So as a result, "skip" is false here.
> +	 */
> +	BTF_FEATURE(consistent, skip_encoding_btf_inconsistent_proto, false),
> +};
> +
> +#define BTF_MAX_FEATURES	32
> +#define BTF_MAX_FEATURE_STR	256
> +
> +/* Translate --btf_features=feature1[,feature2] into conf_load values.
> + * Explicitly ignores unrecognized features to allow future specification
> + * of new opt-in features.
> + */
> +static void parse_btf_features(const char *features, struct conf_load *conf_load)
> +{
> +	char *feature_list[BTF_MAX_FEATURES] = {};
> +	char f[BTF_MAX_FEATURE_STR];
> +	bool encode_all = false;
> +	int i, j, n = 0;
> +
> +	strncpy(f, features, sizeof(f));
> +
> +	if (strcmp(features, "all") == 0) {
> +		encode_all = true;
> +	} else {
> +		char *saveptr = NULL, *s = f, *t;
> +
> +		while ((t = strtok_r(s, ",", &saveptr)) != NULL) {
> +			s = NULL;
> +			feature_list[n++] = t;

Maybe guard against `n` >= BTF_MAX_FEATURES here?

> +		}
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(btf_features); i++) {
> +		bool *bval = (bool *)(((void *)conf_load) + btf_features[i].conf_load_offset);
> +		bool match = encode_all;
> +
> +		if (!match) {
> +			for (j = 0; j < n; j++) {
> +				if (strcmp(feature_list[j], btf_features[i].name) == 0) {
> +					match = true;
> +					break;
> +				}
> +			}
> +		}
> +		if (match)
> +			*bval = btf_features[i].skip ? false : true;

I'm not sure I understand the logic behind "skip" features.
Take `decl_tag` for example:
- by default conf_load->skip_encoding_btf_decl_tag is 0;
- if `--btf_features=decl_tag` is passed it is still 0 because of the
  `skip ? false : true` logic.

If there is no way to change "skip" features why listing these at all?

Other than that I tested the patch-set with current kernel master and
a change to pahole-flags.sh and bpf tests pass.

> +	}
> +}
>  
>  static const struct argp_option pahole__options[] = {
>  	{
> @@ -1651,6 +1728,12 @@ static const struct argp_option pahole__options[] = {
>  		.key = ARGP_skip_encoding_btf_inconsistent_proto,
>  		.doc = "Skip functions that have multiple inconsistent function prototypes sharing the same name, or that use unexpected registers for parameter values."
>  	},
> +	{
> +		.name = "btf_features",
> +		.key = ARGP_btf_features,
> +		.arg = "FEATURE_LIST",
> +		.doc = "Specify supported BTF features in FEATURE_LIST or 'all' for all supported features. See the pahole manual page for the list of supported features."
> +	},
>  	{
>  		.name = NULL,
>  	}
> @@ -1796,7 +1879,7 @@ static error_t pahole__options_parser(int key, char *arg,
>  	case ARGP_btf_gen_floats:
>  		conf_load.btf_gen_floats = true;	break;
>  	case ARGP_btf_gen_all:
> -		conf_load.btf_gen_floats = true;	break;
> +		parse_btf_features("all", &conf_load);	break;
>  	case ARGP_with_flexible_array:
>  		show_with_flexible_array = true;	break;
>  	case ARGP_prettify_input_filename:
> @@ -1826,6 +1909,8 @@ static error_t pahole__options_parser(int key, char *arg,
>  		conf_load.btf_gen_optimized = true;		break;
>  	case ARGP_skip_encoding_btf_inconsistent_proto:
>  		conf_load.skip_encoding_btf_inconsistent_proto = true; break;
> +	case ARGP_btf_features:
> +		parse_btf_features(arg, &conf_load);	break;
>  	default:
>  		return ARGP_ERR_UNKNOWN;
>  	}
Alan Maguire Oct. 11, 2023, 4:41 p.m. UTC | #2
On 11/10/2023 17:28, Eduard Zingerman wrote:
> On Wed, 2023-10-11 at 10:17 +0100, Alan Maguire wrote:
>> This allows consumers to specify an opt-in set of features
>> they want to use in BTF encoding.
>>
>> Supported features are
>>
>> 	encode_force  Ignore invalid symbols when encoding BTF.
>> 	var           Encode variables using BTF_KIND_VAR in BTF.
>> 	float         Encode floating-point types in BTF.
>> 	decl_tag      Encode declaration tags using BTF_KIND_DECL_TAG.
>> 	type_tag      Encode type tags using BTF_KIND_TYPE_TAG.
>> 	enum64        Encode enum64 values with BTF_KIND_ENUM64.
>> 	optimized     Encode representations of optimized functions
>> 	              with suffixes like ".isra.0" etc
>> 	consistent    Avoid encoding inconsistent static functions.
>> 	              These occur when a parameter is optimized out
>> 	              in some CUs and not others, or when the same
>> 	              function name has inconsistent BTF descriptions
>> 	              in different CUs.
>>
>> Specifying "--btf_features=all" is the equivalent to setting
>> all of the above.  If pahole does not know about a feature
>> it silently ignores it.  These properties allow us to use
>> the --btf_features option in the kernel pahole_flags.sh
>> script to specify the desired set of features.  If a new
>> feature is not present in pahole but requested, pahole
>> BTF encoding will not complain (but will not encode the
>> feature).
>>
>> Suggested-by: Andrii Nakryiko <andrii@kernel.org>
>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>> ---
>>  man-pages/pahole.1 | 20 +++++++++++
>>  pahole.c           | 87 +++++++++++++++++++++++++++++++++++++++++++++-
>>  2 files changed, 106 insertions(+), 1 deletion(-)
>>
>> diff --git a/man-pages/pahole.1 b/man-pages/pahole.1
>> index c1b48de..7c072dc 100644
>> --- a/man-pages/pahole.1
>> +++ b/man-pages/pahole.1
>> @@ -273,6 +273,26 @@ Generate BTF for functions with optimization-related suffixes (.isra, .constprop
>>  .B \-\-btf_gen_all
>>  Allow using all the BTF features supported by pahole.
>>  
>> +.TP
>> +.B \-\-btf_features=FEATURE_LIST
>> +Encode BTF using the specified feature list, or specify 'all' for all features supported.  This single parameter value can be used as an alternative to unsing multiple BTF-related options. Supported features are
>> +
>> +.nf
>> +	encode_force  Ignore invalid symbols when encoding BTF.
>> +	var           Encode variables using BTF_KIND_VAR in BTF.
>> +	float         Encode floating-point types in BTF.
>> +	decl_tag      Encode declaration tags using BTF_KIND_DECL_TAG.
>> +	type_tag      Encode type tags using BTF_KIND_TYPE_TAG.
>> +	enum64        Encode enum64 values with BTF_KIND_ENUM64.
>> +	optimized     Encode representations of optimized functions
>> +	              with suffixes like ".isra.0" etc
>> +	consistent    Avoid encoding inconsistent static functions.
>> +	              These occur when a parameter is optimized out
>> +	              in some CUs and not others, or when the same
>> +	              function name has inconsistent BTF descriptions
>> +	              in different CUs.
>> +.fi
>> +
>>  .TP
>>  .B \-l, \-\-show_first_biggest_size_base_type_member
>>  Show first biggest size base_type member.
>> diff --git a/pahole.c b/pahole.c
>> index 7a41dc3..4f00b08 100644
>> --- a/pahole.c
>> +++ b/pahole.c
>> @@ -1229,6 +1229,83 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
>>  #define ARGP_skip_emitting_atomic_typedefs 338
>>  #define ARGP_btf_gen_optimized  339
>>  #define ARGP_skip_encoding_btf_inconsistent_proto 340
>> +#define ARGP_btf_features	341
>> +
>> +/* --btf_features=feature1[,feature2,..] option allows us to specify
>> + * opt-in features (or "all"); these are translated into conf_load
>> + * values by specifying the associated bool offset and whether it
>> + * is a skip option or not; btf_features is for opting _into_ features
>> + * so for skip options we have to reverse the logic.  For example
>> + * "--skip_encoding_btf_type_tag --btf_gen_floats" translate to
>> + * "--btf_features=type_tag,float"
>> + */
>> +#define BTF_FEATURE(name, alias, skip)				\
>> +	{ #name, #alias, offsetof(struct conf_load, alias), skip }
>> +
>> +struct btf_feature {
>> +	const char      *name;
>> +	const char      *option_alias;
>> +	size_t          conf_load_offset;
>> +	bool		skip;
>> +} btf_features[] = {
>> +	BTF_FEATURE(encode_force, btf_encode_force, false),
>> +	BTF_FEATURE(var, skip_encoding_btf_vars, true),
>> +	BTF_FEATURE(float, btf_gen_floats, false),
>> +	BTF_FEATURE(decl_tag, skip_encoding_btf_decl_tag, true),
>> +	BTF_FEATURE(type_tag, skip_encoding_btf_type_tag, true),
>> +	BTF_FEATURE(enum64, skip_encoding_btf_enum64, true),
>> +	BTF_FEATURE(optimized, btf_gen_optimized, false),
>> +	/* the "skip" in skip_encoding_btf_inconsistent_proto is misleading
>> +	 * here; this is a positive feature to ensure consistency of
>> +	 * representation rather than a negative option which we want
>> +	 * to invert.  So as a result, "skip" is false here.
>> +	 */
>> +	BTF_FEATURE(consistent, skip_encoding_btf_inconsistent_proto, false),
>> +};
>> +
>> +#define BTF_MAX_FEATURES	32
>> +#define BTF_MAX_FEATURE_STR	256
>> +
>> +/* Translate --btf_features=feature1[,feature2] into conf_load values.
>> + * Explicitly ignores unrecognized features to allow future specification
>> + * of new opt-in features.
>> + */
>> +static void parse_btf_features(const char *features, struct conf_load *conf_load)
>> +{
>> +	char *feature_list[BTF_MAX_FEATURES] = {};
>> +	char f[BTF_MAX_FEATURE_STR];
>> +	bool encode_all = false;
>> +	int i, j, n = 0;
>> +
>> +	strncpy(f, features, sizeof(f));
>> +
>> +	if (strcmp(features, "all") == 0) {
>> +		encode_all = true;
>> +	} else {
>> +		char *saveptr = NULL, *s = f, *t;
>> +
>> +		while ((t = strtok_r(s, ",", &saveptr)) != NULL) {
>> +			s = NULL;
>> +			feature_list[n++] = t;
> 
> Maybe guard against `n` >= BTF_MAX_FEATURES here?
>

good point - will fix.

>> +		}
>> +	}
>> +
>> +	for (i = 0; i < ARRAY_SIZE(btf_features); i++) {
>> +		bool *bval = (bool *)(((void *)conf_load) + btf_features[i].conf_load_offset);
>> +		bool match = encode_all;
>> +
>> +		if (!match) {
>> +			for (j = 0; j < n; j++) {
>> +				if (strcmp(feature_list[j], btf_features[i].name) == 0) {
>> +					match = true;
>> +					break;
>> +				}
>> +			}
>> +		}
>> +		if (match)
>> +			*bval = btf_features[i].skip ? false : true;
> 
> I'm not sure I understand the logic behind "skip" features.
> Take `decl_tag` for example:
> - by default conf_load->skip_encoding_btf_decl_tag is 0;
> - if `--btf_features=decl_tag` is passed it is still 0 because of the
>   `skip ? false : true` logic.
> 
> If there is no way to change "skip" features why listing these at all?
> 
You're right; in the case of a skip feature, I think we need the
following behaviour

1. we skip the encoding by default (so the equivalent of
--skip_encoding_btf_decl_tag, setting skip_encoding_btf_decl_tag
to true
2. if the user however specifies the logical inversion of the skip
feature in --btf_features (in this case "decl_tag" - or "all")
skip_encoding_btf_decl_tag is set to false.

So in my code we had 2 above but not 1. If both were in place I think
we'd have the right set of behaviours. Does that sound right?

Maybe a better way to express all this would be to rename the "skip"
field in "struct btf_feature" to "default" - so in the case of a "skip"
feature, the default is true, but for opt-in features, the default is false.

> Other than that I tested the patch-set with current kernel master and
> a change to pahole-flags.sh and bpf tests pass.
> 

Thanks so much for testing this!

Alan

>> +	}
>> +}
>>  
>>  static const struct argp_option pahole__options[] = {
>>  	{
>> @@ -1651,6 +1728,12 @@ static const struct argp_option pahole__options[] = {
>>  		.key = ARGP_skip_encoding_btf_inconsistent_proto,
>>  		.doc = "Skip functions that have multiple inconsistent function prototypes sharing the same name, or that use unexpected registers for parameter values."
>>  	},
>> +	{
>> +		.name = "btf_features",
>> +		.key = ARGP_btf_features,
>> +		.arg = "FEATURE_LIST",
>> +		.doc = "Specify supported BTF features in FEATURE_LIST or 'all' for all supported features. See the pahole manual page for the list of supported features."
>> +	},
>>  	{
>>  		.name = NULL,
>>  	}
>> @@ -1796,7 +1879,7 @@ static error_t pahole__options_parser(int key, char *arg,
>>  	case ARGP_btf_gen_floats:
>>  		conf_load.btf_gen_floats = true;	break;
>>  	case ARGP_btf_gen_all:
>> -		conf_load.btf_gen_floats = true;	break;
>> +		parse_btf_features("all", &conf_load);	break;
>>  	case ARGP_with_flexible_array:
>>  		show_with_flexible_array = true;	break;
>>  	case ARGP_prettify_input_filename:
>> @@ -1826,6 +1909,8 @@ static error_t pahole__options_parser(int key, char *arg,
>>  		conf_load.btf_gen_optimized = true;		break;
>>  	case ARGP_skip_encoding_btf_inconsistent_proto:
>>  		conf_load.skip_encoding_btf_inconsistent_proto = true; break;
>> +	case ARGP_btf_features:
>> +		parse_btf_features(arg, &conf_load);	break;
>>  	default:
>>  		return ARGP_ERR_UNKNOWN;
>>  	}
>
Eduard Zingerman Oct. 11, 2023, 7:08 p.m. UTC | #3
On Wed, 2023-10-11 at 17:41 +0100, Alan Maguire wrote:
[...]
> > > +		}
> > > +	}
> > > +
> > > +	for (i = 0; i < ARRAY_SIZE(btf_features); i++) {
> > > +		bool *bval = (bool *)(((void *)conf_load) + btf_features[i].conf_load_offset);
> > > +		bool match = encode_all;
> > > +
> > > +		if (!match) {
> > > +			for (j = 0; j < n; j++) {
> > > +				if (strcmp(feature_list[j], btf_features[i].name) == 0) {
> > > +					match = true;
> > > +					break;
> > > +				}
> > > +			}
> > > +		}
> > > +		if (match)
> > > +			*bval = btf_features[i].skip ? false : true;
> > 
> > I'm not sure I understand the logic behind "skip" features.
> > Take `decl_tag` for example:
> > - by default conf_load->skip_encoding_btf_decl_tag is 0;
> > - if `--btf_features=decl_tag` is passed it is still 0 because of the
> >   `skip ? false : true` logic.
> > 
> > If there is no way to change "skip" features why listing these at all?
> > 
> You're right; in the case of a skip feature, I think we need the
> following behaviour
> 
> 1. we skip the encoding by default (so the equivalent of
> --skip_encoding_btf_decl_tag, setting skip_encoding_btf_decl_tag
> to true
>
> 2. if the user however specifies the logical inversion of the skip
> feature in --btf_features (in this case "decl_tag" - or "all")
> skip_encoding_btf_decl_tag is set to false.
> 
> So in my code we had 2 above but not 1. If both were in place I think
> we'd have the right set of behaviours. Does that sound right?

You mean when --features=? is specified we default to
conf_load->skip_encoding_btf_decl_tag = true, and set it to false only
if "all" or "decl_tag" is listed in features, right?
 
> Maybe a better way to express all this would be to rename the "skip"
> field in "struct btf_feature" to "default" - so in the case of a "skip"
> feature, the default is true, but for opt-in features, the default is false.

Yes, I agree, "default" is better as "skip" is a bit confusing.

[...]
Alan Maguire Oct. 11, 2023, 10:05 p.m. UTC | #4
On 11/10/2023 20:08, Eduard Zingerman wrote:
> On Wed, 2023-10-11 at 17:41 +0100, Alan Maguire wrote:
> [...]
>>>> +		}
>>>> +	}
>>>> +
>>>> +	for (i = 0; i < ARRAY_SIZE(btf_features); i++) {
>>>> +		bool *bval = (bool *)(((void *)conf_load) + btf_features[i].conf_load_offset);
>>>> +		bool match = encode_all;
>>>> +
>>>> +		if (!match) {
>>>> +			for (j = 0; j < n; j++) {
>>>> +				if (strcmp(feature_list[j], btf_features[i].name) == 0) {
>>>> +					match = true;
>>>> +					break;
>>>> +				}
>>>> +			}
>>>> +		}
>>>> +		if (match)
>>>> +			*bval = btf_features[i].skip ? false : true;
>>>
>>> I'm not sure I understand the logic behind "skip" features.
>>> Take `decl_tag` for example:
>>> - by default conf_load->skip_encoding_btf_decl_tag is 0;
>>> - if `--btf_features=decl_tag` is passed it is still 0 because of the
>>>   `skip ? false : true` logic.
>>>
>>> If there is no way to change "skip" features why listing these at all?
>>>
>> You're right; in the case of a skip feature, I think we need the
>> following behaviour
>>
>> 1. we skip the encoding by default (so the equivalent of
>> --skip_encoding_btf_decl_tag, setting skip_encoding_btf_decl_tag
>> to true
>>
>> 2. if the user however specifies the logical inversion of the skip
>> feature in --btf_features (in this case "decl_tag" - or "all")
>> skip_encoding_btf_decl_tag is set to false.
>>
>> So in my code we had 2 above but not 1. If both were in place I think
>> we'd have the right set of behaviours. Does that sound right?
> 
> You mean when --features=? is specified we default to
> conf_load->skip_encoding_btf_decl_tag = true, and set it to false only
> if "all" or "decl_tag" is listed in features, right?
>

Yep. Here's the comment I was thinking of adding for the next version,
hopefully it clarifies this all a bit more than the original.

+/* --btf_features=feature1[,feature2,..] allows us to specify
+ * a list of requested BTF features or "all" to enable all features.
+ * These are translated into the appropriate conf_load values via
+ * struct btf_feature which specifies the associated conf_load
+ * boolean field and whether its default (representing the feature being
+ * off) is false or true.
+ *
+ * btf_features is for opting _into_ features so for a case like
+ * conf_load->btf_gen_floats, the translation is simple; the presence
+ * of the "float" feature in --btf_features sets conf_load->btf_gen_floats
+ * to true.
+ *
+ * The more confusing case is for features that are enabled unless
+ * skipping them is specified; for example
+ * conf_load->skip_encoding_btf_type_tag.  By default - to support
+ * the opt-in model of only enabling features the user asks for -
+ * conf_load->skip_encoding_btf_type_tag is set to true (meaning no
+ * type_tags) and it is only set to false if --btf_features contains
+ * the "type_tag" feature.
+ *
+ * So from the user perspective, all features specified via
+ * --btf_features are enabled, and if a feature is not specified,
+ * it is disabled.
  */


>> Maybe a better way to express all this would be to rename the "skip"
>> field in "struct btf_feature" to "default" - so in the case of a "skip"
>> feature, the default is true, but for opt-in features, the default is false.
> 
> Yes, I agree, "default" is better as "skip" is a bit confusing.
>

Thanks; I'll use that next time.

Alan

> [...]
Eduard Zingerman Oct. 11, 2023, 10:14 p.m. UTC | #5
On Wed, 2023-10-11 at 23:05 +0100, Alan Maguire wrote:
[...]
> > > > I'm not sure I understand the logic behind "skip" features.
> > > > Take `decl_tag` for example:
> > > > - by default conf_load->skip_encoding_btf_decl_tag is 0;
> > > > - if `--btf_features=decl_tag` is passed it is still 0 because of the
> > > >   `skip ? false : true` logic.
> > > > 
> > > > If there is no way to change "skip" features why listing these at all?
> > > > 
> > > You're right; in the case of a skip feature, I think we need the
> > > following behaviour
> > > 
> > > 1. we skip the encoding by default (so the equivalent of
> > > --skip_encoding_btf_decl_tag, setting skip_encoding_btf_decl_tag
> > > to true
> > > 
> > > 2. if the user however specifies the logical inversion of the skip
> > > feature in --btf_features (in this case "decl_tag" - or "all")
> > > skip_encoding_btf_decl_tag is set to false.
> > > 
> > > So in my code we had 2 above but not 1. If both were in place I think
> > > we'd have the right set of behaviours. Does that sound right?
> > 
> > You mean when --features=? is specified we default to
> > conf_load->skip_encoding_btf_decl_tag = true, and set it to false only
> > if "all" or "decl_tag" is listed in features, right?
> > 
> 
> Yep. Here's the comment I was thinking of adding for the next version,
> hopefully it clarifies this all a bit more than the original.
> 
> +/* --btf_features=feature1[,feature2,..] allows us to specify
> + * a list of requested BTF features or "all" to enable all features.
> + * These are translated into the appropriate conf_load values via
> + * struct btf_feature which specifies the associated conf_load
> + * boolean field and whether its default (representing the feature being
> + * off) is false or true.
> + *
> + * btf_features is for opting _into_ features so for a case like
> + * conf_load->btf_gen_floats, the translation is simple; the presence
> + * of the "float" feature in --btf_features sets conf_load->btf_gen_floats
> + * to true.
> + *
> + * The more confusing case is for features that are enabled unless
> + * skipping them is specified; for example
> + * conf_load->skip_encoding_btf_type_tag.  By default - to support
> + * the opt-in model of only enabling features the user asks for -
> + * conf_load->skip_encoding_btf_type_tag is set to true (meaning no
> + * type_tags) and it is only set to false if --btf_features contains
> + * the "type_tag" feature.
> + *
> + * So from the user perspective, all features specified via
> + * --btf_features are enabled, and if a feature is not specified,
> + * it is disabled.
>   */

Sounds reasonable. Maybe also add a line saying that
skip_encoding_btf_decl_tag defaults to false if --btf_features is not
specified to remain backwards compatible?

Thanks,
Eduard

[...]
Alan Maguire Oct. 12, 2023, 12:35 p.m. UTC | #6
On 11/10/2023 23:14, Eduard Zingerman wrote:
> On Wed, 2023-10-11 at 23:05 +0100, Alan Maguire wrote:
> [...]
>>>>> I'm not sure I understand the logic behind "skip" features.
>>>>> Take `decl_tag` for example:
>>>>> - by default conf_load->skip_encoding_btf_decl_tag is 0;
>>>>> - if `--btf_features=decl_tag` is passed it is still 0 because of the
>>>>>   `skip ? false : true` logic.
>>>>>
>>>>> If there is no way to change "skip" features why listing these at all?
>>>>>
>>>> You're right; in the case of a skip feature, I think we need the
>>>> following behaviour
>>>>
>>>> 1. we skip the encoding by default (so the equivalent of
>>>> --skip_encoding_btf_decl_tag, setting skip_encoding_btf_decl_tag
>>>> to true
>>>>
>>>> 2. if the user however specifies the logical inversion of the skip
>>>> feature in --btf_features (in this case "decl_tag" - or "all")
>>>> skip_encoding_btf_decl_tag is set to false.
>>>>
>>>> So in my code we had 2 above but not 1. If both were in place I think
>>>> we'd have the right set of behaviours. Does that sound right?
>>>
>>> You mean when --features=? is specified we default to
>>> conf_load->skip_encoding_btf_decl_tag = true, and set it to false only
>>> if "all" or "decl_tag" is listed in features, right?
>>>
>>
>> Yep. Here's the comment I was thinking of adding for the next version,
>> hopefully it clarifies this all a bit more than the original.
>>
>> +/* --btf_features=feature1[,feature2,..] allows us to specify
>> + * a list of requested BTF features or "all" to enable all features.
>> + * These are translated into the appropriate conf_load values via
>> + * struct btf_feature which specifies the associated conf_load
>> + * boolean field and whether its default (representing the feature being
>> + * off) is false or true.
>> + *
>> + * btf_features is for opting _into_ features so for a case like
>> + * conf_load->btf_gen_floats, the translation is simple; the presence
>> + * of the "float" feature in --btf_features sets conf_load->btf_gen_floats
>> + * to true.
>> + *
>> + * The more confusing case is for features that are enabled unless
>> + * skipping them is specified; for example
>> + * conf_load->skip_encoding_btf_type_tag.  By default - to support
>> + * the opt-in model of only enabling features the user asks for -
>> + * conf_load->skip_encoding_btf_type_tag is set to true (meaning no
>> + * type_tags) and it is only set to false if --btf_features contains
>> + * the "type_tag" feature.
>> + *
>> + * So from the user perspective, all features specified via
>> + * --btf_features are enabled, and if a feature is not specified,
>> + * it is disabled.
>>   */
> 
> Sounds reasonable. Maybe also add a line saying that
> skip_encoding_btf_decl_tag defaults to false if --btf_features is not
> specified to remain backwards compatible?
>

good idea, will do! Thanks!

Alan

> Thanks,
> Eduard
> 
> [...]
Jiri Olsa Oct. 12, 2023, 12:53 p.m. UTC | #7
On Wed, Oct 11, 2023 at 10:17:31AM +0100, Alan Maguire wrote:

SNIP

> +#define BTF_FEATURE(name, alias, skip)				\
> +	{ #name, #alias, offsetof(struct conf_load, alias), skip }
> +
> +struct btf_feature {
> +	const char      *name;
> +	const char      *option_alias;
> +	size_t          conf_load_offset;
> +	bool		skip;
> +} btf_features[] = {
> +	BTF_FEATURE(encode_force, btf_encode_force, false),
> +	BTF_FEATURE(var, skip_encoding_btf_vars, true),
> +	BTF_FEATURE(float, btf_gen_floats, false),
> +	BTF_FEATURE(decl_tag, skip_encoding_btf_decl_tag, true),
> +	BTF_FEATURE(type_tag, skip_encoding_btf_type_tag, true),
> +	BTF_FEATURE(enum64, skip_encoding_btf_enum64, true),
> +	BTF_FEATURE(optimized, btf_gen_optimized, false),
> +	/* the "skip" in skip_encoding_btf_inconsistent_proto is misleading
> +	 * here; this is a positive feature to ensure consistency of
> +	 * representation rather than a negative option which we want
> +	 * to invert.  So as a result, "skip" is false here.
> +	 */
> +	BTF_FEATURE(consistent, skip_encoding_btf_inconsistent_proto, false),
> +};
> +
> +#define BTF_MAX_FEATURES	32
> +#define BTF_MAX_FEATURE_STR	256
> +
> +/* Translate --btf_features=feature1[,feature2] into conf_load values.
> + * Explicitly ignores unrecognized features to allow future specification
> + * of new opt-in features.
> + */
> +static void parse_btf_features(const char *features, struct conf_load *conf_load)
> +{
> +	char *feature_list[BTF_MAX_FEATURES] = {};
> +	char f[BTF_MAX_FEATURE_STR];
> +	bool encode_all = false;
> +	int i, j, n = 0;
> +
> +	strncpy(f, features, sizeof(f));
> +
> +	if (strcmp(features, "all") == 0) {
> +		encode_all = true;
> +	} else {
> +		char *saveptr = NULL, *s = f, *t;
> +
> +		while ((t = strtok_r(s, ",", &saveptr)) != NULL) {
> +			s = NULL;
> +			feature_list[n++] = t;
> +		}
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(btf_features); i++) {
> +		bool *bval = (bool *)(((void *)conf_load) + btf_features[i].conf_load_offset);

nit, would it be easier to have btf_features defined inside the function
and pass specific bool pointers directly to BTF_FEATURE macro?

jirka

> +		bool match = encode_all;
> +
> +		if (!match) {
> +			for (j = 0; j < n; j++) {
> +				if (strcmp(feature_list[j], btf_features[i].name) == 0) {
> +					match = true;
> +					break;
> +				}
> +			}
> +		}
> +		if (match)
> +			*bval = btf_features[i].skip ? false : true;
> +	}
> +}
>  
>  static const struct argp_option pahole__options[] = {
>  	{
> @@ -1651,6 +1728,12 @@ static const struct argp_option pahole__options[] = {
>  		.key = ARGP_skip_encoding_btf_inconsistent_proto,
>  		.doc = "Skip functions that have multiple inconsistent function prototypes sharing the same name, or that use unexpected registers for parameter values."
>  	},
> +	{
> +		.name = "btf_features",
> +		.key = ARGP_btf_features,
> +		.arg = "FEATURE_LIST",
> +		.doc = "Specify supported BTF features in FEATURE_LIST or 'all' for all supported features. See the pahole manual page for the list of supported features."
> +	},
>  	{
>  		.name = NULL,
>  	}
> @@ -1796,7 +1879,7 @@ static error_t pahole__options_parser(int key, char *arg,
>  	case ARGP_btf_gen_floats:
>  		conf_load.btf_gen_floats = true;	break;
>  	case ARGP_btf_gen_all:
> -		conf_load.btf_gen_floats = true;	break;
> +		parse_btf_features("all", &conf_load);	break;
>  	case ARGP_with_flexible_array:
>  		show_with_flexible_array = true;	break;
>  	case ARGP_prettify_input_filename:
> @@ -1826,6 +1909,8 @@ static error_t pahole__options_parser(int key, char *arg,
>  		conf_load.btf_gen_optimized = true;		break;
>  	case ARGP_skip_encoding_btf_inconsistent_proto:
>  		conf_load.skip_encoding_btf_inconsistent_proto = true; break;
> +	case ARGP_btf_features:
> +		parse_btf_features(arg, &conf_load);	break;
>  	default:
>  		return ARGP_ERR_UNKNOWN;
>  	}
> -- 
> 2.31.1
>
Alan Maguire Oct. 12, 2023, 1:48 p.m. UTC | #8
On 12/10/2023 13:53, Jiri Olsa wrote:
> On Wed, Oct 11, 2023 at 10:17:31AM +0100, Alan Maguire wrote:
> 
> SNIP
> 
>> +#define BTF_FEATURE(name, alias, skip)				\
>> +	{ #name, #alias, offsetof(struct conf_load, alias), skip }
>> +
>> +struct btf_feature {
>> +	const char      *name;
>> +	const char      *option_alias;
>> +	size_t          conf_load_offset;
>> +	bool		skip;
>> +} btf_features[] = {
>> +	BTF_FEATURE(encode_force, btf_encode_force, false),
>> +	BTF_FEATURE(var, skip_encoding_btf_vars, true),
>> +	BTF_FEATURE(float, btf_gen_floats, false),
>> +	BTF_FEATURE(decl_tag, skip_encoding_btf_decl_tag, true),
>> +	BTF_FEATURE(type_tag, skip_encoding_btf_type_tag, true),
>> +	BTF_FEATURE(enum64, skip_encoding_btf_enum64, true),
>> +	BTF_FEATURE(optimized, btf_gen_optimized, false),
>> +	/* the "skip" in skip_encoding_btf_inconsistent_proto is misleading
>> +	 * here; this is a positive feature to ensure consistency of
>> +	 * representation rather than a negative option which we want
>> +	 * to invert.  So as a result, "skip" is false here.
>> +	 */
>> +	BTF_FEATURE(consistent, skip_encoding_btf_inconsistent_proto, false),
>> +};
>> +
>> +#define BTF_MAX_FEATURES	32
>> +#define BTF_MAX_FEATURE_STR	256
>> +
>> +/* Translate --btf_features=feature1[,feature2] into conf_load values.
>> + * Explicitly ignores unrecognized features to allow future specification
>> + * of new opt-in features.
>> + */
>> +static void parse_btf_features(const char *features, struct conf_load *conf_load)
>> +{
>> +	char *feature_list[BTF_MAX_FEATURES] = {};
>> +	char f[BTF_MAX_FEATURE_STR];
>> +	bool encode_all = false;
>> +	int i, j, n = 0;
>> +
>> +	strncpy(f, features, sizeof(f));
>> +
>> +	if (strcmp(features, "all") == 0) {
>> +		encode_all = true;
>> +	} else {
>> +		char *saveptr = NULL, *s = f, *t;
>> +
>> +		while ((t = strtok_r(s, ",", &saveptr)) != NULL) {
>> +			s = NULL;
>> +			feature_list[n++] = t;
>> +		}
>> +	}
>> +
>> +	for (i = 0; i < ARRAY_SIZE(btf_features); i++) {
>> +		bool *bval = (bool *)(((void *)conf_load) + btf_features[i].conf_load_offset);
> 
> nit, would it be easier to have btf_features defined inside the function
> and pass specific bool pointers directly to BTF_FEATURE macro?
>

thanks for taking a look! I _think_ I see what you mean; if we had
conf_load we could encode the bool pointer directly using
the BTF_FEATURE() definition, something like

#define BTF_FEATURE(name, alias, default_value)                 \
        { #name, #alias, &conf_load->alias, default_value }

struct btf_feature {
        const char      *name;
        const char      *option_alias;
        bool		*conf_value;
        bool            default_value;
} btf_features[] = {
...

This will work I think because conf_load is a global variable,
and I think we need to keep it global since it's also used by
patch 4 to get the list of supported features. Is the above
something like what you had in mind? Thanks!

Alan

> jirka
> 
>> +		bool match = encode_all;
>> +
>> +		if (!match) {
>> +			for (j = 0; j < n; j++) {
>> +				if (strcmp(feature_list[j], btf_features[i].name) == 0) {
>> +					match = true;
>> +					break;
>> +				}
>> +			}
>> +		}
>> +		if (match)
>> +			*bval = btf_features[i].skip ? false : true;
>> +	}
>> +}
>>  
>>  static const struct argp_option pahole__options[] = {
>>  	{
>> @@ -1651,6 +1728,12 @@ static const struct argp_option pahole__options[] = {
>>  		.key = ARGP_skip_encoding_btf_inconsistent_proto,
>>  		.doc = "Skip functions that have multiple inconsistent function prototypes sharing the same name, or that use unexpected registers for parameter values."
>>  	},
>> +	{
>> +		.name = "btf_features",
>> +		.key = ARGP_btf_features,
>> +		.arg = "FEATURE_LIST",
>> +		.doc = "Specify supported BTF features in FEATURE_LIST or 'all' for all supported features. See the pahole manual page for the list of supported features."
>> +	},
>>  	{
>>  		.name = NULL,
>>  	}
>> @@ -1796,7 +1879,7 @@ static error_t pahole__options_parser(int key, char *arg,
>>  	case ARGP_btf_gen_floats:
>>  		conf_load.btf_gen_floats = true;	break;
>>  	case ARGP_btf_gen_all:
>> -		conf_load.btf_gen_floats = true;	break;
>> +		parse_btf_features("all", &conf_load);	break;
>>  	case ARGP_with_flexible_array:
>>  		show_with_flexible_array = true;	break;
>>  	case ARGP_prettify_input_filename:
>> @@ -1826,6 +1909,8 @@ static error_t pahole__options_parser(int key, char *arg,
>>  		conf_load.btf_gen_optimized = true;		break;
>>  	case ARGP_skip_encoding_btf_inconsistent_proto:
>>  		conf_load.skip_encoding_btf_inconsistent_proto = true; break;
>> +	case ARGP_btf_features:
>> +		parse_btf_features(arg, &conf_load);	break;
>>  	default:
>>  		return ARGP_ERR_UNKNOWN;
>>  	}
>> -- 
>> 2.31.1
>>
>
Jiri Olsa Oct. 12, 2023, 9:19 p.m. UTC | #9
On Thu, Oct 12, 2023 at 02:48:50PM +0100, Alan Maguire wrote:
> On 12/10/2023 13:53, Jiri Olsa wrote:
> > On Wed, Oct 11, 2023 at 10:17:31AM +0100, Alan Maguire wrote:
> > 
> > SNIP
> > 
> >> +#define BTF_FEATURE(name, alias, skip)				\
> >> +	{ #name, #alias, offsetof(struct conf_load, alias), skip }
> >> +
> >> +struct btf_feature {
> >> +	const char      *name;
> >> +	const char      *option_alias;
> >> +	size_t          conf_load_offset;
> >> +	bool		skip;
> >> +} btf_features[] = {
> >> +	BTF_FEATURE(encode_force, btf_encode_force, false),
> >> +	BTF_FEATURE(var, skip_encoding_btf_vars, true),
> >> +	BTF_FEATURE(float, btf_gen_floats, false),
> >> +	BTF_FEATURE(decl_tag, skip_encoding_btf_decl_tag, true),
> >> +	BTF_FEATURE(type_tag, skip_encoding_btf_type_tag, true),
> >> +	BTF_FEATURE(enum64, skip_encoding_btf_enum64, true),
> >> +	BTF_FEATURE(optimized, btf_gen_optimized, false),
> >> +	/* the "skip" in skip_encoding_btf_inconsistent_proto is misleading
> >> +	 * here; this is a positive feature to ensure consistency of
> >> +	 * representation rather than a negative option which we want
> >> +	 * to invert.  So as a result, "skip" is false here.
> >> +	 */
> >> +	BTF_FEATURE(consistent, skip_encoding_btf_inconsistent_proto, false),
> >> +};
> >> +
> >> +#define BTF_MAX_FEATURES	32
> >> +#define BTF_MAX_FEATURE_STR	256
> >> +
> >> +/* Translate --btf_features=feature1[,feature2] into conf_load values.
> >> + * Explicitly ignores unrecognized features to allow future specification
> >> + * of new opt-in features.
> >> + */
> >> +static void parse_btf_features(const char *features, struct conf_load *conf_load)
> >> +{
> >> +	char *feature_list[BTF_MAX_FEATURES] = {};
> >> +	char f[BTF_MAX_FEATURE_STR];
> >> +	bool encode_all = false;
> >> +	int i, j, n = 0;
> >> +
> >> +	strncpy(f, features, sizeof(f));
> >> +
> >> +	if (strcmp(features, "all") == 0) {
> >> +		encode_all = true;
> >> +	} else {
> >> +		char *saveptr = NULL, *s = f, *t;
> >> +
> >> +		while ((t = strtok_r(s, ",", &saveptr)) != NULL) {
> >> +			s = NULL;
> >> +			feature_list[n++] = t;
> >> +		}
> >> +	}
> >> +
> >> +	for (i = 0; i < ARRAY_SIZE(btf_features); i++) {
> >> +		bool *bval = (bool *)(((void *)conf_load) + btf_features[i].conf_load_offset);
> > 
> > nit, would it be easier to have btf_features defined inside the function
> > and pass specific bool pointers directly to BTF_FEATURE macro?
> >
> 
> thanks for taking a look! I _think_ I see what you mean; if we had
> conf_load we could encode the bool pointer directly using
> the BTF_FEATURE() definition, something like
> 
> #define BTF_FEATURE(name, alias, default_value)                 \
>         { #name, #alias, &conf_load->alias, default_value }
> 
> struct btf_feature {
>         const char      *name;
>         const char      *option_alias;
>         bool		*conf_value;
>         bool            default_value;
> } btf_features[] = {
> ...
> 
> This will work I think because conf_load is a global variable,

yes, it's global, but it's also passed as an argument to parse_btf_features,
so it could be done on top of that conf_load pointer

> and I think we need to keep it global since it's also used by
> patch 4 to get the list of supported features. Is the above
> something like what you had in mind? Thanks!

yes, using the bools directly

thanks,
jirka
Andrii Nakryiko Oct. 13, 2023, 12:21 a.m. UTC | #10
On Wed, Oct 11, 2023 at 2:17 AM Alan Maguire <alan.maguire@oracle.com> wrote:
>
> This allows consumers to specify an opt-in set of features
> they want to use in BTF encoding.

This is exactly what I had in mind, so thanks a lot for doing this!
Minor nits below, but otherwise a big thumb up from me for the overall
approach.

>
> Supported features are
>
>         encode_force  Ignore invalid symbols when encoding BTF.

ignore_invalid? Even then I don't really know what this means even
after reading the description, but that's ok :)

>         var           Encode variables using BTF_KIND_VAR in BTF.
>         float         Encode floating-point types in BTF.
>         decl_tag      Encode declaration tags using BTF_KIND_DECL_TAG.
>         type_tag      Encode type tags using BTF_KIND_TYPE_TAG.
>         enum64        Encode enum64 values with BTF_KIND_ENUM64.
>         optimized     Encode representations of optimized functions
>                       with suffixes like ".isra.0" etc
>         consistent    Avoid encoding inconsistent static functions.
>                       These occur when a parameter is optimized out
>                       in some CUs and not others, or when the same
>                       function name has inconsistent BTF descriptions
>                       in different CUs.

both optimized and consistent refer to functions, so shouldn't the
feature name include func somewhere?

>
> Specifying "--btf_features=all" is the equivalent to setting
> all of the above.  If pahole does not know about a feature
> it silently ignores it.  These properties allow us to use
> the --btf_features option in the kernel pahole_flags.sh
> script to specify the desired set of features.  If a new
> feature is not present in pahole but requested, pahole
> BTF encoding will not complain (but will not encode the
> feature).

As I mentioned in the cover letter reply, we might add a "strict mode"
flag, that will error out on unknown features. I don't have much
opinion here, up to Arnaldo and others whether this is useful.

>
> Suggested-by: Andrii Nakryiko <andrii@kernel.org>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
>  man-pages/pahole.1 | 20 +++++++++++
>  pahole.c           | 87 +++++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 106 insertions(+), 1 deletion(-)
>
> diff --git a/man-pages/pahole.1 b/man-pages/pahole.1
> index c1b48de..7c072dc 100644
> --- a/man-pages/pahole.1
> +++ b/man-pages/pahole.1
> @@ -273,6 +273,26 @@ Generate BTF for functions with optimization-related suffixes (.isra, .constprop
>  .B \-\-btf_gen_all
>  Allow using all the BTF features supported by pahole.
>
> +.TP
> +.B \-\-btf_features=FEATURE_LIST
> +Encode BTF using the specified feature list, or specify 'all' for all features supported.  This single parameter value can be used as an alternative to unsing multiple BTF-related options. Supported features are
> +
> +.nf
> +       encode_force  Ignore invalid symbols when encoding BTF.
> +       var           Encode variables using BTF_KIND_VAR in BTF.
> +       float         Encode floating-point types in BTF.
> +       decl_tag      Encode declaration tags using BTF_KIND_DECL_TAG.
> +       type_tag      Encode type tags using BTF_KIND_TYPE_TAG.
> +       enum64        Encode enum64 values with BTF_KIND_ENUM64.
> +       optimized     Encode representations of optimized functions
> +                     with suffixes like ".isra.0" etc
> +       consistent    Avoid encoding inconsistent static functions.
> +                     These occur when a parameter is optimized out
> +                     in some CUs and not others, or when the same
> +                     function name has inconsistent BTF descriptions
> +                     in different CUs.
> +.fi
> +
>  .TP
>  .B \-l, \-\-show_first_biggest_size_base_type_member
>  Show first biggest size base_type member.
> diff --git a/pahole.c b/pahole.c
> index 7a41dc3..4f00b08 100644
> --- a/pahole.c
> +++ b/pahole.c
> @@ -1229,6 +1229,83 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
>  #define ARGP_skip_emitting_atomic_typedefs 338
>  #define ARGP_btf_gen_optimized  339
>  #define ARGP_skip_encoding_btf_inconsistent_proto 340
> +#define ARGP_btf_features      341
> +
> +/* --btf_features=feature1[,feature2,..] option allows us to specify
> + * opt-in features (or "all"); these are translated into conf_load
> + * values by specifying the associated bool offset and whether it
> + * is a skip option or not; btf_features is for opting _into_ features
> + * so for skip options we have to reverse the logic.  For example
> + * "--skip_encoding_btf_type_tag --btf_gen_floats" translate to
> + * "--btf_features=type_tag,float"
> + */
> +#define BTF_FEATURE(name, alias, skip)                         \
> +       { #name, #alias, offsetof(struct conf_load, alias), skip }
> +
> +struct btf_feature {
> +       const char      *name;
> +       const char      *option_alias;
> +       size_t          conf_load_offset;
> +       bool            skip;
> +} btf_features[] = {
> +       BTF_FEATURE(encode_force, btf_encode_force, false),
> +       BTF_FEATURE(var, skip_encoding_btf_vars, true),
> +       BTF_FEATURE(float, btf_gen_floats, false),
> +       BTF_FEATURE(decl_tag, skip_encoding_btf_decl_tag, true),
> +       BTF_FEATURE(type_tag, skip_encoding_btf_type_tag, true),
> +       BTF_FEATURE(enum64, skip_encoding_btf_enum64, true),
> +       BTF_FEATURE(optimized, btf_gen_optimized, false),
> +       /* the "skip" in skip_encoding_btf_inconsistent_proto is misleading
> +        * here; this is a positive feature to ensure consistency of
> +        * representation rather than a negative option which we want
> +        * to invert.  So as a result, "skip" is false here.
> +        */
> +       BTF_FEATURE(consistent, skip_encoding_btf_inconsistent_proto, false),
> +};
> +
> +#define BTF_MAX_FEATURES       32
> +#define BTF_MAX_FEATURE_STR    256
> +
> +/* Translate --btf_features=feature1[,feature2] into conf_load values.
> + * Explicitly ignores unrecognized features to allow future specification
> + * of new opt-in features.
> + */
> +static void parse_btf_features(const char *features, struct conf_load *conf_load)
> +{
> +       char *feature_list[BTF_MAX_FEATURES] = {};
> +       char f[BTF_MAX_FEATURE_STR];
> +       bool encode_all = false;
> +       int i, j, n = 0;
> +
> +       strncpy(f, features, sizeof(f));
> +
> +       if (strcmp(features, "all") == 0) {
> +               encode_all = true;
> +       } else {
> +               char *saveptr = NULL, *s = f, *t;
> +
> +               while ((t = strtok_r(s, ",", &saveptr)) != NULL) {
> +                       s = NULL;
> +                       feature_list[n++] = t;
> +               }
> +       }

I see that pahole uses argp for argument parsing. argp supports
specifying the same parameter multiple times, so it's very natural to
support

--btf_feature=var --btf_feature=float --btf_feature enum64

without doing any of this parsing. Just find a matching feature and
set corresponding bool value in the callback.

> +
> +       for (i = 0; i < ARRAY_SIZE(btf_features); i++) {
> +               bool *bval = (bool *)(((void *)conf_load) + btf_features[i].conf_load_offset);
> +               bool match = encode_all;
> +
> +               if (!match) {
> +                       for (j = 0; j < n; j++) {
> +                               if (strcmp(feature_list[j], btf_features[i].name) == 0) {
> +                                       match = true;
> +                                       break;
> +                               }
> +                       }
> +               }
> +               if (match)
> +                       *bval = btf_features[i].skip ? false : true;
> +       }
> +}
>
>  static const struct argp_option pahole__options[] = {
>         {
> @@ -1651,6 +1728,12 @@ static const struct argp_option pahole__options[] = {
>                 .key = ARGP_skip_encoding_btf_inconsistent_proto,
>                 .doc = "Skip functions that have multiple inconsistent function prototypes sharing the same name, or that use unexpected registers for parameter values."
>         },
> +       {
> +               .name = "btf_features",
> +               .key = ARGP_btf_features,
> +               .arg = "FEATURE_LIST",
> +               .doc = "Specify supported BTF features in FEATURE_LIST or 'all' for all supported features. See the pahole manual page for the list of supported features."
> +       },
>         {
>                 .name = NULL,
>         }
> @@ -1796,7 +1879,7 @@ static error_t pahole__options_parser(int key, char *arg,
>         case ARGP_btf_gen_floats:
>                 conf_load.btf_gen_floats = true;        break;
>         case ARGP_btf_gen_all:
> -               conf_load.btf_gen_floats = true;        break;
> +               parse_btf_features("all", &conf_load);  break;
>         case ARGP_with_flexible_array:
>                 show_with_flexible_array = true;        break;
>         case ARGP_prettify_input_filename:
> @@ -1826,6 +1909,8 @@ static error_t pahole__options_parser(int key, char *arg,
>                 conf_load.btf_gen_optimized = true;             break;
>         case ARGP_skip_encoding_btf_inconsistent_proto:
>                 conf_load.skip_encoding_btf_inconsistent_proto = true; break;
> +       case ARGP_btf_features:
> +               parse_btf_features(arg, &conf_load);    break;
>         default:
>                 return ARGP_ERR_UNKNOWN;
>         }
> --
> 2.31.1
>
Alan Maguire Oct. 13, 2023, 11:54 a.m. UTC | #11
On 13/10/2023 01:21, Andrii Nakryiko wrote:
> On Wed, Oct 11, 2023 at 2:17 AM Alan Maguire <alan.maguire@oracle.com> wrote:
>>
>> This allows consumers to specify an opt-in set of features
>> they want to use in BTF encoding.
> 
> This is exactly what I had in mind, so thanks a lot for doing this!
> Minor nits below, but otherwise a big thumb up from me for the overall
> approach.
> 

Great!

>>
>> Supported features are
>>
>>         encode_force  Ignore invalid symbols when encoding BTF.
> 
> ignore_invalid? Even then I don't really know what this means even
> after reading the description, but that's ok :)
>

The only place it is currently used is when checking btf_name_valid()
on a variable - if encode_force is specified we skip invalidly-named
symbols and drive on. I'll try and flesh out the description a bit.


>>         var           Encode variables using BTF_KIND_VAR in BTF.
>>         float         Encode floating-point types in BTF.
>>         decl_tag      Encode declaration tags using BTF_KIND_DECL_TAG.
>>         type_tag      Encode type tags using BTF_KIND_TYPE_TAG.
>>         enum64        Encode enum64 values with BTF_KIND_ENUM64.
>>         optimized     Encode representations of optimized functions
>>                       with suffixes like ".isra.0" etc
>>         consistent    Avoid encoding inconsistent static functions.
>>                       These occur when a parameter is optimized out
>>                       in some CUs and not others, or when the same
>>                       function name has inconsistent BTF descriptions
>>                       in different CUs.
> 
> both optimized and consistent refer to functions, so shouldn't the
> feature name include func somewhere?
> 

Yeah, though consistent may eventually need to apply to variables too.
As Stephen and I have been exploring adding global variable support for
all variables, we've run across a bunch of cases where the same variable
name refers to different types too. Worse, it often happens that the
same variable name refers to a "struct foo" and a "struct foo *" which
is liable to be very confusing. So I think we will either need to skip
encoding such variables for now (the "consistent" approach used for
functions) or we may have to sort out the symbol->address mapping issue
in BTF for functions _and_ variables before we land variable support.
My preference would be the latter - since it will solve the issues with
functions too - but I think we can probably make either sequence work.

So all of that is to say we can either stick with "consistent" with
the expectation that it may be more broadly applied to variables, or
convert to "consistent_func", I've no major preference which.

Optimized definitely refers to functions so we can switch that to
"optimized_func" if you like.

>>
>> Specifying "--btf_features=all" is the equivalent to setting
>> all of the above.  If pahole does not know about a feature
>> it silently ignores it.  These properties allow us to use
>> the --btf_features option in the kernel pahole_flags.sh
>> script to specify the desired set of features.  If a new
>> feature is not present in pahole but requested, pahole
>> BTF encoding will not complain (but will not encode the
>> feature).
> 
> As I mentioned in the cover letter reply, we might add a "strict mode"
> flag, that will error out on unknown features. I don't have much
> opinion here, up to Arnaldo and others whether this is useful.
> 

I think this is a good idea. I'll add it to v2 unless anyone has major
objections.

>>
>> Suggested-by: Andrii Nakryiko <andrii@kernel.org>
>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>> ---
>>  man-pages/pahole.1 | 20 +++++++++++
>>  pahole.c           | 87 +++++++++++++++++++++++++++++++++++++++++++++-
>>  2 files changed, 106 insertions(+), 1 deletion(-)
>>
>> diff --git a/man-pages/pahole.1 b/man-pages/pahole.1
>> index c1b48de..7c072dc 100644
>> --- a/man-pages/pahole.1
>> +++ b/man-pages/pahole.1
>> @@ -273,6 +273,26 @@ Generate BTF for functions with optimization-related suffixes (.isra, .constprop
>>  .B \-\-btf_gen_all
>>  Allow using all the BTF features supported by pahole.
>>
>> +.TP
>> +.B \-\-btf_features=FEATURE_LIST
>> +Encode BTF using the specified feature list, or specify 'all' for all features supported.  This single parameter value can be used as an alternative to unsing multiple BTF-related options. Supported features are
>> +
>> +.nf
>> +       encode_force  Ignore invalid symbols when encoding BTF.
>> +       var           Encode variables using BTF_KIND_VAR in BTF.
>> +       float         Encode floating-point types in BTF.
>> +       decl_tag      Encode declaration tags using BTF_KIND_DECL_TAG.
>> +       type_tag      Encode type tags using BTF_KIND_TYPE_TAG.
>> +       enum64        Encode enum64 values with BTF_KIND_ENUM64.
>> +       optimized     Encode representations of optimized functions
>> +                     with suffixes like ".isra.0" etc
>> +       consistent    Avoid encoding inconsistent static functions.
>> +                     These occur when a parameter is optimized out
>> +                     in some CUs and not others, or when the same
>> +                     function name has inconsistent BTF descriptions
>> +                     in different CUs.
>> +.fi
>> +
>>  .TP
>>  .B \-l, \-\-show_first_biggest_size_base_type_member
>>  Show first biggest size base_type member.
>> diff --git a/pahole.c b/pahole.c
>> index 7a41dc3..4f00b08 100644
>> --- a/pahole.c
>> +++ b/pahole.c
>> @@ -1229,6 +1229,83 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
>>  #define ARGP_skip_emitting_atomic_typedefs 338
>>  #define ARGP_btf_gen_optimized  339
>>  #define ARGP_skip_encoding_btf_inconsistent_proto 340
>> +#define ARGP_btf_features      341
>> +
>> +/* --btf_features=feature1[,feature2,..] option allows us to specify
>> + * opt-in features (or "all"); these are translated into conf_load
>> + * values by specifying the associated bool offset and whether it
>> + * is a skip option or not; btf_features is for opting _into_ features
>> + * so for skip options we have to reverse the logic.  For example
>> + * "--skip_encoding_btf_type_tag --btf_gen_floats" translate to
>> + * "--btf_features=type_tag,float"
>> + */
>> +#define BTF_FEATURE(name, alias, skip)                         \
>> +       { #name, #alias, offsetof(struct conf_load, alias), skip }
>> +
>> +struct btf_feature {
>> +       const char      *name;
>> +       const char      *option_alias;
>> +       size_t          conf_load_offset;
>> +       bool            skip;
>> +} btf_features[] = {
>> +       BTF_FEATURE(encode_force, btf_encode_force, false),
>> +       BTF_FEATURE(var, skip_encoding_btf_vars, true),
>> +       BTF_FEATURE(float, btf_gen_floats, false),
>> +       BTF_FEATURE(decl_tag, skip_encoding_btf_decl_tag, true),
>> +       BTF_FEATURE(type_tag, skip_encoding_btf_type_tag, true),
>> +       BTF_FEATURE(enum64, skip_encoding_btf_enum64, true),
>> +       BTF_FEATURE(optimized, btf_gen_optimized, false),
>> +       /* the "skip" in skip_encoding_btf_inconsistent_proto is misleading
>> +        * here; this is a positive feature to ensure consistency of
>> +        * representation rather than a negative option which we want
>> +        * to invert.  So as a result, "skip" is false here.
>> +        */
>> +       BTF_FEATURE(consistent, skip_encoding_btf_inconsistent_proto, false),
>> +};
>> +
>> +#define BTF_MAX_FEATURES       32
>> +#define BTF_MAX_FEATURE_STR    256
>> +
>> +/* Translate --btf_features=feature1[,feature2] into conf_load values.
>> + * Explicitly ignores unrecognized features to allow future specification
>> + * of new opt-in features.
>> + */
>> +static void parse_btf_features(const char *features, struct conf_load *conf_load)
>> +{
>> +       char *feature_list[BTF_MAX_FEATURES] = {};
>> +       char f[BTF_MAX_FEATURE_STR];
>> +       bool encode_all = false;
>> +       int i, j, n = 0;
>> +
>> +       strncpy(f, features, sizeof(f));
>> +
>> +       if (strcmp(features, "all") == 0) {
>> +               encode_all = true;
>> +       } else {
>> +               char *saveptr = NULL, *s = f, *t;
>> +
>> +               while ((t = strtok_r(s, ",", &saveptr)) != NULL) {
>> +                       s = NULL;
>> +                       feature_list[n++] = t;
>> +               }
>> +       }
> 
> I see that pahole uses argp for argument parsing. argp supports
> specifying the same parameter multiple times, so it's very natural to
> support
> 
> --btf_feature=var --btf_feature=float --btf_feature enum64
> 
> without doing any of this parsing. Just find a matching feature and
> set corresponding bool value in the callback.
>

Sure, will do.

>> +
>> +       for (i = 0; i < ARRAY_SIZE(btf_features); i++) {
>> +               bool *bval = (bool *)(((void *)conf_load) + btf_features[i].conf_load_offset);
>> +               bool match = encode_all;
>> +
>> +               if (!match) {
>> +                       for (j = 0; j < n; j++) {
>> +                               if (strcmp(feature_list[j], btf_features[i].name) == 0) {
>> +                                       match = true;
>> +                                       break;
>> +                               }
>> +                       }
>> +               }
>> +               if (match)
>> +                       *bval = btf_features[i].skip ? false : true;
>> +       }
>> +}
>>
>>  static const struct argp_option pahole__options[] = {
>>         {
>> @@ -1651,6 +1728,12 @@ static const struct argp_option pahole__options[] = {
>>                 .key = ARGP_skip_encoding_btf_inconsistent_proto,
>>                 .doc = "Skip functions that have multiple inconsistent function prototypes sharing the same name, or that use unexpected registers for parameter values."
>>         },
>> +       {
>> +               .name = "btf_features",
>> +               .key = ARGP_btf_features,
>> +               .arg = "FEATURE_LIST",
>> +               .doc = "Specify supported BTF features in FEATURE_LIST or 'all' for all supported features. See the pahole manual page for the list of supported features."
>> +       },
>>         {
>>                 .name = NULL,
>>         }
>> @@ -1796,7 +1879,7 @@ static error_t pahole__options_parser(int key, char *arg,
>>         case ARGP_btf_gen_floats:
>>                 conf_load.btf_gen_floats = true;        break;
>>         case ARGP_btf_gen_all:
>> -               conf_load.btf_gen_floats = true;        break;
>> +               parse_btf_features("all", &conf_load);  break;
>>         case ARGP_with_flexible_array:
>>                 show_with_flexible_array = true;        break;
>>         case ARGP_prettify_input_filename:
>> @@ -1826,6 +1909,8 @@ static error_t pahole__options_parser(int key, char *arg,
>>                 conf_load.btf_gen_optimized = true;             break;
>>         case ARGP_skip_encoding_btf_inconsistent_proto:
>>                 conf_load.skip_encoding_btf_inconsistent_proto = true; break;
>> +       case ARGP_btf_features:
>> +               parse_btf_features(arg, &conf_load);    break;
>>         default:
>>                 return ARGP_ERR_UNKNOWN;
>>         }
>> --
>> 2.31.1
>>
Arnaldo Carvalho de Melo Oct. 13, 2023, 2:17 p.m. UTC | #12
Em Thu, Oct 12, 2023 at 01:35:41PM +0100, Alan Maguire escreveu:
> On 11/10/2023 23:14, Eduard Zingerman wrote:
> > On Wed, 2023-10-11 at 23:05 +0100, Alan Maguire wrote:
> > [...]
> >>>>> I'm not sure I understand the logic behind "skip" features.
> >>>>> Take `decl_tag` for example:
> >>>>> - by default conf_load->skip_encoding_btf_decl_tag is 0;
> >>>>> - if `--btf_features=decl_tag` is passed it is still 0 because of the
> >>>>>   `skip ? false : true` logic.
> >>>>>
> >>>>> If there is no way to change "skip" features why listing these at all?
> >>>>>
> >>>> You're right; in the case of a skip feature, I think we need the
> >>>> following behaviour
> >>>>
> >>>> 1. we skip the encoding by default (so the equivalent of
> >>>> --skip_encoding_btf_decl_tag, setting skip_encoding_btf_decl_tag
> >>>> to true
> >>>>
> >>>> 2. if the user however specifies the logical inversion of the skip
> >>>> feature in --btf_features (in this case "decl_tag" - or "all")
> >>>> skip_encoding_btf_decl_tag is set to false.
> >>>>
> >>>> So in my code we had 2 above but not 1. If both were in place I think
> >>>> we'd have the right set of behaviours. Does that sound right?
> >>>
> >>> You mean when --features=? is specified we default to
> >>> conf_load->skip_encoding_btf_decl_tag = true, and set it to false only
> >>> if "all" or "decl_tag" is listed in features, right?
> >>>
> >>
> >> Yep. Here's the comment I was thinking of adding for the next version,
> >> hopefully it clarifies this all a bit more than the original.
> >>
> >> +/* --btf_features=feature1[,feature2,..] allows us to specify
> >> + * a list of requested BTF features or "all" to enable all features.
> >> + * These are translated into the appropriate conf_load values via
> >> + * struct btf_feature which specifies the associated conf_load
> >> + * boolean field and whether its default (representing the feature being
> >> + * off) is false or true.
> >> + *
> >> + * btf_features is for opting _into_ features so for a case like
> >> + * conf_load->btf_gen_floats, the translation is simple; the presence
> >> + * of the "float" feature in --btf_features sets conf_load->btf_gen_floats
> >> + * to true.
> >> + *
> >> + * The more confusing case is for features that are enabled unless
> >> + * skipping them is specified; for example
> >> + * conf_load->skip_encoding_btf_type_tag.  By default - to support
> >> + * the opt-in model of only enabling features the user asks for -
> >> + * conf_load->skip_encoding_btf_type_tag is set to true (meaning no
> >> + * type_tags) and it is only set to false if --btf_features contains
> >> + * the "type_tag" feature.
> >> + *
> >> + * So from the user perspective, all features specified via
> >> + * --btf_features are enabled, and if a feature is not specified,
> >> + * it is disabled.
> >>   */
> > 
> > Sounds reasonable. Maybe also add a line saying that
> > skip_encoding_btf_decl_tag defaults to false if --btf_features is not
> > specified to remain backwards compatible?
> >
> 
> good idea, will do! Thanks!

I have to catch up with all the comments on this thread, but does this
mean you're respinning the series now?

- Arnaldo
Alan Maguire Oct. 13, 2023, 2:43 p.m. UTC | #13
On 13/10/2023 15:17, Arnaldo Carvalho de Melo wrote:
> Em Thu, Oct 12, 2023 at 01:35:41PM +0100, Alan Maguire escreveu:
>> On 11/10/2023 23:14, Eduard Zingerman wrote:
>>> On Wed, 2023-10-11 at 23:05 +0100, Alan Maguire wrote:
>>> [...]
>>>>>>> I'm not sure I understand the logic behind "skip" features.
>>>>>>> Take `decl_tag` for example:
>>>>>>> - by default conf_load->skip_encoding_btf_decl_tag is 0;
>>>>>>> - if `--btf_features=decl_tag` is passed it is still 0 because of the
>>>>>>>   `skip ? false : true` logic.
>>>>>>>
>>>>>>> If there is no way to change "skip" features why listing these at all?
>>>>>>>
>>>>>> You're right; in the case of a skip feature, I think we need the
>>>>>> following behaviour
>>>>>>
>>>>>> 1. we skip the encoding by default (so the equivalent of
>>>>>> --skip_encoding_btf_decl_tag, setting skip_encoding_btf_decl_tag
>>>>>> to true
>>>>>>
>>>>>> 2. if the user however specifies the logical inversion of the skip
>>>>>> feature in --btf_features (in this case "decl_tag" - or "all")
>>>>>> skip_encoding_btf_decl_tag is set to false.
>>>>>>
>>>>>> So in my code we had 2 above but not 1. If both were in place I think
>>>>>> we'd have the right set of behaviours. Does that sound right?
>>>>>
>>>>> You mean when --features=? is specified we default to
>>>>> conf_load->skip_encoding_btf_decl_tag = true, and set it to false only
>>>>> if "all" or "decl_tag" is listed in features, right?
>>>>>
>>>>
>>>> Yep. Here's the comment I was thinking of adding for the next version,
>>>> hopefully it clarifies this all a bit more than the original.
>>>>
>>>> +/* --btf_features=feature1[,feature2,..] allows us to specify
>>>> + * a list of requested BTF features or "all" to enable all features.
>>>> + * These are translated into the appropriate conf_load values via
>>>> + * struct btf_feature which specifies the associated conf_load
>>>> + * boolean field and whether its default (representing the feature being
>>>> + * off) is false or true.
>>>> + *
>>>> + * btf_features is for opting _into_ features so for a case like
>>>> + * conf_load->btf_gen_floats, the translation is simple; the presence
>>>> + * of the "float" feature in --btf_features sets conf_load->btf_gen_floats
>>>> + * to true.
>>>> + *
>>>> + * The more confusing case is for features that are enabled unless
>>>> + * skipping them is specified; for example
>>>> + * conf_load->skip_encoding_btf_type_tag.  By default - to support
>>>> + * the opt-in model of only enabling features the user asks for -
>>>> + * conf_load->skip_encoding_btf_type_tag is set to true (meaning no
>>>> + * type_tags) and it is only set to false if --btf_features contains
>>>> + * the "type_tag" feature.
>>>> + *
>>>> + * So from the user perspective, all features specified via
>>>> + * --btf_features are enabled, and if a feature is not specified,
>>>> + * it is disabled.
>>>>   */
>>>
>>> Sounds reasonable. Maybe also add a line saying that
>>> skip_encoding_btf_decl_tag defaults to false if --btf_features is not
>>> specified to remain backwards compatible?
>>>
>>
>> good idea, will do! Thanks!
> 
> I have to catch up with all the comments on this thread, but does this
> mean you're respinning the series now?
>

Yep, I'm respinning now. There were a few small things in Andrii's
suggestions that we might need to figure out - mostly around names - but
there's enough change since the RFC that it would probably be best to
discuss those with the v2 series. Thanks!

Alan
> - Arnaldo
>
Andrii Nakryiko Oct. 13, 2023, 6:39 p.m. UTC | #14
On Fri, Oct 13, 2023 at 4:54 AM Alan Maguire <alan.maguire@oracle.com> wrote:
>
> On 13/10/2023 01:21, Andrii Nakryiko wrote:
> > On Wed, Oct 11, 2023 at 2:17 AM Alan Maguire <alan.maguire@oracle.com> wrote:
> >>
> >> This allows consumers to specify an opt-in set of features
> >> they want to use in BTF encoding.
> >
> > This is exactly what I had in mind, so thanks a lot for doing this!
> > Minor nits below, but otherwise a big thumb up from me for the overall
> > approach.
> >
>
> Great!
>
> >>
> >> Supported features are
> >>
> >>         encode_force  Ignore invalid symbols when encoding BTF.
> >
> > ignore_invalid? Even then I don't really know what this means even
> > after reading the description, but that's ok :)
> >
>
> The only place it is currently used is when checking btf_name_valid()
> on a variable - if encode_force is specified we skip invalidly-named
> symbols and drive on. I'll try and flesh out the description a bit.
>
>
> >>         var           Encode variables using BTF_KIND_VAR in BTF.
> >>         float         Encode floating-point types in BTF.
> >>         decl_tag      Encode declaration tags using BTF_KIND_DECL_TAG.
> >>         type_tag      Encode type tags using BTF_KIND_TYPE_TAG.
> >>         enum64        Encode enum64 values with BTF_KIND_ENUM64.
> >>         optimized     Encode representations of optimized functions
> >>                       with suffixes like ".isra.0" etc
> >>         consistent    Avoid encoding inconsistent static functions.
> >>                       These occur when a parameter is optimized out
> >>                       in some CUs and not others, or when the same
> >>                       function name has inconsistent BTF descriptions
> >>                       in different CUs.
> >
> > both optimized and consistent refer to functions, so shouldn't the
> > feature name include func somewhere?
> >
>
> Yeah, though consistent may eventually need to apply to variables too.
> As Stephen and I have been exploring adding global variable support for
> all variables, we've run across a bunch of cases where the same variable
> name refers to different types too. Worse, it often happens that the
> same variable name refers to a "struct foo" and a "struct foo *" which
> is liable to be very confusing. So I think we will either need to skip
> encoding such variables for now (the "consistent" approach used for
> functions) or we may have to sort out the symbol->address mapping issue
> in BTF for functions _and_ variables before we land variable support.
> My preference would be the latter - since it will solve the issues with
> functions too - but I think we can probably make either sequence work.
>
> So all of that is to say we can either stick with "consistent" with
> the expectation that it may be more broadly applied to variables, or
> convert to "consistent_func", I've no major preference which.
>
> Optimized definitely refers to functions so we can switch that to
> "optimized_func" if you like.
>

So I'd say optimized params will be its own feature, no? So yeah, I
think optimized_funcs is a better and more specific name. We can
probably add groups/aliases separate or later on, so then "optimized"
will mean both optimized_funcs and optimized_params, etc. Just like
you have all.

But this starts to sounds like a feature creep, so let's go with
specific names now, and worry about aliases when we need them.

> >>
> >> Specifying "--btf_features=all" is the equivalent to setting
> >> all of the above.  If pahole does not know about a feature
> >> it silently ignores it.  These properties allow us to use
> >> the --btf_features option in the kernel pahole_flags.sh
> >> script to specify the desired set of features.  If a new
> >> feature is not present in pahole but requested, pahole
> >> BTF encoding will not complain (but will not encode the
> >> feature).
> >
> > As I mentioned in the cover letter reply, we might add a "strict mode"
> > flag, that will error out on unknown features. I don't have much
> > opinion here, up to Arnaldo and others whether this is useful.
> >
>
> I think this is a good idea. I'll add it to v2 unless anyone has major
> objections.
>

SGTM

> >>
> >> Suggested-by: Andrii Nakryiko <andrii@kernel.org>
> >> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> >> ---
> >>  man-pages/pahole.1 | 20 +++++++++++
> >>  pahole.c           | 87 +++++++++++++++++++++++++++++++++++++++++++++-
> >>  2 files changed, 106 insertions(+), 1 deletion(-)
> >>

[...]
diff mbox series

Patch

diff --git a/man-pages/pahole.1 b/man-pages/pahole.1
index c1b48de..7c072dc 100644
--- a/man-pages/pahole.1
+++ b/man-pages/pahole.1
@@ -273,6 +273,26 @@  Generate BTF for functions with optimization-related suffixes (.isra, .constprop
 .B \-\-btf_gen_all
 Allow using all the BTF features supported by pahole.
 
+.TP
+.B \-\-btf_features=FEATURE_LIST
+Encode BTF using the specified feature list, or specify 'all' for all features supported.  This single parameter value can be used as an alternative to unsing multiple BTF-related options. Supported features are
+
+.nf
+	encode_force  Ignore invalid symbols when encoding BTF.
+	var           Encode variables using BTF_KIND_VAR in BTF.
+	float         Encode floating-point types in BTF.
+	decl_tag      Encode declaration tags using BTF_KIND_DECL_TAG.
+	type_tag      Encode type tags using BTF_KIND_TYPE_TAG.
+	enum64        Encode enum64 values with BTF_KIND_ENUM64.
+	optimized     Encode representations of optimized functions
+	              with suffixes like ".isra.0" etc
+	consistent    Avoid encoding inconsistent static functions.
+	              These occur when a parameter is optimized out
+	              in some CUs and not others, or when the same
+	              function name has inconsistent BTF descriptions
+	              in different CUs.
+.fi
+
 .TP
 .B \-l, \-\-show_first_biggest_size_base_type_member
 Show first biggest size base_type member.
diff --git a/pahole.c b/pahole.c
index 7a41dc3..4f00b08 100644
--- a/pahole.c
+++ b/pahole.c
@@ -1229,6 +1229,83 @@  ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
 #define ARGP_skip_emitting_atomic_typedefs 338
 #define ARGP_btf_gen_optimized  339
 #define ARGP_skip_encoding_btf_inconsistent_proto 340
+#define ARGP_btf_features	341
+
+/* --btf_features=feature1[,feature2,..] option allows us to specify
+ * opt-in features (or "all"); these are translated into conf_load
+ * values by specifying the associated bool offset and whether it
+ * is a skip option or not; btf_features is for opting _into_ features
+ * so for skip options we have to reverse the logic.  For example
+ * "--skip_encoding_btf_type_tag --btf_gen_floats" translate to
+ * "--btf_features=type_tag,float"
+ */
+#define BTF_FEATURE(name, alias, skip)				\
+	{ #name, #alias, offsetof(struct conf_load, alias), skip }
+
+struct btf_feature {
+	const char      *name;
+	const char      *option_alias;
+	size_t          conf_load_offset;
+	bool		skip;
+} btf_features[] = {
+	BTF_FEATURE(encode_force, btf_encode_force, false),
+	BTF_FEATURE(var, skip_encoding_btf_vars, true),
+	BTF_FEATURE(float, btf_gen_floats, false),
+	BTF_FEATURE(decl_tag, skip_encoding_btf_decl_tag, true),
+	BTF_FEATURE(type_tag, skip_encoding_btf_type_tag, true),
+	BTF_FEATURE(enum64, skip_encoding_btf_enum64, true),
+	BTF_FEATURE(optimized, btf_gen_optimized, false),
+	/* the "skip" in skip_encoding_btf_inconsistent_proto is misleading
+	 * here; this is a positive feature to ensure consistency of
+	 * representation rather than a negative option which we want
+	 * to invert.  So as a result, "skip" is false here.
+	 */
+	BTF_FEATURE(consistent, skip_encoding_btf_inconsistent_proto, false),
+};
+
+#define BTF_MAX_FEATURES	32
+#define BTF_MAX_FEATURE_STR	256
+
+/* Translate --btf_features=feature1[,feature2] into conf_load values.
+ * Explicitly ignores unrecognized features to allow future specification
+ * of new opt-in features.
+ */
+static void parse_btf_features(const char *features, struct conf_load *conf_load)
+{
+	char *feature_list[BTF_MAX_FEATURES] = {};
+	char f[BTF_MAX_FEATURE_STR];
+	bool encode_all = false;
+	int i, j, n = 0;
+
+	strncpy(f, features, sizeof(f));
+
+	if (strcmp(features, "all") == 0) {
+		encode_all = true;
+	} else {
+		char *saveptr = NULL, *s = f, *t;
+
+		while ((t = strtok_r(s, ",", &saveptr)) != NULL) {
+			s = NULL;
+			feature_list[n++] = t;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(btf_features); i++) {
+		bool *bval = (bool *)(((void *)conf_load) + btf_features[i].conf_load_offset);
+		bool match = encode_all;
+
+		if (!match) {
+			for (j = 0; j < n; j++) {
+				if (strcmp(feature_list[j], btf_features[i].name) == 0) {
+					match = true;
+					break;
+				}
+			}
+		}
+		if (match)
+			*bval = btf_features[i].skip ? false : true;
+	}
+}
 
 static const struct argp_option pahole__options[] = {
 	{
@@ -1651,6 +1728,12 @@  static const struct argp_option pahole__options[] = {
 		.key = ARGP_skip_encoding_btf_inconsistent_proto,
 		.doc = "Skip functions that have multiple inconsistent function prototypes sharing the same name, or that use unexpected registers for parameter values."
 	},
+	{
+		.name = "btf_features",
+		.key = ARGP_btf_features,
+		.arg = "FEATURE_LIST",
+		.doc = "Specify supported BTF features in FEATURE_LIST or 'all' for all supported features. See the pahole manual page for the list of supported features."
+	},
 	{
 		.name = NULL,
 	}
@@ -1796,7 +1879,7 @@  static error_t pahole__options_parser(int key, char *arg,
 	case ARGP_btf_gen_floats:
 		conf_load.btf_gen_floats = true;	break;
 	case ARGP_btf_gen_all:
-		conf_load.btf_gen_floats = true;	break;
+		parse_btf_features("all", &conf_load);	break;
 	case ARGP_with_flexible_array:
 		show_with_flexible_array = true;	break;
 	case ARGP_prettify_input_filename:
@@ -1826,6 +1909,8 @@  static error_t pahole__options_parser(int key, char *arg,
 		conf_load.btf_gen_optimized = true;		break;
 	case ARGP_skip_encoding_btf_inconsistent_proto:
 		conf_load.skip_encoding_btf_inconsistent_proto = true; break;
+	case ARGP_btf_features:
+		parse_btf_features(arg, &conf_load);	break;
 	default:
 		return ARGP_ERR_UNKNOWN;
 	}