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 "debug.h"
  10#include "evlist.h"
  11#include "evsel.h"
  12#include "strbuf.h"
  13#include "pmu.h"
  14#include "expr.h"
  15#include "rblist.h"
  16#include <string.h>
  17#include <errno.h>
  18#include "pmu-events/pmu-events.h"
  19#include "strlist.h"
  20#include <assert.h>
  21#include <linux/ctype.h>
  22#include <linux/string.h>
  23#include <linux/zalloc.h>
  24#include <subcmd/parse-options.h>
  25#include <api/fs/fs.h>
  26#include "util.h"
  27#include <asm/bug.h>
  28
  29struct metric_event *metricgroup__lookup(struct rblist *metric_events,
  30                                         struct evsel *evsel,
  31                                         bool create)
  32{
  33        struct rb_node *nd;
  34        struct metric_event me = {
  35                .evsel = evsel
  36        };
  37
  38        if (!metric_events)
  39                return NULL;
  40
  41        nd = rblist__find(metric_events, &me);
  42        if (nd)
  43                return container_of(nd, struct metric_event, nd);
  44        if (create) {
  45                rblist__add_node(metric_events, &me);
  46                nd = rblist__find(metric_events, &me);
  47                if (nd)
  48                        return container_of(nd, struct metric_event, nd);
  49        }
  50        return NULL;
  51}
  52
  53static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
  54{
  55        struct metric_event *a = container_of(rb_node,
  56                                              struct metric_event,
  57                                              nd);
  58        const struct metric_event *b = entry;
  59
  60        if (a->evsel == b->evsel)
  61                return 0;
  62        if ((char *)a->evsel < (char *)b->evsel)
  63                return -1;
  64        return +1;
  65}
  66
  67static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
  68                                        const void *entry)
  69{
  70        struct metric_event *me = malloc(sizeof(struct metric_event));
  71
  72        if (!me)
  73                return NULL;
  74        memcpy(me, entry, sizeof(struct metric_event));
  75        me->evsel = ((struct metric_event *)entry)->evsel;
  76        INIT_LIST_HEAD(&me->head);
  77        return &me->nd;
  78}
  79
  80static void metric_event_delete(struct rblist *rblist __maybe_unused,
  81                                struct rb_node *rb_node)
  82{
  83        struct metric_event *me = container_of(rb_node, struct metric_event, nd);
  84        struct metric_expr *expr, *tmp;
  85
  86        list_for_each_entry_safe(expr, tmp, &me->head, nd) {
  87                free(expr->metric_refs);
  88                free(expr->metric_events);
  89                free(expr);
  90        }
  91
  92        free(me);
  93}
  94
  95static void metricgroup__rblist_init(struct rblist *metric_events)
  96{
  97        rblist__init(metric_events);
  98        metric_events->node_cmp = metric_event_cmp;
  99        metric_events->node_new = metric_event_new;
 100        metric_events->node_delete = metric_event_delete;
 101}
 102
 103void metricgroup__rblist_exit(struct rblist *metric_events)
 104{
 105        rblist__exit(metric_events);
 106}
 107
 108/*
 109 * A node in the list of referenced metrics. metric_expr
 110 * is held as a convenience to avoid a search through the
 111 * metric list.
 112 */
 113struct metric_ref_node {
 114        const char *metric_name;
 115        const char *metric_expr;
 116        struct list_head list;
 117};
 118
 119struct metric {
 120        struct list_head nd;
 121        struct expr_parse_ctx pctx;
 122        const char *metric_name;
 123        const char *metric_expr;
 124        const char *metric_unit;
 125        struct list_head metric_refs;
 126        int metric_refs_cnt;
 127        int runtime;
 128        bool has_constraint;
 129};
 130
 131#define RECURSION_ID_MAX 1000
 132
 133struct expr_ids {
 134        struct expr_id  id[RECURSION_ID_MAX];
 135        int             cnt;
 136};
 137
 138static struct expr_id *expr_ids__alloc(struct expr_ids *ids)
 139{
 140        if (ids->cnt >= RECURSION_ID_MAX)
 141                return NULL;
 142        return &ids->id[ids->cnt++];
 143}
 144
 145static void expr_ids__exit(struct expr_ids *ids)
 146{
 147        int i;
 148
 149        for (i = 0; i < ids->cnt; i++)
 150                free(ids->id[i].id);
 151}
 152
 153/**
 154 * Find a group of events in perf_evlist that correpond to those from a parsed
 155 * metric expression. Note, as find_evsel_group is called in the same order as
 156 * perf_evlist was constructed, metric_no_merge doesn't need to test for
 157 * underfilling a group.
 158 * @perf_evlist: a list of events something like: {metric1 leader, metric1
 159 * sibling, metric1 sibling}:W,duration_time,{metric2 leader, metric2 sibling,
 160 * metric2 sibling}:W,duration_time
 161 * @pctx: the parse context for the metric expression.
 162 * @metric_no_merge: don't attempt to share events for the metric with other
 163 * metrics.
 164 * @has_constraint: is there a contraint on the group of events? In which case
 165 * the events won't be grouped.
 166 * @metric_events: out argument, null terminated array of evsel's associated
 167 * with the metric.
 168 * @evlist_used: in/out argument, bitmap tracking which evlist events are used.
 169 * @return the first metric event or NULL on failure.
 170 */
 171static struct evsel *find_evsel_group(struct evlist *perf_evlist,
 172                                      struct expr_parse_ctx *pctx,
 173                                      bool metric_no_merge,
 174                                      bool has_constraint,
 175                                      struct evsel **metric_events,
 176                                      unsigned long *evlist_used)
 177{
 178        struct evsel *ev, *current_leader = NULL;
 179        struct expr_id_data *val_ptr;
 180        int i = 0, matched_events = 0, events_to_match;
 181        const int idnum = (int)hashmap__size(&pctx->ids);
 182
 183        /* duration_time is grouped separately. */
 184        if (!has_constraint &&
 185            hashmap__find(&pctx->ids, "duration_time", (void **)&val_ptr))
 186                events_to_match = idnum - 1;
 187        else
 188                events_to_match = idnum;
 189
 190        evlist__for_each_entry (perf_evlist, ev) {
 191                /*
 192                 * Events with a constraint aren't grouped and match the first
 193                 * events available.
 194                 */
 195                if (has_constraint && ev->weak_group)
 196                        continue;
 197                /* Ignore event if already used and merging is disabled. */
 198                if (metric_no_merge && test_bit(ev->idx, evlist_used))
 199                        continue;
 200                if (!has_constraint && ev->leader != current_leader) {
 201                        /*
 202                         * Start of a new group, discard the whole match and
 203                         * start again.
 204                         */
 205                        matched_events = 0;
 206                        memset(metric_events, 0,
 207                                sizeof(struct evsel *) * idnum);
 208                        current_leader = ev->leader;
 209                }
 210                if (hashmap__find(&pctx->ids, ev->name, (void **)&val_ptr)) {
 211                        if (has_constraint) {
 212                                /*
 213                                 * Events aren't grouped, ensure the same event
 214                                 * isn't matched from two groups.
 215                                 */
 216                                for (i = 0; i < matched_events; i++) {
 217                                        if (!strcmp(ev->name,
 218                                                    metric_events[i]->name)) {
 219                                                break;
 220                                        }
 221                                }
 222                                if (i != matched_events)
 223                                        continue;
 224                        }
 225                        metric_events[matched_events++] = ev;
 226                }
 227                if (matched_events == events_to_match)
 228                        break;
 229        }
 230
 231        if (events_to_match != idnum) {
 232                /* Add the first duration_time. */
 233                evlist__for_each_entry(perf_evlist, ev) {
 234                        if (!strcmp(ev->name, "duration_time")) {
 235                                metric_events[matched_events++] = ev;
 236                                break;
 237                        }
 238                }
 239        }
 240
 241        if (matched_events != idnum) {
 242                /* Not whole match */
 243                return NULL;
 244        }
 245
 246        metric_events[idnum] = NULL;
 247
 248        for (i = 0; i < idnum; i++) {
 249                ev = metric_events[i];
 250                ev->metric_leader = ev;
 251                set_bit(ev->idx, evlist_used);
 252        }
 253
 254        return metric_events[0];
 255}
 256
 257static int metricgroup__setup_events(struct list_head *groups,
 258                                     bool metric_no_merge,
 259                                     struct evlist *perf_evlist,
 260                                     struct rblist *metric_events_list)
 261{
 262        struct metric_event *me;
 263        struct metric_expr *expr;
 264        int i = 0;
 265        int ret = 0;
 266        struct metric *m;
 267        struct evsel *evsel, *tmp;
 268        unsigned long *evlist_used;
 269
 270        evlist_used = bitmap_alloc(perf_evlist->core.nr_entries);
 271        if (!evlist_used)
 272                return -ENOMEM;
 273
 274        list_for_each_entry (m, groups, nd) {
 275                struct evsel **metric_events;
 276                struct metric_ref *metric_refs = NULL;
 277
 278                metric_events = calloc(sizeof(void *),
 279                                hashmap__size(&m->pctx.ids) + 1);
 280                if (!metric_events) {
 281                        ret = -ENOMEM;
 282                        break;
 283                }
 284                evsel = find_evsel_group(perf_evlist, &m->pctx,
 285                                         metric_no_merge,
 286                                         m->has_constraint, metric_events,
 287                                         evlist_used);
 288                if (!evsel) {
 289                        pr_debug("Cannot resolve %s: %s\n",
 290                                        m->metric_name, m->metric_expr);
 291                        free(metric_events);
 292                        continue;
 293                }
 294                for (i = 0; metric_events[i]; i++)
 295                        metric_events[i]->collect_stat = true;
 296                me = metricgroup__lookup(metric_events_list, evsel, true);
 297                if (!me) {
 298                        ret = -ENOMEM;
 299                        free(metric_events);
 300                        break;
 301                }
 302                expr = malloc(sizeof(struct metric_expr));
 303                if (!expr) {
 304                        ret = -ENOMEM;
 305                        free(metric_events);
 306                        break;
 307                }
 308
 309                /*
 310                 * Collect and store collected nested expressions
 311                 * for metric processing.
 312                 */
 313                if (m->metric_refs_cnt) {
 314                        struct metric_ref_node *ref;
 315
 316                        metric_refs = zalloc(sizeof(struct metric_ref) * (m->metric_refs_cnt + 1));
 317                        if (!metric_refs) {
 318                                ret = -ENOMEM;
 319                                free(metric_events);
 320                                free(expr);
 321                                break;
 322                        }
 323
 324                        i = 0;
 325                        list_for_each_entry(ref, &m->metric_refs, list) {
 326                                /*
 327                                 * Intentionally passing just const char pointers,
 328                                 * originally from 'struct pmu_event' object.
 329                                 * We don't need to change them, so there's no
 330                                 * need to create our own copy.
 331                                 */
 332                                metric_refs[i].metric_name = ref->metric_name;
 333                                metric_refs[i].metric_expr = ref->metric_expr;
 334                                i++;
 335                        }
 336                };
 337
 338                expr->metric_refs = metric_refs;
 339                expr->metric_expr = m->metric_expr;
 340                expr->metric_name = m->metric_name;
 341                expr->metric_unit = m->metric_unit;
 342                expr->metric_events = metric_events;
 343                expr->runtime = m->runtime;
 344                list_add(&expr->nd, &me->head);
 345        }
 346
 347        evlist__for_each_entry_safe(perf_evlist, tmp, evsel) {
 348                if (!test_bit(evsel->idx, evlist_used)) {
 349                        evlist__remove(perf_evlist, evsel);
 350                        evsel__delete(evsel);
 351                }
 352        }
 353        bitmap_free(evlist_used);
 354
 355        return ret;
 356}
 357
 358static bool match_metric(const char *n, const char *list)
 359{
 360        int len;
 361        char *m;
 362
 363        if (!list)
 364                return false;
 365        if (!strcmp(list, "all"))
 366                return true;
 367        if (!n)
 368                return !strcasecmp(list, "No_group");
 369        len = strlen(list);
 370        m = strcasestr(n, list);
 371        if (!m)
 372                return false;
 373        if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
 374            (m[len] == 0 || m[len] == ';'))
 375                return true;
 376        return false;
 377}
 378
 379struct mep {
 380        struct rb_node nd;
 381        const char *name;
 382        struct strlist *metrics;
 383};
 384
 385static int mep_cmp(struct rb_node *rb_node, const void *entry)
 386{
 387        struct mep *a = container_of(rb_node, struct mep, nd);
 388        struct mep *b = (struct mep *)entry;
 389
 390        return strcmp(a->name, b->name);
 391}
 392
 393static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
 394                                        const void *entry)
 395{
 396        struct mep *me = malloc(sizeof(struct mep));
 397
 398        if (!me)
 399                return NULL;
 400        memcpy(me, entry, sizeof(struct mep));
 401        me->name = strdup(me->name);
 402        if (!me->name)
 403                goto out_me;
 404        me->metrics = strlist__new(NULL, NULL);
 405        if (!me->metrics)
 406                goto out_name;
 407        return &me->nd;
 408out_name:
 409        zfree(&me->name);
 410out_me:
 411        free(me);
 412        return NULL;
 413}
 414
 415static struct mep *mep_lookup(struct rblist *groups, const char *name)
 416{
 417        struct rb_node *nd;
 418        struct mep me = {
 419                .name = name
 420        };
 421        nd = rblist__find(groups, &me);
 422        if (nd)
 423                return container_of(nd, struct mep, nd);
 424        rblist__add_node(groups, &me);
 425        nd = rblist__find(groups, &me);
 426        if (nd)
 427                return container_of(nd, struct mep, nd);
 428        return NULL;
 429}
 430
 431static void mep_delete(struct rblist *rl __maybe_unused,
 432                       struct rb_node *nd)
 433{
 434        struct mep *me = container_of(nd, struct mep, nd);
 435
 436        strlist__delete(me->metrics);
 437        zfree(&me->name);
 438        free(me);
 439}
 440
 441static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
 442{
 443        struct str_node *sn;
 444        int n = 0;
 445
 446        strlist__for_each_entry (sn, metrics) {
 447                if (raw)
 448                        printf("%s%s", n > 0 ? " " : "", sn->s);
 449                else
 450                        printf("  %s\n", sn->s);
 451                n++;
 452        }
 453        if (raw)
 454                putchar('\n');
 455}
 456
 457void metricgroup__print(bool metrics, bool metricgroups, char *filter,
 458                        bool raw, bool details)
 459{
 460        struct pmu_events_map *map = perf_pmu__find_map(NULL);
 461        struct pmu_event *pe;
 462        int i;
 463        struct rblist groups;
 464        struct rb_node *node, *next;
 465        struct strlist *metriclist = NULL;
 466
 467        if (!map)
 468                return;
 469
 470        if (!metricgroups) {
 471                metriclist = strlist__new(NULL, NULL);
 472                if (!metriclist)
 473                        return;
 474        }
 475
 476        rblist__init(&groups);
 477        groups.node_new = mep_new;
 478        groups.node_cmp = mep_cmp;
 479        groups.node_delete = mep_delete;
 480        for (i = 0; ; i++) {
 481                const char *g;
 482                pe = &map->table[i];
 483
 484                if (!pe->name && !pe->metric_group && !pe->metric_name)
 485                        break;
 486                if (!pe->metric_expr)
 487                        continue;
 488                g = pe->metric_group;
 489                if (!g && pe->metric_name) {
 490                        if (pe->name)
 491                                continue;
 492                        g = "No_group";
 493                }
 494                if (g) {
 495                        char *omg;
 496                        char *mg = strdup(g);
 497
 498                        if (!mg)
 499                                return;
 500                        omg = mg;
 501                        while ((g = strsep(&mg, ";")) != NULL) {
 502                                struct mep *me;
 503                                char *s;
 504
 505                                g = skip_spaces(g);
 506                                if (*g == 0)
 507                                        g = "No_group";
 508                                if (filter && !strstr(g, filter))
 509                                        continue;
 510                                if (raw)
 511                                        s = (char *)pe->metric_name;
 512                                else {
 513                                        if (asprintf(&s, "%s\n%*s%s]",
 514                                                     pe->metric_name, 8, "[", pe->desc) < 0)
 515                                                return;
 516
 517                                        if (details) {
 518                                                if (asprintf(&s, "%s\n%*s%s]",
 519                                                             s, 8, "[", pe->metric_expr) < 0)
 520                                                        return;
 521                                        }
 522                                }
 523
 524                                if (!s)
 525                                        continue;
 526
 527                                if (!metricgroups) {
 528                                        strlist__add(metriclist, s);
 529                                } else {
 530                                        me = mep_lookup(&groups, g);
 531                                        if (!me)
 532                                                continue;
 533                                        strlist__add(me->metrics, s);
 534                                }
 535
 536                                if (!raw)
 537                                        free(s);
 538                        }
 539                        free(omg);
 540                }
 541        }
 542
 543        if (metricgroups && !raw)
 544                printf("\nMetric Groups:\n\n");
 545        else if (metrics && !raw)
 546                printf("\nMetrics:\n\n");
 547
 548        for (node = rb_first_cached(&groups.entries); node; node = next) {
 549                struct mep *me = container_of(node, struct mep, nd);
 550
 551                if (metricgroups)
 552                        printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
 553                if (metrics)
 554                        metricgroup__print_strlist(me->metrics, raw);
 555                next = rb_next(node);
 556                rblist__remove_node(&groups, node);
 557        }
 558        if (!metricgroups)
 559                metricgroup__print_strlist(metriclist, raw);
 560        strlist__delete(metriclist);
 561}
 562
 563static void metricgroup__add_metric_weak_group(struct strbuf *events,
 564                                               struct expr_parse_ctx *ctx)
 565{
 566        struct hashmap_entry *cur;
 567        size_t bkt;
 568        bool no_group = true, has_duration = false;
 569
 570        hashmap__for_each_entry((&ctx->ids), cur, bkt) {
 571                pr_debug("found event %s\n", (const char *)cur->key);
 572                /*
 573                 * Duration time maps to a software event and can make
 574                 * groups not count. Always use it outside a
 575                 * group.
 576                 */
 577                if (!strcmp(cur->key, "duration_time")) {
 578                        has_duration = true;
 579                        continue;
 580                }
 581                strbuf_addf(events, "%s%s",
 582                        no_group ? "{" : ",",
 583                        (const char *)cur->key);
 584                no_group = false;
 585        }
 586        if (!no_group) {
 587                strbuf_addf(events, "}:W");
 588                if (has_duration)
 589                        strbuf_addf(events, ",duration_time");
 590        } else if (has_duration)
 591                strbuf_addf(events, "duration_time");
 592}
 593
 594static void metricgroup__add_metric_non_group(struct strbuf *events,
 595                                              struct expr_parse_ctx *ctx)
 596{
 597        struct hashmap_entry *cur;
 598        size_t bkt;
 599        bool first = true;
 600
 601        hashmap__for_each_entry((&ctx->ids), cur, bkt) {
 602                if (!first)
 603                        strbuf_addf(events, ",");
 604                strbuf_addf(events, "%s", (const char *)cur->key);
 605                first = false;
 606        }
 607}
 608
 609static void metricgroup___watchdog_constraint_hint(const char *name, bool foot)
 610{
 611        static bool violate_nmi_constraint;
 612
 613        if (!foot) {
 614                pr_warning("Splitting metric group %s into standalone metrics.\n", name);
 615                violate_nmi_constraint = true;
 616                return;
 617        }
 618
 619        if (!violate_nmi_constraint)
 620                return;
 621
 622        pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
 623                   "    echo 0 > /proc/sys/kernel/nmi_watchdog\n"
 624                   "    perf stat ...\n"
 625                   "    echo 1 > /proc/sys/kernel/nmi_watchdog\n");
 626}
 627
 628static bool metricgroup__has_constraint(struct pmu_event *pe)
 629{
 630        if (!pe->metric_constraint)
 631                return false;
 632
 633        if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") &&
 634            sysctl__nmi_watchdog_enabled()) {
 635                metricgroup___watchdog_constraint_hint(pe->metric_name, false);
 636                return true;
 637        }
 638
 639        return false;
 640}
 641
 642int __weak arch_get_runtimeparam(void)
 643{
 644        return 1;
 645}
 646
 647static int __add_metric(struct list_head *metric_list,
 648                        struct pmu_event *pe,
 649                        bool metric_no_group,
 650                        int runtime,
 651                        struct metric **mp,
 652                        struct expr_id *parent,
 653                        struct expr_ids *ids)
 654{
 655        struct metric_ref_node *ref;
 656        struct metric *m;
 657
 658        if (*mp == NULL) {
 659                /*
 660                 * We got in here for the parent group,
 661                 * allocate it and put it on the list.
 662                 */
 663                m = zalloc(sizeof(*m));
 664                if (!m)
 665                        return -ENOMEM;
 666
 667                expr__ctx_init(&m->pctx);
 668                m->metric_name = pe->metric_name;
 669                m->metric_expr = pe->metric_expr;
 670                m->metric_unit = pe->unit;
 671                m->runtime = runtime;
 672                m->has_constraint = metric_no_group || metricgroup__has_constraint(pe);
 673                INIT_LIST_HEAD(&m->metric_refs);
 674                m->metric_refs_cnt = 0;
 675
 676                parent = expr_ids__alloc(ids);
 677                if (!parent) {
 678                        free(m);
 679                        return -EINVAL;
 680                }
 681
 682                parent->id = strdup(pe->metric_name);
 683                if (!parent->id) {
 684                        free(m);
 685                        return -ENOMEM;
 686                }
 687                *mp = m;
 688        } else {
 689                /*
 690                 * We got here for the referenced metric, via the
 691                 * recursive metricgroup__add_metric call, add
 692                 * it to the parent group.
 693                 */
 694                m = *mp;
 695
 696                ref = malloc(sizeof(*ref));
 697                if (!ref)
 698                        return -ENOMEM;
 699
 700                /*
 701                 * Intentionally passing just const char pointers,
 702                 * from 'pe' object, so they never go away. We don't
 703                 * need to change them, so there's no need to create
 704                 * our own copy.
 705                 */
 706                ref->metric_name = pe->metric_name;
 707                ref->metric_expr = pe->metric_expr;
 708
 709                list_add(&ref->list, &m->metric_refs);
 710                m->metric_refs_cnt++;
 711        }
 712
 713        /* Force all found IDs in metric to have us as parent ID. */
 714        WARN_ON_ONCE(!parent);
 715        m->pctx.parent = parent;
 716
 717        /*
 718         * For both the parent and referenced metrics, we parse
 719         * all the metric's IDs and add it to the parent context.
 720         */
 721        if (expr__find_other(pe->metric_expr, NULL, &m->pctx, runtime) < 0) {
 722                if (m->metric_refs_cnt == 0) {
 723                        expr__ctx_clear(&m->pctx);
 724                        free(m);
 725                        *mp = NULL;
 726                }
 727                return -EINVAL;
 728        }
 729
 730        /*
 731         * We add new group only in the 'parent' call,
 732         * so bail out for referenced metric case.
 733         */
 734        if (m->metric_refs_cnt)
 735                return 0;
 736
 737        if (list_empty(metric_list))
 738                list_add(&m->nd, metric_list);
 739        else {
 740                struct list_head *pos;
 741
 742                /* Place the largest groups at the front. */
 743                list_for_each_prev(pos, metric_list) {
 744                        struct metric *old = list_entry(pos, struct metric, nd);
 745
 746                        if (hashmap__size(&m->pctx.ids) <=
 747                            hashmap__size(&old->pctx.ids))
 748                                break;
 749                }
 750                list_add(&m->nd, pos);
 751        }
 752
 753        return 0;
 754}
 755
 756#define map_for_each_event(__pe, __idx, __map)                          \
 757        for (__idx = 0, __pe = &__map->table[__idx];                    \
 758             __pe->name || __pe->metric_group || __pe->metric_name;     \
 759             __pe = &__map->table[++__idx])
 760
 761#define map_for_each_metric(__pe, __idx, __map, __metric)               \
 762        map_for_each_event(__pe, __idx, __map)                          \
 763                if (__pe->metric_expr &&                                \
 764                    (match_metric(__pe->metric_group, __metric) ||      \
 765                     match_metric(__pe->metric_name, __metric)))
 766
 767static struct pmu_event *find_metric(const char *metric, struct pmu_events_map *map)
 768{
 769        struct pmu_event *pe;
 770        int i;
 771
 772        map_for_each_event(pe, i, map) {
 773                if (match_metric(pe->metric_name, metric))
 774                        return pe;
 775        }
 776
 777        return NULL;
 778}
 779
 780static int recursion_check(struct metric *m, const char *id, struct expr_id **parent,
 781                           struct expr_ids *ids)
 782{
 783        struct expr_id_data *data;
 784        struct expr_id *p;
 785        int ret;
 786
 787        /*
 788         * We get the parent referenced by 'id' argument and
 789         * traverse through all the parent object IDs to check
 790         * if we already processed 'id', if we did, it's recursion
 791         * and we fail.
 792         */
 793        ret = expr__get_id(&m->pctx, id, &data);
 794        if (ret)
 795                return ret;
 796
 797        p = data->parent;
 798
 799        while (p->parent) {
 800                if (!strcmp(p->id, id)) {
 801                        pr_err("failed: recursion detected for %s\n", id);
 802                        return -1;
 803                }
 804                p = p->parent;
 805        }
 806
 807        /*
 808         * If we are over the limit of static entris, the metric
 809         * is too difficult/nested to process, fail as well.
 810         */
 811        p = expr_ids__alloc(ids);
 812        if (!p) {
 813                pr_err("failed: too many nested metrics\n");
 814                return -EINVAL;
 815        }
 816
 817        p->id     = strdup(id);
 818        p->parent = data->parent;
 819        *parent   = p;
 820
 821        return p->id ? 0 : -ENOMEM;
 822}
 823
 824static int add_metric(struct list_head *metric_list,
 825                      struct pmu_event *pe,
 826                      bool metric_no_group,
 827                      struct metric **mp,
 828                      struct expr_id *parent,
 829                      struct expr_ids *ids);
 830
 831static int __resolve_metric(struct metric *m,
 832                            bool metric_no_group,
 833                            struct list_head *metric_list,
 834                            struct pmu_events_map *map,
 835                            struct expr_ids *ids)
 836{
 837        struct hashmap_entry *cur;
 838        size_t bkt;
 839        bool all;
 840        int ret;
 841
 842        /*
 843         * Iterate all the parsed IDs and if there's metric,
 844         * add it to the context.
 845         */
 846        do {
 847                all = true;
 848                hashmap__for_each_entry((&m->pctx.ids), cur, bkt) {
 849                        struct expr_id *parent;
 850                        struct pmu_event *pe;
 851
 852                        pe = find_metric(cur->key, map);
 853                        if (!pe)
 854                                continue;
 855
 856                        ret = recursion_check(m, cur->key, &parent, ids);
 857                        if (ret)
 858                                return ret;
 859
 860                        all = false;
 861                        /* The metric key itself needs to go out.. */
 862                        expr__del_id(&m->pctx, cur->key);
 863
 864                        /* ... and it gets resolved to the parent context. */
 865                        ret = add_metric(metric_list, pe, metric_no_group, &m, parent, ids);
 866                        if (ret)
 867                                return ret;
 868
 869                        /*
 870                         * We added new metric to hashmap, so we need
 871                         * to break the iteration and start over.
 872                         */
 873                        break;
 874                }
 875        } while (!all);
 876
 877        return 0;
 878}
 879
 880static int resolve_metric(bool metric_no_group,
 881                          struct list_head *metric_list,
 882                          struct pmu_events_map *map,
 883                          struct expr_ids *ids)
 884{
 885        struct metric *m;
 886        int err;
 887
 888        list_for_each_entry(m, metric_list, nd) {
 889                err = __resolve_metric(m, metric_no_group, metric_list, map, ids);
 890                if (err)
 891                        return err;
 892        }
 893        return 0;
 894}
 895
 896static int add_metric(struct list_head *metric_list,
 897                      struct pmu_event *pe,
 898                      bool metric_no_group,
 899                      struct metric **m,
 900                      struct expr_id *parent,
 901                      struct expr_ids *ids)
 902{
 903        struct metric *orig = *m;
 904        int ret = 0;
 905
 906        pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
 907
 908        if (!strstr(pe->metric_expr, "?")) {
 909                ret = __add_metric(metric_list, pe, metric_no_group, 1, m, parent, ids);
 910        } else {
 911                int j, count;
 912
 913                count = arch_get_runtimeparam();
 914
 915                /* This loop is added to create multiple
 916                 * events depend on count value and add
 917                 * those events to metric_list.
 918                 */
 919
 920                for (j = 0; j < count && !ret; j++, *m = orig)
 921                        ret = __add_metric(metric_list, pe, metric_no_group, j, m, parent, ids);
 922        }
 923
 924        return ret;
 925}
 926
 927static int metricgroup__add_metric(const char *metric, bool metric_no_group,
 928                                   struct strbuf *events,
 929                                   struct list_head *metric_list,
 930                                   struct pmu_events_map *map)
 931{
 932        struct expr_ids ids = { .cnt = 0, };
 933        struct pmu_event *pe;
 934        struct metric *m;
 935        LIST_HEAD(list);
 936        int i, ret;
 937        bool has_match = false;
 938
 939        map_for_each_metric(pe, i, map, metric) {
 940                has_match = true;
 941                m = NULL;
 942
 943                ret = add_metric(&list, pe, metric_no_group, &m, NULL, &ids);
 944                if (ret)
 945                        goto out;
 946
 947                /*
 948                 * Process any possible referenced metrics
 949                 * included in the expression.
 950                 */
 951                ret = resolve_metric(metric_no_group,
 952                                     &list, map, &ids);
 953                if (ret)
 954                        goto out;
 955        }
 956
 957        /* End of pmu events. */
 958        if (!has_match) {
 959                ret = -EINVAL;
 960                goto out;
 961        }
 962
 963        list_for_each_entry(m, &list, nd) {
 964                if (events->len > 0)
 965                        strbuf_addf(events, ",");
 966
 967                if (m->has_constraint) {
 968                        metricgroup__add_metric_non_group(events,
 969                                                          &m->pctx);
 970                } else {
 971                        metricgroup__add_metric_weak_group(events,
 972                                                           &m->pctx);
 973                }
 974        }
 975
 976out:
 977        /*
 978         * add to metric_list so that they can be released
 979         * even if it's failed
 980         */
 981        list_splice(&list, metric_list);
 982        expr_ids__exit(&ids);
 983        return ret;
 984}
 985
 986static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
 987                                        struct strbuf *events,
 988                                        struct list_head *metric_list,
 989                                        struct pmu_events_map *map)
 990{
 991        char *llist, *nlist, *p;
 992        int ret = -EINVAL;
 993
 994        nlist = strdup(list);
 995        if (!nlist)
 996                return -ENOMEM;
 997        llist = nlist;
 998
 999        strbuf_init(events, 100);
1000        strbuf_addf(events, "%s", "");
1001
1002        while ((p = strsep(&llist, ",")) != NULL) {
1003                ret = metricgroup__add_metric(p, metric_no_group, events,
1004                                              metric_list, map);
1005                if (ret == -EINVAL) {
1006                        fprintf(stderr, "Cannot find metric or group `%s'\n",
1007                                        p);
1008                        break;
1009                }
1010        }
1011        free(nlist);
1012
1013        if (!ret)
1014                metricgroup___watchdog_constraint_hint(NULL, true);
1015
1016        return ret;
1017}
1018
1019static void metric__free_refs(struct metric *metric)
1020{
1021        struct metric_ref_node *ref, *tmp;
1022
1023        list_for_each_entry_safe(ref, tmp, &metric->metric_refs, list) {
1024                list_del(&ref->list);
1025                free(ref);
1026        }
1027}
1028
1029static void metricgroup__free_metrics(struct list_head *metric_list)
1030{
1031        struct metric *m, *tmp;
1032
1033        list_for_each_entry_safe (m, tmp, metric_list, nd) {
1034                metric__free_refs(m);
1035                expr__ctx_clear(&m->pctx);
1036                list_del_init(&m->nd);
1037                free(m);
1038        }
1039}
1040
1041static int parse_groups(struct evlist *perf_evlist, const char *str,
1042                        bool metric_no_group,
1043                        bool metric_no_merge,
1044                        struct perf_pmu *fake_pmu,
1045                        struct rblist *metric_events,
1046                        struct pmu_events_map *map)
1047{
1048        struct parse_events_error parse_error;
1049        struct strbuf extra_events;
1050        LIST_HEAD(metric_list);
1051        int ret;
1052
1053        if (metric_events->nr_entries == 0)
1054                metricgroup__rblist_init(metric_events);
1055        ret = metricgroup__add_metric_list(str, metric_no_group,
1056                                           &extra_events, &metric_list, map);
1057        if (ret)
1058                goto out;
1059        pr_debug("adding %s\n", extra_events.buf);
1060        bzero(&parse_error, sizeof(parse_error));
1061        ret = __parse_events(perf_evlist, extra_events.buf, &parse_error, fake_pmu);
1062        if (ret) {
1063                parse_events_print_error(&parse_error, extra_events.buf);
1064                goto out;
1065        }
1066        ret = metricgroup__setup_events(&metric_list, metric_no_merge,
1067                                        perf_evlist, metric_events);
1068out:
1069        metricgroup__free_metrics(&metric_list);
1070        strbuf_release(&extra_events);
1071        return ret;
1072}
1073
1074int metricgroup__parse_groups(const struct option *opt,
1075                              const char *str,
1076                              bool metric_no_group,
1077                              bool metric_no_merge,
1078                              struct rblist *metric_events)
1079{
1080        struct evlist *perf_evlist = *(struct evlist **)opt->value;
1081        struct pmu_events_map *map = perf_pmu__find_map(NULL);
1082
1083        if (!map)
1084                return 0;
1085
1086        return parse_groups(perf_evlist, str, metric_no_group,
1087                            metric_no_merge, NULL, metric_events, map);
1088}
1089
1090int metricgroup__parse_groups_test(struct evlist *evlist,
1091                                   struct pmu_events_map *map,
1092                                   const char *str,
1093                                   bool metric_no_group,
1094                                   bool metric_no_merge,
1095                                   struct rblist *metric_events)
1096{
1097        return parse_groups(evlist, str, metric_no_group,
1098                            metric_no_merge, &perf_pmu__fake, metric_events, map);
1099}
1100
1101bool metricgroup__has_metric(const char *metric)
1102{
1103        struct pmu_events_map *map = perf_pmu__find_map(NULL);
1104        struct pmu_event *pe;
1105        int i;
1106
1107        if (!map)
1108                return false;
1109
1110        for (i = 0; ; i++) {
1111                pe = &map->table[i];
1112
1113                if (!pe->name && !pe->metric_group && !pe->metric_name)
1114                        break;
1115                if (!pe->metric_expr)
1116                        continue;
1117                if (match_metric(pe->metric_name, metric))
1118                        return true;
1119        }
1120        return false;
1121}
1122