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