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