public inbox for linuxppc-dev@ozlabs.org
 help / color / mirror / Atom feed
From: Athira Rajeev <atrajeev@linux.ibm.com>
To: Ian Rogers <irogers@google.com>
Cc: Namhyung Kim <namhyung@kernel.org>,
	Arnaldo Carvalho de Melo <acme@kernel.org>,
	Adrian Hunter <adrian.hunter@intel.com>,
	linux-perf-users@vger.kernel.org, Venkat <venkat88@linux.ibm.com>,
	jolsa@kernel.org, maddy@linux.ibm.com,
	linuxppc-dev@lists.ozlabs.org, hbathini@linux.vnet.ibm.com,
	Tejas.Manhas1@ibm.com, Tanushree.Shah@ibm.com,
	Shivani.Nittor@ibm.com
Subject: Re: [PATCH] tools/perf: Fix the check for parameterized field in event term
Date: Tue, 24 Mar 2026 11:26:54 +0530	[thread overview]
Message-ID: <4E333DED-B88A-4071-9828-C46F50EB0090@linux.ibm.com> (raw)
In-Reply-To: <CAP-5=fXcZVxKcfV5giqDWxBPm53cNYLGhrTL8Q=uibrqJPmzgw@mail.gmail.com>



> On 24 Mar 2026, at 12:17 AM, Ian Rogers <irogers@google.com> wrote:
> 
> On Mon, Mar 23, 2026 at 5:19 AM Athira Rajeev <atrajeev@linux.ibm.com> wrote:
>> 
>> 
>> 
>>> On 18 Mar 2026, at 12:05 PM, Athira Rajeev <atrajeev@linux.ibm.com> wrote:
>>> 
>>> 
>>> 
>>>> On 17 Mar 2026, at 9:39 PM, Ian Rogers <irogers@google.com> wrote:
>>>> 
>>>> On Tue, Mar 17, 2026 at 1:56 AM Venkat <venkat88@linux.ibm.com> wrote:
>>>>> 
>>>>>> On 14 Mar 2026, at 2:03 PM, Athira Rajeev <atrajeev@linux.ibm.com> wrote:
>>>>>> 
>>>>>> The format_alias() function in util/pmu.c has a check to
>>>>>> detect whether the event has parameterized field ( =? ).
>>>>>> The string alias->terms contains the event and if the event
>>>>>> has user configurable parameter, there will be presence of
>>>>>> sub string "=?" in the alias->terms.
>>>>>> 
>>>>>> Snippet of code:
>>>>>> 
>>>>>> /* Paramemterized events have the parameters shown. */
>>>>>>     if (strstr(alias->terms, "=?")) {
>>>>>>             /* No parameters. */
>>>>>>             snprintf(buf, len, "%.*s/%s/", (int)pmu_name_len, pmu->name, alias->name);
>>>>>> 
>>>>>> if "strstr" contains the substring, it returns a pointer
>>>>>> and hence enters the above check which is not the expected
>>>>>> check. And hence "perf list" doesn't have the parameterized
>>>>>> fields in the result.
>>>>>> 
>>>>>> Fix this check to use:
>>>>>> 
>>>>>> if (!strstr(alias->terms, "=?")) {
>>>>>> 
>>>>>> With this change, perf list shows the events correctly with
>>>>>> the strings showing parameters.
>>>>>> 
>>>>>> Signed-off-by: Athira Rajeev <atrajeev@linux.ibm.com>
>>>> 
>>>> Thanks Athira, Sashiko is noting in its review:
>>>> https://sashiko.dev/#/patchset/20260314083304.75321-1-atrajeev%40linux.ibm.com
>>> 
>>> Thanks Ian for pointing this. Its interesting to see this review.
>>> I will check through the review.
>>> 
>>> Thanks
>>> Athira
>>>> 
>>>> By inverting this check, parameterized events now proceed to
>>>> parse_events_terms() and the rest of format_alias().
>>>> 
>>>> If a parameterized event uses a built-in perf keyword for its parameter name
>>>> (e.g., config=?), the lexer parses it as a predefined term token, which sets
>>>> term->config to NULL.
>> 
>> Hi All,
>> 
>> sashiko brought up a scenario :
>> 
>> “If a parameterized event uses a built-in perf keyword for its parameter name
>> (e.g., config=?), the lexer parses it as a predefined term token, which sets
>> term->config to NULL."
>> 
>> Existing usage for parameterized events in perf:
>> ======================================
>> 
>> In perf,  “?” is often used in event aliases to indicate that a parameter needs to be provided
>> by the user (e.g., param1=?). This information is exposed via “format” in sysfs too.  The current way,
>> how this is consumed in different PMU drivers is: example:
>> 
>>  # cat /sys/devices/pmu/format/param1
>>    config:12-15
>> 
>>   # cat /sys/devices/pmu/format/param2
>>    config1:0-17
>> 
>> Here param1 uses bits 12:15 in config and param2 used bits 0:17 in config1
>> 
>> And event will use these parameterized fields:
>>    #cat /sys/devices/pmu/events/event1       domain=1,param1=?,param2=?
>> 
>> “Perf list” expects "param1=?,param2=?” In the event listing
>>   # perf list|grep event1
>>   pmu/event1,param1=?,param2=?/                       [Kernel PMU event]
>> 
>> The patch here address fix in “strstr” check in format_alias for “perf list” to display these user expected “?” fields
>> in event alias.  Traditionally, perf treats config, config1, and config2 etc as hardcoded numeric
>> keywords or builtin keyword.
>> 
>> The scneario sashiko brought up is interesting. Where a parameterized event
>> uses a built-in perf keyword  for its parameter name.
>> 
>> sashiko review : Scenario where parameterized event uses a built-in perf keyword
>> ============================================================
>> Example, having “config2” in sysfs format-driven logic. Below is an example case.
>> 
>>    #cat /sys/devices/pmu/events/event1
>>     config2=?,domain=1,param1=?,param2=?
>> 
>>      # cat /sys/devices/hv_24x7/format/config2
>>         config2:0-16
>> 
>> Here “perf list” will show as (null) or might crash too as sashiko review pointed out.
>> 
>>    # perf list|grep event1
>>    pmu/event1,(null)=?,param1=?,param2=?/                    [Kernel PMU event]
>> 
>> This is because term->config is null for built-in keywords. perf lexer sets this to NULL
>> for predefined keywords ( like 'config', 'period'), storing the type in the enum instead.
>> 
>> To fix displaying the “=?” in “perf list” for keywords like config2,  format_alias() function needs to handle
>> null term->config , ie by using parse_events__term_type_str() to map NULL config
>> pointers back to their string keywords
>> 
>> But this only will fix the “perf list” part of it.  “Perf stat” or record will still fail since
>> currently these keywords are not considered as placeholders for “?” values.
>> 
>> If a PMU wants to use any builtin-keywords in parameters and want to ensure
>> it is set by user, this could be useful.  Again considering this as a use case, we need other changes
>> to allow these built-in keywords to function as placeholders (e.g., config2=?) within a PMU alias.
>> 
>> I am adding changes which I have identified needed for this support.
>> If it is better to post this separately as patch set and discuss, definitely will do that.
>> I wanted to run the idea of this new scenario with everyone. Apologies for the long text.
>> 
>> Thanks and looking for comments.
>> 
>> 
>> From 21179a76be97ef81c69a9492f0f432dec5b5361c Mon Sep 17 00:00:00 2001
>> From: Athira Rajeev <atrajeev@linux.ibm.com>
>> Date: Mon, 23 Mar 2026 08:38:30 -0400
>> Subject: [PATCH] tools/perf: Enable parameterized values for keywords like
>> config1
>> 
>> PMU defines events that require user-supplied parameters (like chip
>> or core) are packed into specific bit-fields of the different config
>> registers. Traditionally, perf treats config, config1, and config2
>> as hardcoded numeric keywords, causing it to bypass the sysfs
>> format-driven bit-masking logic.
>> 
>> This patch allows these built-in keywords to function as placeholders
>> (e.g., config2=?) within a PMU alias.
>> 
>> Changes includes:
>> - Updated config_term_pmu in parse-events.c to recognize string
>>  placeholders for event terms. otherwise it will expect those to
>>  be of type PARSE_EVENTS__TERM_TYPE_NUM
>> - Updated pmu_config_term in pmu.c to make sure if keywords are used
>>  as placeholders , they pass through sysfs based format logic
>> (pmu_find_format) and pmu_resolve_param_term() logic
>> - Modified format_alias() to ensure "perf list" display the parameterized
>>  events correctly
>> - Replace snprintf with scnprintf in buffer offset calculations to
>>   ensure the 'used' count will not exceed the "len"
>> 
>> Signed-off-by: Athira Rajeev <atrajeev@linux.ibm.com>
>> ---
>> tools/perf/util/parse-events.c |  5 +++++
>> tools/perf/util/pmu.c          | 37 +++++++++++++++++++++++++---------
>> 2 files changed, 33 insertions(+), 9 deletions(-)
>> 
>> diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
>> index b9efb296bba5..282b0a0a5340 100644
>> --- a/tools/perf/util/parse-events.c
>> +++ b/tools/perf/util/parse-events.c
>> @@ -1051,6 +1051,11 @@ static int config_term_pmu(struct perf_event_attr *attr,
>>                 */
>>                return 0;
>>        }
>> +
>> +       if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR && term->val.str)
>> +               if (strstr(term->val.str, "?"))
>> +                       return 0;
> 
> nit: Since the nested if spans more than two lines, could you add
> curly braces {} to the outer if statement. Could you also add a
> comment explaining why the runtime parameter causes an early return
> and why you used `strstr` instead of `strcmp`?


Hi Ian

Thanks for checking and responding with review comments.
Sure I will handle this.
> 
>> +
>>        return config_term_common(attr, term, parse_state);
>> }
>> 
>> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
>> index 23337d2fa281..2c4503e1a11b 100644
>> --- a/tools/perf/util/pmu.c
>> +++ b/tools/perf/util/pmu.c
>> @@ -1442,10 +1442,19 @@ static int pmu_resolve_param_term(struct parse_events_term *term,
>>                                  __u64 *value)
>> {
>>        struct parse_events_term *t;
>> +       const char *name = term->config;
>> +
>> +       if (!term->config)
> 
> I think you meant "if (!name)" here.
Yes, will correct this 
> 
>> +               name = parse_events__term_type_str(term->type_term);
>> 
>>        list_for_each_entry(t, &head_terms->terms, list) {
>> +               const char *t_name = t->config;
>> +
>> +               if (!t_name)
>> +                       t_name = parse_events__term_type_str(t->type_term);
>> +
>>                if (t->type_val == PARSE_EVENTS__TERM_TYPE_NUM &&
>> -                   t->config && !strcmp(t->config, term->config)) {
>> +                   t_name && name && !strcmp(t_name, name)) {
>>                        t->used = true;
>>                        *value = t->val.num;
>>                        return 0;
>> @@ -1453,7 +1462,7 @@ static int pmu_resolve_param_term(struct parse_events_term *term,
>>        }
>> 
>>        if (verbose > 0)
>> -               printf("Required parameter '%s' not specified\n", term->config);
>> +               printf("Required parameter '%s' not specified\n", name);
>> 
>>        return -1;
>> }
>> @@ -1494,6 +1503,7 @@ static int pmu_config_term(const struct perf_pmu *pmu,
>>        struct perf_pmu_format *format;
>>        __u64 *vp;
>>        __u64 val, max_val;
>> +       const char *name;
>> 
>>        /*
>>         * If this is a parameter we've already used for parameterized-eval,
>> @@ -1507,7 +1517,8 @@ static int pmu_config_term(const struct perf_pmu *pmu,
>>         * traditionally have had to handle not having a PMU. An alias may
>>         * have hard coded config values, optionally apply them below.
>>         */
>> -       if (parse_events__is_hardcoded_term(term)) {
>> +       if (parse_events__is_hardcoded_term(term) &&
>> +                      term->type_val != PARSE_EVENTS__TERM_TYPE_STR) {
> 
> What is being guarded against in this case? Could you update or add a comment.

Sure
> 
>>                /* Config terms set all bits in the config. */
>>                DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
>> 
>> @@ -1576,7 +1587,11 @@ static int pmu_config_term(const struct perf_pmu *pmu,
>>                return 0;
>>        }
>> 
>> -       format = pmu_find_format(&pmu->format, term->config);
>> +       name = term->config;
>> +       if (!name)
>> +               name = parse_events__term_type_str(term->type_term);
>> +
>> +       format = pmu_find_format(&pmu->format, name);
>>        if (!format) {
>>                char *pmu_term = pmu_formats_string(&pmu->format);
>>                char *unknown_term;
>> @@ -2117,7 +2132,7 @@ static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
>>                                                   skip_duplicate_pmus);
>> 
>>        /* Paramemterized events have the parameters shown. */
>> -       if (strstr(alias->terms, "=?")) {
>> +       if (!strstr(alias->terms, "=?")) {
> 
> I wonder if it would be safer if we did a `str_ends_with(alias->terms,
> "=?")`. This likely applies to the other strstr usage.
Ok , 

I will post a V2 patchset with all changes, splitting the fix patch which address the “str” check above
and changes to allow using the builtin-keywords as place holders within PMU event alias

Thanks
Athira
> 
> Thanks,
> Ian
> 
>>                /* No parameters. */
>>                snprintf(buf, len, "%.*s/%s/", (int)pmu_name_len, pmu->name, alias->name);
>>                return buf;
>> @@ -2129,15 +2144,19 @@ static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
>>                pr_err("Failure to parse '%s' terms '%s': %d\n",
>>                        alias->name, alias->terms, ret);
>>                parse_events_terms__exit(&terms);
>> -               snprintf(buf, len, "%.*s/%s/", (int)pmu_name_len, pmu->name, alias->name);
>> +               scnprintf(buf, len, "%.*s/%s/", (int)pmu_name_len, pmu->name, alias->name);
>>                return buf;
>>        }
>> -       used = snprintf(buf, len, "%.*s/%s", (int)pmu_name_len, pmu->name, alias->name);
>> +       used = scnprintf(buf, len, "%.*s/%s", (int)pmu_name_len, pmu->name, alias->name);
>> 
>>        list_for_each_entry(term, &terms.terms, list) {
>> +               const char *name = term->config;
>> +
>> +               if (!name)
>> +                       name = parse_events__term_type_str(term->type_term);
>>                if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
>> -                       used += snprintf(buf + used, sub_non_neg(len, used),
>> -                                       ",%s=%s", term->config,
>> +                       used += scnprintf(buf + used, sub_non_neg(len, used),
>> +                                       ",%s=%s", name,
>>                                        term->val.str);
>>        }
>>        parse_events_terms__exit(&terms);
>> --  2.47.3
>> 
>> 
>> 
>> Thanks
>> Athira
>> 
>>>> 
>>>> Does this cause a NULL pointer dereference in the term iteration loop?
>>>> 
>>>> list_for_each_entry(term, &terms.terms, list) {
>>>> if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
>>>>  used += snprintf(buf + used, sub_non_neg(len, used),
>>>>    ",%s=%s", term->config,
>>>>    term->val.str);
>>>> }
>>>> 
>>>> Passing a NULL pointer to the %s format specifier for term->config might
>>>> cause a segmentation fault or formatting corruption.
>>>> 
>>>> Can this also lead to a stack buffer overflow due to how snprintf() return
>>>> values are accumulated?
>>>> 
>>>> snprintf() returns the number of characters that would have been written.
>>>> If the event names and terms are long enough, the "used" variable can exceed
>>>> "len" (which is typically 1024 bytes).
>>>> 
>>>> When evaluating "buf + used" in the same loop:
>>>> 
>>>> used += snprintf(buf + used, sub_non_neg(len, used), ...);
>>>> 
>>>> If "used" exceeds 1024, this creates an out-of-bounds pointer. Modern
>>>> compilers can use this undefined behavior to infer that "used" <= 1024
>>>> must be true, which optimizes out the safety check inside sub_non_neg():
>>>> 
>>>> static int sub_non_neg(int a, int b)
>>>> {
>>>> if (b > a)
>>>>  return 0;
>>>> return a - b;
>>>> }
>>>> 
>>>> The compiler can reduce this to simply "a - b" (or "len - used").
>>>> If "used" is actually greater than 1024, "len - used" evaluates to a
>>>> negative integer. This negative value is implicitly cast to size_t for
>>>> snprintf(), resulting in a massive size limit.
>>>> 
>>>> Would this cause snprintf() to write past the end of the stack buffer
>>>> without bounds checking? Using scnprintf() might prevent "used" from
>>>> exceeding "len".
>>>> 
>>>> Thanks,
>>>> Ian
>>>> 
>>>>>> ---
>>>>>> tools/perf/util/pmu.c | 2 +-
>>>>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>> 
>>>>>> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
>>>>>> index 23337d2fa281..0b8d58543f17 100644
>>>>>> --- a/tools/perf/util/pmu.c
>>>>>> +++ b/tools/perf/util/pmu.c
>>>>>> @@ -2117,7 +2117,7 @@ static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
>>>>>> skip_duplicate_pmus);
>>>>>> 
>>>>>> /* Paramemterized events have the parameters shown. */
>>>>>> - if (strstr(alias->terms, "=?")) {
>>>>>> + if (!strstr(alias->terms, "=?")) {
>>>>>> /* No parameters. */
>>>>>> snprintf(buf, len, "%.*s/%s/", (int)pmu_name_len, pmu->name, alias->name);
>>>>>> return buf;
>>>>>> --
>>>>>> 2.47.3
>>>>>> 
>>>>> 
>>>>> Tested this patch, and its working as expected.
>>>>> 
>>>>> Before Patch:
>>>>> 
>>>>> ./perf list hv_24x7 | grep -i CPM_EXT_INT_OS
>>>>> hv_24x7/CPM_EXT_INT_OS/                            [Kernel PMU event]
>>>>> 
>>>>> After Patch:
>>>>> 
>>>>> ./perf list hv_24x7 | grep -i CPM_EXT_INT_OS
>>>>> hv_24x7/CPM_EXT_INT_OS,domain=?,core=?/ [Kernel PMU event]
>>>>> 
>>>>> 
>>>>> ./perf stat -e hv_24x7/PM_PAU_CYC,chip=0/
>>>>> 
>>>>> 
>>>>> Performance counter stats for 'system wide':
>>>>> 
>>>>>      2018866563      hv_24x7/PM_PAU_CYC,chip=0/
>>>>> 
>>>>>   229.938231314 seconds time elapsed
>>>>> 
>>>>> Tested-by: Venkat Rao Bagalkote <venkat88@linux.ibm.com>
>>>>> 
>>>>> Regards,
>>>>> Venkat.




      reply	other threads:[~2026-03-24  5:57 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-14  8:33 [PATCH] tools/perf: Fix the check for parameterized field in event term Athira Rajeev
2026-03-17  8:55 ` Venkat
2026-03-17 16:09   ` Ian Rogers
2026-03-18  6:35     ` Athira Rajeev
2026-03-23 12:18       ` Athira Rajeev
2026-03-23 18:47         ` Ian Rogers
2026-03-24  5:56           ` Athira Rajeev [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4E333DED-B88A-4071-9828-C46F50EB0090@linux.ibm.com \
    --to=atrajeev@linux.ibm.com \
    --cc=Shivani.Nittor@ibm.com \
    --cc=Tanushree.Shah@ibm.com \
    --cc=Tejas.Manhas1@ibm.com \
    --cc=acme@kernel.org \
    --cc=adrian.hunter@intel.com \
    --cc=hbathini@linux.vnet.ibm.com \
    --cc=irogers@google.com \
    --cc=jolsa@kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=linuxppc-dev@lists.ozlabs.org \
    --cc=maddy@linux.ibm.com \
    --cc=namhyung@kernel.org \
    --cc=venkat88@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox