0001
0002
0003
0004
0005
0006
0007
0008 #include "metricgroup.h"
0009 #include "debug.h"
0010 #include "evlist.h"
0011 #include "evsel.h"
0012 #include "strbuf.h"
0013 #include "pmu.h"
0014 #include "pmu-hybrid.h"
0015 #include "expr.h"
0016 #include "rblist.h"
0017 #include <string.h>
0018 #include <errno.h>
0019 #include "strlist.h"
0020 #include <assert.h>
0021 #include <linux/ctype.h>
0022 #include <linux/list_sort.h>
0023 #include <linux/string.h>
0024 #include <linux/zalloc.h>
0025 #include <subcmd/parse-options.h>
0026 #include <api/fs/fs.h>
0027 #include "util.h"
0028 #include <asm/bug.h>
0029 #include "cgroup.h"
0030
0031 struct metric_event *metricgroup__lookup(struct rblist *metric_events,
0032 struct evsel *evsel,
0033 bool create)
0034 {
0035 struct rb_node *nd;
0036 struct metric_event me = {
0037 .evsel = evsel
0038 };
0039
0040 if (!metric_events)
0041 return NULL;
0042
0043 nd = rblist__find(metric_events, &me);
0044 if (nd)
0045 return container_of(nd, struct metric_event, nd);
0046 if (create) {
0047 rblist__add_node(metric_events, &me);
0048 nd = rblist__find(metric_events, &me);
0049 if (nd)
0050 return container_of(nd, struct metric_event, nd);
0051 }
0052 return NULL;
0053 }
0054
0055 static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
0056 {
0057 struct metric_event *a = container_of(rb_node,
0058 struct metric_event,
0059 nd);
0060 const struct metric_event *b = entry;
0061
0062 if (a->evsel == b->evsel)
0063 return 0;
0064 if ((char *)a->evsel < (char *)b->evsel)
0065 return -1;
0066 return +1;
0067 }
0068
0069 static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
0070 const void *entry)
0071 {
0072 struct metric_event *me = malloc(sizeof(struct metric_event));
0073
0074 if (!me)
0075 return NULL;
0076 memcpy(me, entry, sizeof(struct metric_event));
0077 me->evsel = ((struct metric_event *)entry)->evsel;
0078 INIT_LIST_HEAD(&me->head);
0079 return &me->nd;
0080 }
0081
0082 static void metric_event_delete(struct rblist *rblist __maybe_unused,
0083 struct rb_node *rb_node)
0084 {
0085 struct metric_event *me = container_of(rb_node, struct metric_event, nd);
0086 struct metric_expr *expr, *tmp;
0087
0088 list_for_each_entry_safe(expr, tmp, &me->head, nd) {
0089 free((char *)expr->metric_name);
0090 free(expr->metric_refs);
0091 free(expr->metric_events);
0092 free(expr);
0093 }
0094
0095 free(me);
0096 }
0097
0098 static void metricgroup__rblist_init(struct rblist *metric_events)
0099 {
0100 rblist__init(metric_events);
0101 metric_events->node_cmp = metric_event_cmp;
0102 metric_events->node_new = metric_event_new;
0103 metric_events->node_delete = metric_event_delete;
0104 }
0105
0106 void metricgroup__rblist_exit(struct rblist *metric_events)
0107 {
0108 rblist__exit(metric_events);
0109 }
0110
0111
0112
0113
0114
0115
0116 struct metric_ref_node {
0117 const char *metric_name;
0118 const char *metric_expr;
0119 struct list_head list;
0120 };
0121
0122
0123
0124
0125
0126 struct metric {
0127 struct list_head nd;
0128
0129
0130
0131
0132 struct expr_parse_ctx *pctx;
0133
0134 const char *metric_name;
0135
0136 const char *modifier;
0137
0138 const char *metric_expr;
0139
0140
0141
0142
0143 const char *metric_unit;
0144
0145 struct metric_ref *metric_refs;
0146
0147
0148
0149
0150 bool has_constraint;
0151
0152
0153
0154
0155
0156 struct evlist *evlist;
0157 };
0158
0159 static void metricgroup___watchdog_constraint_hint(const char *name, bool foot)
0160 {
0161 static bool violate_nmi_constraint;
0162
0163 if (!foot) {
0164 pr_warning("Splitting metric group %s into standalone metrics.\n", name);
0165 violate_nmi_constraint = true;
0166 return;
0167 }
0168
0169 if (!violate_nmi_constraint)
0170 return;
0171
0172 pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
0173 " echo 0 > /proc/sys/kernel/nmi_watchdog\n"
0174 " perf stat ...\n"
0175 " echo 1 > /proc/sys/kernel/nmi_watchdog\n");
0176 }
0177
0178 static bool metricgroup__has_constraint(const struct pmu_event *pe)
0179 {
0180 if (!pe->metric_constraint)
0181 return false;
0182
0183 if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") &&
0184 sysctl__nmi_watchdog_enabled()) {
0185 metricgroup___watchdog_constraint_hint(pe->metric_name, false);
0186 return true;
0187 }
0188
0189 return false;
0190 }
0191
0192 static struct metric *metric__new(const struct pmu_event *pe,
0193 const char *modifier,
0194 bool metric_no_group,
0195 int runtime)
0196 {
0197 struct metric *m;
0198
0199 m = zalloc(sizeof(*m));
0200 if (!m)
0201 return NULL;
0202
0203 m->pctx = expr__ctx_new();
0204 if (!m->pctx) {
0205 free(m);
0206 return NULL;
0207 }
0208
0209 m->metric_name = pe->metric_name;
0210 m->modifier = modifier ? strdup(modifier) : NULL;
0211 if (modifier && !m->modifier) {
0212 expr__ctx_free(m->pctx);
0213 free(m);
0214 return NULL;
0215 }
0216 m->metric_expr = pe->metric_expr;
0217 m->metric_unit = pe->unit;
0218 m->pctx->runtime = runtime;
0219 m->has_constraint = metric_no_group || metricgroup__has_constraint(pe);
0220 m->metric_refs = NULL;
0221 m->evlist = NULL;
0222
0223 return m;
0224 }
0225
0226 static void metric__free(struct metric *m)
0227 {
0228 free(m->metric_refs);
0229 expr__ctx_free(m->pctx);
0230 free((char *)m->modifier);
0231 evlist__delete(m->evlist);
0232 free(m);
0233 }
0234
0235 static bool contains_metric_id(struct evsel **metric_events, int num_events,
0236 const char *metric_id)
0237 {
0238 int i;
0239
0240 for (i = 0; i < num_events; i++) {
0241 if (!strcmp(evsel__metric_id(metric_events[i]), metric_id))
0242 return true;
0243 }
0244 return false;
0245 }
0246
0247
0248
0249
0250
0251
0252
0253
0254 static int setup_metric_events(struct hashmap *ids,
0255 struct evlist *metric_evlist,
0256 struct evsel ***out_metric_events)
0257 {
0258 struct evsel **metric_events;
0259 const char *metric_id;
0260 struct evsel *ev;
0261 size_t ids_size, matched_events, i;
0262
0263 *out_metric_events = NULL;
0264 ids_size = hashmap__size(ids);
0265
0266 metric_events = calloc(sizeof(void *), ids_size + 1);
0267 if (!metric_events)
0268 return -ENOMEM;
0269
0270 matched_events = 0;
0271 evlist__for_each_entry(metric_evlist, ev) {
0272 struct expr_id_data *val_ptr;
0273
0274
0275
0276
0277
0278
0279
0280 metric_id = evsel__metric_id(ev);
0281 if (contains_metric_id(metric_events, matched_events, metric_id))
0282 continue;
0283
0284
0285
0286
0287
0288 if (hashmap__find(ids, metric_id, (void **)&val_ptr)) {
0289 metric_events[matched_events++] = ev;
0290
0291 if (matched_events >= ids_size)
0292 break;
0293 }
0294 }
0295 if (matched_events < ids_size) {
0296 free(metric_events);
0297 return -EINVAL;
0298 }
0299 for (i = 0; i < ids_size; i++) {
0300 ev = metric_events[i];
0301 ev->collect_stat = true;
0302
0303
0304
0305
0306
0307 ev->metric_leader = ev;
0308
0309
0310
0311
0312
0313
0314
0315 metric_id = evsel__metric_id(ev);
0316 evlist__for_each_entry_continue(metric_evlist, ev) {
0317 if (!strcmp(evsel__metric_id(ev), metric_id))
0318 ev->metric_leader = metric_events[i];
0319 }
0320 }
0321 *out_metric_events = metric_events;
0322 return 0;
0323 }
0324
0325 static bool match_metric(const char *n, const char *list)
0326 {
0327 int len;
0328 char *m;
0329
0330 if (!list)
0331 return false;
0332 if (!strcmp(list, "all"))
0333 return true;
0334 if (!n)
0335 return !strcasecmp(list, "No_group");
0336 len = strlen(list);
0337 m = strcasestr(n, list);
0338 if (!m)
0339 return false;
0340 if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
0341 (m[len] == 0 || m[len] == ';'))
0342 return true;
0343 return false;
0344 }
0345
0346 static bool match_pe_metric(const struct pmu_event *pe, const char *metric)
0347 {
0348 return match_metric(pe->metric_group, metric) ||
0349 match_metric(pe->metric_name, metric);
0350 }
0351
0352 struct mep {
0353 struct rb_node nd;
0354 const char *name;
0355 struct strlist *metrics;
0356 };
0357
0358 static int mep_cmp(struct rb_node *rb_node, const void *entry)
0359 {
0360 struct mep *a = container_of(rb_node, struct mep, nd);
0361 struct mep *b = (struct mep *)entry;
0362
0363 return strcmp(a->name, b->name);
0364 }
0365
0366 static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
0367 const void *entry)
0368 {
0369 struct mep *me = malloc(sizeof(struct mep));
0370
0371 if (!me)
0372 return NULL;
0373 memcpy(me, entry, sizeof(struct mep));
0374 me->name = strdup(me->name);
0375 if (!me->name)
0376 goto out_me;
0377 me->metrics = strlist__new(NULL, NULL);
0378 if (!me->metrics)
0379 goto out_name;
0380 return &me->nd;
0381 out_name:
0382 zfree(&me->name);
0383 out_me:
0384 free(me);
0385 return NULL;
0386 }
0387
0388 static struct mep *mep_lookup(struct rblist *groups, const char *name)
0389 {
0390 struct rb_node *nd;
0391 struct mep me = {
0392 .name = name
0393 };
0394 nd = rblist__find(groups, &me);
0395 if (nd)
0396 return container_of(nd, struct mep, nd);
0397 rblist__add_node(groups, &me);
0398 nd = rblist__find(groups, &me);
0399 if (nd)
0400 return container_of(nd, struct mep, nd);
0401 return NULL;
0402 }
0403
0404 static void mep_delete(struct rblist *rl __maybe_unused,
0405 struct rb_node *nd)
0406 {
0407 struct mep *me = container_of(nd, struct mep, nd);
0408
0409 strlist__delete(me->metrics);
0410 zfree(&me->name);
0411 free(me);
0412 }
0413
0414 static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
0415 {
0416 struct str_node *sn;
0417 int n = 0;
0418
0419 strlist__for_each_entry (sn, metrics) {
0420 if (raw)
0421 printf("%s%s", n > 0 ? " " : "", sn->s);
0422 else
0423 printf(" %s\n", sn->s);
0424 n++;
0425 }
0426 if (raw)
0427 putchar('\n');
0428 }
0429
0430 static int metricgroup__print_pmu_event(const struct pmu_event *pe,
0431 bool metricgroups, char *filter,
0432 bool raw, bool details,
0433 struct rblist *groups,
0434 struct strlist *metriclist)
0435 {
0436 const char *g;
0437 char *omg, *mg;
0438
0439 g = pe->metric_group;
0440 if (!g && pe->metric_name) {
0441 if (pe->name)
0442 return 0;
0443 g = "No_group";
0444 }
0445
0446 if (!g)
0447 return 0;
0448
0449 mg = strdup(g);
0450
0451 if (!mg)
0452 return -ENOMEM;
0453 omg = mg;
0454 while ((g = strsep(&mg, ";")) != NULL) {
0455 struct mep *me;
0456 char *s;
0457
0458 g = skip_spaces(g);
0459 if (*g == 0)
0460 g = "No_group";
0461 if (filter && !strstr(g, filter))
0462 continue;
0463 if (raw)
0464 s = (char *)pe->metric_name;
0465 else {
0466 if (asprintf(&s, "%s\n%*s%s]",
0467 pe->metric_name, 8, "[", pe->desc) < 0)
0468 return -1;
0469 if (details) {
0470 if (asprintf(&s, "%s\n%*s%s]",
0471 s, 8, "[", pe->metric_expr) < 0)
0472 return -1;
0473 }
0474 }
0475
0476 if (!s)
0477 continue;
0478
0479 if (!metricgroups) {
0480 strlist__add(metriclist, s);
0481 } else {
0482 me = mep_lookup(groups, g);
0483 if (!me)
0484 continue;
0485 strlist__add(me->metrics, s);
0486 }
0487
0488 if (!raw)
0489 free(s);
0490 }
0491 free(omg);
0492
0493 return 0;
0494 }
0495
0496 struct metricgroup_print_sys_idata {
0497 struct strlist *metriclist;
0498 char *filter;
0499 struct rblist *groups;
0500 bool metricgroups;
0501 bool raw;
0502 bool details;
0503 };
0504
0505 struct metricgroup_iter_data {
0506 pmu_event_iter_fn fn;
0507 void *data;
0508 };
0509
0510 static int metricgroup__sys_event_iter(const struct pmu_event *pe,
0511 const struct pmu_events_table *table,
0512 void *data)
0513 {
0514 struct metricgroup_iter_data *d = data;
0515 struct perf_pmu *pmu = NULL;
0516
0517 if (!pe->metric_expr || !pe->compat)
0518 return 0;
0519
0520 while ((pmu = perf_pmu__scan(pmu))) {
0521
0522 if (!pmu->id || strcmp(pmu->id, pe->compat))
0523 continue;
0524
0525 return d->fn(pe, table, d->data);
0526 }
0527
0528 return 0;
0529 }
0530
0531 static int metricgroup__print_sys_event_iter(const struct pmu_event *pe,
0532 const struct pmu_events_table *table __maybe_unused,
0533 void *data)
0534 {
0535 struct metricgroup_print_sys_idata *d = data;
0536
0537 return metricgroup__print_pmu_event(pe, d->metricgroups, d->filter, d->raw,
0538 d->details, d->groups, d->metriclist);
0539 }
0540
0541 struct metricgroup_print_data {
0542 const char *pmu_name;
0543 struct strlist *metriclist;
0544 char *filter;
0545 struct rblist *groups;
0546 bool metricgroups;
0547 bool raw;
0548 bool details;
0549 };
0550
0551 static int metricgroup__print_callback(const struct pmu_event *pe,
0552 const struct pmu_events_table *table __maybe_unused,
0553 void *vdata)
0554 {
0555 struct metricgroup_print_data *data = vdata;
0556
0557 if (!pe->metric_expr)
0558 return 0;
0559
0560 if (data->pmu_name && perf_pmu__is_hybrid(pe->pmu) && strcmp(data->pmu_name, pe->pmu))
0561 return 0;
0562
0563 return metricgroup__print_pmu_event(pe, data->metricgroups, data->filter,
0564 data->raw, data->details, data->groups,
0565 data->metriclist);
0566 }
0567
0568 void metricgroup__print(bool metrics, bool metricgroups, char *filter,
0569 bool raw, bool details, const char *pmu_name)
0570 {
0571 struct rblist groups;
0572 struct rb_node *node, *next;
0573 struct strlist *metriclist = NULL;
0574 const struct pmu_events_table *table;
0575
0576 if (!metricgroups) {
0577 metriclist = strlist__new(NULL, NULL);
0578 if (!metriclist)
0579 return;
0580 }
0581
0582 rblist__init(&groups);
0583 groups.node_new = mep_new;
0584 groups.node_cmp = mep_cmp;
0585 groups.node_delete = mep_delete;
0586 table = pmu_events_table__find();
0587 if (table) {
0588 struct metricgroup_print_data data = {
0589 .pmu_name = pmu_name,
0590 .metriclist = metriclist,
0591 .metricgroups = metricgroups,
0592 .filter = filter,
0593 .raw = raw,
0594 .details = details,
0595 .groups = &groups,
0596 };
0597
0598 pmu_events_table_for_each_event(table,
0599 metricgroup__print_callback,
0600 &data);
0601 }
0602 {
0603 struct metricgroup_iter_data data = {
0604 .fn = metricgroup__print_sys_event_iter,
0605 .data = (void *) &(struct metricgroup_print_sys_idata){
0606 .metriclist = metriclist,
0607 .metricgroups = metricgroups,
0608 .filter = filter,
0609 .raw = raw,
0610 .details = details,
0611 .groups = &groups,
0612 },
0613 };
0614
0615 pmu_for_each_sys_event(metricgroup__sys_event_iter, &data);
0616 }
0617
0618 if (!filter || !rblist__empty(&groups)) {
0619 if (metricgroups && !raw)
0620 printf("\nMetric Groups:\n\n");
0621 else if (metrics && !raw)
0622 printf("\nMetrics:\n\n");
0623 }
0624
0625 for (node = rb_first_cached(&groups.entries); node; node = next) {
0626 struct mep *me = container_of(node, struct mep, nd);
0627
0628 if (metricgroups)
0629 printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
0630 if (metrics)
0631 metricgroup__print_strlist(me->metrics, raw);
0632 next = rb_next(node);
0633 rblist__remove_node(&groups, node);
0634 }
0635 if (!metricgroups)
0636 metricgroup__print_strlist(metriclist, raw);
0637 strlist__delete(metriclist);
0638 }
0639
0640 static const char *code_characters = ",-=@";
0641
0642 static int encode_metric_id(struct strbuf *sb, const char *x)
0643 {
0644 char *c;
0645 int ret = 0;
0646
0647 for (; *x; x++) {
0648 c = strchr(code_characters, *x);
0649 if (c) {
0650 ret = strbuf_addch(sb, '!');
0651 if (ret)
0652 break;
0653
0654 ret = strbuf_addch(sb, '0' + (c - code_characters));
0655 if (ret)
0656 break;
0657 } else {
0658 ret = strbuf_addch(sb, *x);
0659 if (ret)
0660 break;
0661 }
0662 }
0663 return ret;
0664 }
0665
0666 static int decode_metric_id(struct strbuf *sb, const char *x)
0667 {
0668 const char *orig = x;
0669 size_t i;
0670 char c;
0671 int ret;
0672
0673 for (; *x; x++) {
0674 c = *x;
0675 if (*x == '!') {
0676 x++;
0677 i = *x - '0';
0678 if (i > strlen(code_characters)) {
0679 pr_err("Bad metric-id encoding in: '%s'", orig);
0680 return -1;
0681 }
0682 c = code_characters[i];
0683 }
0684 ret = strbuf_addch(sb, c);
0685 if (ret)
0686 return ret;
0687 }
0688 return 0;
0689 }
0690
0691 static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifier)
0692 {
0693 struct evsel *ev;
0694 struct strbuf sb = STRBUF_INIT;
0695 char *cur;
0696 int ret = 0;
0697
0698 evlist__for_each_entry(perf_evlist, ev) {
0699 if (!ev->metric_id)
0700 continue;
0701
0702 ret = strbuf_setlen(&sb, 0);
0703 if (ret)
0704 break;
0705
0706 ret = decode_metric_id(&sb, ev->metric_id);
0707 if (ret)
0708 break;
0709
0710 free((char *)ev->metric_id);
0711 ev->metric_id = strdup(sb.buf);
0712 if (!ev->metric_id) {
0713 ret = -ENOMEM;
0714 break;
0715 }
0716
0717
0718
0719
0720 if (strstr(ev->name, "metric-id=")) {
0721 bool has_slash = false;
0722
0723 free(ev->name);
0724 for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) {
0725 *cur = '/';
0726 has_slash = true;
0727 }
0728
0729 if (modifier) {
0730 if (!has_slash && !strchr(sb.buf, ':')) {
0731 ret = strbuf_addch(&sb, ':');
0732 if (ret)
0733 break;
0734 }
0735 ret = strbuf_addstr(&sb, modifier);
0736 if (ret)
0737 break;
0738 }
0739 ev->name = strdup(sb.buf);
0740 if (!ev->name) {
0741 ret = -ENOMEM;
0742 break;
0743 }
0744 }
0745 }
0746 strbuf_release(&sb);
0747 return ret;
0748 }
0749
0750 static int metricgroup__build_event_string(struct strbuf *events,
0751 const struct expr_parse_ctx *ctx,
0752 const char *modifier,
0753 bool has_constraint)
0754 {
0755 struct hashmap_entry *cur;
0756 size_t bkt;
0757 bool no_group = true, has_tool_events = false;
0758 bool tool_events[PERF_TOOL_MAX] = {false};
0759 int ret = 0;
0760
0761 #define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0)
0762
0763 hashmap__for_each_entry(ctx->ids, cur, bkt) {
0764 const char *sep, *rsep, *id = cur->key;
0765 enum perf_tool_event ev;
0766
0767 pr_debug("found event %s\n", id);
0768
0769
0770 ev = perf_tool_event__from_str(id);
0771 if (ev != PERF_TOOL_NONE) {
0772 has_tool_events = true;
0773 tool_events[ev] = true;
0774 continue;
0775 }
0776
0777 if (no_group) {
0778 if (!has_constraint) {
0779 ret = strbuf_addch(events, '{');
0780 RETURN_IF_NON_ZERO(ret);
0781 }
0782
0783 no_group = false;
0784 } else {
0785 ret = strbuf_addch(events, ',');
0786 RETURN_IF_NON_ZERO(ret);
0787 }
0788
0789
0790
0791
0792
0793
0794 sep = strchr(id, '@');
0795 if (sep != NULL) {
0796 ret = strbuf_add(events, id, sep - id);
0797 RETURN_IF_NON_ZERO(ret);
0798 ret = strbuf_addch(events, '/');
0799 RETURN_IF_NON_ZERO(ret);
0800 rsep = strrchr(sep, '@');
0801 ret = strbuf_add(events, sep + 1, rsep - sep - 1);
0802 RETURN_IF_NON_ZERO(ret);
0803 ret = strbuf_addstr(events, ",metric-id=");
0804 RETURN_IF_NON_ZERO(ret);
0805 sep = rsep;
0806 } else {
0807 sep = strchr(id, ':');
0808 if (sep != NULL) {
0809 ret = strbuf_add(events, id, sep - id);
0810 RETURN_IF_NON_ZERO(ret);
0811 } else {
0812 ret = strbuf_addstr(events, id);
0813 RETURN_IF_NON_ZERO(ret);
0814 }
0815 ret = strbuf_addstr(events, "/metric-id=");
0816 RETURN_IF_NON_ZERO(ret);
0817 }
0818 ret = encode_metric_id(events, id);
0819 RETURN_IF_NON_ZERO(ret);
0820 ret = strbuf_addstr(events, "/");
0821 RETURN_IF_NON_ZERO(ret);
0822
0823 if (sep != NULL) {
0824 ret = strbuf_addstr(events, sep + 1);
0825 RETURN_IF_NON_ZERO(ret);
0826 }
0827 if (modifier) {
0828 ret = strbuf_addstr(events, modifier);
0829 RETURN_IF_NON_ZERO(ret);
0830 }
0831 }
0832 if (!no_group && !has_constraint) {
0833 ret = strbuf_addf(events, "}:W");
0834 RETURN_IF_NON_ZERO(ret);
0835 }
0836 if (has_tool_events) {
0837 int i;
0838
0839 perf_tool_event__for_each_event(i) {
0840 if (tool_events[i]) {
0841 if (!no_group) {
0842 ret = strbuf_addch(events, ',');
0843 RETURN_IF_NON_ZERO(ret);
0844 }
0845 no_group = false;
0846 ret = strbuf_addstr(events, perf_tool_event__to_str(i));
0847 RETURN_IF_NON_ZERO(ret);
0848 }
0849 }
0850 }
0851
0852 return ret;
0853 #undef RETURN_IF_NON_ZERO
0854 }
0855
0856 int __weak arch_get_runtimeparam(const struct pmu_event *pe __maybe_unused)
0857 {
0858 return 1;
0859 }
0860
0861
0862
0863
0864
0865 struct visited_metric {
0866 const char *name;
0867 const struct visited_metric *parent;
0868 };
0869
0870 struct metricgroup_add_iter_data {
0871 struct list_head *metric_list;
0872 const char *metric_name;
0873 const char *modifier;
0874 int *ret;
0875 bool *has_match;
0876 bool metric_no_group;
0877 struct metric *root_metric;
0878 const struct visited_metric *visited;
0879 const struct pmu_events_table *table;
0880 };
0881
0882 static bool metricgroup__find_metric(const char *metric,
0883 const struct pmu_events_table *table,
0884 struct pmu_event *pe);
0885
0886 static int add_metric(struct list_head *metric_list,
0887 const struct pmu_event *pe,
0888 const char *modifier,
0889 bool metric_no_group,
0890 struct metric *root_metric,
0891 const struct visited_metric *visited,
0892 const struct pmu_events_table *table);
0893
0894
0895
0896
0897
0898
0899
0900
0901
0902
0903
0904
0905
0906
0907
0908
0909
0910 static int resolve_metric(struct list_head *metric_list,
0911 const char *modifier,
0912 bool metric_no_group,
0913 struct metric *root_metric,
0914 const struct visited_metric *visited,
0915 const struct pmu_events_table *table)
0916 {
0917 struct hashmap_entry *cur;
0918 size_t bkt;
0919 struct to_resolve {
0920
0921 struct pmu_event pe;
0922
0923
0924
0925
0926 const char *key;
0927 } *pending = NULL;
0928 int i, ret = 0, pending_cnt = 0;
0929
0930
0931
0932
0933
0934 hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
0935 struct pmu_event pe;
0936
0937 if (metricgroup__find_metric(cur->key, table, &pe)) {
0938 pending = realloc(pending,
0939 (pending_cnt + 1) * sizeof(struct to_resolve));
0940 if (!pending)
0941 return -ENOMEM;
0942
0943 memcpy(&pending[pending_cnt].pe, &pe, sizeof(pe));
0944 pending[pending_cnt].key = cur->key;
0945 pending_cnt++;
0946 }
0947 }
0948
0949
0950 for (i = 0; i < pending_cnt; i++)
0951 expr__del_id(root_metric->pctx, pending[i].key);
0952
0953
0954
0955
0956
0957 for (i = 0; i < pending_cnt; i++) {
0958 ret = add_metric(metric_list, &pending[i].pe, modifier, metric_no_group,
0959 root_metric, visited, table);
0960 if (ret)
0961 break;
0962 }
0963
0964 free(pending);
0965 return ret;
0966 }
0967
0968
0969
0970
0971
0972
0973
0974
0975
0976
0977
0978
0979
0980
0981
0982
0983
0984
0985 static int __add_metric(struct list_head *metric_list,
0986 const struct pmu_event *pe,
0987 const char *modifier,
0988 bool metric_no_group,
0989 int runtime,
0990 struct metric *root_metric,
0991 const struct visited_metric *visited,
0992 const struct pmu_events_table *table)
0993 {
0994 const struct visited_metric *vm;
0995 int ret;
0996 bool is_root = !root_metric;
0997 struct visited_metric visited_node = {
0998 .name = pe->metric_name,
0999 .parent = visited,
1000 };
1001
1002 for (vm = visited; vm; vm = vm->parent) {
1003 if (!strcmp(pe->metric_name, vm->name)) {
1004 pr_err("failed: recursion detected for %s\n", pe->metric_name);
1005 return -1;
1006 }
1007 }
1008
1009 if (is_root) {
1010
1011
1012
1013
1014 root_metric = metric__new(pe, modifier, metric_no_group, runtime);
1015 if (!root_metric)
1016 return -ENOMEM;
1017
1018 } else {
1019 int cnt = 0;
1020
1021
1022
1023
1024
1025
1026 if (root_metric->metric_refs) {
1027 for (; root_metric->metric_refs[cnt].metric_name; cnt++) {
1028 if (!strcmp(pe->metric_name,
1029 root_metric->metric_refs[cnt].metric_name))
1030 return 0;
1031 }
1032 }
1033
1034
1035 root_metric->metric_refs = realloc(root_metric->metric_refs,
1036 (cnt + 2) * sizeof(struct metric_ref));
1037 if (!root_metric->metric_refs)
1038 return -ENOMEM;
1039
1040
1041
1042
1043
1044
1045
1046 root_metric->metric_refs[cnt].metric_name = pe->metric_name;
1047 root_metric->metric_refs[cnt].metric_expr = pe->metric_expr;
1048
1049
1050 root_metric->metric_refs[cnt+1].metric_name = NULL;
1051 root_metric->metric_refs[cnt+1].metric_expr = NULL;
1052 }
1053
1054
1055
1056
1057
1058 if (expr__find_ids(pe->metric_expr, NULL, root_metric->pctx) < 0) {
1059
1060 ret = -EINVAL;
1061 } else {
1062
1063 ret = resolve_metric(metric_list, modifier, metric_no_group, root_metric,
1064 &visited_node, table);
1065 }
1066
1067 if (ret) {
1068 if (is_root)
1069 metric__free(root_metric);
1070
1071 } else if (is_root)
1072 list_add(&root_metric->nd, metric_list);
1073
1074 return ret;
1075 }
1076
1077 struct metricgroup__find_metric_data {
1078 const char *metric;
1079 struct pmu_event *pe;
1080 };
1081
1082 static int metricgroup__find_metric_callback(const struct pmu_event *pe,
1083 const struct pmu_events_table *table __maybe_unused,
1084 void *vdata)
1085 {
1086 struct metricgroup__find_metric_data *data = vdata;
1087
1088 if (!match_metric(pe->metric_name, data->metric))
1089 return 0;
1090
1091 memcpy(data->pe, pe, sizeof(*pe));
1092 return 1;
1093 }
1094
1095 static bool metricgroup__find_metric(const char *metric,
1096 const struct pmu_events_table *table,
1097 struct pmu_event *pe)
1098 {
1099 struct metricgroup__find_metric_data data = {
1100 .metric = metric,
1101 .pe = pe,
1102 };
1103
1104 return pmu_events_table_for_each_event(table, metricgroup__find_metric_callback, &data)
1105 ? true : false;
1106 }
1107
1108 static int add_metric(struct list_head *metric_list,
1109 const struct pmu_event *pe,
1110 const char *modifier,
1111 bool metric_no_group,
1112 struct metric *root_metric,
1113 const struct visited_metric *visited,
1114 const struct pmu_events_table *table)
1115 {
1116 int ret = 0;
1117
1118 pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
1119
1120 if (!strstr(pe->metric_expr, "?")) {
1121 ret = __add_metric(metric_list, pe, modifier, metric_no_group, 0,
1122 root_metric, visited, table);
1123 } else {
1124 int j, count;
1125
1126 count = arch_get_runtimeparam(pe);
1127
1128
1129
1130
1131
1132
1133 for (j = 0; j < count && !ret; j++)
1134 ret = __add_metric(metric_list, pe, modifier, metric_no_group, j,
1135 root_metric, visited, table);
1136 }
1137
1138 return ret;
1139 }
1140
1141 static int metricgroup__add_metric_sys_event_iter(const struct pmu_event *pe,
1142 const struct pmu_events_table *table __maybe_unused,
1143 void *data)
1144 {
1145 struct metricgroup_add_iter_data *d = data;
1146 int ret;
1147
1148 if (!match_pe_metric(pe, d->metric_name))
1149 return 0;
1150
1151 ret = add_metric(d->metric_list, pe, d->modifier, d->metric_no_group,
1152 d->root_metric, d->visited, d->table);
1153 if (ret)
1154 goto out;
1155
1156 *(d->has_match) = true;
1157
1158 out:
1159 *(d->ret) = ret;
1160 return ret;
1161 }
1162
1163
1164
1165
1166
1167 static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
1168 const struct list_head *r)
1169 {
1170 const struct metric *left = container_of(l, struct metric, nd);
1171 const struct metric *right = container_of(r, struct metric, nd);
1172 struct expr_id_data *data;
1173 int i, left_count, right_count;
1174
1175 left_count = hashmap__size(left->pctx->ids);
1176 perf_tool_event__for_each_event(i) {
1177 if (!expr__get_id(left->pctx, perf_tool_event__to_str(i), &data))
1178 left_count--;
1179 }
1180
1181 right_count = hashmap__size(right->pctx->ids);
1182 perf_tool_event__for_each_event(i) {
1183 if (!expr__get_id(right->pctx, perf_tool_event__to_str(i), &data))
1184 right_count--;
1185 }
1186
1187 return right_count - left_count;
1188 }
1189
1190 struct metricgroup__add_metric_data {
1191 struct list_head *list;
1192 const char *metric_name;
1193 const char *modifier;
1194 bool metric_no_group;
1195 bool has_match;
1196 };
1197
1198 static int metricgroup__add_metric_callback(const struct pmu_event *pe,
1199 const struct pmu_events_table *table,
1200 void *vdata)
1201 {
1202 struct metricgroup__add_metric_data *data = vdata;
1203 int ret = 0;
1204
1205 if (pe->metric_expr &&
1206 (match_metric(pe->metric_group, data->metric_name) ||
1207 match_metric(pe->metric_name, data->metric_name))) {
1208
1209 data->has_match = true;
1210 ret = add_metric(data->list, pe, data->modifier, data->metric_no_group,
1211 NULL,
1212 NULL, table);
1213 }
1214 return ret;
1215 }
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230 static int metricgroup__add_metric(const char *metric_name, const char *modifier,
1231 bool metric_no_group,
1232 struct list_head *metric_list,
1233 const struct pmu_events_table *table)
1234 {
1235 LIST_HEAD(list);
1236 int ret;
1237 bool has_match = false;
1238
1239 {
1240 struct metricgroup__add_metric_data data = {
1241 .list = &list,
1242 .metric_name = metric_name,
1243 .modifier = modifier,
1244 .metric_no_group = metric_no_group,
1245 .has_match = false,
1246 };
1247
1248
1249
1250
1251 ret = pmu_events_table_for_each_event(table, metricgroup__add_metric_callback,
1252 &data);
1253 if (ret)
1254 goto out;
1255
1256 has_match = data.has_match;
1257 }
1258 {
1259 struct metricgroup_iter_data data = {
1260 .fn = metricgroup__add_metric_sys_event_iter,
1261 .data = (void *) &(struct metricgroup_add_iter_data) {
1262 .metric_list = &list,
1263 .metric_name = metric_name,
1264 .modifier = modifier,
1265 .metric_no_group = metric_no_group,
1266 .has_match = &has_match,
1267 .ret = &ret,
1268 .table = table,
1269 },
1270 };
1271
1272 pmu_for_each_sys_event(metricgroup__sys_event_iter, &data);
1273 }
1274
1275 if (!has_match)
1276 ret = -EINVAL;
1277
1278 out:
1279
1280
1281
1282
1283 list_splice(&list, metric_list);
1284 return ret;
1285 }
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300 static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
1301 struct list_head *metric_list,
1302 const struct pmu_events_table *table)
1303 {
1304 char *list_itr, *list_copy, *metric_name, *modifier;
1305 int ret, count = 0;
1306
1307 list_copy = strdup(list);
1308 if (!list_copy)
1309 return -ENOMEM;
1310 list_itr = list_copy;
1311
1312 while ((metric_name = strsep(&list_itr, ",")) != NULL) {
1313 modifier = strchr(metric_name, ':');
1314 if (modifier)
1315 *modifier++ = '\0';
1316
1317 ret = metricgroup__add_metric(metric_name, modifier,
1318 metric_no_group, metric_list,
1319 table);
1320 if (ret == -EINVAL)
1321 pr_err("Cannot find metric or group `%s'\n", metric_name);
1322
1323 if (ret)
1324 break;
1325
1326 count++;
1327 }
1328 free(list_copy);
1329
1330 if (!ret) {
1331
1332
1333
1334
1335 metricgroup___watchdog_constraint_hint(NULL, true);
1336
1337 if (count == 0)
1338 return -EINVAL;
1339 }
1340 return ret;
1341 }
1342
1343 static void metricgroup__free_metrics(struct list_head *metric_list)
1344 {
1345 struct metric *m, *tmp;
1346
1347 list_for_each_entry_safe (m, tmp, metric_list, nd) {
1348 list_del_init(&m->nd);
1349 metric__free(m);
1350 }
1351 }
1352
1353
1354
1355
1356
1357
1358
1359 static void find_tool_events(const struct list_head *metric_list,
1360 bool tool_events[PERF_TOOL_MAX])
1361 {
1362 struct metric *m;
1363
1364 list_for_each_entry(m, metric_list, nd) {
1365 int i;
1366
1367 perf_tool_event__for_each_event(i) {
1368 struct expr_id_data *data;
1369
1370 if (!tool_events[i] &&
1371 !expr__get_id(m->pctx, perf_tool_event__to_str(i), &data))
1372 tool_events[i] = true;
1373 }
1374 }
1375 }
1376
1377
1378
1379
1380
1381
1382
1383
1384 static int build_combined_expr_ctx(const struct list_head *metric_list,
1385 struct expr_parse_ctx **combined)
1386 {
1387 struct hashmap_entry *cur;
1388 size_t bkt;
1389 struct metric *m;
1390 char *dup;
1391 int ret;
1392
1393 *combined = expr__ctx_new();
1394 if (!*combined)
1395 return -ENOMEM;
1396
1397 list_for_each_entry(m, metric_list, nd) {
1398 if (m->has_constraint && !m->modifier) {
1399 hashmap__for_each_entry(m->pctx->ids, cur, bkt) {
1400 dup = strdup(cur->key);
1401 if (!dup) {
1402 ret = -ENOMEM;
1403 goto err_out;
1404 }
1405 ret = expr__add_id(*combined, dup);
1406 if (ret)
1407 goto err_out;
1408 }
1409 }
1410 }
1411 return 0;
1412 err_out:
1413 expr__ctx_free(*combined);
1414 *combined = NULL;
1415 return ret;
1416 }
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430 static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu,
1431 struct expr_parse_ctx *ids, const char *modifier,
1432 bool has_constraint, const bool tool_events[PERF_TOOL_MAX],
1433 struct evlist **out_evlist)
1434 {
1435 struct parse_events_error parse_error;
1436 struct evlist *parsed_evlist;
1437 struct strbuf events = STRBUF_INIT;
1438 int ret;
1439
1440 *out_evlist = NULL;
1441 if (!metric_no_merge || hashmap__size(ids->ids) == 0) {
1442 bool added_event = false;
1443 int i;
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457 perf_tool_event__for_each_event(i) {
1458 if (tool_events[i]) {
1459 char *tmp = strdup(perf_tool_event__to_str(i));
1460
1461 if (!tmp)
1462 return -ENOMEM;
1463 ids__insert(ids->ids, tmp);
1464 added_event = true;
1465 }
1466 }
1467 if (!added_event && hashmap__size(ids->ids) == 0) {
1468 char *tmp = strdup("duration_time");
1469
1470 if (!tmp)
1471 return -ENOMEM;
1472 ids__insert(ids->ids, tmp);
1473 }
1474 }
1475 ret = metricgroup__build_event_string(&events, ids, modifier,
1476 has_constraint);
1477 if (ret)
1478 return ret;
1479
1480 parsed_evlist = evlist__new();
1481 if (!parsed_evlist) {
1482 ret = -ENOMEM;
1483 goto err_out;
1484 }
1485 pr_debug("Parsing metric events '%s'\n", events.buf);
1486 parse_events_error__init(&parse_error);
1487 ret = __parse_events(parsed_evlist, events.buf, &parse_error, fake_pmu);
1488 if (ret) {
1489 parse_events_error__print(&parse_error, events.buf);
1490 goto err_out;
1491 }
1492 ret = decode_all_metric_ids(parsed_evlist, modifier);
1493 if (ret)
1494 goto err_out;
1495
1496 *out_evlist = parsed_evlist;
1497 parsed_evlist = NULL;
1498 err_out:
1499 parse_events_error__exit(&parse_error);
1500 evlist__delete(parsed_evlist);
1501 strbuf_release(&events);
1502 return ret;
1503 }
1504
1505 static int parse_groups(struct evlist *perf_evlist, const char *str,
1506 bool metric_no_group,
1507 bool metric_no_merge,
1508 struct perf_pmu *fake_pmu,
1509 struct rblist *metric_events_list,
1510 const struct pmu_events_table *table)
1511 {
1512 struct evlist *combined_evlist = NULL;
1513 LIST_HEAD(metric_list);
1514 struct metric *m;
1515 bool tool_events[PERF_TOOL_MAX] = {false};
1516 int ret;
1517
1518 if (metric_events_list->nr_entries == 0)
1519 metricgroup__rblist_init(metric_events_list);
1520 ret = metricgroup__add_metric_list(str, metric_no_group,
1521 &metric_list, table);
1522 if (ret)
1523 goto out;
1524
1525
1526 list_sort(NULL, &metric_list, metric_list_cmp);
1527
1528 if (!metric_no_merge) {
1529 struct expr_parse_ctx *combined = NULL;
1530
1531 find_tool_events(&metric_list, tool_events);
1532
1533 ret = build_combined_expr_ctx(&metric_list, &combined);
1534
1535 if (!ret && combined && hashmap__size(combined->ids)) {
1536 ret = parse_ids(metric_no_merge, fake_pmu, combined,
1537 NULL,
1538 true,
1539 tool_events,
1540 &combined_evlist);
1541 }
1542 if (combined)
1543 expr__ctx_free(combined);
1544
1545 if (ret)
1546 goto out;
1547 }
1548
1549 list_for_each_entry(m, &metric_list, nd) {
1550 struct metric_event *me;
1551 struct evsel **metric_events;
1552 struct evlist *metric_evlist = NULL;
1553 struct metric *n;
1554 struct metric_expr *expr;
1555
1556 if (combined_evlist && m->has_constraint) {
1557 metric_evlist = combined_evlist;
1558 } else if (!metric_no_merge) {
1559
1560
1561
1562
1563 list_for_each_entry(n, &metric_list, nd) {
1564 if (m == n)
1565 break;
1566
1567 if (n->evlist == NULL)
1568 continue;
1569
1570 if ((!m->modifier && n->modifier) ||
1571 (m->modifier && !n->modifier) ||
1572 (m->modifier && n->modifier &&
1573 strcmp(m->modifier, n->modifier)))
1574 continue;
1575
1576 if (expr__subset_of_ids(n->pctx, m->pctx)) {
1577 pr_debug("Events in '%s' fully contained within '%s'\n",
1578 m->metric_name, n->metric_name);
1579 metric_evlist = n->evlist;
1580 break;
1581 }
1582
1583 }
1584 }
1585 if (!metric_evlist) {
1586 ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier,
1587 m->has_constraint, tool_events, &m->evlist);
1588 if (ret)
1589 goto out;
1590
1591 metric_evlist = m->evlist;
1592 }
1593 ret = setup_metric_events(m->pctx->ids, metric_evlist, &metric_events);
1594 if (ret) {
1595 pr_debug("Cannot resolve IDs for %s: %s\n",
1596 m->metric_name, m->metric_expr);
1597 goto out;
1598 }
1599
1600 me = metricgroup__lookup(metric_events_list, metric_events[0], true);
1601
1602 expr = malloc(sizeof(struct metric_expr));
1603 if (!expr) {
1604 ret = -ENOMEM;
1605 free(metric_events);
1606 goto out;
1607 }
1608
1609 expr->metric_refs = m->metric_refs;
1610 m->metric_refs = NULL;
1611 expr->metric_expr = m->metric_expr;
1612 if (m->modifier) {
1613 char *tmp;
1614
1615 if (asprintf(&tmp, "%s:%s", m->metric_name, m->modifier) < 0)
1616 expr->metric_name = NULL;
1617 else
1618 expr->metric_name = tmp;
1619 } else
1620 expr->metric_name = strdup(m->metric_name);
1621
1622 if (!expr->metric_name) {
1623 ret = -ENOMEM;
1624 free(metric_events);
1625 goto out;
1626 }
1627 expr->metric_unit = m->metric_unit;
1628 expr->metric_events = metric_events;
1629 expr->runtime = m->pctx->runtime;
1630 list_add(&expr->nd, &me->head);
1631 }
1632
1633
1634 if (combined_evlist) {
1635 evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries);
1636 evlist__delete(combined_evlist);
1637 }
1638
1639 list_for_each_entry(m, &metric_list, nd) {
1640 if (m->evlist)
1641 evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries);
1642 }
1643
1644 out:
1645 metricgroup__free_metrics(&metric_list);
1646 return ret;
1647 }
1648
1649 int metricgroup__parse_groups(const struct option *opt,
1650 const char *str,
1651 bool metric_no_group,
1652 bool metric_no_merge,
1653 struct rblist *metric_events)
1654 {
1655 struct evlist *perf_evlist = *(struct evlist **)opt->value;
1656 const struct pmu_events_table *table = pmu_events_table__find();
1657
1658 if (!table)
1659 return -EINVAL;
1660
1661 return parse_groups(perf_evlist, str, metric_no_group,
1662 metric_no_merge, NULL, metric_events, table);
1663 }
1664
1665 int metricgroup__parse_groups_test(struct evlist *evlist,
1666 const struct pmu_events_table *table,
1667 const char *str,
1668 bool metric_no_group,
1669 bool metric_no_merge,
1670 struct rblist *metric_events)
1671 {
1672 return parse_groups(evlist, str, metric_no_group,
1673 metric_no_merge, &perf_pmu__fake, metric_events, table);
1674 }
1675
1676 static int metricgroup__has_metric_callback(const struct pmu_event *pe,
1677 const struct pmu_events_table *table __maybe_unused,
1678 void *vdata)
1679 {
1680 const char *metric = vdata;
1681
1682 if (!pe->metric_expr)
1683 return 0;
1684
1685 if (match_metric(pe->metric_name, metric))
1686 return 1;
1687
1688 return 0;
1689 }
1690
1691 bool metricgroup__has_metric(const char *metric)
1692 {
1693 const struct pmu_events_table *table = pmu_events_table__find();
1694
1695 if (!table)
1696 return false;
1697
1698 return pmu_events_table_for_each_event(table, metricgroup__has_metric_callback,
1699 (void *)metric) ? true : false;
1700 }
1701
1702 int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
1703 struct rblist *new_metric_events,
1704 struct rblist *old_metric_events)
1705 {
1706 unsigned i;
1707
1708 for (i = 0; i < rblist__nr_entries(old_metric_events); i++) {
1709 struct rb_node *nd;
1710 struct metric_event *old_me, *new_me;
1711 struct metric_expr *old_expr, *new_expr;
1712 struct evsel *evsel;
1713 size_t alloc_size;
1714 int idx, nr;
1715
1716 nd = rblist__entry(old_metric_events, i);
1717 old_me = container_of(nd, struct metric_event, nd);
1718
1719 evsel = evlist__find_evsel(evlist, old_me->evsel->core.idx);
1720 if (!evsel)
1721 return -EINVAL;
1722 new_me = metricgroup__lookup(new_metric_events, evsel, true);
1723 if (!new_me)
1724 return -ENOMEM;
1725
1726 pr_debug("copying metric event for cgroup '%s': %s (idx=%d)\n",
1727 cgrp ? cgrp->name : "root", evsel->name, evsel->core.idx);
1728
1729 list_for_each_entry(old_expr, &old_me->head, nd) {
1730 new_expr = malloc(sizeof(*new_expr));
1731 if (!new_expr)
1732 return -ENOMEM;
1733
1734 new_expr->metric_expr = old_expr->metric_expr;
1735 new_expr->metric_name = strdup(old_expr->metric_name);
1736 if (!new_expr->metric_name)
1737 return -ENOMEM;
1738
1739 new_expr->metric_unit = old_expr->metric_unit;
1740 new_expr->runtime = old_expr->runtime;
1741
1742 if (old_expr->metric_refs) {
1743
1744 for (nr = 0; old_expr->metric_refs[nr].metric_name; nr++)
1745 continue;
1746 alloc_size = sizeof(*new_expr->metric_refs);
1747 new_expr->metric_refs = calloc(nr + 1, alloc_size);
1748 if (!new_expr->metric_refs) {
1749 free(new_expr);
1750 return -ENOMEM;
1751 }
1752
1753 memcpy(new_expr->metric_refs, old_expr->metric_refs,
1754 nr * alloc_size);
1755 } else {
1756 new_expr->metric_refs = NULL;
1757 }
1758
1759
1760 for (nr = 0; old_expr->metric_events[nr]; nr++)
1761 continue;
1762 alloc_size = sizeof(*new_expr->metric_events);
1763 new_expr->metric_events = calloc(nr + 1, alloc_size);
1764 if (!new_expr->metric_events) {
1765 free(new_expr->metric_refs);
1766 free(new_expr);
1767 return -ENOMEM;
1768 }
1769
1770
1771 for (idx = 0; idx < nr; idx++) {
1772 evsel = old_expr->metric_events[idx];
1773 evsel = evlist__find_evsel(evlist, evsel->core.idx);
1774 if (evsel == NULL) {
1775 free(new_expr->metric_events);
1776 free(new_expr->metric_refs);
1777 free(new_expr);
1778 return -EINVAL;
1779 }
1780 new_expr->metric_events[idx] = evsel;
1781 }
1782
1783 list_add(&new_expr->nd, &new_me->head);
1784 }
1785 }
1786 return 0;
1787 }