linux/tools/perf/util/pfm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Support for libpfm4 event encoding.
   4 *
   5 * Copyright 2020 Google LLC.
   6 */
   7#include "util/cpumap.h"
   8#include "util/debug.h"
   9#include "util/event.h"
  10#include "util/evlist.h"
  11#include "util/evsel.h"
  12#include "util/parse-events.h"
  13#include "util/pmu.h"
  14#include "util/pfm.h"
  15
  16#include <string.h>
  17#include <linux/kernel.h>
  18#include <perfmon/pfmlib_perf_event.h>
  19
  20static void libpfm_initialize(void)
  21{
  22        int ret;
  23
  24        ret = pfm_initialize();
  25        if (ret != PFM_SUCCESS) {
  26                ui__warning("libpfm failed to initialize: %s\n",
  27                        pfm_strerror(ret));
  28        }
  29}
  30
  31int parse_libpfm_events_option(const struct option *opt, const char *str,
  32                        int unset __maybe_unused)
  33{
  34        struct evlist *evlist = *(struct evlist **)opt->value;
  35        struct perf_event_attr attr;
  36        struct perf_pmu *pmu;
  37        struct evsel *evsel, *grp_leader = NULL;
  38        char *p, *q, *p_orig;
  39        const char *sep;
  40        int grp_evt = -1;
  41        int ret;
  42
  43        libpfm_initialize();
  44
  45        p_orig = p = strdup(str);
  46        if (!p)
  47                return -1;
  48        /*
  49         * force loading of the PMU list
  50         */
  51        perf_pmu__scan(NULL);
  52
  53        for (q = p; strsep(&p, ",{}"); q = p) {
  54                sep = p ? str + (p - p_orig - 1) : "";
  55                if (*sep == '{') {
  56                        if (grp_evt > -1) {
  57                                ui__error(
  58                                        "nested event groups not supported\n");
  59                                goto error;
  60                        }
  61                        grp_evt++;
  62                }
  63
  64                /* no event */
  65                if (*q == '\0') {
  66                        if (*sep == '}') {
  67                                if (grp_evt < 0) {
  68                                        ui__error("cannot close a non-existing event group\n");
  69                                        goto error;
  70                                }
  71                                grp_evt--;
  72                        }
  73                        continue;
  74                }
  75
  76                memset(&attr, 0, sizeof(attr));
  77                event_attr_init(&attr);
  78
  79                ret = pfm_get_perf_event_encoding(q, PFM_PLM0|PFM_PLM3,
  80                                                &attr, NULL, NULL);
  81
  82                if (ret != PFM_SUCCESS) {
  83                        ui__error("failed to parse event %s : %s\n", str,
  84                                  pfm_strerror(ret));
  85                        goto error;
  86                }
  87
  88                pmu = perf_pmu__find_by_type((unsigned int)attr.type);
  89                evsel = parse_events__add_event(evlist->core.nr_entries,
  90                                                &attr, q, pmu);
  91                if (evsel == NULL)
  92                        goto error;
  93
  94                evsel->is_libpfm_event = true;
  95
  96                evlist__add(evlist, evsel);
  97
  98                if (grp_evt == 0)
  99                        grp_leader = evsel;
 100
 101                if (grp_evt > -1) {
 102                        evsel__set_leader(evsel, grp_leader);
 103                        grp_leader->core.nr_members++;
 104                        grp_evt++;
 105                }
 106
 107                if (*sep == '}') {
 108                        if (grp_evt < 0) {
 109                                ui__error(
 110                                   "cannot close a non-existing event group\n");
 111                                goto error;
 112                        }
 113                        evlist->core.nr_groups++;
 114                        grp_leader = NULL;
 115                        grp_evt = -1;
 116                }
 117        }
 118        free(p_orig);
 119        return 0;
 120error:
 121        free(p_orig);
 122        return -1;
 123}
 124
 125static const char *srcs[PFM_ATTR_CTRL_MAX] = {
 126        [PFM_ATTR_CTRL_UNKNOWN] = "???",
 127        [PFM_ATTR_CTRL_PMU] = "PMU",
 128        [PFM_ATTR_CTRL_PERF_EVENT] = "perf_event",
 129};
 130
 131static void
 132print_attr_flags(pfm_event_attr_info_t *info)
 133{
 134        int n = 0;
 135
 136        if (info->is_dfl) {
 137                printf("[default] ");
 138                n++;
 139        }
 140
 141        if (info->is_precise) {
 142                printf("[precise] ");
 143                n++;
 144        }
 145
 146        if (!n)
 147                printf("- ");
 148}
 149
 150static void
 151print_libpfm_events_detailed(pfm_event_info_t *info, bool long_desc)
 152{
 153        pfm_event_attr_info_t ainfo;
 154        const char *src;
 155        int j, ret;
 156
 157        ainfo.size = sizeof(ainfo);
 158
 159        printf("  %s\n", info->name);
 160        printf("    [%s]\n", info->desc);
 161        if (long_desc) {
 162                if (info->equiv)
 163                        printf("      Equiv: %s\n", info->equiv);
 164
 165                printf("      Code  : 0x%"PRIx64"\n", info->code);
 166        }
 167        pfm_for_each_event_attr(j, info) {
 168                ret = pfm_get_event_attr_info(info->idx, j,
 169                                              PFM_OS_PERF_EVENT_EXT, &ainfo);
 170                if (ret != PFM_SUCCESS)
 171                        continue;
 172
 173                if (ainfo.type == PFM_ATTR_UMASK) {
 174                        printf("      %s:%s\n", info->name, ainfo.name);
 175                        printf("        [%s]\n", ainfo.desc);
 176                }
 177
 178                if (!long_desc)
 179                        continue;
 180
 181                if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX)
 182                        ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN;
 183
 184                src = srcs[ainfo.ctrl];
 185                switch (ainfo.type) {
 186                case PFM_ATTR_UMASK:
 187                        printf("        Umask : 0x%02"PRIx64" : %s: ",
 188                                ainfo.code, src);
 189                        print_attr_flags(&ainfo);
 190                        putchar('\n');
 191                        break;
 192                case PFM_ATTR_MOD_BOOL:
 193                        printf("      Modif : %s: [%s] : %s (boolean)\n", src,
 194                                ainfo.name, ainfo.desc);
 195                        break;
 196                case PFM_ATTR_MOD_INTEGER:
 197                        printf("      Modif : %s: [%s] : %s (integer)\n", src,
 198                                ainfo.name, ainfo.desc);
 199                        break;
 200                case PFM_ATTR_NONE:
 201                case PFM_ATTR_RAW_UMASK:
 202                case PFM_ATTR_MAX:
 203                default:
 204                        printf("      Attr  : %s: [%s] : %s\n", src,
 205                                ainfo.name, ainfo.desc);
 206                }
 207        }
 208}
 209
 210/*
 211 * list all pmu::event:umask, pmu::event
 212 * printed events may not be all valid combinations of umask for an event
 213 */
 214static void
 215print_libpfm_events_raw(pfm_pmu_info_t *pinfo, pfm_event_info_t *info)
 216{
 217        pfm_event_attr_info_t ainfo;
 218        int j, ret;
 219        bool has_umask = false;
 220
 221        ainfo.size = sizeof(ainfo);
 222
 223        pfm_for_each_event_attr(j, info) {
 224                ret = pfm_get_event_attr_info(info->idx, j,
 225                                              PFM_OS_PERF_EVENT_EXT, &ainfo);
 226                if (ret != PFM_SUCCESS)
 227                        continue;
 228
 229                if (ainfo.type != PFM_ATTR_UMASK)
 230                        continue;
 231
 232                printf("%s::%s:%s\n", pinfo->name, info->name, ainfo.name);
 233                has_umask = true;
 234        }
 235        if (!has_umask)
 236                printf("%s::%s\n", pinfo->name, info->name);
 237}
 238
 239void print_libpfm_events(bool name_only, bool long_desc)
 240{
 241        pfm_event_info_t info;
 242        pfm_pmu_info_t pinfo;
 243        int i, p, ret;
 244
 245        libpfm_initialize();
 246
 247        /* initialize to zero to indicate ABI version */
 248        info.size  = sizeof(info);
 249        pinfo.size = sizeof(pinfo);
 250
 251        if (!name_only)
 252                puts("\nList of pre-defined events (to be used in --pfm-events):\n");
 253
 254        pfm_for_all_pmus(p) {
 255                bool printed_pmu = false;
 256
 257                ret = pfm_get_pmu_info(p, &pinfo);
 258                if (ret != PFM_SUCCESS)
 259                        continue;
 260
 261                /* only print events that are supported by host HW */
 262                if (!pinfo.is_present)
 263                        continue;
 264
 265                /* handled by perf directly */
 266                if (pinfo.pmu == PFM_PMU_PERF_EVENT)
 267                        continue;
 268
 269                for (i = pinfo.first_event; i != -1;
 270                     i = pfm_get_event_next(i)) {
 271
 272                        ret = pfm_get_event_info(i, PFM_OS_PERF_EVENT_EXT,
 273                                                &info);
 274                        if (ret != PFM_SUCCESS)
 275                                continue;
 276
 277                        if (!name_only && !printed_pmu) {
 278                                printf("%s:\n", pinfo.name);
 279                                printed_pmu = true;
 280                        }
 281
 282                        if (!name_only)
 283                                print_libpfm_events_detailed(&info, long_desc);
 284                        else
 285                                print_libpfm_events_raw(&pinfo, &info);
 286                }
 287                if (!name_only && printed_pmu)
 288                        putchar('\n');
 289        }
 290}
 291