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