]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - tools/perf/builtin-c2c.c
perf c2c report: Fallback to standard dimensions
[mirror_ubuntu-jammy-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
903a6f15 19struct perf_c2c {
c75540e3
JO
20 struct perf_tool tool;
21 struct c2c_hists hists;
903a6f15
JO
22};
23
24static struct perf_c2c c2c;
7aef3bf3
JO
25
26static const char * const c2c_usage[] = {
903a6f15 27 "perf c2c {record|report}",
7aef3bf3
JO
28 NULL
29};
30
903a6f15
JO
31static const char * const __usage_report[] = {
32 "perf c2c report",
33 NULL
34};
35
36static const char * const *report_c2c_usage = __usage_report;
37
c75540e3
JO
38#define C2C_HEADER_MAX 2
39
40struct c2c_header {
41 struct {
42 const char *text;
43 int span;
44 } line[C2C_HEADER_MAX];
45};
46
47struct c2c_dimension {
48 struct c2c_header header;
49 const char *name;
50 int width;
8d3f938d 51 struct sort_entry *se;
c75540e3
JO
52
53 int64_t (*cmp)(struct perf_hpp_fmt *fmt,
54 struct hist_entry *, struct hist_entry *);
55 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
56 struct hist_entry *he);
57 int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
58 struct hist_entry *he);
59};
60
61struct c2c_fmt {
62 struct perf_hpp_fmt fmt;
63 struct c2c_dimension *dim;
64};
65
66static int c2c_width(struct perf_hpp_fmt *fmt,
67 struct perf_hpp *hpp __maybe_unused,
68 struct hists *hists __maybe_unused)
69{
70 struct c2c_fmt *c2c_fmt;
8d3f938d 71 struct c2c_dimension *dim;
c75540e3
JO
72
73 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
8d3f938d
JO
74 dim = c2c_fmt->dim;
75
76 return dim->se ? hists__col_len(hists, dim->se->se_width_idx) :
77 c2c_fmt->dim->width;
c75540e3
JO
78}
79
80static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
8d3f938d 81 struct hists *hists, int line, int *span)
c75540e3 82{
8d3f938d 83 struct perf_hpp_list *hpp_list = hists->hpp_list;
c75540e3
JO
84 struct c2c_fmt *c2c_fmt;
85 struct c2c_dimension *dim;
8d3f938d
JO
86 const char *text = NULL;
87 int width = c2c_width(fmt, hpp, hists);
c75540e3
JO
88
89 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
90 dim = c2c_fmt->dim;
91
8d3f938d
JO
92 if (dim->se) {
93 text = dim->header.line[line].text;
94 /* Use the last line from sort_entry if not defined. */
95 if (!text && (line == hpp_list->nr_header_lines - 1))
96 text = dim->se->se_header;
c75540e3 97 } else {
8d3f938d
JO
98 text = dim->header.line[line].text;
99
100 if (*span) {
101 (*span)--;
102 return 0;
103 } else {
104 *span = dim->header.line[line].span;
105 }
c75540e3
JO
106 }
107
8d3f938d
JO
108 if (text == NULL)
109 text = "";
110
111 return scnprintf(hpp->buf, hpp->size, "%*s", width, text);
c75540e3
JO
112}
113
114static struct c2c_dimension *dimensions[] = {
115 NULL,
116};
117
118static void fmt_free(struct perf_hpp_fmt *fmt)
119{
120 struct c2c_fmt *c2c_fmt;
121
122 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
123 free(c2c_fmt);
124}
125
126static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
127{
128 struct c2c_fmt *c2c_a = container_of(a, struct c2c_fmt, fmt);
129 struct c2c_fmt *c2c_b = container_of(b, struct c2c_fmt, fmt);
130
131 return c2c_a->dim == c2c_b->dim;
132}
133
134static struct c2c_dimension *get_dimension(const char *name)
135{
136 unsigned int i;
137
138 for (i = 0; dimensions[i]; i++) {
139 struct c2c_dimension *dim = dimensions[i];
140
141 if (!strcmp(dim->name, name))
142 return dim;
143 };
144
145 return NULL;
146}
147
8d3f938d
JO
148static int c2c_se_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
149 struct hist_entry *he)
150{
151 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
152 struct c2c_dimension *dim = c2c_fmt->dim;
153 size_t len = fmt->user_len;
154
155 if (!len)
156 len = hists__col_len(he->hists, dim->se->se_width_idx);
157
158 return dim->se->se_snprintf(he, hpp->buf, hpp->size, len);
159}
160
161static int64_t c2c_se_cmp(struct perf_hpp_fmt *fmt,
162 struct hist_entry *a, struct hist_entry *b)
163{
164 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
165 struct c2c_dimension *dim = c2c_fmt->dim;
166
167 return dim->se->se_cmp(a, b);
168}
169
170static int64_t c2c_se_collapse(struct perf_hpp_fmt *fmt,
171 struct hist_entry *a, struct hist_entry *b)
172{
173 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
174 struct c2c_dimension *dim = c2c_fmt->dim;
175 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
176
177 collapse_fn = dim->se->se_collapse ?: dim->se->se_cmp;
178 return collapse_fn(a, b);
179}
180
c75540e3
JO
181static struct c2c_fmt *get_format(const char *name)
182{
183 struct c2c_dimension *dim = get_dimension(name);
184 struct c2c_fmt *c2c_fmt;
185 struct perf_hpp_fmt *fmt;
186
187 if (!dim)
188 return NULL;
189
190 c2c_fmt = zalloc(sizeof(*c2c_fmt));
191 if (!c2c_fmt)
192 return NULL;
193
194 c2c_fmt->dim = dim;
195
196 fmt = &c2c_fmt->fmt;
197 INIT_LIST_HEAD(&fmt->list);
198 INIT_LIST_HEAD(&fmt->sort_list);
199
8d3f938d
JO
200 fmt->cmp = dim->se ? c2c_se_cmp : dim->cmp;
201 fmt->sort = dim->se ? c2c_se_cmp : dim->cmp;
202 fmt->entry = dim->se ? c2c_se_entry : dim->entry;
c75540e3
JO
203 fmt->header = c2c_header;
204 fmt->width = c2c_width;
8d3f938d 205 fmt->collapse = dim->se ? c2c_se_collapse : dim->cmp;
c75540e3
JO
206 fmt->equal = fmt_equal;
207 fmt->free = fmt_free;
208
209 return c2c_fmt;
210}
211
212static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name)
213{
214 struct c2c_fmt *c2c_fmt = get_format(name);
215
5f2eca83
JO
216 if (!c2c_fmt) {
217 reset_dimensions();
218 return output_field_add(hpp_list, name);
219 }
c75540e3
JO
220
221 perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt);
222 return 0;
223}
224
225static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name)
226{
227 struct c2c_fmt *c2c_fmt = get_format(name);
228
5f2eca83
JO
229 if (!c2c_fmt) {
230 reset_dimensions();
231 return sort_dimension__add(hpp_list, name, NULL, 0);
232 }
c75540e3
JO
233
234 perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
235 return 0;
236}
237
238#define PARSE_LIST(_list, _fn) \
239 do { \
240 char *tmp, *tok; \
241 ret = 0; \
242 \
243 if (!_list) \
244 break; \
245 \
246 for (tok = strtok_r((char *)_list, ", ", &tmp); \
247 tok; tok = strtok_r(NULL, ", ", &tmp)) { \
248 ret = _fn(hpp_list, tok); \
249 if (ret == -EINVAL) { \
250 error("Invalid --fields key: `%s'", tok); \
251 break; \
252 } else if (ret == -ESRCH) { \
253 error("Unknown --fields key: `%s'", tok); \
254 break; \
255 } \
256 } \
257 } while (0)
258
259static int hpp_list__parse(struct perf_hpp_list *hpp_list,
260 const char *output_,
261 const char *sort_)
262{
263 char *output = output_ ? strdup(output_) : NULL;
264 char *sort = sort_ ? strdup(sort_) : NULL;
265 int ret;
266
267 PARSE_LIST(output, c2c_hists__init_output);
268 PARSE_LIST(sort, c2c_hists__init_sort);
269
270 /* copy sort keys to output fields */
271 perf_hpp__setup_output_field(hpp_list);
272
273 /*
274 * We dont need other sorting keys other than those
275 * we already specified. It also really slows down
276 * the processing a lot with big number of output
277 * fields, so switching this off for c2c.
278 */
279
280#if 0
281 /* and then copy output fields to sort keys */
282 perf_hpp__append_sort_keys(&hists->list);
283#endif
284
285 free(output);
286 free(sort);
287 return ret;
288}
289
290static int c2c_hists__init(struct c2c_hists *hists,
291 const char *sort)
292{
293 __hists__init(&hists->hists, &hists->list);
294
295 /*
296 * Initialize only with sort fields, we need to resort
297 * later anyway, and that's where we add output fields
298 * as well.
299 */
300 perf_hpp_list__init(&hists->list);
301
302 return hpp_list__parse(&hists->list, NULL, sort);
303}
304
305__maybe_unused
306static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
307 const char *output,
308 const char *sort)
309{
310 perf_hpp__reset_output_field(&c2c_hists->list);
311 return hpp_list__parse(&c2c_hists->list, output, sort);
312}
313
903a6f15
JO
314static int perf_c2c__report(int argc, const char **argv)
315{
316 struct perf_session *session;
317 struct perf_data_file file = {
318 .mode = PERF_DATA_MODE_READ,
319 };
320 const struct option c2c_options[] = {
321 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
322 "file", "vmlinux pathname"),
323 OPT_INCR('v', "verbose", &verbose,
324 "be more verbose (show counter open errors, etc)"),
325 OPT_STRING('i', "input", &input_name, "file",
326 "the input file to process"),
327 OPT_END()
328 };
329 int err = 0;
330
331 argc = parse_options(argc, argv, c2c_options, report_c2c_usage,
332 PARSE_OPT_STOP_AT_NON_OPTION);
333 if (!argc)
334 usage_with_options(report_c2c_usage, c2c_options);
335
336 file.path = input_name;
337
c75540e3
JO
338 err = c2c_hists__init(&c2c.hists, "dcacheline");
339 if (err) {
340 pr_debug("Failed to initialize hists\n");
341 goto out;
342 }
343
903a6f15
JO
344 session = perf_session__new(&file, 0, &c2c.tool);
345 if (session == NULL) {
346 pr_debug("No memory for session\n");
347 goto out;
348 }
349
350 if (symbol__init(&session->header.env) < 0)
351 goto out_session;
352
353 /* No pipe support at the moment. */
354 if (perf_data_file__is_pipe(session->file)) {
355 pr_debug("No pipe support at the moment.\n");
356 goto out_session;
357 }
358
359out_session:
360 perf_session__delete(session);
361out:
362 return err;
363}
364
39bcd4a4
JO
365static int parse_record_events(const struct option *opt __maybe_unused,
366 const char *str, int unset __maybe_unused)
367{
368 bool *event_set = (bool *) opt->value;
369
370 *event_set = true;
371 return perf_mem_events__parse(str);
372}
373
374
375static const char * const __usage_record[] = {
376 "perf c2c record [<options>] [<command>]",
377 "perf c2c record [<options>] -- <command> [<options>]",
378 NULL
379};
380
381static const char * const *record_mem_usage = __usage_record;
382
383static int perf_c2c__record(int argc, const char **argv)
384{
385 int rec_argc, i = 0, j;
386 const char **rec_argv;
387 int ret;
388 bool all_user = false, all_kernel = false;
389 bool event_set = false;
390 struct option options[] = {
391 OPT_CALLBACK('e', "event", &event_set, "event",
392 "event selector. Use 'perf mem record -e list' to list available events",
393 parse_record_events),
394 OPT_INCR('v', "verbose", &verbose,
395 "be more verbose (show counter open errors, etc)"),
396 OPT_BOOLEAN('u', "all-user", &all_user, "collect only user level data"),
397 OPT_BOOLEAN('k', "all-kernel", &all_kernel, "collect only kernel level data"),
398 OPT_UINTEGER('l', "ldlat", &perf_mem_events__loads_ldlat, "setup mem-loads latency"),
399 OPT_END()
400 };
401
402 if (perf_mem_events__init()) {
403 pr_err("failed: memory events not supported\n");
404 return -1;
405 }
406
407 argc = parse_options(argc, argv, options, record_mem_usage,
408 PARSE_OPT_KEEP_UNKNOWN);
409
410 rec_argc = argc + 10; /* max number of arguments */
411 rec_argv = calloc(rec_argc + 1, sizeof(char *));
412 if (!rec_argv)
413 return -1;
414
415 rec_argv[i++] = "record";
416
417 if (!event_set) {
418 perf_mem_events[PERF_MEM_EVENTS__LOAD].record = true;
419 perf_mem_events[PERF_MEM_EVENTS__STORE].record = true;
420 }
421
422 if (perf_mem_events[PERF_MEM_EVENTS__LOAD].record)
423 rec_argv[i++] = "-W";
424
425 rec_argv[i++] = "-d";
426 rec_argv[i++] = "--sample-cpu";
427
428 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
429 if (!perf_mem_events[j].record)
430 continue;
431
432 if (!perf_mem_events[j].supported) {
433 pr_err("failed: event '%s' not supported\n",
434 perf_mem_events[j].name);
435 return -1;
436 }
437
438 rec_argv[i++] = "-e";
439 rec_argv[i++] = perf_mem_events__name(j);
440 };
441
442 if (all_user)
443 rec_argv[i++] = "--all-user";
444
445 if (all_kernel)
446 rec_argv[i++] = "--all-kernel";
447
448 for (j = 0; j < argc; j++, i++)
449 rec_argv[i] = argv[j];
450
451 if (verbose > 0) {
452 pr_debug("calling: ");
453
454 j = 0;
455
456 while (rec_argv[j]) {
457 pr_debug("%s ", rec_argv[j]);
458 j++;
459 }
460 pr_debug("\n");
461 }
462
463 ret = cmd_record(i, rec_argv, NULL);
464 free(rec_argv);
465 return ret;
466}
467
7aef3bf3
JO
468int cmd_c2c(int argc, const char **argv, const char *prefix __maybe_unused)
469{
470 const struct option c2c_options[] = {
471 OPT_INCR('v', "verbose", &verbose, "be more verbose"),
472 OPT_END()
473 };
474
475 argc = parse_options(argc, argv, c2c_options, c2c_usage,
476 PARSE_OPT_STOP_AT_NON_OPTION);
39bcd4a4
JO
477
478 if (!argc)
479 usage_with_options(c2c_usage, c2c_options);
480
481 if (!strncmp(argv[0], "rec", 3)) {
482 return perf_c2c__record(argc, argv);
903a6f15
JO
483 } else if (!strncmp(argv[0], "rep", 3)) {
484 return perf_c2c__report(argc, argv);
39bcd4a4
JO
485 } else {
486 usage_with_options(c2c_usage, c2c_options);
487 }
488
7aef3bf3
JO
489 return 0;
490}