linux/tools/perf/util/metricgroup.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2017, Intel Corporation.
   4 */
   5
   6/* Manage metrics and groups of metrics from JSON files */
   7
   8#include "metricgroup.h"
   9#include "evlist.h"
  10#include "strbuf.h"
  11#include "pmu.h"
  12#include "expr.h"
  13#include "rblist.h"
  14#include <string.h>
  15#include <stdbool.h>
  16#include <errno.h>
  17#include "pmu-events/pmu-events.h"
  18#include "strlist.h"
  19#include <assert.h>
  20#include <linux/ctype.h>
  21#include <linux/zalloc.h>
  22
  23struct metric_event *metricgroup__lookup(struct rblist *metric_events,
  24                                         struct perf_evsel *evsel,
  25                                         bool create)
  26{
  27        struct rb_node *nd;
  28        struct metric_event me = {
  29                .evsel = evsel
  30        };
  31
  32        if (!metric_events)
  33                return NULL;
  34
  35        nd = rblist__find(metric_events, &me);
  36        if (nd)
  37                return container_of(nd, struct metric_event, nd);
  38        if (create) {
  39                rblist__add_node(metric_events, &me);
  40                nd = rblist__find(metric_events, &me);
  41                if (nd)
  42                        return container_of(nd, struct metric_event, nd);
  43        }
  44        return NULL;
  45}
  46
  47static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
  48{
  49        struct metric_event *a = container_of(rb_node,
  50                                              struct metric_event,
  51                                              nd);
  52        const struct metric_event *b = entry;
  53
  54        if (a->evsel == b->evsel)
  55                return 0;
  56        if ((char *)a->evsel < (char *)b->evsel)
  57                return -1;
  58        return +1;
  59}
  60
  61static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
  62                                        const void *entry)
  63{
  64        struct metric_event *me = malloc(sizeof(struct metric_event));
  65
  66        if (!me)
  67                return NULL;
  68        memcpy(me, entry, sizeof(struct metric_event));
  69        me->evsel = ((struct metric_event *)entry)->evsel;
  70        INIT_LIST_HEAD(&me->head);
  71        return &me->nd;
  72}
  73
  74static void metricgroup__rblist_init(struct rblist *metric_events)
  75{
  76        rblist__init(metric_events);
  77        metric_events->node_cmp = metric_event_cmp;
  78        metric_events->node_new = metric_event_new;
  79}
  80
  81struct egroup {
  82        struct list_head nd;
  83        int idnum;
  84        const char **ids;
  85        const char *metric_name;
  86        const char *metric_expr;
  87};
  88
  89static bool record_evsel(int *ind, struct perf_evsel **start,
  90                         int idnum,
  91                         struct perf_evsel **metric_events,
  92                         struct perf_evsel *ev)
  93{
  94        metric_events[*ind] = ev;
  95        if (*ind == 0)
  96                *start = ev;
  97        if (++*ind == idnum) {
  98                metric_events[*ind] = NULL;
  99                return true;
 100        }
 101        return false;
 102}
 103
 104static struct perf_evsel *find_evsel_group(struct perf_evlist *perf_evlist,
 105                                           const char **ids,
 106                                           int idnum,
 107                                           struct perf_evsel **metric_events)
 108{
 109        struct perf_evsel *ev, *start = NULL;
 110        int ind = 0;
 111
 112        evlist__for_each_entry (perf_evlist, ev) {
 113                if (ev->collect_stat)
 114                        continue;
 115                if (!strcmp(ev->name, ids[ind])) {
 116                        if (record_evsel(&ind, &start, idnum,
 117                                         metric_events, ev))
 118                                return start;
 119                } else {
 120                        /*
 121                         * We saw some other event that is not
 122                         * in our list of events. Discard
 123                         * the whole match and start again.
 124                         */
 125                        ind = 0;
 126                        start = NULL;
 127                        if (!strcmp(ev->name, ids[ind])) {
 128                                if (record_evsel(&ind, &start, idnum,
 129                                                 metric_events, ev))
 130                                        return start;
 131                        }
 132                }
 133        }
 134        /*
 135         * This can happen when an alias expands to multiple
 136         * events, like for uncore events.
 137         * We don't support this case for now.
 138         */
 139        return NULL;
 140}
 141
 142static int metricgroup__setup_events(struct list_head *groups,
 143                                     struct perf_evlist *perf_evlist,
 144                                     struct rblist *metric_events_list)
 145{
 146        struct metric_event *me;
 147        struct metric_expr *expr;
 148        int i = 0;
 149        int ret = 0;
 150        struct egroup *eg;
 151        struct perf_evsel *evsel;
 152
 153        list_for_each_entry (eg, groups, nd) {
 154                struct perf_evsel **metric_events;
 155
 156                metric_events = calloc(sizeof(void *), eg->idnum + 1);
 157                if (!metric_events) {
 158                        ret = -ENOMEM;
 159                        break;
 160                }
 161                evsel = find_evsel_group(perf_evlist, eg->ids, eg->idnum,
 162                                         metric_events);
 163                if (!evsel) {
 164                        pr_debug("Cannot resolve %s: %s\n",
 165                                        eg->metric_name, eg->metric_expr);
 166                        continue;
 167                }
 168                for (i = 0; i < eg->idnum; i++)
 169                        metric_events[i]->collect_stat = true;
 170                me = metricgroup__lookup(metric_events_list, evsel, true);
 171                if (!me) {
 172                        ret = -ENOMEM;
 173                        break;
 174                }
 175                expr = malloc(sizeof(struct metric_expr));
 176                if (!expr) {
 177                        ret = -ENOMEM;
 178                        break;
 179                }
 180                expr->metric_expr = eg->metric_expr;
 181                expr->metric_name = eg->metric_name;
 182                expr->metric_events = metric_events;
 183                list_add(&expr->nd, &me->head);
 184        }
 185        return ret;
 186}
 187
 188static bool match_metric(const char *n, const char *list)
 189{
 190        int len;
 191        char *m;
 192
 193        if (!list)
 194                return false;
 195        if (!strcmp(list, "all"))
 196                return true;
 197        if (!n)
 198                return !strcasecmp(list, "No_group");
 199        len = strlen(list);
 200        m = strcasestr(n, list);
 201        if (!m)
 202                return false;
 203        if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
 204            (m[len] == 0 || m[len] == ';'))
 205                return true;
 206        return false;
 207}
 208
 209struct mep {
 210        struct rb_node nd;
 211        const char *name;
 212        struct strlist *metrics;
 213};
 214
 215static int mep_cmp(struct rb_node *rb_node, const void *entry)
 216{
 217        struct mep *a = container_of(rb_node, struct mep, nd);
 218        struct mep *b = (struct mep *)entry;
 219
 220        return strcmp(a->name, b->name);
 221}
 222
 223static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
 224                                        const void *entry)
 225{
 226        struct mep *me = malloc(sizeof(struct mep));
 227
 228        if (!me)
 229                return NULL;
 230        memcpy(me, entry, sizeof(struct mep));
 231        me->name = strdup(me->name);
 232        if (!me->name)
 233                goto out_me;
 234        me->metrics = strlist__new(NULL, NULL);
 235        if (!me->metrics)
 236                goto out_name;
 237        return &me->nd;
 238out_name:
 239        zfree(&me->name);
 240out_me:
 241        free(me);
 242        return NULL;
 243}
 244
 245static struct mep *mep_lookup(struct rblist *groups, const char *name)
 246{
 247        struct rb_node *nd;
 248        struct mep me = {
 249                .name = name
 250        };
 251        nd = rblist__find(groups, &me);
 252        if (nd)
 253                return container_of(nd, struct mep, nd);
 254        rblist__add_node(groups, &me);
 255        nd = rblist__find(groups, &me);
 256        if (nd)
 257                return container_of(nd, struct mep, nd);
 258        return NULL;
 259}
 260
 261static void mep_delete(struct rblist *rl __maybe_unused,
 262                       struct rb_node *nd)
 263{
 264        struct mep *me = container_of(nd, struct mep, nd);
 265
 266        strlist__delete(me->metrics);
 267        zfree(&me->name);
 268        free(me);
 269}
 270
 271static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
 272{
 273        struct str_node *sn;
 274        int n = 0;
 275
 276        strlist__for_each_entry (sn, metrics) {
 277                if (raw)
 278                        printf("%s%s", n > 0 ? " " : "", sn->s);
 279                else
 280                        printf("  %s\n", sn->s);
 281                n++;
 282        }
 283        if (raw)
 284                putchar('\n');
 285}
 286
 287void metricgroup__print(bool metrics, bool metricgroups, char *filter,
 288                        bool raw, bool details)
 289{
 290        struct pmu_events_map *map = perf_pmu__find_map(NULL);
 291        struct pmu_event *pe;
 292        int i;
 293        struct rblist groups;
 294        struct rb_node *node, *next;
 295        struct strlist *metriclist = NULL;
 296
 297        if (!map)
 298                return;
 299
 300        if (!metricgroups) {
 301                metriclist = strlist__new(NULL, NULL);
 302                if (!metriclist)
 303                        return;
 304        }
 305
 306        rblist__init(&groups);
 307        groups.node_new = mep_new;
 308        groups.node_cmp = mep_cmp;
 309        groups.node_delete = mep_delete;
 310        for (i = 0; ; i++) {
 311                const char *g;
 312                pe = &map->table[i];
 313
 314                if (!pe->name && !pe->metric_group && !pe->metric_name)
 315                        break;
 316                if (!pe->metric_expr)
 317                        continue;
 318                g = pe->metric_group;
 319                if (!g && pe->metric_name) {
 320                        if (pe->name)
 321                                continue;
 322                        g = "No_group";
 323                }
 324                if (g) {
 325                        char *omg;
 326                        char *mg = strdup(g);
 327
 328                        if (!mg)
 329                                return;
 330                        omg = mg;
 331                        while ((g = strsep(&mg, ";")) != NULL) {
 332                                struct mep *me;
 333                                char *s;
 334
 335                                g = skip_spaces(g);
 336                                if (*g == 0)
 337                                        g = "No_group";
 338                                if (filter && !strstr(g, filter))
 339                                        continue;
 340                                if (raw)
 341                                        s = (char *)pe->metric_name;
 342                                else {
 343                                        if (asprintf(&s, "%s\n%*s%s]",
 344                                                     pe->metric_name, 8, "[", pe->desc) < 0)
 345                                                return;
 346
 347                                        if (details) {
 348                                                if (asprintf(&s, "%s\n%*s%s]",
 349                                                             s, 8, "[", pe->metric_expr) < 0)
 350                                                        return;
 351                                        }
 352                                }
 353
 354                                if (!s)
 355                                        continue;
 356
 357                                if (!metricgroups) {
 358                                        strlist__add(metriclist, s);
 359                                } else {
 360                                        me = mep_lookup(&groups, g);
 361                                        if (!me)
 362                                                continue;
 363                                        strlist__add(me->metrics, s);
 364                                }
 365                        }
 366                        free(omg);
 367                }
 368        }
 369
 370        if (metricgroups && !raw)
 371                printf("\nMetric Groups:\n\n");
 372        else if (metrics && !raw)
 373                printf("\nMetrics:\n\n");
 374
 375        for (node = rb_first_cached(&groups.entries); node; node = next) {
 376                struct mep *me = container_of(node, struct mep, nd);
 377
 378                if (metricgroups)
 379                        printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
 380                if (metrics)
 381                        metricgroup__print_strlist(me->metrics, raw);
 382                next = rb_next(node);
 383                rblist__remove_node(&groups, node);
 384        }
 385        if (!metricgroups)
 386                metricgroup__print_strlist(metriclist, raw);
 387        strlist__delete(metriclist);
 388}
 389
 390static int metricgroup__add_metric(const char *metric, struct strbuf *events,
 391                                   struct list_head *group_list)
 392{
 393        struct pmu_events_map *map = perf_pmu__find_map(NULL);
 394        struct pmu_event *pe;
 395        int ret = -EINVAL;
 396        int i, j;
 397
 398        if (!map)
 399                return 0;
 400
 401        for (i = 0; ; i++) {
 402                pe = &map->table[i];
 403
 404                if (!pe->name && !pe->metric_group && !pe->metric_name)
 405                        break;
 406                if (!pe->metric_expr)
 407                        continue;
 408                if (match_metric(pe->metric_group, metric) ||
 409                    match_metric(pe->metric_name, metric)) {
 410                        const char **ids;
 411                        int idnum;
 412                        struct egroup *eg;
 413                        bool no_group = false;
 414
 415                        pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
 416
 417                        if (expr__find_other(pe->metric_expr,
 418                                             NULL, &ids, &idnum) < 0)
 419                                continue;
 420                        if (events->len > 0)
 421                                strbuf_addf(events, ",");
 422                        for (j = 0; j < idnum; j++) {
 423                                pr_debug("found event %s\n", ids[j]);
 424                                /*
 425                                 * Duration time maps to a software event and can make
 426                                 * groups not count. Always use it outside a
 427                                 * group.
 428                                 */
 429                                if (!strcmp(ids[j], "duration_time")) {
 430                                        if (j > 0)
 431                                                strbuf_addf(events, "}:W,");
 432                                        strbuf_addf(events, "duration_time");
 433                                        no_group = true;
 434                                        continue;
 435                                }
 436                                strbuf_addf(events, "%s%s",
 437                                        j == 0 || no_group ? "{" : ",",
 438                                        ids[j]);
 439                                no_group = false;
 440                        }
 441                        if (!no_group)
 442                                strbuf_addf(events, "}:W");
 443
 444                        eg = malloc(sizeof(struct egroup));
 445                        if (!eg) {
 446                                ret = -ENOMEM;
 447                                break;
 448                        }
 449                        eg->ids = ids;
 450                        eg->idnum = idnum;
 451                        eg->metric_name = pe->metric_name;
 452                        eg->metric_expr = pe->metric_expr;
 453                        list_add_tail(&eg->nd, group_list);
 454                        ret = 0;
 455                }
 456        }
 457        return ret;
 458}
 459
 460static int metricgroup__add_metric_list(const char *list, struct strbuf *events,
 461                                        struct list_head *group_list)
 462{
 463        char *llist, *nlist, *p;
 464        int ret = -EINVAL;
 465
 466        nlist = strdup(list);
 467        if (!nlist)
 468                return -ENOMEM;
 469        llist = nlist;
 470
 471        strbuf_init(events, 100);
 472        strbuf_addf(events, "%s", "");
 473
 474        while ((p = strsep(&llist, ",")) != NULL) {
 475                ret = metricgroup__add_metric(p, events, group_list);
 476                if (ret == -EINVAL) {
 477                        fprintf(stderr, "Cannot find metric or group `%s'\n",
 478                                        p);
 479                        break;
 480                }
 481        }
 482        free(nlist);
 483        return ret;
 484}
 485
 486static void metricgroup__free_egroups(struct list_head *group_list)
 487{
 488        struct egroup *eg, *egtmp;
 489        int i;
 490
 491        list_for_each_entry_safe (eg, egtmp, group_list, nd) {
 492                for (i = 0; i < eg->idnum; i++)
 493                        zfree(&eg->ids[i]);
 494                zfree(&eg->ids);
 495                list_del_init(&eg->nd);
 496                free(eg);
 497        }
 498}
 499
 500int metricgroup__parse_groups(const struct option *opt,
 501                           const char *str,
 502                           struct rblist *metric_events)
 503{
 504        struct parse_events_error parse_error;
 505        struct perf_evlist *perf_evlist = *(struct perf_evlist **)opt->value;
 506        struct strbuf extra_events;
 507        LIST_HEAD(group_list);
 508        int ret;
 509
 510        if (metric_events->nr_entries == 0)
 511                metricgroup__rblist_init(metric_events);
 512        ret = metricgroup__add_metric_list(str, &extra_events, &group_list);
 513        if (ret)
 514                return ret;
 515        pr_debug("adding %s\n", extra_events.buf);
 516        memset(&parse_error, 0, sizeof(struct parse_events_error));
 517        ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
 518        if (ret) {
 519                parse_events_print_error(&parse_error, extra_events.buf);
 520                goto out;
 521        }
 522        strbuf_release(&extra_events);
 523        ret = metricgroup__setup_events(&group_list, perf_evlist,
 524                                        metric_events);
 525out:
 526        metricgroup__free_egroups(&group_list);
 527        return ret;
 528}
 529
 530bool metricgroup__has_metric(const char *metric)
 531{
 532        struct pmu_events_map *map = perf_pmu__find_map(NULL);
 533        struct pmu_event *pe;
 534        int i;
 535
 536        if (!map)
 537                return false;
 538
 539        for (i = 0; ; i++) {
 540                pe = &map->table[i];
 541
 542                if (!pe->name && !pe->metric_group && !pe->metric_name)
 543                        break;
 544                if (!pe->metric_expr)
 545                        continue;
 546                if (match_metric(pe->metric_name, metric))
 547                        return true;
 548        }
 549        return false;
 550}
 551