lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAP-5=fXTqD-QHrrQufXcx=uBgjS1Ae33CCwPaXHJH=rtXFqjRA@mail.gmail.com>
Date:   Tue, 15 Nov 2022 08:53:36 -0800
From:   Ian Rogers <irogers@...gle.com>
To:     Arnaldo Carvalho de Melo <acme@...nel.org>
Cc:     Weilin Wang <weilin.wang@...el.com>,
        Perry Taylor <perry.taylor@...el.com>,
        Caleb Biggers <caleb.biggers@...el.com>,
        Leo Yan <leo.yan@...aro.org>,
        Adrian Hunter <adrian.hunter@...el.com>,
        Peter Zijlstra <peterz@...radead.org>,
        Ingo Molnar <mingo@...hat.com>,
        Mark Rutland <mark.rutland@....com>,
        Alexander Shishkin <alexander.shishkin@...ux.intel.com>,
        Jiri Olsa <jolsa@...nel.org>,
        Namhyung Kim <namhyung@...nel.org>,
        Sandipan Das <sandipan.das@....com>,
        Kajol Jain <kjain@...ux.ibm.com>,
        Zhengjun Xing <zhengjun.xing@...ux.intel.com>,
        Kan Liang <kan.liang@...ux.intel.com>,
        Ravi Bangoria <ravi.bangoria@....com>,
        Xin Gao <gaoxin@...rlc.com>, Rob Herring <robh@...nel.org>,
        linux-kernel@...r.kernel.org, linux-perf-users@...r.kernel.org,
        Stephane Eranian <eranian@...gle.com>
Subject: Re: [PATCH v3 09/10] perf list: Reorganize to use callbacks

On Tue, Nov 15, 2022 at 5:40 AM Arnaldo Carvalho de Melo
<acme@...nel.org> wrote:
>
> Em Mon, Nov 14, 2022 at 01:07:22PM -0800, Ian Rogers escreveu:
> > Rather than controlling the list output with passed flags, add
> > callbacks that are called when an event or metric are
> > encountered. State is passed to the callback so that command line
> > options can be respected, alternatively the callbacks can be changed.
> >
> > Fix a few bugs:
> >  - wordwrap to columns metric descriptions and expressions;
> >  - remove unnecessary whitespace after PMU event names;
> >  - the metric filter is a glob but matched using strstr which will
> >    always fail, switch to using a proper globmatch,
> >  - the detail flag gives details for extra kernel PMU events like
> >    branch-instructions.
> >
> > In metricgroup.c switch from struct mep being a rbtree of metricgroups
> > containing a list of metrics, to the tree directly containing all the
> > metrics. In general the alias for a name is passed to the print
> > routine rather than being contained in the name with OR.
>
> ⬢[acme@...lbox perf]$ gcc --version | head -1
> gcc (GCC) 12.2.1 20220819 (Red Hat 12.2.1-2)
> ⬢[acme@...lbox perf]$
>
>   CC      /tmp/build/perf/util/s390-sample-raw.o
> util/print-events.c: In function ‘print_sdt_events’:
> util/print-events.c:183:33: error: ignoring return value of ‘asprintf’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result]
>   183 |                                 asprintf(&evt_name, "%s@%s(%.12s)", sdt_name->s, path, bid);
>       |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> cc1: all warnings being treated as errors
> make[4]: *** [/var/home/acme/git/perf/tools/build/Makefile.build:97: /tmp/build/perf/util/print-events.o] Error 1
> make[4]: *** Waiting for unfinished jobs....
>
> Since we have a test for evt_name later on, I fixed this with:
>
>
> diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c
> index 5210a168d54f9eb5..2646ae18d9f9b57b 100644
> --- a/tools/perf/util/print-events.c
> +++ b/tools/perf/util/print-events.c
> @@ -180,7 +180,8 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state)
>                         char *path = build_id_cache__origname(bid);
>
>                         if (path) {
> -                               asprintf(&evt_name, "%s@%s(%.12s)", sdt_name->s, path, bid);
> +                               if (asprintf(&evt_name, "%s@%s(%.12s)", sdt_name->s, path, bid) < 0)
> +                                       evt_name = NULL;
>                                 free(path);
>                         }
>                 }


Looks good, thanks!

Ian

