]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - tools/perf/builtin-c2c.c
perf c2c report: Add 'iaddr' dimension key
[mirror_ubuntu-jammy-kernel.git] / tools / perf / builtin-c2c.c
CommitLineData
7aef3bf3
JO
1#include <linux/compiler.h>
2#include <linux/kernel.h>
cbb88500 3#include <linux/stringify.h>
7aef3bf3
JO
4#include "util.h"
5#include "debug.h"
6#include "builtin.h"
7#include <subcmd/parse-options.h>
39bcd4a4 8#include "mem-events.h"
903a6f15
JO
9#include "session.h"
10#include "hist.h"
cbb88500 11#include "sort.h"
903a6f15
JO
12#include "tool.h"
13#include "data.h"
8d3f938d 14#include "sort.h"
903a6f15 15
c75540e3
JO
16struct c2c_hists {
17 struct hists hists;
18 struct perf_hpp_list list;
b2252ae6 19 struct c2c_stats stats;
c75540e3
JO
20};
21
78b27543
JO
22struct c2c_hist_entry {
23 struct c2c_hists *hists;
b2252ae6 24 struct c2c_stats stats;
78b27543
JO
25 /*
26 * must be at the end,
27 * because of its callchain dynamic entry
28 */
29 struct hist_entry he;
30};
31
903a6f15 32struct perf_c2c {
c75540e3
JO
33 struct perf_tool tool;
34 struct c2c_hists hists;
903a6f15
JO
35};
36
37static struct perf_c2c c2c;
7aef3bf3 38
78b27543
JO
39static void *c2c_he_zalloc(size_t size)
40{
41 struct c2c_hist_entry *c2c_he;
42
43 c2c_he = zalloc(size + sizeof(*c2c_he));
44 if (!c2c_he)
45 return NULL;
46
47 return &c2c_he->he;
48}
49
50static void c2c_he_free(void *he)
51{
52 struct c2c_hist_entry *c2c_he;
53
54 c2c_he = container_of(he, struct c2c_hist_entry, he);
55 if (c2c_he->hists) {
56 hists__delete_entries(&c2c_he->hists->hists);
57 free(c2c_he->hists);
58 }
59
60 free(c2c_he);
61}
62
63static struct hist_entry_ops c2c_entry_ops = {
64 .new = c2c_he_zalloc,
65 .free = c2c_he_free,
66};
67
ec06f9b9
JO
68static int c2c_hists__init(struct c2c_hists *hists,
69 const char *sort);
70
b2252ae6
JO
71static struct c2c_hists*
72he__get_c2c_hists(struct hist_entry *he,
73 const char *sort)
ec06f9b9
JO
74{
75 struct c2c_hist_entry *c2c_he;
76 struct c2c_hists *hists;
77 int ret;
78
79 c2c_he = container_of(he, struct c2c_hist_entry, he);
80 if (c2c_he->hists)
b2252ae6 81 return c2c_he->hists;
ec06f9b9
JO
82
83 hists = c2c_he->hists = zalloc(sizeof(*hists));
84 if (!hists)
85 return NULL;
86
87 ret = c2c_hists__init(hists, sort);
88 if (ret) {
89 free(hists);
90 return NULL;
91 }
92
b2252ae6 93 return hists;
ec06f9b9
JO
94}
95
78b27543
JO
96static int process_sample_event(struct perf_tool *tool __maybe_unused,
97 union perf_event *event,
98 struct perf_sample *sample,
99 struct perf_evsel *evsel __maybe_unused,
100 struct machine *machine)
101{
b2252ae6
JO
102 struct c2c_hists *c2c_hists = &c2c.hists;
103 struct c2c_hist_entry *c2c_he;
104 struct c2c_stats stats = { .nr_entries = 0, };
78b27543
JO
105 struct hist_entry *he;
106 struct addr_location al;
ec06f9b9 107 struct mem_info *mi, *mi_dup;
78b27543
JO
108 int ret;
109
110 if (machine__resolve(machine, &al, sample) < 0) {
111 pr_debug("problem processing %d event, skipping it.\n",
112 event->header.type);
113 return -1;
114 }
115
116 mi = sample__resolve_mem(sample, &al);
117 if (mi == NULL)
118 return -ENOMEM;
119
ec06f9b9
JO
120 mi_dup = memdup(mi, sizeof(*mi));
121 if (!mi_dup)
122 goto free_mi;
123
b2252ae6
JO
124 c2c_decode_stats(&stats, mi);
125
126 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
78b27543
JO
127 &al, NULL, NULL, mi,
128 sample, true);
ec06f9b9
JO
129 if (he == NULL)
130 goto free_mi_dup;
78b27543 131
b2252ae6
JO
132 c2c_he = container_of(he, struct c2c_hist_entry, he);
133 c2c_add_stats(&c2c_he->stats, &stats);
134 c2c_add_stats(&c2c_hists->stats, &stats);
135
136 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
78b27543
JO
137 ret = hist_entry__append_callchain(he, sample);
138
ec06f9b9
JO
139 if (!ret) {
140 mi = mi_dup;
141
142 mi_dup = memdup(mi, sizeof(*mi));
143 if (!mi_dup)
144 goto free_mi;
145
b2252ae6
JO
146 c2c_hists = he__get_c2c_hists(he, "offset");
147 if (!c2c_hists)
ec06f9b9
JO
148 goto free_mi_dup;
149
b2252ae6 150 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
ec06f9b9
JO
151 &al, NULL, NULL, mi,
152 sample, true);
153 if (he == NULL)
154 goto free_mi_dup;
155
b2252ae6
JO
156 c2c_he = container_of(he, struct c2c_hist_entry, he);
157 c2c_add_stats(&c2c_he->stats, &stats);
158 c2c_add_stats(&c2c_hists->stats, &stats);
159
160 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ec06f9b9
JO
161 ret = hist_entry__append_callchain(he, sample);
162 }
163
164out:
78b27543
JO
165 addr_location__put(&al);
166 return ret;
ec06f9b9
JO
167
168free_mi_dup:
169 free(mi_dup);
170free_mi:
171 free(mi);
172 ret = -ENOMEM;
173 goto out;
78b27543
JO
174}
175
176static struct perf_c2c c2c = {
177 .tool = {
178 .sample = process_sample_event,
179 .mmap = perf_event__process_mmap,
180 .mmap2 = perf_event__process_mmap2,
181 .comm = perf_event__process_comm,
182 .exit = perf_event__process_exit,
183 .fork = perf_event__process_fork,
184 .lost = perf_event__process_lost,
185 .ordered_events = true,
186 .ordering_requires_timestamps = true,
187 },
188};
189
7aef3bf3 190static const char * const c2c_usage[] = {
903a6f15 191 "perf c2c {record|report}",
7aef3bf3
JO
192 NULL
193};
194
903a6f15
JO
195static const char * const __usage_report[] = {
196 "perf c2c report",
197 NULL
198};
199
200static const char * const *report_c2c_usage = __usage_report;
201
c75540e3
JO
202#define C2C_HEADER_MAX 2
203
204struct c2c_header {
205 struct {
206 const char *text;
207 int span;
208 } line[C2C_HEADER_MAX];
209};
210
211struct c2c_dimension {
212 struct c2c_header header;
213 const char *name;
214 int width;
8d3f938d 215 struct sort_entry *se;
c75540e3
JO
216
217 int64_t (*cmp)(struct perf_hpp_fmt *fmt,
218 struct hist_entry *, struct hist_entry *);
219 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
220 struct hist_entry *he);
221 int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
222 struct hist_entry *he);
223};
224
225struct c2c_fmt {
226 struct perf_hpp_fmt fmt;
227 struct c2c_dimension *dim;
228};
229
230static int c2c_width(struct perf_hpp_fmt *fmt,
231 struct perf_hpp *hpp __maybe_unused,
232 struct hists *hists __maybe_unused)
233{
234 struct c2c_fmt *c2c_fmt;
8d3f938d 235 struct c2c_dimension *dim;
c75540e3
JO
236
237 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
8d3f938d
JO
238 dim = c2c_fmt->dim;
239
240 return dim->se ? hists__col_len(hists, dim->se->se_width_idx) :
241 c2c_fmt->dim->width;
c75540e3
JO
242}
243
244static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
8d3f938d 245 struct hists *hists, int line, int *span)
c75540e3 246{
8d3f938d 247 struct perf_hpp_list *hpp_list = hists->hpp_list;
c75540e3
JO
248 struct c2c_fmt *c2c_fmt;
249 struct c2c_dimension *dim;
8d3f938d
JO
250 const char *text = NULL;
251 int width = c2c_width(fmt, hpp, hists);
c75540e3
JO
252
253 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
254 dim = c2c_fmt->dim;
255
8d3f938d
JO
256 if (dim->se) {
257 text = dim->header.line[line].text;
258 /* Use the last line from sort_entry if not defined. */
259 if (!text && (line == hpp_list->nr_header_lines - 1))
260 text = dim->se->se_header;
c75540e3 261 } else {
8d3f938d
JO
262 text = dim->header.line[line].text;
263
264 if (*span) {
265 (*span)--;
266 return 0;
267 } else {
268 *span = dim->header.line[line].span;
269 }
c75540e3
JO
270 }
271
8d3f938d
JO
272 if (text == NULL)
273 text = "";
274
275 return scnprintf(hpp->buf, hpp->size, "%*s", width, text);
c75540e3
JO
276}
277
cbb88500
JO
278#define HEX_STR(__s, __v) \
279({ \
280 scnprintf(__s, sizeof(__s), "0x%" PRIx64, __v); \
281 __s; \
282})
283
284static int64_t
285dcacheline_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
286 struct hist_entry *left, struct hist_entry *right)
287{
288 return sort__dcacheline_cmp(left, right);
289}
290
291static int dcacheline_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
292 struct hist_entry *he)
293{
294 uint64_t addr = 0;
295 int width = c2c_width(fmt, hpp, he->hists);
296 char buf[20];
297
298 if (he->mem_info)
299 addr = cl_address(he->mem_info->daddr.addr);
300
301 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
302}
303
48acdebd
JO
304static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
305 struct hist_entry *he)
306{
307 uint64_t addr = 0;
308 int width = c2c_width(fmt, hpp, he->hists);
309 char buf[20];
310
311 if (he->mem_info)
312 addr = cl_offset(he->mem_info->daddr.al_addr);
313
314 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
315}
316
317static int64_t
318offset_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
319 struct hist_entry *left, struct hist_entry *right)
320{
321 uint64_t l = 0, r = 0;
322
323 if (left->mem_info)
324 l = cl_offset(left->mem_info->daddr.addr);
325 if (right->mem_info)
326 r = cl_offset(right->mem_info->daddr.addr);
327
328 return (int64_t)(r - l);
329}
330
43575a95
JO
331static int
332iaddr_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
333 struct hist_entry *he)
334{
335 uint64_t addr = 0;
336 int width = c2c_width(fmt, hpp, he->hists);
337 char buf[20];
338
339 if (he->mem_info)
340 addr = he->mem_info->iaddr.addr;
341
342 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
343}
344
345static int64_t
346iaddr_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
347 struct hist_entry *left, struct hist_entry *right)
348{
349 return sort__iaddr_cmp(left, right);
350}
351
600a8cf4
JO
352#define HEADER_LOW(__h) \
353 { \
354 .line[1] = { \
355 .text = __h, \
356 }, \
357 }
358
359#define HEADER_BOTH(__h0, __h1) \
360 { \
361 .line[0] = { \
362 .text = __h0, \
363 }, \
364 .line[1] = { \
365 .text = __h1, \
366 }, \
367 }
368
369#define HEADER_SPAN(__h0, __h1, __s) \
370 { \
371 .line[0] = { \
372 .text = __h0, \
373 .span = __s, \
374 }, \
375 .line[1] = { \
376 .text = __h1, \
377 }, \
378 }
379
380#define HEADER_SPAN_LOW(__h) \
381 { \
382 .line[1] = { \
383 .text = __h, \
384 }, \
385 }
386
cbb88500
JO
387static struct c2c_dimension dim_dcacheline = {
388 .header = HEADER_LOW("Cacheline"),
389 .name = "dcacheline",
390 .cmp = dcacheline_cmp,
391 .entry = dcacheline_entry,
392 .width = 18,
393};
394
48acdebd
JO
395static struct c2c_dimension dim_offset = {
396 .header = HEADER_BOTH("Data address", "Offset"),
397 .name = "offset",
398 .cmp = offset_cmp,
399 .entry = offset_entry,
400 .width = 18,
401};
402
43575a95
JO
403static struct c2c_dimension dim_iaddr = {
404 .header = HEADER_LOW("Code address"),
405 .name = "iaddr",
406 .cmp = iaddr_cmp,
407 .entry = iaddr_entry,
408 .width = 18,
409};
410
c75540e3 411static struct c2c_dimension *dimensions[] = {
cbb88500 412 &dim_dcacheline,
48acdebd 413 &dim_offset,
43575a95 414 &dim_iaddr,
c75540e3
JO
415 NULL,
416};
417
418static void fmt_free(struct perf_hpp_fmt *fmt)
419{
420 struct c2c_fmt *c2c_fmt;
421
422 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
423 free(c2c_fmt);
424}
425
426static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
427{
428 struct c2c_fmt *c2c_a = container_of(a, struct c2c_fmt, fmt);
429 struct c2c_fmt *c2c_b = container_of(b, struct c2c_fmt, fmt);
430
431 return c2c_a->dim == c2c_b->dim;
432}
433
434static struct c2c_dimension *get_dimension(const char *name)
435{
436 unsigned int i;
437
438 for (i = 0; dimensions[i]; i++) {
439 struct c2c_dimension *dim = dimensions[i];
440
441 if (!strcmp(dim->name, name))
442 return dim;
443 };
444
445 return NULL;
446}
447
8d3f938d
JO
448static int c2c_se_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
449 struct hist_entry *he)
450{
451 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
452 struct c2c_dimension *dim = c2c_fmt->dim;
453 size_t len = fmt->user_len;
454
455 if (!len)
456 len = hists__col_len(he->hists, dim->se->se_width_idx);
457
458 return dim->se->se_snprintf(he, hpp->buf, hpp->size, len);
459}
460
461static int64_t c2c_se_cmp(struct perf_hpp_fmt *fmt,
462 struct hist_entry *a, struct hist_entry *b)
463{
464 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
465 struct c2c_dimension *dim = c2c_fmt->dim;
466
467 return dim->se->se_cmp(a, b);
468}
469
470static int64_t c2c_se_collapse(struct perf_hpp_fmt *fmt,
471 struct hist_entry *a, struct hist_entry *b)
472{
473 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
474 struct c2c_dimension *dim = c2c_fmt->dim;
475 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
476
477 collapse_fn = dim->se->se_collapse ?: dim->se->se_cmp;
478 return collapse_fn(a, b);
479}
480
c75540e3
JO
481static struct c2c_fmt *get_format(const char *name)
482{
483 struct c2c_dimension *dim = get_dimension(name);
484 struct c2c_fmt *c2c_fmt;
485 struct perf_hpp_fmt *fmt;
486
487 if (!dim)
488 return NULL;
489
490 c2c_fmt = zalloc(sizeof(*c2c_fmt));
491 if (!c2c_fmt)
492 return NULL;
493
494 c2c_fmt->dim = dim;
495
496 fmt = &c2c_fmt->fmt;
497 INIT_LIST_HEAD(&fmt->list);
498 INIT_LIST_HEAD(&fmt->sort_list);
499
8d3f938d
JO
500 fmt->cmp = dim->se ? c2c_se_cmp : dim->cmp;
501 fmt->sort = dim->se ? c2c_se_cmp : dim->cmp;
502 fmt->entry = dim->se ? c2c_se_entry : dim->entry;
c75540e3
JO
503 fmt->header = c2c_header;
504 fmt->width = c2c_width;
8d3f938d 505 fmt->collapse = dim->se ? c2c_se_collapse : dim->cmp;
c75540e3
JO
506 fmt->equal = fmt_equal;
507 fmt->free = fmt_free;
508
509 return c2c_fmt;
510}
511
512static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name)
513{
514 struct c2c_fmt *c2c_fmt = get_format(name);
515
5f2eca83
JO
516 if (!c2c_fmt) {
517 reset_dimensions();
518 return output_field_add(hpp_list, name);
519 }
c75540e3
JO
520
521 perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt);
522 return 0;
523}
524
525static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name)
526{
527 struct c2c_fmt *c2c_fmt = get_format(name);
528
5f2eca83
JO
529 if (!c2c_fmt) {
530 reset_dimensions();
531 return sort_dimension__add(hpp_list, name, NULL, 0);
532 }
c75540e3
JO
533
534 perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
535 return 0;
536}
537
538#define PARSE_LIST(_list, _fn) \
539 do { \
540 char *tmp, *tok; \
541 ret = 0; \
542 \
543 if (!_list) \
544 break; \
545 \
546 for (tok = strtok_r((char *)_list, ", ", &tmp); \
547 tok; tok = strtok_r(NULL, ", ", &tmp)) { \
548 ret = _fn(hpp_list, tok); \
549 if (ret == -EINVAL) { \
550 error("Invalid --fields key: `%s'", tok); \
551 break; \
552 } else if (ret == -ESRCH) { \
553 error("Unknown --fields key: `%s'", tok); \
554 break; \
555 } \
556 } \
557 } while (0)
558
559static int hpp_list__parse(struct perf_hpp_list *hpp_list,
560 const char *output_,
561 const char *sort_)
562{
563 char *output = output_ ? strdup(output_) : NULL;
564 char *sort = sort_ ? strdup(sort_) : NULL;
565 int ret;
566
567 PARSE_LIST(output, c2c_hists__init_output);
568 PARSE_LIST(sort, c2c_hists__init_sort);
569
570 /* copy sort keys to output fields */
571 perf_hpp__setup_output_field(hpp_list);
572
573 /*
574 * We dont need other sorting keys other than those
575 * we already specified. It also really slows down
576 * the processing a lot with big number of output
577 * fields, so switching this off for c2c.
578 */
579
580#if 0
581 /* and then copy output fields to sort keys */
582 perf_hpp__append_sort_keys(&hists->list);
583#endif
584
585 free(output);
586 free(sort);
587 return ret;
588}
589
590static int c2c_hists__init(struct c2c_hists *hists,
591 const char *sort)
592{
593 __hists__init(&hists->hists, &hists->list);
594
595 /*
596 * Initialize only with sort fields, we need to resort
597 * later anyway, and that's where we add output fields
598 * as well.
599 */
600 perf_hpp_list__init(&hists->list);
601
602 return hpp_list__parse(&hists->list, NULL, sort);
603}
604
605__maybe_unused
606static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
607 const char *output,
608 const char *sort)
609{
610 perf_hpp__reset_output_field(&c2c_hists->list);
611 return hpp_list__parse(&c2c_hists->list, output, sort);
612}
613
ec06f9b9
JO
614static int filter_cb(struct hist_entry *he __maybe_unused)
615{
616 return 0;
617}
618
619static int resort_cl_cb(struct hist_entry *he)
620{
621 struct c2c_hist_entry *c2c_he;
622 struct c2c_hists *c2c_hists;
623
624 c2c_he = container_of(he, struct c2c_hist_entry, he);
625 c2c_hists = c2c_he->hists;
626
627 if (c2c_hists) {
628 hists__collapse_resort(&c2c_hists->hists, NULL);
629 hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb);
630 }
631
632 return 0;
633}
634
903a6f15
JO
635static int perf_c2c__report(int argc, const char **argv)
636{
637 struct perf_session *session;
78b27543 638 struct ui_progress prog;
903a6f15
JO
639 struct perf_data_file file = {
640 .mode = PERF_DATA_MODE_READ,
641 };
642 const struct option c2c_options[] = {
643 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
644 "file", "vmlinux pathname"),
645 OPT_INCR('v', "verbose", &verbose,
646 "be more verbose (show counter open errors, etc)"),
647 OPT_STRING('i', "input", &input_name, "file",
648 "the input file to process"),
649 OPT_END()
650 };
651 int err = 0;
652
653 argc = parse_options(argc, argv, c2c_options, report_c2c_usage,
654 PARSE_OPT_STOP_AT_NON_OPTION);
78b27543 655 if (argc)
903a6f15
JO
656 usage_with_options(report_c2c_usage, c2c_options);
657
78b27543
JO
658 if (!input_name || !strlen(input_name))
659 input_name = "perf.data";
660
903a6f15
JO
661 file.path = input_name;
662
c75540e3
JO
663 err = c2c_hists__init(&c2c.hists, "dcacheline");
664 if (err) {
665 pr_debug("Failed to initialize hists\n");
666 goto out;
667 }
668
903a6f15
JO
669 session = perf_session__new(&file, 0, &c2c.tool);
670 if (session == NULL) {
671 pr_debug("No memory for session\n");
672 goto out;
673 }
674
675 if (symbol__init(&session->header.env) < 0)
676 goto out_session;
677
678 /* No pipe support at the moment. */
679 if (perf_data_file__is_pipe(session->file)) {
680 pr_debug("No pipe support at the moment.\n");
681 goto out_session;
682 }
683
78b27543
JO
684 err = perf_session__process_events(session);
685 if (err) {
686 pr_err("failed to process sample\n");
687 goto out_session;
688 }
689
690 ui_progress__init(&prog, c2c.hists.hists.nr_entries, "Sorting...");
691
692 hists__collapse_resort(&c2c.hists.hists, NULL);
ec06f9b9 693 hists__output_resort_cb(&c2c.hists.hists, &prog, resort_cl_cb);
78b27543
JO
694
695 ui_progress__finish();
696
903a6f15
JO
697out_session:
698 perf_session__delete(session);
699out:
700 return err;
701}
702
39bcd4a4
JO
703static int parse_record_events(const struct option *opt __maybe_unused,
704 const char *str, int unset __maybe_unused)
705{
706 bool *event_set = (bool *) opt->value;
707
708 *event_set = true;
709 return perf_mem_events__parse(str);
710}
711
712
713static const char * const __usage_record[] = {
714 "perf c2c record [<options>] [<command>]",
715 "perf c2c record [<options>] -- <command> [<options>]",
716 NULL
717};
718
719static const char * const *record_mem_usage = __usage_record;
720
721static int perf_c2c__record(int argc, const char **argv)
722{
723 int rec_argc, i = 0, j;
724 const char **rec_argv;
725 int ret;
726 bool all_user = false, all_kernel = false;
727 bool event_set = false;
728 struct option options[] = {
729 OPT_CALLBACK('e', "event", &event_set, "event",
730 "event selector. Use 'perf mem record -e list' to list available events",
731 parse_record_events),
732 OPT_INCR('v', "verbose", &verbose,
733 "be more verbose (show counter open errors, etc)"),
734 OPT_BOOLEAN('u', "all-user", &all_user, "collect only user level data"),
735 OPT_BOOLEAN('k', "all-kernel", &all_kernel, "collect only kernel level data"),
736 OPT_UINTEGER('l', "ldlat", &perf_mem_events__loads_ldlat, "setup mem-loads latency"),
737 OPT_END()
738 };
739
740 if (perf_mem_events__init()) {
741 pr_err("failed: memory events not supported\n");
742 return -1;
743 }
744
745 argc = parse_options(argc, argv, options, record_mem_usage,
746 PARSE_OPT_KEEP_UNKNOWN);
747
748 rec_argc = argc + 10; /* max number of arguments */
749 rec_argv = calloc(rec_argc + 1, sizeof(char *));
750 if (!rec_argv)
751 return -1;
752
753 rec_argv[i++] = "record";
754
755 if (!event_set) {
756 perf_mem_events[PERF_MEM_EVENTS__LOAD].record = true;
757 perf_mem_events[PERF_MEM_EVENTS__STORE].record = true;
758 }
759
760 if (perf_mem_events[PERF_MEM_EVENTS__LOAD].record)
761 rec_argv[i++] = "-W";
762
763 rec_argv[i++] = "-d";
764 rec_argv[i++] = "--sample-cpu";
765
766 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
767 if (!perf_mem_events[j].record)
768 continue;
769
770 if (!perf_mem_events[j].supported) {
771 pr_err("failed: event '%s' not supported\n",
772 perf_mem_events[j].name);
773 return -1;
774 }
775
776 rec_argv[i++] = "-e";
777 rec_argv[i++] = perf_mem_events__name(j);
778 };
779
780 if (all_user)
781 rec_argv[i++] = "--all-user";
782
783 if (all_kernel)
784 rec_argv[i++] = "--all-kernel";
785
786 for (j = 0; j < argc; j++, i++)
787 rec_argv[i] = argv[j];
788
789 if (verbose > 0) {
790 pr_debug("calling: ");
791
792 j = 0;
793
794 while (rec_argv[j]) {
795 pr_debug("%s ", rec_argv[j]);
796 j++;
797 }
798 pr_debug("\n");
799 }
800
801 ret = cmd_record(i, rec_argv, NULL);
802 free(rec_argv);
803 return ret;
804}
805
7aef3bf3
JO
806int cmd_c2c(int argc, const char **argv, const char *prefix __maybe_unused)
807{
808 const struct option c2c_options[] = {
809 OPT_INCR('v', "verbose", &verbose, "be more verbose"),
810 OPT_END()
811 };
812
813 argc = parse_options(argc, argv, c2c_options, c2c_usage,
814 PARSE_OPT_STOP_AT_NON_OPTION);
39bcd4a4
JO
815
816 if (!argc)
817 usage_with_options(c2c_usage, c2c_options);
818
819 if (!strncmp(argv[0], "rec", 3)) {
820 return perf_c2c__record(argc, argv);
903a6f15
JO
821 } else if (!strncmp(argv[0], "rep", 3)) {
822 return perf_c2c__report(argc, argv);
39bcd4a4
JO
823 } else {
824 usage_with_options(c2c_usage, c2c_options);
825 }
826
7aef3bf3
JO
827 return 0;
828}