Loading...
1/*
2 * Copyright (c) 2017, Intel Corporation.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 */
14
15/* Manage metrics and groups of metrics from JSON files */
16
17#include "metricgroup.h"
18#include "evlist.h"
19#include "strbuf.h"
20#include "pmu.h"
21#include "expr.h"
22#include "rblist.h"
23#include <string.h>
24#include <stdbool.h>
25#include <errno.h>
26#include "pmu-events/pmu-events.h"
27#include "strlist.h"
28#include <assert.h>
29#include <ctype.h>
30
31struct metric_event *metricgroup__lookup(struct rblist *metric_events,
32 struct perf_evsel *evsel,
33 bool create)
34{
35 struct rb_node *nd;
36 struct metric_event me = {
37 .evsel = evsel
38 };
39
40 if (!metric_events)
41 return NULL;
42
43 nd = rblist__find(metric_events, &me);
44 if (nd)
45 return container_of(nd, struct metric_event, nd);
46 if (create) {
47 rblist__add_node(metric_events, &me);
48 nd = rblist__find(metric_events, &me);
49 if (nd)
50 return container_of(nd, struct metric_event, nd);
51 }
52 return NULL;
53}
54
55static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
56{
57 struct metric_event *a = container_of(rb_node,
58 struct metric_event,
59 nd);
60 const struct metric_event *b = entry;
61
62 if (a->evsel == b->evsel)
63 return 0;
64 if ((char *)a->evsel < (char *)b->evsel)
65 return -1;
66 return +1;
67}
68
69static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
70 const void *entry)
71{
72 struct metric_event *me = malloc(sizeof(struct metric_event));
73
74 if (!me)
75 return NULL;
76 memcpy(me, entry, sizeof(struct metric_event));
77 me->evsel = ((struct metric_event *)entry)->evsel;
78 INIT_LIST_HEAD(&me->head);
79 return &me->nd;
80}
81
82static void metricgroup__rblist_init(struct rblist *metric_events)
83{
84 rblist__init(metric_events);
85 metric_events->node_cmp = metric_event_cmp;
86 metric_events->node_new = metric_event_new;
87}
88
89struct egroup {
90 struct list_head nd;
91 int idnum;
92 const char **ids;
93 const char *metric_name;
94 const char *metric_expr;
95};
96
97static struct perf_evsel *find_evsel(struct perf_evlist *perf_evlist,
98 const char **ids,
99 int idnum,
100 struct perf_evsel **metric_events)
101{
102 struct perf_evsel *ev, *start = NULL;
103 int ind = 0;
104
105 evlist__for_each_entry (perf_evlist, ev) {
106 if (!strcmp(ev->name, ids[ind])) {
107 metric_events[ind] = ev;
108 if (ind == 0)
109 start = ev;
110 if (++ind == idnum) {
111 metric_events[ind] = NULL;
112 return start;
113 }
114 } else {
115 ind = 0;
116 start = NULL;
117 }
118 }
119 /*
120 * This can happen when an alias expands to multiple
121 * events, like for uncore events.
122 * We don't support this case for now.
123 */
124 return NULL;
125}
126
127static int metricgroup__setup_events(struct list_head *groups,
128 struct perf_evlist *perf_evlist,
129 struct rblist *metric_events_list)
130{
131 struct metric_event *me;
132 struct metric_expr *expr;
133 int i = 0;
134 int ret = 0;
135 struct egroup *eg;
136 struct perf_evsel *evsel;
137
138 list_for_each_entry (eg, groups, nd) {
139 struct perf_evsel **metric_events;
140
141 metric_events = calloc(sizeof(void *), eg->idnum + 1);
142 if (!metric_events) {
143 ret = -ENOMEM;
144 break;
145 }
146 evsel = find_evsel(perf_evlist, eg->ids, eg->idnum,
147 metric_events);
148 if (!evsel) {
149 pr_debug("Cannot resolve %s: %s\n",
150 eg->metric_name, eg->metric_expr);
151 continue;
152 }
153 for (i = 0; i < eg->idnum; i++)
154 metric_events[i]->collect_stat = true;
155 me = metricgroup__lookup(metric_events_list, evsel, true);
156 if (!me) {
157 ret = -ENOMEM;
158 break;
159 }
160 expr = malloc(sizeof(struct metric_expr));
161 if (!expr) {
162 ret = -ENOMEM;
163 break;
164 }
165 expr->metric_expr = eg->metric_expr;
166 expr->metric_name = eg->metric_name;
167 expr->metric_events = metric_events;
168 list_add(&expr->nd, &me->head);
169 }
170 return ret;
171}
172
173static bool match_metric(const char *n, const char *list)
174{
175 int len;
176 char *m;
177
178 if (!list)
179 return false;
180 if (!strcmp(list, "all"))
181 return true;
182 if (!n)
183 return !strcasecmp(list, "No_group");
184 len = strlen(list);
185 m = strcasestr(n, list);
186 if (!m)
187 return false;
188 if ((m == n || m[-1] == ';' || m[-1] == ' ') &&
189 (m[len] == 0 || m[len] == ';'))
190 return true;
191 return false;
192}
193
194struct mep {
195 struct rb_node nd;
196 const char *name;
197 struct strlist *metrics;
198};
199
200static int mep_cmp(struct rb_node *rb_node, const void *entry)
201{
202 struct mep *a = container_of(rb_node, struct mep, nd);
203 struct mep *b = (struct mep *)entry;
204
205 return strcmp(a->name, b->name);
206}
207
208static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
209 const void *entry)
210{
211 struct mep *me = malloc(sizeof(struct mep));
212
213 if (!me)
214 return NULL;
215 memcpy(me, entry, sizeof(struct mep));
216 me->name = strdup(me->name);
217 if (!me->name)
218 goto out_me;
219 me->metrics = strlist__new(NULL, NULL);
220 if (!me->metrics)
221 goto out_name;
222 return &me->nd;
223out_name:
224 free((char *)me->name);
225out_me:
226 free(me);
227 return NULL;
228}
229
230static struct mep *mep_lookup(struct rblist *groups, const char *name)
231{
232 struct rb_node *nd;
233 struct mep me = {
234 .name = name
235 };
236 nd = rblist__find(groups, &me);
237 if (nd)
238 return container_of(nd, struct mep, nd);
239 rblist__add_node(groups, &me);
240 nd = rblist__find(groups, &me);
241 if (nd)
242 return container_of(nd, struct mep, nd);
243 return NULL;
244}
245
246static void mep_delete(struct rblist *rl __maybe_unused,
247 struct rb_node *nd)
248{
249 struct mep *me = container_of(nd, struct mep, nd);
250
251 strlist__delete(me->metrics);
252 free((void *)me->name);
253 free(me);
254}
255
256static void metricgroup__print_strlist(struct strlist *metrics, bool raw)
257{
258 struct str_node *sn;
259 int n = 0;
260
261 strlist__for_each_entry (sn, metrics) {
262 if (raw)
263 printf("%s%s", n > 0 ? " " : "", sn->s);
264 else
265 printf(" %s\n", sn->s);
266 n++;
267 }
268 if (raw)
269 putchar('\n');
270}
271
272void metricgroup__print(bool metrics, bool metricgroups, char *filter,
273 bool raw)
274{
275 struct pmu_events_map *map = perf_pmu__find_map(NULL);
276 struct pmu_event *pe;
277 int i;
278 struct rblist groups;
279 struct rb_node *node, *next;
280 struct strlist *metriclist = NULL;
281
282 if (!map)
283 return;
284
285 if (!metricgroups) {
286 metriclist = strlist__new(NULL, NULL);
287 if (!metriclist)
288 return;
289 }
290
291 rblist__init(&groups);
292 groups.node_new = mep_new;
293 groups.node_cmp = mep_cmp;
294 groups.node_delete = mep_delete;
295 for (i = 0; ; i++) {
296 const char *g;
297 pe = &map->table[i];
298
299 if (!pe->name && !pe->metric_group && !pe->metric_name)
300 break;
301 if (!pe->metric_expr)
302 continue;
303 g = pe->metric_group;
304 if (!g && pe->metric_name) {
305 if (pe->name)
306 continue;
307 g = "No_group";
308 }
309 if (g) {
310 char *omg;
311 char *mg = strdup(g);
312
313 if (!mg)
314 return;
315 omg = mg;
316 while ((g = strsep(&mg, ";")) != NULL) {
317 struct mep *me;
318 char *s;
319
320 if (*g == 0)
321 g = "No_group";
322 while (isspace(*g))
323 g++;
324 if (filter && !strstr(g, filter))
325 continue;
326 if (raw)
327 s = (char *)pe->metric_name;
328 else {
329 if (asprintf(&s, "%s\n\t[%s]",
330 pe->metric_name, pe->desc) < 0)
331 return;
332 }
333
334 if (!s)
335 continue;
336
337 if (!metricgroups) {
338 strlist__add(metriclist, s);
339 } else {
340 me = mep_lookup(&groups, g);
341 if (!me)
342 continue;
343 strlist__add(me->metrics, s);
344 }
345 }
346 free(omg);
347 }
348 }
349
350 if (metricgroups && !raw)
351 printf("\nMetric Groups:\n\n");
352 else if (metrics && !raw)
353 printf("\nMetrics:\n\n");
354
355 for (node = rb_first(&groups.entries); node; node = next) {
356 struct mep *me = container_of(node, struct mep, nd);
357
358 if (metricgroups)
359 printf("%s%s%s", me->name, metrics ? ":" : "", raw ? " " : "\n");
360 if (metrics)
361 metricgroup__print_strlist(me->metrics, raw);
362 next = rb_next(node);
363 rblist__remove_node(&groups, node);
364 }
365 if (!metricgroups)
366 metricgroup__print_strlist(metriclist, raw);
367 strlist__delete(metriclist);
368}
369
370static int metricgroup__add_metric(const char *metric, struct strbuf *events,
371 struct list_head *group_list)
372{
373 struct pmu_events_map *map = perf_pmu__find_map(NULL);
374 struct pmu_event *pe;
375 int ret = -EINVAL;
376 int i, j;
377
378 if (!map)
379 return 0;
380
381 for (i = 0; ; i++) {
382 pe = &map->table[i];
383
384 if (!pe->name && !pe->metric_group && !pe->metric_name)
385 break;
386 if (!pe->metric_expr)
387 continue;
388 if (match_metric(pe->metric_group, metric) ||
389 match_metric(pe->metric_name, metric)) {
390 const char **ids;
391 int idnum;
392 struct egroup *eg;
393
394 pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
395
396 if (expr__find_other(pe->metric_expr,
397 NULL, &ids, &idnum) < 0)
398 continue;
399 if (events->len > 0)
400 strbuf_addf(events, ",");
401 for (j = 0; j < idnum; j++) {
402 pr_debug("found event %s\n", ids[j]);
403 strbuf_addf(events, "%s%s",
404 j == 0 ? "{" : ",",
405 ids[j]);
406 }
407 strbuf_addf(events, "}:W");
408
409 eg = malloc(sizeof(struct egroup));
410 if (!eg) {
411 ret = -ENOMEM;
412 break;
413 }
414 eg->ids = ids;
415 eg->idnum = idnum;
416 eg->metric_name = pe->metric_name;
417 eg->metric_expr = pe->metric_expr;
418 list_add_tail(&eg->nd, group_list);
419 ret = 0;
420 }
421 }
422 return ret;
423}
424
425static int metricgroup__add_metric_list(const char *list, struct strbuf *events,
426 struct list_head *group_list)
427{
428 char *llist, *nlist, *p;
429 int ret = -EINVAL;
430
431 nlist = strdup(list);
432 if (!nlist)
433 return -ENOMEM;
434 llist = nlist;
435
436 strbuf_init(events, 100);
437 strbuf_addf(events, "%s", "");
438
439 while ((p = strsep(&llist, ",")) != NULL) {
440 ret = metricgroup__add_metric(p, events, group_list);
441 if (ret == -EINVAL) {
442 fprintf(stderr, "Cannot find metric or group `%s'\n",
443 p);
444 break;
445 }
446 }
447 free(nlist);
448 return ret;
449}
450
451static void metricgroup__free_egroups(struct list_head *group_list)
452{
453 struct egroup *eg, *egtmp;
454 int i;
455
456 list_for_each_entry_safe (eg, egtmp, group_list, nd) {
457 for (i = 0; i < eg->idnum; i++)
458 free((char *)eg->ids[i]);
459 free(eg->ids);
460 free(eg);
461 }
462}
463
464int metricgroup__parse_groups(const struct option *opt,
465 const char *str,
466 struct rblist *metric_events)
467{
468 struct parse_events_error parse_error;
469 struct perf_evlist *perf_evlist = *(struct perf_evlist **)opt->value;
470 struct strbuf extra_events;
471 LIST_HEAD(group_list);
472 int ret;
473
474 if (metric_events->nr_entries == 0)
475 metricgroup__rblist_init(metric_events);
476 ret = metricgroup__add_metric_list(str, &extra_events, &group_list);
477 if (ret)
478 return ret;
479 pr_debug("adding %s\n", extra_events.buf);
480 memset(&parse_error, 0, sizeof(struct parse_events_error));
481 ret = parse_events(perf_evlist, extra_events.buf, &parse_error);
482 if (ret) {
483 parse_events_print_error(&parse_error, extra_events.buf);
484 goto out;
485 }
486 strbuf_release(&extra_events);
487 ret = metricgroup__setup_events(&group_list, perf_evlist,
488 metric_events);
489out:
490 metricgroup__free_egroups(&group_list);
491 return ret;
492}
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 "pmus.h"
15#include "print-events.h"
16#include "smt.h"
17#include "tool_pmu.h"
18#include "expr.h"
19#include "rblist.h"
20#include <string.h>
21#include <errno.h>
22#include "strlist.h"
23#include <assert.h>
24#include <linux/ctype.h>
25#include <linux/list_sort.h>
26#include <linux/string.h>
27#include <linux/zalloc.h>
28#include <perf/cpumap.h>
29#include <subcmd/parse-options.h>
30#include <api/fs/fs.h>
31#include "util.h"
32#include <asm/bug.h>
33#include "cgroup.h"
34#include "util/hashmap.h"
35
36struct metric_event *metricgroup__lookup(struct rblist *metric_events,
37 struct evsel *evsel,
38 bool create)
39{
40 struct rb_node *nd;
41 struct metric_event me = {
42 .evsel = evsel
43 };
44
45 if (!metric_events)
46 return NULL;
47
48 if (evsel && evsel->metric_leader)
49 me.evsel = evsel->metric_leader;
50 nd = rblist__find(metric_events, &me);
51 if (nd)
52 return container_of(nd, struct metric_event, nd);
53 if (create) {
54 rblist__add_node(metric_events, &me);
55 nd = rblist__find(metric_events, &me);
56 if (nd)
57 return container_of(nd, struct metric_event, nd);
58 }
59 return NULL;
60}
61
62static int metric_event_cmp(struct rb_node *rb_node, const void *entry)
63{
64 struct metric_event *a = container_of(rb_node,
65 struct metric_event,
66 nd);
67 const struct metric_event *b = entry;
68
69 if (a->evsel == b->evsel)
70 return 0;
71 if ((char *)a->evsel < (char *)b->evsel)
72 return -1;
73 return +1;
74}
75
76static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused,
77 const void *entry)
78{
79 struct metric_event *me = malloc(sizeof(struct metric_event));
80
81 if (!me)
82 return NULL;
83 memcpy(me, entry, sizeof(struct metric_event));
84 me->evsel = ((struct metric_event *)entry)->evsel;
85 me->is_default = false;
86 INIT_LIST_HEAD(&me->head);
87 return &me->nd;
88}
89
90static void metric_event_delete(struct rblist *rblist __maybe_unused,
91 struct rb_node *rb_node)
92{
93 struct metric_event *me = container_of(rb_node, struct metric_event, nd);
94 struct metric_expr *expr, *tmp;
95
96 list_for_each_entry_safe(expr, tmp, &me->head, nd) {
97 zfree(&expr->metric_name);
98 zfree(&expr->metric_refs);
99 zfree(&expr->metric_events);
100 free(expr);
101 }
102
103 free(me);
104}
105
106static void metricgroup__rblist_init(struct rblist *metric_events)
107{
108 rblist__init(metric_events);
109 metric_events->node_cmp = metric_event_cmp;
110 metric_events->node_new = metric_event_new;
111 metric_events->node_delete = metric_event_delete;
112}
113
114void metricgroup__rblist_exit(struct rblist *metric_events)
115{
116 rblist__exit(metric_events);
117}
118
119/**
120 * The metric under construction. The data held here will be placed in a
121 * metric_expr.
122 */
123struct metric {
124 struct list_head nd;
125 /**
126 * The expression parse context importantly holding the IDs contained
127 * within the expression.
128 */
129 struct expr_parse_ctx *pctx;
130 const char *pmu;
131 /** The name of the metric such as "IPC". */
132 const char *metric_name;
133 /** Modifier on the metric such as "u" or NULL for none. */
134 const char *modifier;
135 /** The expression to parse, for example, "instructions/cycles". */
136 const char *metric_expr;
137 /** Optional threshold expression where zero value is green, otherwise red. */
138 const char *metric_threshold;
139 /**
140 * The "ScaleUnit" that scales and adds a unit to the metric during
141 * output.
142 */
143 const char *metric_unit;
144 /**
145 * Optional name of the metric group reported
146 * if the Default metric group is being processed.
147 */
148 const char *default_metricgroup_name;
149 /** Optional null terminated array of referenced metrics. */
150 struct metric_ref *metric_refs;
151 /**
152 * Should events of the metric be grouped?
153 */
154 bool group_events;
155 /**
156 * Parsed events for the metric. Optional as events may be taken from a
157 * different metric whose group contains all the IDs necessary for this
158 * one.
159 */
160 struct evlist *evlist;
161};
162
163static void metric__watchdog_constraint_hint(const char *name, bool foot)
164{
165 static bool violate_nmi_constraint;
166
167 if (!foot) {
168 pr_warning("Not grouping metric %s's events.\n", name);
169 violate_nmi_constraint = true;
170 return;
171 }
172
173 if (!violate_nmi_constraint)
174 return;
175
176 pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n"
177 " echo 0 > /proc/sys/kernel/nmi_watchdog\n"
178 " perf stat ...\n"
179 " echo 1 > /proc/sys/kernel/nmi_watchdog\n");
180}
181
182static bool metric__group_events(const struct pmu_metric *pm)
183{
184 switch (pm->event_grouping) {
185 case MetricNoGroupEvents:
186 return false;
187 case MetricNoGroupEventsNmi:
188 if (!sysctl__nmi_watchdog_enabled())
189 return true;
190 metric__watchdog_constraint_hint(pm->metric_name, /*foot=*/false);
191 return false;
192 case MetricNoGroupEventsSmt:
193 return !smt_on();
194 case MetricGroupEvents:
195 default:
196 return true;
197 }
198}
199
200static void metric__free(struct metric *m)
201{
202 if (!m)
203 return;
204
205 zfree(&m->metric_refs);
206 expr__ctx_free(m->pctx);
207 zfree(&m->modifier);
208 evlist__delete(m->evlist);
209 free(m);
210}
211
212static struct metric *metric__new(const struct pmu_metric *pm,
213 const char *modifier,
214 bool metric_no_group,
215 int runtime,
216 const char *user_requested_cpu_list,
217 bool system_wide)
218{
219 struct metric *m;
220
221 m = zalloc(sizeof(*m));
222 if (!m)
223 return NULL;
224
225 m->pctx = expr__ctx_new();
226 if (!m->pctx)
227 goto out_err;
228
229 m->pmu = pm->pmu ?: "cpu";
230 m->metric_name = pm->metric_name;
231 m->default_metricgroup_name = pm->default_metricgroup_name ?: "";
232 m->modifier = NULL;
233 if (modifier) {
234 m->modifier = strdup(modifier);
235 if (!m->modifier)
236 goto out_err;
237 }
238 m->metric_expr = pm->metric_expr;
239 m->metric_threshold = pm->metric_threshold;
240 m->metric_unit = pm->unit;
241 m->pctx->sctx.user_requested_cpu_list = NULL;
242 if (user_requested_cpu_list) {
243 m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list);
244 if (!m->pctx->sctx.user_requested_cpu_list)
245 goto out_err;
246 }
247 m->pctx->sctx.runtime = runtime;
248 m->pctx->sctx.system_wide = system_wide;
249 m->group_events = !metric_no_group && metric__group_events(pm);
250 m->metric_refs = NULL;
251 m->evlist = NULL;
252
253 return m;
254out_err:
255 metric__free(m);
256 return NULL;
257}
258
259static bool contains_metric_id(struct evsel **metric_events, int num_events,
260 const char *metric_id)
261{
262 int i;
263
264 for (i = 0; i < num_events; i++) {
265 if (!strcmp(evsel__metric_id(metric_events[i]), metric_id))
266 return true;
267 }
268 return false;
269}
270
271/**
272 * setup_metric_events - Find a group of events in metric_evlist that correspond
273 * to the IDs from a parsed metric expression.
274 * @pmu: The PMU for the IDs.
275 * @ids: the metric IDs to match.
276 * @metric_evlist: the list of perf events.
277 * @out_metric_events: holds the created metric events array.
278 */
279static int setup_metric_events(const char *pmu, struct hashmap *ids,
280 struct evlist *metric_evlist,
281 struct evsel ***out_metric_events)
282{
283 struct evsel **metric_events;
284 const char *metric_id;
285 struct evsel *ev;
286 size_t ids_size, matched_events, i;
287 bool all_pmus = !strcmp(pmu, "all") || perf_pmus__num_core_pmus() == 1 || !is_pmu_core(pmu);
288
289 *out_metric_events = NULL;
290 ids_size = hashmap__size(ids);
291
292 metric_events = calloc(ids_size + 1, sizeof(void *));
293 if (!metric_events)
294 return -ENOMEM;
295
296 matched_events = 0;
297 evlist__for_each_entry(metric_evlist, ev) {
298 struct expr_id_data *val_ptr;
299
300 /* Don't match events for the wrong hybrid PMU. */
301 if (!all_pmus && ev->pmu && evsel__is_hybrid(ev) &&
302 strcmp(ev->pmu->name, pmu))
303 continue;
304 /*
305 * Check for duplicate events with the same name. For
306 * example, uncore_imc/cas_count_read/ will turn into 6
307 * events per socket on skylakex. Only the first such
308 * event is placed in metric_events.
309 */
310 metric_id = evsel__metric_id(ev);
311 if (contains_metric_id(metric_events, matched_events, metric_id))
312 continue;
313 /*
314 * Does this event belong to the parse context? For
315 * combined or shared groups, this metric may not care
316 * about this event.
317 */
318 if (hashmap__find(ids, metric_id, &val_ptr)) {
319 pr_debug("Matched metric-id %s to %s\n", metric_id, evsel__name(ev));
320 metric_events[matched_events++] = ev;
321
322 if (matched_events >= ids_size)
323 break;
324 }
325 }
326 if (matched_events < ids_size) {
327 free(metric_events);
328 return -EINVAL;
329 }
330 for (i = 0; i < ids_size; i++) {
331 ev = metric_events[i];
332 ev->collect_stat = true;
333
334 /*
335 * The metric leader points to the identically named
336 * event in metric_events.
337 */
338 ev->metric_leader = ev;
339 /*
340 * Mark two events with identical names in the same
341 * group (or globally) as being in use as uncore events
342 * may be duplicated for each pmu. Set the metric leader
343 * of such events to be the event that appears in
344 * metric_events.
345 */
346 metric_id = evsel__metric_id(ev);
347 evlist__for_each_entry_continue(metric_evlist, ev) {
348 if (!strcmp(evsel__metric_id(ev), metric_id))
349 ev->metric_leader = metric_events[i];
350 }
351 }
352 *out_metric_events = metric_events;
353 return 0;
354}
355
356static bool match_metric(const char *metric_or_groups, const char *sought)
357{
358 int len;
359 char *m;
360
361 if (!sought)
362 return false;
363 if (!strcmp(sought, "all"))
364 return true;
365 if (!metric_or_groups)
366 return !strcasecmp(sought, "No_group");
367 len = strlen(sought);
368 if (!strncasecmp(metric_or_groups, sought, len) &&
369 (metric_or_groups[len] == 0 || metric_or_groups[len] == ';'))
370 return true;
371 m = strchr(metric_or_groups, ';');
372 return m && match_metric(m + 1, sought);
373}
374
375static bool match_pm_metric(const struct pmu_metric *pm, const char *pmu, const char *metric)
376{
377 const char *pm_pmu = pm->pmu ?: "cpu";
378
379 if (strcmp(pmu, "all") && strcmp(pm_pmu, pmu))
380 return false;
381
382 return match_metric(pm->metric_group, metric) ||
383 match_metric(pm->metric_name, metric);
384}
385
386/** struct mep - RB-tree node for building printing information. */
387struct mep {
388 /** nd - RB-tree element. */
389 struct rb_node nd;
390 /** @metric_group: Owned metric group name, separated others with ';'. */
391 char *metric_group;
392 const char *metric_name;
393 const char *metric_desc;
394 const char *metric_long_desc;
395 const char *metric_expr;
396 const char *metric_threshold;
397 const char *metric_unit;
398};
399
400static int mep_cmp(struct rb_node *rb_node, const void *entry)
401{
402 struct mep *a = container_of(rb_node, struct mep, nd);
403 struct mep *b = (struct mep *)entry;
404 int ret;
405
406 ret = strcmp(a->metric_group, b->metric_group);
407 if (ret)
408 return ret;
409
410 return strcmp(a->metric_name, b->metric_name);
411}
412
413static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry)
414{
415 struct mep *me = malloc(sizeof(struct mep));
416
417 if (!me)
418 return NULL;
419
420 memcpy(me, entry, sizeof(struct mep));
421 return &me->nd;
422}
423
424static void mep_delete(struct rblist *rl __maybe_unused,
425 struct rb_node *nd)
426{
427 struct mep *me = container_of(nd, struct mep, nd);
428
429 zfree(&me->metric_group);
430 free(me);
431}
432
433static struct mep *mep_lookup(struct rblist *groups, const char *metric_group,
434 const char *metric_name)
435{
436 struct rb_node *nd;
437 struct mep me = {
438 .metric_group = strdup(metric_group),
439 .metric_name = metric_name,
440 };
441 nd = rblist__find(groups, &me);
442 if (nd) {
443 free(me.metric_group);
444 return container_of(nd, struct mep, nd);
445 }
446 rblist__add_node(groups, &me);
447 nd = rblist__find(groups, &me);
448 if (nd)
449 return container_of(nd, struct mep, nd);
450 return NULL;
451}
452
453static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm,
454 struct rblist *groups)
455{
456 const char *g;
457 char *omg, *mg;
458
459 mg = strdup(pm->metric_group ?: pm->metric_name);
460 if (!mg)
461 return -ENOMEM;
462 omg = mg;
463 while ((g = strsep(&mg, ";")) != NULL) {
464 struct mep *me;
465
466 g = skip_spaces(g);
467 if (strlen(g))
468 me = mep_lookup(groups, g, pm->metric_name);
469 else
470 me = mep_lookup(groups, pm->metric_name, pm->metric_name);
471
472 if (me) {
473 me->metric_desc = pm->desc;
474 me->metric_long_desc = pm->long_desc;
475 me->metric_expr = pm->metric_expr;
476 me->metric_threshold = pm->metric_threshold;
477 me->metric_unit = pm->unit;
478 }
479 }
480 free(omg);
481
482 return 0;
483}
484
485struct metricgroup_iter_data {
486 pmu_metric_iter_fn fn;
487 void *data;
488};
489
490static int metricgroup__sys_event_iter(const struct pmu_metric *pm,
491 const struct pmu_metrics_table *table,
492 void *data)
493{
494 struct metricgroup_iter_data *d = data;
495 struct perf_pmu *pmu = NULL;
496
497 if (!pm->metric_expr || !pm->compat)
498 return 0;
499
500 while ((pmu = perf_pmus__scan(pmu))) {
501
502 if (!pmu->id || !pmu_uncore_identifier_match(pm->compat, pmu->id))
503 continue;
504
505 return d->fn(pm, table, d->data);
506 }
507 return 0;
508}
509
510static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm,
511 const struct pmu_metrics_table *table __maybe_unused,
512 void *vdata)
513{
514 struct rblist *groups = vdata;
515
516 return metricgroup__add_to_mep_groups(pm, groups);
517}
518
519void metricgroup__print(const struct print_callbacks *print_cb, void *print_state)
520{
521 struct rblist groups;
522 const struct pmu_metrics_table *table;
523 struct rb_node *node, *next;
524
525 rblist__init(&groups);
526 groups.node_new = mep_new;
527 groups.node_cmp = mep_cmp;
528 groups.node_delete = mep_delete;
529 table = pmu_metrics_table__find();
530 if (table) {
531 pmu_metrics_table__for_each_metric(table,
532 metricgroup__add_to_mep_groups_callback,
533 &groups);
534 }
535 {
536 struct metricgroup_iter_data data = {
537 .fn = metricgroup__add_to_mep_groups_callback,
538 .data = &groups,
539 };
540 pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
541 }
542
543 for (node = rb_first_cached(&groups.entries); node; node = next) {
544 struct mep *me = container_of(node, struct mep, nd);
545
546 print_cb->print_metric(print_state,
547 me->metric_group,
548 me->metric_name,
549 me->metric_desc,
550 me->metric_long_desc,
551 me->metric_expr,
552 me->metric_threshold,
553 me->metric_unit);
554 next = rb_next(node);
555 rblist__remove_node(&groups, node);
556 }
557}
558
559static const char *code_characters = ",-=@";
560
561static int encode_metric_id(struct strbuf *sb, const char *x)
562{
563 char *c;
564 int ret = 0;
565
566 for (; *x; x++) {
567 c = strchr(code_characters, *x);
568 if (c) {
569 ret = strbuf_addch(sb, '!');
570 if (ret)
571 break;
572
573 ret = strbuf_addch(sb, '0' + (c - code_characters));
574 if (ret)
575 break;
576 } else {
577 ret = strbuf_addch(sb, *x);
578 if (ret)
579 break;
580 }
581 }
582 return ret;
583}
584
585static int decode_metric_id(struct strbuf *sb, const char *x)
586{
587 const char *orig = x;
588 size_t i;
589 char c;
590 int ret;
591
592 for (; *x; x++) {
593 c = *x;
594 if (*x == '!') {
595 x++;
596 i = *x - '0';
597 if (i > strlen(code_characters)) {
598 pr_err("Bad metric-id encoding in: '%s'", orig);
599 return -1;
600 }
601 c = code_characters[i];
602 }
603 ret = strbuf_addch(sb, c);
604 if (ret)
605 return ret;
606 }
607 return 0;
608}
609
610static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifier)
611{
612 struct evsel *ev;
613 struct strbuf sb = STRBUF_INIT;
614 char *cur;
615 int ret = 0;
616
617 evlist__for_each_entry(perf_evlist, ev) {
618 if (!ev->metric_id)
619 continue;
620
621 ret = strbuf_setlen(&sb, 0);
622 if (ret)
623 break;
624
625 ret = decode_metric_id(&sb, ev->metric_id);
626 if (ret)
627 break;
628
629 free((char *)ev->metric_id);
630 ev->metric_id = strdup(sb.buf);
631 if (!ev->metric_id) {
632 ret = -ENOMEM;
633 break;
634 }
635 /*
636 * If the name is just the parsed event, use the metric-id to
637 * give a more friendly display version.
638 */
639 if (strstr(ev->name, "metric-id=")) {
640 bool has_slash = false;
641
642 zfree(&ev->name);
643 for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) {
644 *cur = '/';
645 has_slash = true;
646 }
647
648 if (modifier) {
649 if (!has_slash && !strchr(sb.buf, ':')) {
650 ret = strbuf_addch(&sb, ':');
651 if (ret)
652 break;
653 }
654 ret = strbuf_addstr(&sb, modifier);
655 if (ret)
656 break;
657 }
658 ev->name = strdup(sb.buf);
659 if (!ev->name) {
660 ret = -ENOMEM;
661 break;
662 }
663 }
664 }
665 strbuf_release(&sb);
666 return ret;
667}
668
669static int metricgroup__build_event_string(struct strbuf *events,
670 const struct expr_parse_ctx *ctx,
671 const char *modifier,
672 bool group_events)
673{
674 struct hashmap_entry *cur;
675 size_t bkt;
676 bool no_group = true, has_tool_events = false;
677 bool tool_events[TOOL_PMU__EVENT_MAX] = {false};
678 int ret = 0;
679
680#define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0)
681
682 hashmap__for_each_entry(ctx->ids, cur, bkt) {
683 const char *sep, *rsep, *id = cur->pkey;
684 enum tool_pmu_event ev;
685
686 pr_debug("found event %s\n", id);
687
688 /* Always move tool events outside of the group. */
689 ev = tool_pmu__str_to_event(id);
690 if (ev != TOOL_PMU__EVENT_NONE) {
691 has_tool_events = true;
692 tool_events[ev] = true;
693 continue;
694 }
695 /* Separate events with commas and open the group if necessary. */
696 if (no_group) {
697 if (group_events) {
698 ret = strbuf_addch(events, '{');
699 RETURN_IF_NON_ZERO(ret);
700 }
701
702 no_group = false;
703 } else {
704 ret = strbuf_addch(events, ',');
705 RETURN_IF_NON_ZERO(ret);
706 }
707 /*
708 * Encode the ID as an event string. Add a qualifier for
709 * metric_id that is the original name except with characters
710 * that parse-events can't parse replaced. For example,
711 * 'msr@tsc@' gets added as msr/tsc,metric-id=msr!3tsc!3/
712 */
713 sep = strchr(id, '@');
714 if (sep != NULL) {
715 ret = strbuf_add(events, id, sep - id);
716 RETURN_IF_NON_ZERO(ret);
717 ret = strbuf_addch(events, '/');
718 RETURN_IF_NON_ZERO(ret);
719 rsep = strrchr(sep, '@');
720 ret = strbuf_add(events, sep + 1, rsep - sep - 1);
721 RETURN_IF_NON_ZERO(ret);
722 ret = strbuf_addstr(events, ",metric-id=");
723 RETURN_IF_NON_ZERO(ret);
724 sep = rsep;
725 } else {
726 sep = strchr(id, ':');
727 if (sep != NULL) {
728 ret = strbuf_add(events, id, sep - id);
729 RETURN_IF_NON_ZERO(ret);
730 } else {
731 ret = strbuf_addstr(events, id);
732 RETURN_IF_NON_ZERO(ret);
733 }
734 ret = strbuf_addstr(events, "/metric-id=");
735 RETURN_IF_NON_ZERO(ret);
736 }
737 ret = encode_metric_id(events, id);
738 RETURN_IF_NON_ZERO(ret);
739 ret = strbuf_addstr(events, "/");
740 RETURN_IF_NON_ZERO(ret);
741
742 if (sep != NULL) {
743 ret = strbuf_addstr(events, sep + 1);
744 RETURN_IF_NON_ZERO(ret);
745 }
746 if (modifier) {
747 ret = strbuf_addstr(events, modifier);
748 RETURN_IF_NON_ZERO(ret);
749 }
750 }
751 if (!no_group && group_events) {
752 ret = strbuf_addf(events, "}:W");
753 RETURN_IF_NON_ZERO(ret);
754 }
755 if (has_tool_events) {
756 int i;
757
758 tool_pmu__for_each_event(i) {
759 if (tool_events[i]) {
760 if (!no_group) {
761 ret = strbuf_addch(events, ',');
762 RETURN_IF_NON_ZERO(ret);
763 }
764 no_group = false;
765 ret = strbuf_addstr(events, tool_pmu__event_to_str(i));
766 RETURN_IF_NON_ZERO(ret);
767 }
768 }
769 }
770
771 return ret;
772#undef RETURN_IF_NON_ZERO
773}
774
775int __weak arch_get_runtimeparam(const struct pmu_metric *pm __maybe_unused)
776{
777 return 1;
778}
779
780/*
781 * A singly linked list on the stack of the names of metrics being
782 * processed. Used to identify recursion.
783 */
784struct visited_metric {
785 const char *name;
786 const struct visited_metric *parent;
787};
788
789struct metricgroup_add_iter_data {
790 struct list_head *metric_list;
791 const char *pmu;
792 const char *metric_name;
793 const char *modifier;
794 int *ret;
795 bool *has_match;
796 bool metric_no_group;
797 bool metric_no_threshold;
798 const char *user_requested_cpu_list;
799 bool system_wide;
800 struct metric *root_metric;
801 const struct visited_metric *visited;
802 const struct pmu_metrics_table *table;
803};
804
805static bool metricgroup__find_metric(const char *pmu,
806 const char *metric,
807 const struct pmu_metrics_table *table,
808 struct pmu_metric *pm);
809
810static int add_metric(struct list_head *metric_list,
811 const struct pmu_metric *pm,
812 const char *modifier,
813 bool metric_no_group,
814 bool metric_no_threshold,
815 const char *user_requested_cpu_list,
816 bool system_wide,
817 struct metric *root_metric,
818 const struct visited_metric *visited,
819 const struct pmu_metrics_table *table);
820
821/**
822 * resolve_metric - Locate metrics within the root metric and recursively add
823 * references to them.
824 * @metric_list: The list the metric is added to.
825 * @pmu: The PMU name to resolve metrics on, or "all" for all PMUs.
826 * @modifier: if non-null event modifiers like "u".
827 * @metric_no_group: Should events written to events be grouped "{}" or
828 * global. Grouping is the default but due to multiplexing the
829 * user may override.
830 * @user_requested_cpu_list: Command line specified CPUs to record on.
831 * @system_wide: Are events for all processes recorded.
832 * @root_metric: Metrics may reference other metrics to form a tree. In this
833 * case the root_metric holds all the IDs and a list of referenced
834 * metrics. When adding a root this argument is NULL.
835 * @visited: A singly linked list of metric names being added that is used to
836 * detect recursion.
837 * @table: The table that is searched for metrics, most commonly the table for the
838 * architecture perf is running upon.
839 */
840static int resolve_metric(struct list_head *metric_list,
841 const char *pmu,
842 const char *modifier,
843 bool metric_no_group,
844 bool metric_no_threshold,
845 const char *user_requested_cpu_list,
846 bool system_wide,
847 struct metric *root_metric,
848 const struct visited_metric *visited,
849 const struct pmu_metrics_table *table)
850{
851 struct hashmap_entry *cur;
852 size_t bkt;
853 struct to_resolve {
854 /* The metric to resolve. */
855 struct pmu_metric pm;
856 /*
857 * The key in the IDs map, this may differ from in case,
858 * etc. from pm->metric_name.
859 */
860 const char *key;
861 } *pending = NULL;
862 int i, ret = 0, pending_cnt = 0;
863
864 /*
865 * Iterate all the parsed IDs and if there's a matching metric and it to
866 * the pending array.
867 */
868 hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
869 struct pmu_metric pm;
870
871 if (metricgroup__find_metric(pmu, cur->pkey, table, &pm)) {
872 pending = realloc(pending,
873 (pending_cnt + 1) * sizeof(struct to_resolve));
874 if (!pending)
875 return -ENOMEM;
876
877 memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm));
878 pending[pending_cnt].key = cur->pkey;
879 pending_cnt++;
880 }
881 }
882
883 /* Remove the metric IDs from the context. */
884 for (i = 0; i < pending_cnt; i++)
885 expr__del_id(root_metric->pctx, pending[i].key);
886
887 /*
888 * Recursively add all the metrics, IDs are added to the root metric's
889 * context.
890 */
891 for (i = 0; i < pending_cnt; i++) {
892 ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group,
893 metric_no_threshold, user_requested_cpu_list, system_wide,
894 root_metric, visited, table);
895 if (ret)
896 break;
897 }
898
899 free(pending);
900 return ret;
901}
902
903/**
904 * __add_metric - Add a metric to metric_list.
905 * @metric_list: The list the metric is added to.
906 * @pm: The pmu_metric containing the metric to be added.
907 * @modifier: if non-null event modifiers like "u".
908 * @metric_no_group: Should events written to events be grouped "{}" or
909 * global. Grouping is the default but due to multiplexing the
910 * user may override.
911 * @metric_no_threshold: Should threshold expressions be ignored?
912 * @runtime: A special argument for the parser only known at runtime.
913 * @user_requested_cpu_list: Command line specified CPUs to record on.
914 * @system_wide: Are events for all processes recorded.
915 * @root_metric: Metrics may reference other metrics to form a tree. In this
916 * case the root_metric holds all the IDs and a list of referenced
917 * metrics. When adding a root this argument is NULL.
918 * @visited: A singly linked list of metric names being added that is used to
919 * detect recursion.
920 * @table: The table that is searched for metrics, most commonly the table for the
921 * architecture perf is running upon.
922 */
923static int __add_metric(struct list_head *metric_list,
924 const struct pmu_metric *pm,
925 const char *modifier,
926 bool metric_no_group,
927 bool metric_no_threshold,
928 int runtime,
929 const char *user_requested_cpu_list,
930 bool system_wide,
931 struct metric *root_metric,
932 const struct visited_metric *visited,
933 const struct pmu_metrics_table *table)
934{
935 const struct visited_metric *vm;
936 int ret;
937 bool is_root = !root_metric;
938 const char *expr;
939 struct visited_metric visited_node = {
940 .name = pm->metric_name,
941 .parent = visited,
942 };
943
944 for (vm = visited; vm; vm = vm->parent) {
945 if (!strcmp(pm->metric_name, vm->name)) {
946 pr_err("failed: recursion detected for %s\n", pm->metric_name);
947 return -1;
948 }
949 }
950
951 if (is_root) {
952 /*
953 * This metric is the root of a tree and may reference other
954 * metrics that are added recursively.
955 */
956 root_metric = metric__new(pm, modifier, metric_no_group, runtime,
957 user_requested_cpu_list, system_wide);
958 if (!root_metric)
959 return -ENOMEM;
960
961 } else {
962 int cnt = 0;
963
964 /*
965 * This metric was referenced in a metric higher in the
966 * tree. Check if the same metric is already resolved in the
967 * metric_refs list.
968 */
969 if (root_metric->metric_refs) {
970 for (; root_metric->metric_refs[cnt].metric_name; cnt++) {
971 if (!strcmp(pm->metric_name,
972 root_metric->metric_refs[cnt].metric_name))
973 return 0;
974 }
975 }
976
977 /* Create reference. Need space for the entry and the terminator. */
978 root_metric->metric_refs = realloc(root_metric->metric_refs,
979 (cnt + 2) * sizeof(struct metric_ref));
980 if (!root_metric->metric_refs)
981 return -ENOMEM;
982
983 /*
984 * Intentionally passing just const char pointers,
985 * from 'pe' object, so they never go away. We don't
986 * need to change them, so there's no need to create
987 * our own copy.
988 */
989 root_metric->metric_refs[cnt].metric_name = pm->metric_name;
990 root_metric->metric_refs[cnt].metric_expr = pm->metric_expr;
991
992 /* Null terminate array. */
993 root_metric->metric_refs[cnt+1].metric_name = NULL;
994 root_metric->metric_refs[cnt+1].metric_expr = NULL;
995 }
996
997 /*
998 * For both the parent and referenced metrics, we parse
999 * all the metric's IDs and add it to the root context.
1000 */
1001 ret = 0;
1002 expr = pm->metric_expr;
1003 if (is_root && pm->metric_threshold) {
1004 /*
1005 * Threshold expressions are built off the actual metric. Switch
1006 * to use that in case of additional necessary events. Change
1007 * the visited node name to avoid this being flagged as
1008 * recursion. If the threshold events are disabled, just use the
1009 * metric's name as a reference. This allows metric threshold
1010 * computation if there are sufficient events.
1011 */
1012 assert(strstr(pm->metric_threshold, pm->metric_name));
1013 expr = metric_no_threshold ? pm->metric_name : pm->metric_threshold;
1014 visited_node.name = "__threshold__";
1015 }
1016 if (expr__find_ids(expr, NULL, root_metric->pctx) < 0) {
1017 /* Broken metric. */
1018 ret = -EINVAL;
1019 }
1020 if (!ret) {
1021 /* Resolve referenced metrics. */
1022 const char *pmu = pm->pmu ?: "cpu";
1023
1024 ret = resolve_metric(metric_list, pmu, modifier, metric_no_group,
1025 metric_no_threshold, user_requested_cpu_list,
1026 system_wide, root_metric, &visited_node,
1027 table);
1028 }
1029 if (ret) {
1030 if (is_root)
1031 metric__free(root_metric);
1032
1033 } else if (is_root)
1034 list_add(&root_metric->nd, metric_list);
1035
1036 return ret;
1037}
1038
1039struct metricgroup__find_metric_data {
1040 const char *pmu;
1041 const char *metric;
1042 struct pmu_metric *pm;
1043};
1044
1045static int metricgroup__find_metric_callback(const struct pmu_metric *pm,
1046 const struct pmu_metrics_table *table __maybe_unused,
1047 void *vdata)
1048{
1049 struct metricgroup__find_metric_data *data = vdata;
1050 const char *pm_pmu = pm->pmu ?: "cpu";
1051
1052 if (strcmp(data->pmu, "all") && strcmp(pm_pmu, data->pmu))
1053 return 0;
1054
1055 if (!match_metric(pm->metric_name, data->metric))
1056 return 0;
1057
1058 memcpy(data->pm, pm, sizeof(*pm));
1059 return 1;
1060}
1061
1062static bool metricgroup__find_metric(const char *pmu,
1063 const char *metric,
1064 const struct pmu_metrics_table *table,
1065 struct pmu_metric *pm)
1066{
1067 struct metricgroup__find_metric_data data = {
1068 .pmu = pmu,
1069 .metric = metric,
1070 .pm = pm,
1071 };
1072
1073 return pmu_metrics_table__for_each_metric(table, metricgroup__find_metric_callback, &data)
1074 ? true : false;
1075}
1076
1077static int add_metric(struct list_head *metric_list,
1078 const struct pmu_metric *pm,
1079 const char *modifier,
1080 bool metric_no_group,
1081 bool metric_no_threshold,
1082 const char *user_requested_cpu_list,
1083 bool system_wide,
1084 struct metric *root_metric,
1085 const struct visited_metric *visited,
1086 const struct pmu_metrics_table *table)
1087{
1088 int ret = 0;
1089
1090 pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name);
1091
1092 if (!strstr(pm->metric_expr, "?")) {
1093 ret = __add_metric(metric_list, pm, modifier, metric_no_group,
1094 metric_no_threshold, 0, user_requested_cpu_list,
1095 system_wide, root_metric, visited, table);
1096 } else {
1097 int j, count;
1098
1099 count = arch_get_runtimeparam(pm);
1100
1101 /* This loop is added to create multiple
1102 * events depend on count value and add
1103 * those events to metric_list.
1104 */
1105
1106 for (j = 0; j < count && !ret; j++)
1107 ret = __add_metric(metric_list, pm, modifier, metric_no_group,
1108 metric_no_threshold, j, user_requested_cpu_list,
1109 system_wide, root_metric, visited, table);
1110 }
1111
1112 return ret;
1113}
1114
1115static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm,
1116 const struct pmu_metrics_table *table __maybe_unused,
1117 void *data)
1118{
1119 struct metricgroup_add_iter_data *d = data;
1120 int ret;
1121
1122 if (!match_pm_metric(pm, d->pmu, d->metric_name))
1123 return 0;
1124
1125 ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group,
1126 d->metric_no_threshold, d->user_requested_cpu_list,
1127 d->system_wide, d->root_metric, d->visited, d->table);
1128 if (ret)
1129 goto out;
1130
1131 *(d->has_match) = true;
1132
1133out:
1134 *(d->ret) = ret;
1135 return ret;
1136}
1137
1138/**
1139 * metric_list_cmp - list_sort comparator that sorts metrics with more events to
1140 * the front. tool events are excluded from the count.
1141 */
1142static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
1143 const struct list_head *r)
1144{
1145 const struct metric *left = container_of(l, struct metric, nd);
1146 const struct metric *right = container_of(r, struct metric, nd);
1147 struct expr_id_data *data;
1148 int i, left_count, right_count;
1149
1150 left_count = hashmap__size(left->pctx->ids);
1151 tool_pmu__for_each_event(i) {
1152 if (!expr__get_id(left->pctx, tool_pmu__event_to_str(i), &data))
1153 left_count--;
1154 }
1155
1156 right_count = hashmap__size(right->pctx->ids);
1157 tool_pmu__for_each_event(i) {
1158 if (!expr__get_id(right->pctx, tool_pmu__event_to_str(i), &data))
1159 right_count--;
1160 }
1161
1162 return right_count - left_count;
1163}
1164
1165/**
1166 * default_metricgroup_cmp - Implements complex key for the Default metricgroup
1167 * that first sorts by default_metricgroup_name, then
1168 * metric_name.
1169 */
1170static int default_metricgroup_cmp(void *priv __maybe_unused,
1171 const struct list_head *l,
1172 const struct list_head *r)
1173{
1174 const struct metric *left = container_of(l, struct metric, nd);
1175 const struct metric *right = container_of(r, struct metric, nd);
1176 int diff = strcmp(right->default_metricgroup_name, left->default_metricgroup_name);
1177
1178 if (diff)
1179 return diff;
1180
1181 return strcmp(right->metric_name, left->metric_name);
1182}
1183
1184struct metricgroup__add_metric_data {
1185 struct list_head *list;
1186 const char *pmu;
1187 const char *metric_name;
1188 const char *modifier;
1189 const char *user_requested_cpu_list;
1190 bool metric_no_group;
1191 bool metric_no_threshold;
1192 bool system_wide;
1193 bool has_match;
1194};
1195
1196static int metricgroup__add_metric_callback(const struct pmu_metric *pm,
1197 const struct pmu_metrics_table *table,
1198 void *vdata)
1199{
1200 struct metricgroup__add_metric_data *data = vdata;
1201 int ret = 0;
1202
1203 if (pm->metric_expr && match_pm_metric(pm, data->pmu, data->metric_name)) {
1204 bool metric_no_group = data->metric_no_group ||
1205 match_metric(pm->metricgroup_no_group, data->metric_name);
1206
1207 data->has_match = true;
1208 ret = add_metric(data->list, pm, data->modifier, metric_no_group,
1209 data->metric_no_threshold, data->user_requested_cpu_list,
1210 data->system_wide, /*root_metric=*/NULL,
1211 /*visited_metrics=*/NULL, table);
1212 }
1213 return ret;
1214}
1215
1216/**
1217 * metricgroup__add_metric - Find and add a metric, or a metric group.
1218 * @pmu: The PMU name to search for metrics on, or "all" for all PMUs.
1219 * @metric_name: The name of the metric or metric group. For example, "IPC"
1220 * could be the name of a metric and "TopDownL1" the name of a
1221 * metric group.
1222 * @modifier: if non-null event modifiers like "u".
1223 * @metric_no_group: Should events written to events be grouped "{}" or
1224 * global. Grouping is the default but due to multiplexing the
1225 * user may override.
1226 * @user_requested_cpu_list: Command line specified CPUs to record on.
1227 * @system_wide: Are events for all processes recorded.
1228 * @metric_list: The list that the metric or metric group are added to.
1229 * @table: The table that is searched for metrics, most commonly the table for the
1230 * architecture perf is running upon.
1231 */
1232static int metricgroup__add_metric(const char *pmu, const char *metric_name, const char *modifier,
1233 bool metric_no_group, bool metric_no_threshold,
1234 const char *user_requested_cpu_list,
1235 bool system_wide,
1236 struct list_head *metric_list,
1237 const struct pmu_metrics_table *table)
1238{
1239 LIST_HEAD(list);
1240 int ret;
1241 bool has_match = false;
1242
1243 {
1244 struct metricgroup__add_metric_data data = {
1245 .list = &list,
1246 .pmu = pmu,
1247 .metric_name = metric_name,
1248 .modifier = modifier,
1249 .metric_no_group = metric_no_group,
1250 .metric_no_threshold = metric_no_threshold,
1251 .user_requested_cpu_list = user_requested_cpu_list,
1252 .system_wide = system_wide,
1253 .has_match = false,
1254 };
1255 /*
1256 * Iterate over all metrics seeing if metric matches either the
1257 * name or group. When it does add the metric to the list.
1258 */
1259 ret = pmu_metrics_table__for_each_metric(table, metricgroup__add_metric_callback,
1260 &data);
1261 if (ret)
1262 goto out;
1263
1264 has_match = data.has_match;
1265 }
1266 {
1267 struct metricgroup_iter_data data = {
1268 .fn = metricgroup__add_metric_sys_event_iter,
1269 .data = (void *) &(struct metricgroup_add_iter_data) {
1270 .metric_list = &list,
1271 .pmu = pmu,
1272 .metric_name = metric_name,
1273 .modifier = modifier,
1274 .metric_no_group = metric_no_group,
1275 .user_requested_cpu_list = user_requested_cpu_list,
1276 .system_wide = system_wide,
1277 .has_match = &has_match,
1278 .ret = &ret,
1279 .table = table,
1280 },
1281 };
1282
1283 pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
1284 }
1285 /* End of pmu events. */
1286 if (!has_match)
1287 ret = -EINVAL;
1288
1289out:
1290 /*
1291 * add to metric_list so that they can be released
1292 * even if it's failed
1293 */
1294 list_splice(&list, metric_list);
1295 return ret;
1296}
1297
1298/**
1299 * metricgroup__add_metric_list - Find and add metrics, or metric groups,
1300 * specified in a list.
1301 * @pmu: A pmu to restrict the metrics to, or "all" for all PMUS.
1302 * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1"
1303 * would match the IPC and CPI metrics, and TopDownL1 would match all
1304 * the metrics in the TopDownL1 group.
1305 * @metric_no_group: Should events written to events be grouped "{}" or
1306 * global. Grouping is the default but due to multiplexing the
1307 * user may override.
1308 * @user_requested_cpu_list: Command line specified CPUs to record on.
1309 * @system_wide: Are events for all processes recorded.
1310 * @metric_list: The list that metrics are added to.
1311 * @table: The table that is searched for metrics, most commonly the table for the
1312 * architecture perf is running upon.
1313 */
1314static int metricgroup__add_metric_list(const char *pmu, const char *list,
1315 bool metric_no_group,
1316 bool metric_no_threshold,
1317 const char *user_requested_cpu_list,
1318 bool system_wide, struct list_head *metric_list,
1319 const struct pmu_metrics_table *table)
1320{
1321 char *list_itr, *list_copy, *metric_name, *modifier;
1322 int ret, count = 0;
1323
1324 list_copy = strdup(list);
1325 if (!list_copy)
1326 return -ENOMEM;
1327 list_itr = list_copy;
1328
1329 while ((metric_name = strsep(&list_itr, ",")) != NULL) {
1330 modifier = strchr(metric_name, ':');
1331 if (modifier)
1332 *modifier++ = '\0';
1333
1334 ret = metricgroup__add_metric(pmu, metric_name, modifier,
1335 metric_no_group, metric_no_threshold,
1336 user_requested_cpu_list,
1337 system_wide, metric_list, table);
1338 if (ret == -EINVAL)
1339 pr_err("Cannot find metric or group `%s'\n", metric_name);
1340
1341 if (ret)
1342 break;
1343
1344 count++;
1345 }
1346 free(list_copy);
1347
1348 if (!ret) {
1349 /*
1350 * Warn about nmi_watchdog if any parsed metrics had the
1351 * NO_NMI_WATCHDOG constraint.
1352 */
1353 metric__watchdog_constraint_hint(NULL, /*foot=*/true);
1354 /* No metrics. */
1355 if (count == 0)
1356 return -EINVAL;
1357 }
1358 return ret;
1359}
1360
1361static void metricgroup__free_metrics(struct list_head *metric_list)
1362{
1363 struct metric *m, *tmp;
1364
1365 list_for_each_entry_safe (m, tmp, metric_list, nd) {
1366 list_del_init(&m->nd);
1367 metric__free(m);
1368 }
1369}
1370
1371/**
1372 * find_tool_events - Search for the pressence of tool events in metric_list.
1373 * @metric_list: List to take metrics from.
1374 * @tool_events: Array of false values, indices corresponding to tool events set
1375 * to true if tool event is found.
1376 */
1377static void find_tool_events(const struct list_head *metric_list,
1378 bool tool_events[TOOL_PMU__EVENT_MAX])
1379{
1380 struct metric *m;
1381
1382 list_for_each_entry(m, metric_list, nd) {
1383 int i;
1384
1385 tool_pmu__for_each_event(i) {
1386 struct expr_id_data *data;
1387
1388 if (!tool_events[i] &&
1389 !expr__get_id(m->pctx, tool_pmu__event_to_str(i), &data))
1390 tool_events[i] = true;
1391 }
1392 }
1393}
1394
1395/**
1396 * build_combined_expr_ctx - Make an expr_parse_ctx with all !group_events
1397 * metric IDs, as the IDs are held in a set,
1398 * duplicates will be removed.
1399 * @metric_list: List to take metrics from.
1400 * @combined: Out argument for result.
1401 */
1402static int build_combined_expr_ctx(const struct list_head *metric_list,
1403 struct expr_parse_ctx **combined)
1404{
1405 struct hashmap_entry *cur;
1406 size_t bkt;
1407 struct metric *m;
1408 char *dup;
1409 int ret;
1410
1411 *combined = expr__ctx_new();
1412 if (!*combined)
1413 return -ENOMEM;
1414
1415 list_for_each_entry(m, metric_list, nd) {
1416 if (!m->group_events && !m->modifier) {
1417 hashmap__for_each_entry(m->pctx->ids, cur, bkt) {
1418 dup = strdup(cur->pkey);
1419 if (!dup) {
1420 ret = -ENOMEM;
1421 goto err_out;
1422 }
1423 ret = expr__add_id(*combined, dup);
1424 if (ret)
1425 goto err_out;
1426 }
1427 }
1428 }
1429 return 0;
1430err_out:
1431 expr__ctx_free(*combined);
1432 *combined = NULL;
1433 return ret;
1434}
1435
1436/**
1437 * parse_ids - Build the event string for the ids and parse them creating an
1438 * evlist. The encoded metric_ids are decoded.
1439 * @metric_no_merge: is metric sharing explicitly disabled.
1440 * @fake_pmu: use a fake PMU when testing metrics not supported by the current CPU.
1441 * @ids: the event identifiers parsed from a metric.
1442 * @modifier: any modifiers added to the events.
1443 * @group_events: should events be placed in a weak group.
1444 * @tool_events: entries set true if the tool event of index could be present in
1445 * the overall list of metrics.
1446 * @out_evlist: the created list of events.
1447 */
1448static int parse_ids(bool metric_no_merge, bool fake_pmu,
1449 struct expr_parse_ctx *ids, const char *modifier,
1450 bool group_events, const bool tool_events[TOOL_PMU__EVENT_MAX],
1451 struct evlist **out_evlist)
1452{
1453 struct parse_events_error parse_error;
1454 struct evlist *parsed_evlist;
1455 struct strbuf events = STRBUF_INIT;
1456 int ret;
1457
1458 *out_evlist = NULL;
1459 if (!metric_no_merge || hashmap__size(ids->ids) == 0) {
1460 bool added_event = false;
1461 int i;
1462 /*
1463 * We may fail to share events between metrics because a tool
1464 * event isn't present in one metric. For example, a ratio of
1465 * cache misses doesn't need duration_time but the same events
1466 * may be used for a misses per second. Events without sharing
1467 * implies multiplexing, that is best avoided, so place
1468 * all tool events in every group.
1469 *
1470 * Also, there may be no ids/events in the expression parsing
1471 * context because of constant evaluation, e.g.:
1472 * event1 if #smt_on else 0
1473 * Add a tool event to avoid a parse error on an empty string.
1474 */
1475 tool_pmu__for_each_event(i) {
1476 if (tool_events[i]) {
1477 char *tmp = strdup(tool_pmu__event_to_str(i));
1478
1479 if (!tmp)
1480 return -ENOMEM;
1481 ids__insert(ids->ids, tmp);
1482 added_event = true;
1483 }
1484 }
1485 if (!added_event && hashmap__size(ids->ids) == 0) {
1486 char *tmp = strdup("duration_time");
1487
1488 if (!tmp)
1489 return -ENOMEM;
1490 ids__insert(ids->ids, tmp);
1491 }
1492 }
1493 ret = metricgroup__build_event_string(&events, ids, modifier,
1494 group_events);
1495 if (ret)
1496 return ret;
1497
1498 parsed_evlist = evlist__new();
1499 if (!parsed_evlist) {
1500 ret = -ENOMEM;
1501 goto err_out;
1502 }
1503 pr_debug("Parsing metric events '%s'\n", events.buf);
1504 parse_events_error__init(&parse_error);
1505 ret = __parse_events(parsed_evlist, events.buf, /*pmu_filter=*/NULL,
1506 &parse_error, fake_pmu, /*warn_if_reordered=*/false,
1507 /*fake_tp=*/false);
1508 if (ret) {
1509 parse_events_error__print(&parse_error, events.buf);
1510 goto err_out;
1511 }
1512 ret = decode_all_metric_ids(parsed_evlist, modifier);
1513 if (ret)
1514 goto err_out;
1515
1516 *out_evlist = parsed_evlist;
1517 parsed_evlist = NULL;
1518err_out:
1519 parse_events_error__exit(&parse_error);
1520 evlist__delete(parsed_evlist);
1521 strbuf_release(&events);
1522 return ret;
1523}
1524
1525static int parse_groups(struct evlist *perf_evlist,
1526 const char *pmu, const char *str,
1527 bool metric_no_group,
1528 bool metric_no_merge,
1529 bool metric_no_threshold,
1530 const char *user_requested_cpu_list,
1531 bool system_wide,
1532 bool fake_pmu,
1533 struct rblist *metric_events_list,
1534 const struct pmu_metrics_table *table)
1535{
1536 struct evlist *combined_evlist = NULL;
1537 LIST_HEAD(metric_list);
1538 struct metric *m;
1539 bool tool_events[TOOL_PMU__EVENT_MAX] = {false};
1540 bool is_default = !strcmp(str, "Default");
1541 int ret;
1542
1543 if (metric_events_list->nr_entries == 0)
1544 metricgroup__rblist_init(metric_events_list);
1545 ret = metricgroup__add_metric_list(pmu, str, metric_no_group, metric_no_threshold,
1546 user_requested_cpu_list,
1547 system_wide, &metric_list, table);
1548 if (ret)
1549 goto out;
1550
1551 /* Sort metrics from largest to smallest. */
1552 list_sort(NULL, &metric_list, metric_list_cmp);
1553
1554 if (!metric_no_merge) {
1555 struct expr_parse_ctx *combined = NULL;
1556
1557 find_tool_events(&metric_list, tool_events);
1558
1559 ret = build_combined_expr_ctx(&metric_list, &combined);
1560
1561 if (!ret && combined && hashmap__size(combined->ids)) {
1562 ret = parse_ids(metric_no_merge, fake_pmu, combined,
1563 /*modifier=*/NULL,
1564 /*group_events=*/false,
1565 tool_events,
1566 &combined_evlist);
1567 }
1568 if (combined)
1569 expr__ctx_free(combined);
1570
1571 if (ret)
1572 goto out;
1573 }
1574
1575 if (is_default)
1576 list_sort(NULL, &metric_list, default_metricgroup_cmp);
1577
1578 list_for_each_entry(m, &metric_list, nd) {
1579 struct metric_event *me;
1580 struct evsel **metric_events;
1581 struct evlist *metric_evlist = NULL;
1582 struct metric *n;
1583 struct metric_expr *expr;
1584
1585 if (combined_evlist && !m->group_events) {
1586 metric_evlist = combined_evlist;
1587 } else if (!metric_no_merge) {
1588 /*
1589 * See if the IDs for this metric are a subset of an
1590 * earlier metric.
1591 */
1592 list_for_each_entry(n, &metric_list, nd) {
1593 if (m == n)
1594 break;
1595
1596 if (n->evlist == NULL)
1597 continue;
1598
1599 if ((!m->modifier && n->modifier) ||
1600 (m->modifier && !n->modifier) ||
1601 (m->modifier && n->modifier &&
1602 strcmp(m->modifier, n->modifier)))
1603 continue;
1604
1605 if ((!m->pmu && n->pmu) ||
1606 (m->pmu && !n->pmu) ||
1607 (m->pmu && n->pmu && strcmp(m->pmu, n->pmu)))
1608 continue;
1609
1610 if (expr__subset_of_ids(n->pctx, m->pctx)) {
1611 pr_debug("Events in '%s' fully contained within '%s'\n",
1612 m->metric_name, n->metric_name);
1613 metric_evlist = n->evlist;
1614 break;
1615 }
1616
1617 }
1618 }
1619 if (!metric_evlist) {
1620 ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier,
1621 m->group_events, tool_events, &m->evlist);
1622 if (ret)
1623 goto out;
1624
1625 metric_evlist = m->evlist;
1626 }
1627 ret = setup_metric_events(fake_pmu ? "all" : m->pmu, m->pctx->ids,
1628 metric_evlist, &metric_events);
1629 if (ret) {
1630 pr_err("Cannot resolve IDs for %s: %s\n",
1631 m->metric_name, m->metric_expr);
1632 goto out;
1633 }
1634
1635 me = metricgroup__lookup(metric_events_list, metric_events[0], true);
1636
1637 expr = malloc(sizeof(struct metric_expr));
1638 if (!expr) {
1639 ret = -ENOMEM;
1640 free(metric_events);
1641 goto out;
1642 }
1643
1644 expr->metric_refs = m->metric_refs;
1645 m->metric_refs = NULL;
1646 expr->metric_expr = m->metric_expr;
1647 if (m->modifier) {
1648 char *tmp;
1649
1650 if (asprintf(&tmp, "%s:%s", m->metric_name, m->modifier) < 0)
1651 expr->metric_name = NULL;
1652 else
1653 expr->metric_name = tmp;
1654 } else
1655 expr->metric_name = strdup(m->metric_name);
1656
1657 if (!expr->metric_name) {
1658 ret = -ENOMEM;
1659 free(metric_events);
1660 goto out;
1661 }
1662 expr->metric_threshold = m->metric_threshold;
1663 expr->metric_unit = m->metric_unit;
1664 expr->metric_events = metric_events;
1665 expr->runtime = m->pctx->sctx.runtime;
1666 expr->default_metricgroup_name = m->default_metricgroup_name;
1667 me->is_default = is_default;
1668 list_add(&expr->nd, &me->head);
1669 }
1670
1671
1672 if (combined_evlist) {
1673 evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries);
1674 evlist__delete(combined_evlist);
1675 }
1676
1677 list_for_each_entry(m, &metric_list, nd) {
1678 if (m->evlist)
1679 evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries);
1680 }
1681
1682out:
1683 metricgroup__free_metrics(&metric_list);
1684 return ret;
1685}
1686
1687int metricgroup__parse_groups(struct evlist *perf_evlist,
1688 const char *pmu,
1689 const char *str,
1690 bool metric_no_group,
1691 bool metric_no_merge,
1692 bool metric_no_threshold,
1693 const char *user_requested_cpu_list,
1694 bool system_wide,
1695 bool hardware_aware_grouping,
1696 struct rblist *metric_events)
1697{
1698 const struct pmu_metrics_table *table = pmu_metrics_table__find();
1699
1700 if (!table)
1701 return -EINVAL;
1702 if (hardware_aware_grouping)
1703 pr_debug("Use hardware aware grouping instead of traditional metric grouping method\n");
1704
1705 return parse_groups(perf_evlist, pmu, str, metric_no_group, metric_no_merge,
1706 metric_no_threshold, user_requested_cpu_list, system_wide,
1707 /*fake_pmu=*/false, metric_events, table);
1708}
1709
1710int metricgroup__parse_groups_test(struct evlist *evlist,
1711 const struct pmu_metrics_table *table,
1712 const char *str,
1713 struct rblist *metric_events)
1714{
1715 return parse_groups(evlist, "all", str,
1716 /*metric_no_group=*/false,
1717 /*metric_no_merge=*/false,
1718 /*metric_no_threshold=*/false,
1719 /*user_requested_cpu_list=*/NULL,
1720 /*system_wide=*/false,
1721 /*fake_pmu=*/true, metric_events, table);
1722}
1723
1724struct metricgroup__has_metric_data {
1725 const char *pmu;
1726 const char *metric;
1727};
1728static int metricgroup__has_metric_callback(const struct pmu_metric *pm,
1729 const struct pmu_metrics_table *table __maybe_unused,
1730 void *vdata)
1731{
1732 struct metricgroup__has_metric_data *data = vdata;
1733
1734 return match_pm_metric(pm, data->pmu, data->metric) ? 1 : 0;
1735}
1736
1737bool metricgroup__has_metric(const char *pmu, const char *metric)
1738{
1739 const struct pmu_metrics_table *table = pmu_metrics_table__find();
1740 struct metricgroup__has_metric_data data = {
1741 .pmu = pmu,
1742 .metric = metric,
1743 };
1744
1745 if (!table)
1746 return false;
1747
1748 return pmu_metrics_table__for_each_metric(table, metricgroup__has_metric_callback, &data)
1749 ? true : false;
1750}
1751
1752static int metricgroup__topdown_max_level_callback(const struct pmu_metric *pm,
1753 const struct pmu_metrics_table *table __maybe_unused,
1754 void *data)
1755{
1756 unsigned int *max_level = data;
1757 unsigned int level;
1758 const char *p = strstr(pm->metric_group ?: "", "TopdownL");
1759
1760 if (!p || p[8] == '\0')
1761 return 0;
1762
1763 level = p[8] - '0';
1764 if (level > *max_level)
1765 *max_level = level;
1766
1767 return 0;
1768}
1769
1770unsigned int metricgroups__topdown_max_level(void)
1771{
1772 unsigned int max_level = 0;
1773 const struct pmu_metrics_table *table = pmu_metrics_table__find();
1774
1775 if (!table)
1776 return false;
1777
1778 pmu_metrics_table__for_each_metric(table, metricgroup__topdown_max_level_callback,
1779 &max_level);
1780 return max_level;
1781}
1782
1783int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
1784 struct rblist *new_metric_events,
1785 struct rblist *old_metric_events)
1786{
1787 unsigned int i;
1788
1789 for (i = 0; i < rblist__nr_entries(old_metric_events); i++) {
1790 struct rb_node *nd;
1791 struct metric_event *old_me, *new_me;
1792 struct metric_expr *old_expr, *new_expr;
1793 struct evsel *evsel;
1794 size_t alloc_size;
1795 int idx, nr;
1796
1797 nd = rblist__entry(old_metric_events, i);
1798 old_me = container_of(nd, struct metric_event, nd);
1799
1800 evsel = evlist__find_evsel(evlist, old_me->evsel->core.idx);
1801 if (!evsel)
1802 return -EINVAL;
1803 new_me = metricgroup__lookup(new_metric_events, evsel, true);
1804 if (!new_me)
1805 return -ENOMEM;
1806
1807 pr_debug("copying metric event for cgroup '%s': %s (idx=%d)\n",
1808 cgrp ? cgrp->name : "root", evsel->name, evsel->core.idx);
1809
1810 list_for_each_entry(old_expr, &old_me->head, nd) {
1811 new_expr = malloc(sizeof(*new_expr));
1812 if (!new_expr)
1813 return -ENOMEM;
1814
1815 new_expr->metric_expr = old_expr->metric_expr;
1816 new_expr->metric_threshold = old_expr->metric_threshold;
1817 new_expr->metric_name = strdup(old_expr->metric_name);
1818 if (!new_expr->metric_name)
1819 return -ENOMEM;
1820
1821 new_expr->metric_unit = old_expr->metric_unit;
1822 new_expr->runtime = old_expr->runtime;
1823
1824 if (old_expr->metric_refs) {
1825 /* calculate number of metric_events */
1826 for (nr = 0; old_expr->metric_refs[nr].metric_name; nr++)
1827 continue;
1828 alloc_size = sizeof(*new_expr->metric_refs);
1829 new_expr->metric_refs = calloc(nr + 1, alloc_size);
1830 if (!new_expr->metric_refs) {
1831 free(new_expr);
1832 return -ENOMEM;
1833 }
1834
1835 memcpy(new_expr->metric_refs, old_expr->metric_refs,
1836 nr * alloc_size);
1837 } else {
1838 new_expr->metric_refs = NULL;
1839 }
1840
1841 /* calculate number of metric_events */
1842 for (nr = 0; old_expr->metric_events[nr]; nr++)
1843 continue;
1844 alloc_size = sizeof(*new_expr->metric_events);
1845 new_expr->metric_events = calloc(nr + 1, alloc_size);
1846 if (!new_expr->metric_events) {
1847 zfree(&new_expr->metric_refs);
1848 free(new_expr);
1849 return -ENOMEM;
1850 }
1851
1852 /* copy evsel in the same position */
1853 for (idx = 0; idx < nr; idx++) {
1854 evsel = old_expr->metric_events[idx];
1855 evsel = evlist__find_evsel(evlist, evsel->core.idx);
1856 if (evsel == NULL) {
1857 zfree(&new_expr->metric_events);
1858 zfree(&new_expr->metric_refs);
1859 free(new_expr);
1860 return -EINVAL;
1861 }
1862 new_expr->metric_events[idx] = evsel;
1863 }
1864
1865 list_add(&new_expr->nd, &new_me->head);
1866 }
1867 }
1868 return 0;
1869}