> Thanks,
>
> - Arnaldo
>
>
> > Signed-off-by: Ian Rogers <irogers@...gle.com>
> > ---
> >  tools/perf/builtin-list.c      | 331 +++++++++++++++++++++++++-----
> >  tools/perf/util/metricgroup.c  | 243 ++++++----------------
> >  tools/perf/util/metricgroup.h  |   4 +-
> >  tools/perf/util/pmu.c          | 145 +++++--------
> >  tools/perf/util/pmu.h          |   5 +-
> >  tools/perf/util/print-events.c | 363 +++++++++++++++++----------------
> >  tools/perf/util/print-events.h |  42 ++--
> >  7 files changed, 621 insertions(+), 512 deletions(-)
> >
> > diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
> > index cc84ced6da26..12811fc40a30 100644
> > --- a/tools/perf/builtin-list.c
> > +++ b/tools/perf/builtin-list.c
> > @@ -15,31 +15,240 @@
> >  #include "util/pmu-hybrid.h"
> >  #include "util/debug.h"
> >  #include "util/metricgroup.h"
> > +#include "util/string2.h"
> > +#include "util/strlist.h"
> >  #include <subcmd/pager.h>
> >  #include <subcmd/parse-options.h>
> >  #include <stdio.h>
> >
> > -static bool desc_flag = true;
> > -static bool details_flag;
> > +/**
> > + * struct print_state - State and configuration passed to the default_print
> > + * functions.
> > + */
> > +struct print_state {
> > +     /**
> > +      * @pmu_glob: Optionally restrict PMU and metric matching to PMU or
> > +      * debugfs subsystem name.
> > +      */
> > +     char *pmu_glob;
> > +     /** @event_glob: Optional pattern matching glob. */
> > +     char *event_glob;
> > +     /** @name_only: Print event or metric names only. */
> > +     bool name_only;
> > +     /** @desc: Print the event or metric description. */
> > +     bool desc;
> > +     /** @long_desc: Print longer event or metric description. */
> > +     bool long_desc;
> > +     /** @deprecated: Print deprecated events or metrics. */
> > +     bool deprecated;
> > +     /**
> > +      * @detailed: Print extra information on the perf event such as names
> > +      * and expressions used internally by events.
> > +      */
> > +     bool detailed;
> > +     /** @metrics: Controls printing of metric and metric groups. */
> > +     bool metrics;
> > +     /** @metricgroups: Controls printing of metric and metric groups. */
> > +     bool metricgroups;
> > +     /** @last_topic: The last printed event topic. */
> > +     char *last_topic;
> > +     /** @last_metricgroups: The last printed metric group. */
> > +     char *last_metricgroups;
> > +     /** @visited_metrics: Metrics that are printed to avoid duplicates. */
> > +     struct strlist *visited_metrics;
> > +};
> > +
> > +static void default_print_start(void *ps)
> > +{
> > +     struct print_state *print_state = ps;
> > +
> > +     if (!print_state->name_only && pager_in_use())
> > +             printf("\nList of pre-defined events (to be used in -e or -M):\n\n");
> > +}
> > +
> > +static void default_print_end(void *print_state __maybe_unused) {}
> > +
> > +static void wordwrap(const char *s, int start, int max, int corr)
> > +{
> > +     int column = start;
> > +     int n;
> > +
> > +     while (*s) {
> > +             int wlen = strcspn(s, " \t");
> > +
> > +             if (column + wlen >= max && column > start) {
> > +                     printf("\n%*s", start, "");
> > +                     column = start + corr;
> > +             }
> > +             n = printf("%s%.*s", column > start ? " " : "", wlen, s);
> > +             if (n <= 0)
> > +                     break;
> > +             s += wlen;
> > +             column += n;
> > +             s = skip_spaces(s);
> > +     }
> > +}
> > +
> > +static void default_print_event(void *ps, const char *pmu_name, const char *topic,
> > +                             const char *event_name, const char *event_alias,
> > +                             const char *scale_unit __maybe_unused,
> > +                             bool deprecated, const char *event_type_desc,
> > +                             const char *desc, const char *long_desc,
> > +                             const char *encoding_desc,
> > +                             const char *metric_name, const char *metric_expr)
> > +{
> > +     struct print_state *print_state = ps;
> > +     int pos;
> > +
> > +     if (deprecated && !print_state->deprecated)
> > +             return;
> > +
> > +     if (print_state->pmu_glob && !strglobmatch(pmu_name, print_state->pmu_glob))
> > +             return;
> > +
> > +     if (print_state->event_glob &&
> > +         (!event_name || !strglobmatch(event_name, print_state->event_glob)) &&
> > +         (!event_alias || !strglobmatch(event_alias, print_state->event_glob)) &&
> > +         (!topic || !strglobmatch_nocase(topic, print_state->event_glob)))
> > +             return;
> > +
> > +     if (print_state->name_only) {
> > +             if (event_alias && strlen(event_alias))
> > +                     printf("%s ", event_alias);
> > +             else
> > +                     printf("%s ", event_name);
> > +             return;
> > +     }
> > +
> > +     if (strcmp(print_state->last_topic, topic ?: "")) {
> > +             if (topic)
> > +                     printf("\n%s:\n", topic);
> > +             free(print_state->last_topic);
> > +             print_state->last_topic = strdup(topic ?: "");
> > +     }
> > +
> > +     if (event_alias && strlen(event_alias))
> > +             pos = printf("  %s OR %s", event_name, event_alias);
> > +     else
> > +             pos = printf("  %s", event_name);
> > +
> > +     if (!topic && event_type_desc) {
> > +             for (; pos < 53; pos++)
> > +                     putchar(' ');
> > +             printf("[%s]\n", event_type_desc);
> > +     } else
> > +             putchar('\n');
> > +
> > +     if (desc && print_state->desc) {
> > +             printf("%*s", 8, "[");
> > +             wordwrap(desc, 8, pager_get_columns(), 0);
> > +             printf("]\n");
> > +     }
> > +
> > +     if (long_desc && print_state->long_desc) {
> > +             printf("%*s", 8, "[");
> > +             wordwrap(long_desc, 8, pager_get_columns(), 0);
> > +             printf("]\n");
> > +     }
> > +
> > +     if (print_state->detailed && encoding_desc) {
> > +             printf("%*s%s", 8, "", encoding_desc);
> > +             if (metric_name)
> > +                     printf(" MetricName: %s", metric_name);
> > +             if (metric_expr)
> > +                     printf(" MetricExpr: %s", metric_expr);
> > +             putchar('\n');
> > +     }
> > +}
> > +
> > +static void default_print_metric(void *ps,
> > +                             const char *group,
> > +                             const char *name,
> > +                             const char *desc,
> > +                             const char *long_desc,
> > +                             const char *expr,
> > +                             const char *unit __maybe_unused)
> > +{
> > +     struct print_state *print_state = ps;
> > +
> > +     if (print_state->event_glob &&
> > +         (!print_state->metrics || !name || !strglobmatch(name, print_state->event_glob)) &&
> > +         (!print_state->metricgroups || !group || !strglobmatch(group, print_state->event_glob)))
> > +             return;
> > +
> > +     if (!print_state->name_only && !print_state->last_metricgroups) {
> > +             if (print_state->metricgroups) {
> > +                     printf("\nMetric Groups:\n");
> > +                     if (!print_state->metrics)
> > +                             putchar('\n');
> > +             } else {
> > +                     printf("\nMetrics:\n\n");
> > +             }
> > +     }
> > +     if (!print_state->last_metricgroups ||
> > +         strcmp(print_state->last_metricgroups, group ?: "")) {
> > +             if (group && print_state->metricgroups) {
> > +                     if (print_state->name_only)
> > +                             printf("%s ", group);
> > +                     else if (print_state->metrics)
> > +                             printf("\n%s:\n", group);
> > +                     else
> > +                             printf("%s\n", group);
> > +             }
> > +             free(print_state->last_metricgroups);
> > +             print_state->last_metricgroups = strdup(group ?: "");
> > +     }
> > +     if (!print_state->metrics)
> > +             return;
> > +
> > +     if (print_state->name_only) {
> > +             if (print_state->metrics &&
> > +                 !strlist__has_entry(print_state->visited_metrics, name)) {
> > +                     printf("%s ", name);
> > +                     strlist__add(print_state->visited_metrics, name);
> > +             }
> > +             return;
> > +     }
> > +     printf("  %s\n", name);
> > +
> > +     if (desc && print_state->desc) {
> > +             printf("%*s", 8, "[");
> > +             wordwrap(desc, 8, pager_get_columns(), 0);
> > +             printf("]\n");
> > +     }
> > +     if (long_desc && print_state->long_desc) {
> > +             printf("%*s", 8, "[");
> > +             wordwrap(long_desc, 8, pager_get_columns(), 0);
> > +             printf("]\n");
> > +     }
> > +     if (expr && print_state->detailed) {
> > +             printf("%*s", 8, "[");
> > +             wordwrap(expr, 8, pager_get_columns(), 0);
> > +             printf("]\n");
> > +     }
> > +}
> >
> >  int cmd_list(int argc, const char **argv)
> >  {
> >       int i, ret = 0;
> > -     bool raw_dump = false;
> > -     bool long_desc_flag = false;
> > -     bool deprecated = false;
> > -     char *pmu_name = NULL;
> > +     struct print_state ps = {};
> > +     struct print_callbacks print_cb = {
> > +             .print_start = default_print_start,
> > +             .print_end = default_print_end,
> > +             .print_event = default_print_event,
> > +             .print_metric = default_print_metric,
> > +     };
> >       const char *hybrid_name = NULL;
> >       const char *unit_name = NULL;
> >       struct option list_options[] = {
> > -             OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"),
> > -             OPT_BOOLEAN('d', "desc", &desc_flag,
> > +             OPT_BOOLEAN(0, "raw-dump", &ps.name_only, "Dump raw events"),
> > +             OPT_BOOLEAN('d', "desc", &ps.desc,
> >                           "Print extra event descriptions. --no-desc to not print."),
> > -             OPT_BOOLEAN('v', "long-desc", &long_desc_flag,
> > +             OPT_BOOLEAN('v', "long-desc", &ps.long_desc,
> >                           "Print longer event descriptions."),
> > -             OPT_BOOLEAN(0, "details", &details_flag,
> > +             OPT_BOOLEAN(0, "details", &ps.detailed,
> >                           "Print information on the perf event names and expressions used internally by events."),
> > -             OPT_BOOLEAN(0, "deprecated", &deprecated,
> > +             OPT_BOOLEAN(0, "deprecated", &ps.deprecated,
> >                           "Print deprecated events."),
> >               OPT_STRING(0, "cputype", &hybrid_name, "hybrid cpu type",
> >                          "Limit PMU or metric printing to the given hybrid PMU (e.g. core or atom)."),
> > @@ -63,20 +272,28 @@ int cmd_list(int argc, const char **argv)
> >
> >       setup_pager();
> >
> > -     if (!raw_dump && pager_in_use())
> > -             printf("\nList of pre-defined events (to be used in -e or -M):\n\n");
> > +     if (!ps.name_only)
> > +             setup_pager();
> >
> > +     ps.desc = !ps.long_desc;
> > +     ps.last_topic = strdup("");
> > +     assert(ps.last_topic);
> > +     ps.visited_metrics = strlist__new(NULL, NULL);
> > +     assert(ps.visited_metrics);
> >       if (unit_name)
> > -             pmu_name = strdup(unit_name);
> > +             ps.pmu_glob = strdup(unit_name);
> >       else if (hybrid_name) {
> > -             pmu_name = perf_pmu__hybrid_type_to_pmu(hybrid_name);
> > -             if (!pmu_name)
> > +             ps.pmu_glob = perf_pmu__hybrid_type_to_pmu(hybrid_name);
> > +             if (!ps.pmu_glob)
> >                       pr_warning("WARNING: hybrid cputype is not supported!\n");
> >       }
> >
> > +     print_cb.print_start(&ps);
> > +
> >       if (argc == 0) {
> > -             print_events(NULL, raw_dump, !desc_flag, long_desc_flag,
> > -                             details_flag, deprecated, pmu_name);
> > +             ps.metrics = true;
> > +             ps.metricgroups = true;
> > +             print_events(&print_cb, &ps);
> >               goto out;
> >       }
> >
> > @@ -84,30 +301,33 @@ int cmd_list(int argc, const char **argv)
> >               char *sep, *s;
> >
> >               if (strcmp(argv[i], "tracepoint") == 0)
> > -                     print_tracepoint_events(NULL, NULL, raw_dump);
> > +                     print_tracepoint_events(&print_cb, &ps);
> >               else if (strcmp(argv[i], "hw") == 0 ||
> >                        strcmp(argv[i], "hardware") == 0)
> > -                     print_symbol_events(NULL, PERF_TYPE_HARDWARE,
> > -                                     event_symbols_hw, PERF_COUNT_HW_MAX, raw_dump);
> > +                     print_symbol_events(&print_cb, &ps, PERF_TYPE_HARDWARE,
> > +                                     event_symbols_hw, PERF_COUNT_HW_MAX);
> >               else if (strcmp(argv[i], "sw") == 0 ||
> >                        strcmp(argv[i], "software") == 0) {
> > -                     print_symbol_events(NULL, PERF_TYPE_SOFTWARE,
> > -                                     event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump);
> > -                     print_tool_events(NULL, raw_dump);
> > +                     print_symbol_events(&print_cb, &ps, PERF_TYPE_SOFTWARE,
> > +                                     event_symbols_sw, PERF_COUNT_SW_MAX);
> > +                     print_tool_events(&print_cb, &ps);
> >               } else if (strcmp(argv[i], "cache") == 0 ||
> >                        strcmp(argv[i], "hwcache") == 0)
> > -                     print_hwcache_events(NULL, raw_dump);
> > +                     print_hwcache_events(&print_cb, &ps);
> >               else if (strcmp(argv[i], "pmu") == 0)
> > -                     print_pmu_events(NULL, raw_dump, !desc_flag,
> > -                                             long_desc_flag, details_flag,
> > -                                             deprecated, pmu_name);
> > +                     print_pmu_events(&print_cb, &ps);
> >               else if (strcmp(argv[i], "sdt") == 0)
> > -                     print_sdt_events(NULL, NULL, raw_dump);
> > -             else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0)
> > -                     metricgroup__print(true, false, NULL, raw_dump, details_flag, pmu_name);
> > -             else if (strcmp(argv[i], "metricgroup") == 0 || strcmp(argv[i], "metricgroups") == 0)
> > -                     metricgroup__print(false, true, NULL, raw_dump, details_flag, pmu_name);
> > -             else if ((sep = strchr(argv[i], ':')) != NULL) {
> > +                     print_sdt_events(&print_cb, &ps);
> > +             else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) {
> > +                     ps.metricgroups = false;
> > +                     ps.metrics = true;
> > +                     metricgroup__print(&print_cb, &ps);
> > +             } else if (strcmp(argv[i], "metricgroup") == 0 ||
> > +                        strcmp(argv[i], "metricgroups") == 0) {
> > +                     ps.metricgroups = true;
> > +                     ps.metrics = false;
> > +                     metricgroup__print(&print_cb, &ps);
> > +             } else if ((sep = strchr(argv[i], ':')) != NULL) {
> >                       int sep_idx;
> >
> >                       sep_idx = sep - argv[i];
> > @@ -118,34 +338,41 @@ int cmd_list(int argc, const char **argv)
> >                       }
> >
> >                       s[sep_idx] = '\0';
> > -                     print_tracepoint_events(s, s + sep_idx + 1, raw_dump);
> > -                     print_sdt_events(s, s + sep_idx + 1, raw_dump);
> > -                     metricgroup__print(true, true, s, raw_dump, details_flag, pmu_name);
> > +                     ps.pmu_glob = s;
> > +                     ps.event_glob = s + sep_idx + 1;
> > +                     print_tracepoint_events(&print_cb, &ps);
> > +                     print_sdt_events(&print_cb, &ps);
> > +                     ps.metrics = true;
> > +                     ps.metricgroups = true;
> > +                     metricgroup__print(&print_cb, &ps);
> >                       free(s);
> >               } else {
> >                       if (asprintf(&s, "*%s*", argv[i]) < 0) {
> >                               printf("Critical: Not enough memory! Trying to continue...\n");
> >                               continue;
> >                       }
> > -                     print_symbol_events(s, PERF_TYPE_HARDWARE,
> > -                                         event_symbols_hw, PERF_COUNT_HW_MAX, raw_dump);
> > -                     print_symbol_events(s, PERF_TYPE_SOFTWARE,
> > -                                         event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump);
> > -                     print_tool_events(s, raw_dump);
> > -                     print_hwcache_events(s, raw_dump);
> > -                     print_pmu_events(s, raw_dump, !desc_flag,
> > -                                             long_desc_flag,
> > -                                             details_flag,
> > -                                             deprecated,
> > -                                             pmu_name);
> > -                     print_tracepoint_events(NULL, s, raw_dump);
> > -                     print_sdt_events(NULL, s, raw_dump);
> > -                     metricgroup__print(true, true, s, raw_dump, details_flag, pmu_name);
> > +                     ps.event_glob = s;
> > +                     print_symbol_events(&print_cb, &ps, PERF_TYPE_HARDWARE,
> > +                                     event_symbols_hw, PERF_COUNT_HW_MAX);
> > +                     print_symbol_events(&print_cb, &ps, PERF_TYPE_SOFTWARE,
> > +                                     event_symbols_sw, PERF_COUNT_SW_MAX);
> > +                     print_tool_events(&print_cb, &ps);
> > +                     print_hwcache_events(&print_cb, &ps);
> > +                     print_pmu_events(&print_cb, &ps);
> > +                     print_tracepoint_events(&print_cb, &ps);
> > +                     print_sdt_events(&print_cb, &ps);
> > +                     ps.metrics = true;
> > +                     ps.metricgroups = true;
> > +                     metricgroup__print(&print_cb, &ps);
> >                       free(s);
> >               }
> >       }
> >
> >  out:
> > -     free(pmu_name);
> > +     print_cb.print_end(&ps);
> > +     free(ps.pmu_glob);
> > +     free(ps.last_topic);
> > +     free(ps.last_metricgroups);
> > +     strlist__delete(ps.visited_metrics);
> >       return ret;
> >  }
> > diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
> > index 1943fed9b6d9..76501009ead5 100644
> > --- a/tools/perf/util/metricgroup.c
> > +++ b/tools/perf/util/metricgroup.c
> > @@ -12,6 +12,7 @@
> >  #include "strbuf.h"
> >  #include "pmu.h"
> >  #include "pmu-hybrid.h"
> > +#include "print-events.h"
> >  #include "expr.h"
> >  #include "rblist.h"
> >  #include <string.h>
> > @@ -352,51 +353,65 @@ static bool match_pe_metric(const struct pmu_event *pe, const char *metric)
> >              match_metric(pe->metric_name, metric);
> >  }
> >
> > +/** struct mep - RB-tree node for building printing information. */
> >  struct mep {
> > +     /** nd - RB-tree element. */
> >       struct rb_node nd;
> > -     const char *name;
> > -     struct strlist *metrics;
> > +     /** @metric_group: Owned metric group name, separated others with ';'. */
> > +     char *metric_group;
> > +     const char *metric_name;
> > +     const char *metric_desc;
> > +     const char *metric_long_desc;
> > +     const char *metric_expr;
> > +     const char *metric_unit;
> >  };
> >
> >  static int mep_cmp(struct rb_node *rb_node, const void *entry)
> >  {
> >       struct mep *a = container_of(rb_node, struct mep, nd);
> >       struct mep *b = (struct mep *)entry;
> > +     int ret;
> >
> > -     return strcmp(a->name, b->name);
> > +     ret = strcmp(a->metric_group, b->metric_group);
> > +     if (ret)
> > +             return ret;
> > +
> > +     return strcmp(a->metric_name, b->metric_name);
> >  }
> >
> > -static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
> > -                                     const void *entry)
> > +static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry)
> >  {
> >       struct mep *me = malloc(sizeof(struct mep));
> >
> >       if (!me)
> >               return NULL;
> > +
> >       memcpy(me, entry, sizeof(struct mep));
> > -     me->name = strdup(me->name);
> > -     if (!me->name)
> > -             goto out_me;
> > -     me->metrics = strlist__new(NULL, NULL);
> > -     if (!me->metrics)
> > -             goto out_name;
> >       return &me->nd;
> > -out_name:
> > -     zfree(&me->name);
> > -out_me:
> > +}
> > +
> > +static void mep_delete(struct rblist *rl __maybe_unused,
> > +                    struct rb_node *nd)
> > +{
> > +     struct mep *me = container_of(nd, struct mep, nd);
> > +
> > +     zfree(&me->metric_group);
> >       free(me);
> > -     return NULL;
> >  }
> >
> > -static struct mep *mep_lookup(struct rblist *groups, const char *name)
> > +static struct mep *mep_lookup(struct rblist *groups, const char *metric_group,
> > +                           const char *metric_name)
> >  {
> >       struct rb_node *nd;
> >       struct mep me = {
> > -             .name = name
> > +             .metric_group = strdup(metric_group),
> > +             .metric_name = metric_name,
> >       };
> >       nd = rblist__find(groups, &me);
> > -     if (nd)
> > +     if (nd) {
> > +             free(me.metric_group);
> >               return container_of(nd, struct mep, nd);
> > +     }
> >       rblist__add_node(groups, &me);
> >       nd = rblist__find(groups, &me);
> >       if (nd)
> > @@ -404,107 +419,37 @@ static struct mep *mep_lookup(struct rblist *groups, const char *name)
> >       return NULL;
> >  }
> >
> > -static void mep_delete(struct rblist *rl __maybe_unused,
> > -                    struct rb_node *nd)
> > -{
> > -     struct mep *me = container_of(nd, struct mep, nd);
> > -
> > -     strlist__delete(me->metrics);
> > -     zfree(&me->name);
> > -     free(me);
> > -}
> > -
> > -static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
> > -{
> > -     struct str_node *sn;
> > -     int n = 0;
> > -
> > -     strlist__for_each_entry (sn, metrics) {
> > -             if (raw)
> > -                     printf("%s%s", n > 0 ? " " : "", sn->s);
> > -             else
> > -                     printf("  %s\n", sn->s);
> > -             n++;
> > -     }
> > -     if (raw)
> > -             putchar('\n');
> > -}
> > -
> > -static int metricgroup__print_pmu_event(const struct pmu_event *pe,
> > -                                     bool metricgroups, char *filter,
> > -                                     bool raw, bool details,
> > -                                     struct rblist *groups,
> > -                                     struct strlist *metriclist)
> > +static int metricgroup__add_to_mep_groups(const struct pmu_event *pe,
> > +                                     struct rblist *groups)
> >  {
> >       const char *g;
> >       char *omg, *mg;
> >
> > -     g = pe->metric_group;
> > -     if (!g && pe->metric_name) {
> > -             if (pe->name)
> > -                     return 0;
> > -             g = "No_group";
> > -     }
> > -
> > -     if (!g)
> > -             return 0;
> > -
> > -     mg = strdup(g);
> > -
> > +     mg = strdup(pe->metric_group ?: "No_group");
> >       if (!mg)
> >               return -ENOMEM;
> >       omg = mg;
> >       while ((g = strsep(&mg, ";")) != NULL) {
> >               struct mep *me;
> > -             char *s;
> >
> >               g = skip_spaces(g);
> > -             if (*g == 0)
> > -                     g = "No_group";
> > -             if (filter && !strstr(g, filter))
> > -                     continue;
> > -             if (raw)
> > -                     s = (char *)pe->metric_name;
> > -             else {
> > -                     if (asprintf(&s, "%s\n%*s%s]",
> > -                                  pe->metric_name, 8, "[", pe->desc) < 0)
> > -                             return -1;
> > -                     if (details) {
> > -                             if (asprintf(&s, "%s\n%*s%s]",
> > -                                          s, 8, "[", pe->metric_expr) < 0)
> > -                                     return -1;
> > -                     }
> > -             }
> > -
> > -             if (!s)
> > -                     continue;
> > +             if (strlen(g))
> > +                     me = mep_lookup(groups, g, pe->metric_name);
> > +             else
> > +                     me = mep_lookup(groups, "No_group", pe->metric_name);
> >
> > -             if (!metricgroups) {
> > -                     strlist__add(metriclist, s);
> > -             } else {
> > -                     me = mep_lookup(groups, g);
> > -                     if (!me)
> > -                             continue;
> > -                     strlist__add(me->metrics, s);
> > +             if (me) {
> > +                     me->metric_desc = pe->desc;
> > +                     me->metric_long_desc = pe->long_desc;
> > +                     me->metric_expr = pe->metric_expr;
> > +                     me->metric_unit = pe->unit;
> >               }
> > -
> > -             if (!raw)
> > -                     free(s);
> >       }
> >       free(omg);
> >
> >       return 0;
> >  }
> >
> > -struct metricgroup_print_sys_idata {
> > -     struct strlist *metriclist;
> > -     char *filter;
> > -     struct rblist *groups;
> > -     bool metricgroups;
> > -     bool raw;
> > -     bool details;
> > -};
> > -
> >  struct metricgroup_iter_data {
> >       pmu_event_iter_fn fn;
> >       void *data;
> > @@ -527,61 +472,26 @@ static int metricgroup__sys_event_iter(const struct pmu_event *pe,
> >
> >               return d->fn(pe, table, d->data);
> >       }
> > -
> >       return 0;
> >  }
> >
> > -static int metricgroup__print_sys_event_iter(const struct pmu_event *pe,
> > -                                          const struct pmu_events_table *table __maybe_unused,
> > -                                          void *data)
> > -{
> > -     struct metricgroup_print_sys_idata *d = data;
> > -
> > -     return metricgroup__print_pmu_event(pe, d->metricgroups, d->filter, d->raw,
> > -                                  d->details, d->groups, d->metriclist);
> > -}
> > -
> > -struct metricgroup_print_data {
> > -     const char *pmu_name;
> > -     struct strlist *metriclist;
> > -     char *filter;
> > -     struct rblist *groups;
> > -     bool metricgroups;
> > -     bool raw;
> > -     bool details;
> > -};
> > -
> > -static int metricgroup__print_callback(const struct pmu_event *pe,
> > -                                    const struct pmu_events_table *table __maybe_unused,
> > -                                    void *vdata)
> > +static int metricgroup__add_to_mep_groups_callback(const struct pmu_event *pe,
> > +                                             const struct pmu_events_table *table __maybe_unused,
> > +                                             void *vdata)
> >  {
> > -     struct metricgroup_print_data *data = vdata;
> > -     const char *pmu = pe->pmu ?: "cpu";
> > +     struct rblist *groups = vdata;
> >
> > -     if (!pe->metric_expr)
> > -             return 0;
> > -
> > -     if (data->pmu_name && strcmp(data->pmu_name, pmu))
> > +     if (!pe->metric_name)
> >               return 0;
> >
> > -     return metricgroup__print_pmu_event(pe, data->metricgroups, data->filter,
> > -                                         data->raw, data->details, data->groups,
> > -                                         data->metriclist);
> > +     return metricgroup__add_to_mep_groups(pe, groups);
> >  }
> >
> > -void metricgroup__print(bool metrics, bool metricgroups, char *filter,
> > -                     bool raw, bool details, const char *pmu_name)
> > +void metricgroup__print(const struct print_callbacks *print_cb, void *print_state)
> >  {
> >       struct rblist groups;
> > -     struct rb_node *node, *next;
> > -     struct strlist *metriclist = NULL;
> >       const struct pmu_events_table *table;
> > -
> > -     if (!metricgroups) {
> > -             metriclist = strlist__new(NULL, NULL);
> > -             if (!metriclist)
> > -                     return;
> > -     }
> > +     struct rb_node *node, *next;
> >
> >       rblist__init(&groups);
> >       groups.node_new = mep_new;
> > @@ -589,56 +499,31 @@ void metricgroup__print(bool metrics, bool metricgroups, char *filter,
> >       groups.node_delete = mep_delete;
> >       table = pmu_events_table__find();
> >       if (table) {
> > -             struct metricgroup_print_data data = {
> > -                     .pmu_name = pmu_name,
> > -                     .metriclist = metriclist,
> > -                     .metricgroups = metricgroups,
> > -                     .filter = filter,
> > -                     .raw = raw,
> > -                     .details = details,
> > -                     .groups = &groups,
> > -             };
> > -
> >               pmu_events_table_for_each_event(table,
> > -                                             metricgroup__print_callback,
> > -                                             &data);
> > +                                             metricgroup__add_to_mep_groups_callback,
> > +                                             &groups);
> >       }
> >       {
> >               struct metricgroup_iter_data data = {
> > -                     .fn = metricgroup__print_sys_event_iter,
> > -                     .data = (void *) &(struct metricgroup_print_sys_idata){
> > -                             .metriclist = metriclist,
> > -                             .metricgroups = metricgroups,
> > -                             .filter = filter,
> > -                             .raw = raw,
> > -                             .details = details,
> > -                             .groups = &groups,
> > -                     },
> > +                     .fn = metricgroup__add_to_mep_groups_callback,
> > +                     .data = &groups,
> >               };
> > -
> >               pmu_for_each_sys_event(metricgroup__sys_event_iter, &data);
> >       }
> >
> > -     if (!filter || !rblist__empty(&groups)) {
> > -             if (metricgroups && !raw)
> > -                     printf("\nMetric Groups:\n\n");
> > -             else if (metrics && !raw)
> > -                     printf("\nMetrics:\n\n");
> > -     }
> > -
> >       for (node = rb_first_cached(&groups.entries); node; node = next) {
> >               struct mep *me = container_of(node, struct mep, nd);
> >
> > -             if (metricgroups)
> > -                     printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
> > -             if (metrics)
> > -                     metricgroup__print_strlist(me->metrics, raw);
> > +             print_cb->print_metric(print_state,
> > +                             me->metric_group,
> > +                             me->metric_name,
> > +                             me->metric_desc,
> > +                             me->metric_long_desc,
> > +                             me->metric_expr,
> > +                             me->metric_unit);
> >               next = rb_next(node);
> >               rblist__remove_node(&groups, node);
> >       }
> > -     if (!metricgroups)
> > -             metricgroup__print_strlist(metriclist, raw);
> > -     strlist__delete(metriclist);
> >  }
> >
> >  static const char *code_characters = ",-=@";
> > diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h
> > index 732d3a0d3334..0013cf582173 100644
> > --- a/tools/perf/util/metricgroup.h
> > +++ b/tools/perf/util/metricgroup.h
> > @@ -10,6 +10,7 @@
> >  struct evlist;
> >  struct evsel;
> >  struct option;
> > +struct print_callbacks;
> >  struct rblist;
> >  struct cgroup;
> >
> > @@ -78,8 +79,7 @@ int metricgroup__parse_groups_test(struct evlist *evlist,
> >                                  bool metric_no_merge,
> >                                  struct rblist *metric_events);
> >
> > -void metricgroup__print(bool metrics, bool groups, char *filter,
> > -                     bool raw, bool details, const char *pmu_name);
> > +void metricgroup__print(const struct print_callbacks *print_cb, void *print_state);
> >  bool metricgroup__has_metric(const char *metric);
> >  int arch_get_runtimeparam(const struct pmu_event *pe __maybe_unused);
> >  void metricgroup__rblist_exit(struct rblist *metric_events);
> > diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> > index 075c82dd1347..e9a4f31926bf 100644
> > --- a/tools/perf/util/pmu.c
> > +++ b/tools/perf/util/pmu.c
> > @@ -23,6 +23,7 @@
> >  #include "evsel.h"
> >  #include "pmu.h"
> >  #include "parse-events.h"
> > +#include "print-events.h"
> >  #include "header.h"
> >  #include "string2.h"
> >  #include "strbuf.h"
> > @@ -1579,13 +1580,6 @@ static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
> >       return buf;
> >  }
> >
> > -static char *format_alias_or(char *buf, int len, const struct perf_pmu *pmu,
> > -                          const struct perf_pmu_alias *alias)
> > -{
> > -     snprintf(buf, len, "%s OR %s/%s/", alias->name, pmu->name, alias->name);
> > -     return buf;
> > -}
> > -
> >  /** Struct for ordering events as output in perf list. */
> >  struct sevent {
> >       /** PMU for event. */
> > @@ -1629,7 +1623,7 @@ static int cmp_sevent(const void *a, const void *b)
> >
> >       /* Order CPU core events to be first */
> >       if (as->is_cpu != bs->is_cpu)
> > -             return bs->is_cpu - as->is_cpu;
> > +             return as->is_cpu ? -1 : 1;
> >
> >       /* Order by PMU name. */
> >       a_pmu_name = as->pmu->name ?: "";
> > @@ -1642,27 +1636,6 @@ static int cmp_sevent(const void *a, const void *b)
> >       return strcmp(a_name, b_name);
> >  }
> >
> > -static void wordwrap(char *s, int start, int max, int corr)
> > -{
> > -     int column = start;
> > -     int n;
> > -
> > -     while (*s) {
> > -             int wlen = strcspn(s, " \t");
> > -
> > -             if (column + wlen >= max && column > start) {
> > -                     printf("\n%*s", start, "");
> > -                     column = start + corr;
> > -             }
> > -             n = printf("%s%.*s", column > start ? " " : "", wlen, s);
> > -             if (n <= 0)
> > -                     break;
> > -             s += wlen;
> > -             column += n;
> > -             s = skip_spaces(s);
> > -     }
> > -}
> > -
> >  bool is_pmu_core(const char *name)
> >  {
> >       return !strcmp(name, "cpu") || is_arm_pmu_core(name);
> > @@ -1685,24 +1658,19 @@ static bool pmu_alias_is_duplicate(struct sevent *alias_a,
> >       return strcmp(a_pmu_name, b_pmu_name) == 0;
> >  }
> >
> > -void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
> > -                     bool long_desc, bool details_flag, bool deprecated,
> > -                     const char *pmu_name)
> > +void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
> >  {
> >       struct perf_pmu *pmu;
> > -     struct perf_pmu_alias *alias;
> > +     struct perf_pmu_alias *event;
> >       char buf[1024];
> >       int printed = 0;
> >       int len, j;
> >       struct sevent *aliases;
> > -     int numdesc = 0;
> > -     int columns = pager_get_columns();
> > -     char *topic = NULL;
> >
> >       pmu = NULL;
> >       len = 0;
> >       while ((pmu = perf_pmu__scan(pmu)) != NULL) {
> > -             list_for_each_entry(alias, &pmu->aliases, list)
> > +             list_for_each_entry(event, &pmu->aliases, list)
> >                       len++;
> >               if (pmu->selectable)
> >                       len++;
> > @@ -1715,32 +1683,15 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
> >       pmu = NULL;
> >       j = 0;
> >       while ((pmu = perf_pmu__scan(pmu)) != NULL) {
> > -             bool is_cpu;
> > +             bool is_cpu = is_pmu_core(pmu->name) || perf_pmu__is_hybrid(pmu->name);
> >
> > -             if (pmu_name && pmu->name && strcmp(pmu_name, pmu->name))
> > -                     continue;
> > -
> > -             is_cpu = is_pmu_core(pmu->name) || perf_pmu__is_hybrid(pmu->name);
> > -
> > -             list_for_each_entry(alias, &pmu->aliases, list) {
> > -                     if (alias->deprecated && !deprecated)
> > -                             continue;
> > -
> > -                     if (event_glob != NULL &&
> > -                         !(strglobmatch_nocase(alias->name, event_glob) ||
> > -                           (!is_cpu &&
> > -                            strglobmatch_nocase(alias->name, event_glob)) ||
> > -                           (alias->topic &&
> > -                            strglobmatch_nocase(alias->topic, event_glob))))
> > -                             continue;
> > -
> > -                     aliases[j].event = alias;
> > +             list_for_each_entry(event, &pmu->aliases, list) {
> > +                     aliases[j].event = event;
> >                       aliases[j].pmu = pmu;
> >                       aliases[j].is_cpu = is_cpu;
> >                       j++;
> >               }
> > -             if (pmu->selectable &&
> > -                 (event_glob == NULL || strglobmatch(pmu->name, event_glob))) {
> > +             if (pmu->selectable) {
> >                       aliases[j].event = NULL;
> >                       aliases[j].pmu = pmu;
> >                       aliases[j].is_cpu = is_cpu;
> > @@ -1750,7 +1701,12 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
> >       len = j;
> >       qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
> >       for (j = 0; j < len; j++) {
> > -             char *name, *desc;
> > +             const char *name, *alias = NULL, *scale_unit = NULL,
> > +                     *desc = NULL, *long_desc = NULL,
> > +                     *encoding_desc = NULL, *topic = NULL,
> > +                     *metric_name = NULL, *metric_expr = NULL;
> > +             bool deprecated = false;
> > +             size_t buf_used;
> >
> >               /* Skip duplicates */
> >               if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1]))
> > @@ -1758,48 +1714,51 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
> >
> >               if (!aliases[j].event) {
> >                       /* A selectable event. */
> > -                     snprintf(buf, sizeof(buf), "%s//", aliases[j].pmu->name);
> > +                     buf_used = snprintf(buf, sizeof(buf), "%s//", aliases[j].pmu->name) + 1;
> >                       name = buf;
> > -             } else if (aliases[j].event->desc) {
> > -                     name = aliases[j].event->name;
> >               } else {
> > -                     if (!name_only && aliases[j].is_cpu) {
> > -                             name = format_alias_or(buf, sizeof(buf), aliases[j].pmu,
> > -                                                    aliases[j].event);
> > +                     if (aliases[j].event->desc) {
> > +                             name = aliases[j].event->name;
> > +                             buf_used = 0;
> >                       } else {
> >                               name = format_alias(buf, sizeof(buf), aliases[j].pmu,
> >                                                   aliases[j].event);
> > +                             if (aliases[j].is_cpu) {
> > +                                     alias = name;
> > +                                     name = aliases[j].event->name;
> > +                             }
> > +                             buf_used = strlen(buf) + 1;
> >                       }
> > -             }
> > -             if (name_only) {
> > -                     printf("%s ", name);
> > -                     continue;
> > -             }
> > -             printed++;
> > -             if (!aliases[j].event || !aliases[j].event->desc || quiet_flag) {
> > -                     printf("  %-50s [Kernel PMU event]\n", name);
> > -                     continue;
> > -             }
> > -             if (numdesc++ == 0)
> > -                     printf("\n");
> > -             if (aliases[j].event->topic && (!topic ||
> > -                                             strcmp(topic, aliases[j].event->topic))) {
> > -                     printf("%s%s:\n", topic ? "\n" : "", aliases[j].event->topic);
> > +                     if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) {
> > +                             scale_unit = buf + buf_used;
> > +                             buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
> > +                                             "%G%s", aliases[j].event->scale,
> > +                                             aliases[j].event->unit) + 1;
> > +                     }
> > +                     desc = aliases[j].event->desc;
> > +                     long_desc = aliases[j].event->long_desc;
> >                       topic = aliases[j].event->topic;
> > +                     encoding_desc = buf + buf_used;
> > +                     buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
> > +                                     "%s/%s/", aliases[j].pmu->name,
> > +                                     aliases[j].event->str) + 1;
> > +                     metric_name = aliases[j].event->metric_name;
> > +                     metric_expr = aliases[j].event->metric_expr;
> > +                     deprecated = aliases[j].event->deprecated;
> >               }
> > -             printf("  %-50s\n", name);
> > -             printf("%*s", 8, "[");
> > -             desc = long_desc ? aliases[j].event->long_desc : aliases[j].event->desc;
> > -             wordwrap(desc, 8, columns, 0);
> > -             printf("]\n");
> > -             if (details_flag) {
> > -                     printf("%*s%s/%s/ ", 8, "", aliases[j].pmu->name, aliases[j].event->str);
> > -                     if (aliases[j].event->metric_name)
> > -                             printf(" MetricName: %s", aliases[j].event->metric_name);
> > -                     if (aliases[j].event->metric_expr)
> > -                             printf(" MetricExpr: %s", aliases[j].event->metric_expr);
> > -                     putchar('\n');
> > -             }
> > +             print_cb->print_event(print_state,
> > +                             aliases[j].pmu->name,
> > +                             topic,
> > +                             name,
> > +                             alias,
> > +                             scale_unit,
> > +                             deprecated,
> > +                             "Kernel PMU event",
> > +                             desc,
> > +                             long_desc,
> > +                             encoding_desc,
> > +                             metric_name,
> > +                             metric_expr);
> >       }
> >       if (printed && pager_in_use())
> >               printf("\n");
> > diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
> > index ee02e1ef9187..69ca0004f94f 100644
> > --- a/tools/perf/util/pmu.h
> > +++ b/tools/perf/util/pmu.h
> > @@ -12,6 +12,7 @@
> >
> >  struct evsel_config_term;
> >  struct perf_cpu_map;
> > +struct print_callbacks;
> >
> >  enum {
> >       PERF_PMU_FORMAT_VALUE_CONFIG,
> > @@ -225,9 +226,7 @@ void perf_pmu__del_formats(struct list_head *formats);
> >  struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
> >
> >  bool is_pmu_core(const char *name);
> > -void print_pmu_events(const char *event_glob, bool name_only, bool quiet,
> > -                   bool long_desc, bool details_flag,
> > -                   bool deprecated, const char *pmu_name);
> > +void print_pmu_events(const struct print_callbacks *print_cb, void *print_state);
> >  bool pmu_have_event(const char *pname, const char *name);
> >
> >  int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...) __scanf(3, 4);
> > diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c
> > index d53dba033597..5210a168d54f 100644
> > --- a/tools/perf/util/print-events.c
> > +++ b/tools/perf/util/print-events.c
> > @@ -28,6 +28,7 @@
> >
> >  #define MAX_NAME_LEN 100
> >
> > +/** Strings corresponding to enum perf_type_id. */
> >  static const char * const event_type_descriptors[] = {
> >       "Hardware event",
> >       "Software event",
> > @@ -55,11 +56,9 @@ static const struct event_symbol event_symbols_tool[PERF_TOOL_MAX] = {
> >  /*
> >   * Print the events from <debugfs_mount_point>/tracing/events
> >   */
> > -void print_tracepoint_events(const char *subsys_glob,
> > -                          const char *event_glob, bool name_only)
> > +void print_tracepoint_events(const struct print_callbacks *print_cb, void *print_state)
> >  {
> >       struct dirent **sys_namelist = NULL;
> > -     bool printed = false;
> >       int sys_items = tracing_events__scandir_alphasort(&sys_namelist);
> >
> >       for (int i = 0; i < sys_items; i++) {
> > @@ -73,10 +72,6 @@ void print_tracepoint_events(const char *subsys_glob,
> >                   !strcmp(sys_dirent->d_name, ".."))
> >                       continue;
> >
> > -             if (subsys_glob != NULL &&
> > -                 !strglobmatch(sys_dirent->d_name, subsys_glob))
> > -                     continue;
> > -
> >               dir_path = get_events_file(sys_dirent->d_name);
> >               if (!dir_path)
> >                       continue;
> > @@ -94,41 +89,41 @@ void print_tracepoint_events(const char *subsys_glob,
> >                       if (tp_event_has_id(dir_path, evt_dirent) != 0)
> >                               continue;
> >
> > -                     if (event_glob != NULL &&
> > -                         !strglobmatch(evt_dirent->d_name, event_glob))
> > -                             continue;
> > -
> >                       snprintf(evt_path, MAXPATHLEN, "%s:%s",
> >                                sys_dirent->d_name, evt_dirent->d_name);
> > -                     if (name_only)
> > -                             printf("%s ", evt_path);
> > -                     else {
> > -                             printf("  %-50s [%s]\n", evt_path,
> > -                                    event_type_descriptors[PERF_TYPE_TRACEPOINT]);
> > -                     }
> > -                     printed = true;
> > +                     print_cb->print_event(print_state,
> > +                                     /*topic=*/NULL,
> > +                                     /*pmu_name=*/NULL,
> > +                                     evt_path,
> > +                                     /*event_alias=*/NULL,
> > +                                     /*scale_unit=*/NULL,
> > +                                     /*deprecated=*/false,
> > +                                     "Tracepoint event",
> > +                                     /*desc=*/NULL,
> > +                                     /*long_desc=*/NULL,
> > +                                     /*encoding_desc=*/NULL,
> > +                                     /*metric_name=*/NULL,
> > +                                     /*metric_expr=*/NULL);
> >               }
> >               free(dir_path);
> >               free(evt_namelist);
> >       }
> >       free(sys_namelist);
> > -     if (printed && pager_in_use())
> > -             printf("\n");
> >  }
> >
> > -void print_sdt_events(const char *subsys_glob, const char *event_glob,
> > -                   bool name_only)
> > +void print_sdt_events(const struct print_callbacks *print_cb, void *print_state)
> >  {
> > -     struct probe_cache *pcache;
> > -     struct probe_cache_entry *ent;
> >       struct strlist *bidlist, *sdtlist;
> > -     struct strlist_config cfg = {.dont_dupstr = true};
> > -     struct str_node *nd, *nd2;
> > -     char *buf, *path, *ptr = NULL;
> > -     bool show_detail = false;
> > -     int ret;
> > -
> > -     sdtlist = strlist__new(NULL, &cfg);
> > +     struct str_node *bid_nd, *sdt_name, *next_sdt_name;
> > +     const char *last_sdt_name = NULL;
> > +
> > +     /*
> > +      * The implicitly sorted sdtlist will hold the tracepoint name followed
> > +      * by @<buildid>. If the tracepoint name is unique (determined by
> > +      * looking at the adjacent nodes) the @<buildid> is dropped otherwise
> > +      * the executable path and buildid are added to the name.
> > +      */
> > +     sdtlist = strlist__new(NULL, NULL);
> >       if (!sdtlist) {
> >               pr_debug("Failed to allocate new strlist for SDT\n");
> >               return;
> > @@ -138,65 +133,77 @@ void print_sdt_events(const char *subsys_glob, const char *event_glob,
> >               pr_debug("Failed to get buildids: %d\n", errno);
> >               return;
> >       }
> > -     strlist__for_each_entry(nd, bidlist) {
> > -             pcache = probe_cache__new(nd->s, NULL);
> > +     strlist__for_each_entry(bid_nd, bidlist) {
> > +             struct probe_cache *pcache;
> > +             struct probe_cache_entry *ent;
> > +
> > +             pcache = probe_cache__new(bid_nd->s, NULL);
> >               if (!pcache)
> >                       continue;
> >               list_for_each_entry(ent, &pcache->entries, node) {
> > -                     if (!ent->sdt)
> > -                             continue;
> > -                     if (subsys_glob &&
> > -                         !strglobmatch(ent->pev.group, subsys_glob))
> > -                             continue;
> > -                     if (event_glob &&
> > -                         !strglobmatch(ent->pev.event, event_glob))
> > -                             continue;
> > -                     ret = asprintf(&buf, "%s:%s@%s", ent->pev.group,
> > -                                     ent->pev.event, nd->s);
> > -                     if (ret > 0)
> > -                             strlist__add(sdtlist, buf);
> > +                     char buf[1024];
> > +
> > +                     snprintf(buf, sizeof(buf), "%s:%s@%s",
> > +                              ent->pev.group, ent->pev.event, bid_nd->s);
> > +                     strlist__add(sdtlist, buf);
> >               }
> >               probe_cache__delete(pcache);
> >       }
> >       strlist__delete(bidlist);
> >
> > -     strlist__for_each_entry(nd, sdtlist) {
> > -             buf = strchr(nd->s, '@');
> > -             if (buf)
> > -                     *(buf++) = '\0';
> > -             if (name_only) {
> > -                     printf("%s ", nd->s);
> > -                     continue;
> > -             }
> > -             nd2 = strlist__next(nd);
> > -             if (nd2) {
> > -                     ptr = strchr(nd2->s, '@');
> > -                     if (ptr)
> > -                             *ptr = '\0';
> > -                     if (strcmp(nd->s, nd2->s) == 0)
> > -                             show_detail = true;
> > +     strlist__for_each_entry(sdt_name, sdtlist) {
> > +             bool show_detail = false;
> > +             char *bid = strchr(sdt_name->s, '@');
> > +             char *evt_name = NULL;
> > +
> > +             if (bid)
> > +                     *(bid++) = '\0';
> > +
> > +             if (last_sdt_name && !strcmp(last_sdt_name, sdt_name->s)) {
> > +                     show_detail = true;
> > +             } else {
> > +                     next_sdt_name = strlist__next(sdt_name);
> > +                     if (next_sdt_name) {
> > +                             char *bid2 = strchr(next_sdt_name->s, '@');
> > +
> > +                             if (bid2)
> > +                                     *bid2 = '\0';
> > +                             if (strcmp(sdt_name->s, next_sdt_name->s) == 0)
> > +                                     show_detail = true;
> > +                             if (bid2)
> > +                                     *bid2 = '@';
> > +                     }
> >               }
> > +             last_sdt_name = sdt_name->s;
> > +
> >               if (show_detail) {
> > -                     path = build_id_cache__origname(buf);
> > -                     ret = asprintf(&buf, "%s@%s(%.12s)", nd->s, path, buf);
> > -                     if (ret > 0) {
> > -                             printf("  %-50s [%s]\n", buf, "SDT event");
> > -                             free(buf);
> > +                     char *path = build_id_cache__origname(bid);
> > +
> > +                     if (path) {
> > +                             asprintf(&evt_name, "%s@%s(%.12s)", sdt_name->s, path, bid);
> > +                             free(path);
> >                       }
> > -                     free(path);
> > -             } else
> > -                     printf("  %-50s [%s]\n", nd->s, "SDT event");
> > -             if (nd2) {
> > -                     if (strcmp(nd->s, nd2->s) != 0)
> > -                             show_detail = false;
> > -                     if (ptr)
> > -                             *ptr = '@';
> >               }
> > +             print_cb->print_event(print_state,
> > +                             /*topic=*/NULL,
> > +                             /*pmu_name=*/NULL,
> > +                             evt_name ?: sdt_name->s,
> > +                             /*event_alias=*/NULL,
> > +                             /*deprecated=*/false,
> > +                             /*scale_unit=*/NULL,
> > +                             "SDT event",
> > +                             /*desc=*/NULL,
> > +                             /*long_desc=*/NULL,
> > +                             /*encoding_desc=*/NULL,
> > +                             /*metric_name=*/NULL,
> > +                             /*metric_expr=*/NULL);
> > +
> > +             free(evt_name);
> >       }
> >       strlist__delete(sdtlist);
> >  }
> >
> > -int print_hwcache_events(const char *event_glob, bool name_only)
> > +int print_hwcache_events(const struct print_callbacks *print_cb, void *print_state)
> >  {
> >       struct strlist *evt_name_list = strlist__new(NULL, NULL);
> >       struct str_node *nd;
> > @@ -216,9 +223,6 @@ int print_hwcache_events(const char *event_glob, bool name_only)
> >                               char name[64];
> >
> >                               __evsel__hw_cache_type_op_res_name(type, op, i, name, sizeof(name));
> > -                             if (event_glob != NULL && !strglobmatch(name, event_glob))
> > -                                     continue;
> > -
> >                               if (!perf_pmu__has_hybrid()) {
> >                                       if (is_event_supported(PERF_TYPE_HW_CACHE,
> >                                                              type | (op << 8) | (i << 16)))
> > @@ -240,55 +244,47 @@ int print_hwcache_events(const char *event_glob, bool name_only)
> >       }
> >
> >       strlist__for_each_entry(nd, evt_name_list) {
> > -             if (name_only) {
> > -                     printf("%s ", nd->s);
> > -                     continue;
> > -             }
> > -             printf("  %-50s [%s]\n", nd->s, event_type_descriptors[PERF_TYPE_HW_CACHE]);
> > +             print_cb->print_event(print_state,
> > +                             "cache",
> > +                             /*pmu_name=*/NULL,
> > +                             nd->s,
> > +                             /*event_alias=*/NULL,
> > +                             /*scale_unit=*/NULL,
> > +                             /*deprecated=*/false,
> > +                             event_type_descriptors[PERF_TYPE_HW_CACHE],
> > +                             /*desc=*/NULL,
> > +                             /*long_desc=*/NULL,
> > +                             /*encoding_desc=*/NULL,
> > +                             /*metric_name=*/NULL,
> > +                             /*metric_expr=*/NULL);
> >       }
> > -     if (!strlist__empty(evt_name_list) && pager_in_use())
> > -             printf("\n");
> > -
> >       strlist__delete(evt_name_list);
> >       return 0;
> >  }
> >
> > -static void print_tool_event(const struct event_symbol *syms, const char *event_glob,
> > -                          bool name_only)
> > -{
> > -     if (syms->symbol == NULL)
> > -             return;
> > -
> > -     if (event_glob && !(strglobmatch(syms->symbol, event_glob) ||
> > -           (syms->alias && strglobmatch(syms->alias, event_glob))))
> > -             return;
> > -
> > -     if (name_only)
> > -             printf("%s ", syms->symbol);
> > -     else {
> > -             char name[MAX_NAME_LEN];
> > -
> > -             if (syms->alias && strlen(syms->alias))
> > -                     snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias);
> > -             else
> > -                     strlcpy(name, syms->symbol, MAX_NAME_LEN);
> > -             printf("  %-50s [%s]\n", name, "Tool event");
> > -     }
> > -}
> > -
> > -void print_tool_events(const char *event_glob, bool name_only)
> > +void print_tool_events(const struct print_callbacks *print_cb, void *print_state)
> >  {
> >       // Start at 1 because the first enum entry means no tool event.
> > -     for (int i = 1; i < PERF_TOOL_MAX; ++i)
> > -             print_tool_event(event_symbols_tool + i, event_glob, name_only);
> > -
> > -     if (pager_in_use())
> > -             printf("\n");
> > +     for (int i = 1; i < PERF_TOOL_MAX; ++i) {
> > +             print_cb->print_event(print_state,
> > +                             "tool",
> > +                             /*pmu_name=*/NULL,
> > +                             event_symbols_tool[i].symbol,
> > +                             event_symbols_tool[i].alias,
> > +                             /*scale_unit=*/NULL,
> > +                             /*deprecated=*/false,
> > +                             "Tool event",
> > +                             /*desc=*/NULL,
> > +                             /*long_desc=*/NULL,
> > +                             /*encoding_desc=*/NULL,
> > +                             /*metric_name=*/NULL,
> > +                             /*metric_expr=*/NULL);
> > +     }
> >  }
> >
> > -void print_symbol_events(const char *event_glob, unsigned int type,
> > -                      struct event_symbol *syms, unsigned int max,
> > -                      bool name_only)
> > +void print_symbol_events(const struct print_callbacks *print_cb, void *print_state,
> > +                      unsigned int type, const struct event_symbol *syms,
> > +                      unsigned int max)
> >  {
> >       struct strlist *evt_name_list = strlist__new(NULL, NULL);
> >       struct str_node *nd;
> > @@ -305,10 +301,6 @@ void print_symbol_events(const char *event_glob, unsigned int type,
> >               if (syms[i].symbol == NULL)
> >                       continue;
> >
> > -             if (event_glob != NULL && !(strglobmatch(syms[i].symbol, event_glob) ||
> > -                   (syms[i].alias && strglobmatch(syms[i].alias, event_glob))))
> > -                     continue;
> > -
> >               if (!is_event_supported(type, i))
> >                       continue;
> >
> > @@ -322,63 +314,92 @@ void print_symbol_events(const char *event_glob, unsigned int type,
> >       }
> >
> >       strlist__for_each_entry(nd, evt_name_list) {
> > -             if (name_only) {
> > -                     printf("%s ", nd->s);
> > -                     continue;
> > +             char *alias = strstr(nd->s, " OR ");
> > +
> > +             if (alias) {
> > +                     *alias = '\0';
> > +                     alias += 4;
> >               }
> > -             printf("  %-50s [%s]\n", nd->s, event_type_descriptors[type]);
> > +             print_cb->print_event(print_state,
> > +                             /*topic=*/NULL,
> > +                             /*pmu_name=*/NULL,
> > +                             nd->s,
> > +                             alias,
> > +                             /*scale_unit=*/NULL,
> > +                             /*deprecated=*/false,
> > +                             event_type_descriptors[type],
> > +                             /*desc=*/NULL,
> > +                             /*long_desc=*/NULL,
> > +                             /*encoding_desc=*/NULL,
> > +                             /*metric_name=*/NULL,
> > +                             /*metric_expr=*/NULL);
> >       }
> > -     if (!strlist__empty(evt_name_list) && pager_in_use())
> > -             printf("\n");
> > -
> >       strlist__delete(evt_name_list);
> >  }
> >
> >  /*
> >   * Print the help text for the event symbols:
> >   */
> > -void print_events(const char *event_glob, bool name_only, bool quiet_flag,
> > -                     bool long_desc, bool details_flag, bool deprecated,
> > -                     const char *pmu_name)
> > +void print_events(const struct print_callbacks *print_cb, void *print_state)
> >  {
> > -     print_symbol_events(event_glob, PERF_TYPE_HARDWARE,
> > -                         event_symbols_hw, PERF_COUNT_HW_MAX, name_only);
> > -
> > -     print_symbol_events(event_glob, PERF_TYPE_SOFTWARE,
> > -                         event_symbols_sw, PERF_COUNT_SW_MAX, name_only);
> > -     print_tool_events(event_glob, name_only);
> > -
> > -     print_hwcache_events(event_glob, name_only);
> > -
> > -     print_pmu_events(event_glob, name_only, quiet_flag, long_desc,
> > -                     details_flag, deprecated, pmu_name);
> > -
> > -     if (event_glob != NULL)
> > -             return;
> > -
> > -     if (!name_only) {
> > -             printf("  %-50s [%s]\n",
> > -                    "rNNN",
> > -                    event_type_descriptors[PERF_TYPE_RAW]);
> > -             printf("  %-50s [%s]\n",
> > -                    "cpu/t1=v1[,t2=v2,t3 ...]/modifier",
> > -                    event_type_descriptors[PERF_TYPE_RAW]);
> > -             if (pager_in_use())
> > -                     printf("   (see 'man perf-list' on how to encode it)\n\n");
> > -
> > -             printf("  %-50s [%s]\n",
> > -                    "mem:<addr>[/len][:access]",
> > -                     event_type_descriptors[PERF_TYPE_BREAKPOINT]);
> > -             if (pager_in_use())
> > -                     printf("\n");
> > -     }
> > -
> > -     print_tracepoint_events(NULL, NULL, name_only);
> > -
> > -     print_sdt_events(NULL, NULL, name_only);
> > -
> > -     metricgroup__print(true, true, NULL, name_only, details_flag,
> > -                        pmu_name);
> > -
> > -     print_libpfm_events(name_only, long_desc);
> > +     print_symbol_events(print_cb, print_state, PERF_TYPE_HARDWARE,
> > +                     event_symbols_hw, PERF_COUNT_HW_MAX);
> > +     print_symbol_events(print_cb, print_state, PERF_TYPE_SOFTWARE,
> > +                     event_symbols_sw, PERF_COUNT_SW_MAX);
> > +
> > +     print_tool_events(print_cb, print_state);
> > +
> > +     print_hwcache_events(print_cb, print_state);
> > +
> > +     print_pmu_events(print_cb, print_state);
> > +
> > +     print_cb->print_event(print_state,
> > +                     /*topic=*/NULL,
> > +                     /*pmu_name=*/NULL,
> > +                     "rNNN",
> > +                     /*event_alias=*/NULL,
> > +                     /*scale_unit=*/NULL,
> > +                     /*deprecated=*/false,
> > +                     event_type_descriptors[PERF_TYPE_RAW],
> > +                     /*desc=*/NULL,
> > +                     /*long_desc=*/NULL,
> > +                     /*encoding_desc=*/NULL,
> > +                     /*metric_name=*/NULL,
> > +                     /*metric_expr=*/NULL);
> > +
> > +     print_cb->print_event(print_state,
> > +                     /*topic=*/NULL,
> > +                     /*pmu_name=*/NULL,
> > +                     "cpu/t1=v1[,t2=v2,t3 ...]/modifier",
> > +                     /*event_alias=*/NULL,
> > +                     /*scale_unit=*/NULL,
> > +                     /*deprecated=*/false,
> > +                     event_type_descriptors[PERF_TYPE_RAW],
> > +                     "(see 'man perf-list' on how to encode it)",
> > +                     /*long_desc=*/NULL,
> > +                     /*encoding_desc=*/NULL,
> > +                     /*metric_name=*/NULL,
> > +                     /*metric_expr=*/NULL);
> > +
> > +     print_cb->print_event(print_state,
> > +                     /*topic=*/NULL,
> > +                     /*pmu_name=*/NULL,
> > +                     "mem:<addr>[/len][:access]",
> > +                     /*scale_unit=*/NULL,
> > +                     /*event_alias=*/NULL,
> > +                     /*deprecated=*/false,
> > +                     event_type_descriptors[PERF_TYPE_BREAKPOINT],
> > +                     /*desc=*/NULL,
> > +                     /*long_desc=*/NULL,
> > +                     /*encoding_desc=*/NULL,
> > +                     /*metric_name=*/NULL,
> > +                     /*metric_expr=*/NULL);
> > +
> > +     print_tracepoint_events(print_cb, print_state);
> > +
> > +     print_sdt_events(print_cb, print_state);
> > +
> > +     metricgroup__print(print_cb, print_state);
> > +
> > +     print_libpfm_events(print_cb, print_state);
> >  }
> > diff --git a/tools/perf/util/print-events.h b/tools/perf/util/print-events.h
> > index 1da9910d83a6..c237e53c4487 100644
> > --- a/tools/perf/util/print-events.h
> > +++ b/tools/perf/util/print-events.h
> > @@ -2,21 +2,39 @@
> >  #ifndef __PERF_PRINT_EVENTS_H
> >  #define __PERF_PRINT_EVENTS_H
> >
> > +#include <linux/perf_event.h>
> >  #include <stdbool.h>
> >
> >  struct event_symbol;
> >
> > -void print_events(const char *event_glob, bool name_only, bool quiet_flag,
> > -               bool long_desc, bool details_flag, bool deprecated,
> > -               const char *pmu_name);
> > -int print_hwcache_events(const char *event_glob, bool name_only);
> > -void print_sdt_events(const char *subsys_glob, const char *event_glob,
> > -                   bool name_only);
> > -void print_symbol_events(const char *event_glob, unsigned int type,
> > -                      struct event_symbol *syms, unsigned int max,
> > -                      bool name_only);
> > -void print_tool_events(const char *event_glob, bool name_only);
> > -void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
> > -                          bool name_only);
> > +struct print_callbacks {
> > +     void (*print_start)(void *print_state);
> > +     void (*print_end)(void *print_state);
> > +     void (*print_event)(void *print_state, const char *topic,
> > +                     const char *pmu_name,
> > +                     const char *event_name, const char *event_alias,
> > +                     const char *scale_unit,
> > +                     bool deprecated, const char *event_type_desc,
> > +                     const char *desc, const char *long_desc,
> > +                     const char *encoding_desc,
> > +                     const char *metric_name, const char *metric_expr);
> > +     void (*print_metric)(void *print_state,
> > +                     const char *group,
> > +                     const char *name,
> > +                     const char *desc,
> > +                     const char *long_desc,
> > +                     const char *expr,
> > +                     const char *unit);
> > +};
> > +
> > +/** Print all events, the default when no options are specified. */
> > +void print_events(const struct print_callbacks *print_cb, void *print_state);
> > +int print_hwcache_events(const struct print_callbacks *print_cb, void *print_state);
> > +void print_sdt_events(const struct print_callbacks *print_cb, void *print_state);
> > +void print_symbol_events(const struct print_callbacks *print_cb, void *print_state,
> > +                      unsigned int type, const struct event_symbol *syms,
> > +                      unsigned int max);
> > +void print_tool_events(const struct print_callbacks *print_cb, void *print_state);
> > +void print_tracepoint_events(const struct print_callbacks *print_cb, void *print_state);
> >
> >  #endif /* __PERF_PRINT_EVENTS_H */
> > --
> > 2.38.1.431.g37b22c650d-goog
>
> --
>
> - Arnaldo

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