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