1
2#include <inttypes.h>
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <unistd.h>
6#include "builtin.h"
7#include "perf.h"
8
9#include <subcmd/parse-options.h>
10#include "util/auxtrace.h"
11#include "util/trace-event.h"
12#include "util/tool.h"
13#include "util/session.h"
14#include "util/data.h"
15#include "util/map_symbol.h"
16#include "util/mem-events.h"
17#include "util/debug.h"
18#include "util/dso.h"
19#include "util/map.h"
20#include "util/symbol.h"
21#include <linux/err.h>
22
23#define MEM_OPERATION_LOAD 0x1
24#define MEM_OPERATION_STORE 0x2
25
26struct perf_mem {
27 struct perf_tool tool;
28 char const *input_name;
29 bool hide_unresolved;
30 bool dump_raw;
31 bool force;
32 bool phys_addr;
33 int operation;
34 const char *cpu_list;
35 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
36};
37
38static int parse_record_events(const struct option *opt,
39 const char *str, int unset __maybe_unused)
40{
41 struct perf_mem *mem = *(struct perf_mem **)opt->value;
42
43 if (!strcmp(str, "list")) {
44 perf_mem_events__list();
45 exit(0);
46 }
47 if (perf_mem_events__parse(str))
48 exit(-1);
49
50 mem->operation = 0;
51 return 0;
52}
53
54static const char * const __usage[] = {
55 "perf mem record [<options>] [<command>]",
56 "perf mem record [<options>] -- <command> [<options>]",
57 NULL
58};
59
60static const char * const *record_mem_usage = __usage;
61
62static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
63{
64 int rec_argc, i = 0, j;
65 const char **rec_argv;
66 int ret;
67 bool all_user = false, all_kernel = false;
68 struct perf_mem_event *e;
69 struct option options[] = {
70 OPT_CALLBACK('e', "event", &mem, "event",
71 "event selector. use 'perf mem record -e list' to list available events",
72 parse_record_events),
73 OPT_UINTEGER(0, "ldlat", &perf_mem_events__loads_ldlat, "mem-loads latency"),
74 OPT_INCR('v', "verbose", &verbose,
75 "be more verbose (show counter open errors, etc)"),
76 OPT_BOOLEAN('U', "all-user", &all_user, "collect only user level data"),
77 OPT_BOOLEAN('K', "all-kernel", &all_kernel, "collect only kernel level data"),
78 OPT_END()
79 };
80
81 if (perf_mem_events__init()) {
82 pr_err("failed: memory events not supported\n");
83 return -1;
84 }
85
86 argc = parse_options(argc, argv, options, record_mem_usage,
87 PARSE_OPT_KEEP_UNKNOWN);
88
89 rec_argc = argc + 9;
90 rec_argv = calloc(rec_argc + 1, sizeof(char *));
91 if (!rec_argv)
92 return -1;
93
94 rec_argv[i++] = "record";
95
96 e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD_STORE);
97
98
99
100
101
102 if (e->tag &&
103 (mem->operation & MEM_OPERATION_LOAD) &&
104 (mem->operation & MEM_OPERATION_STORE)) {
105 e->record = true;
106 } else {
107 if (mem->operation & MEM_OPERATION_LOAD) {
108 e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD);
109 e->record = true;
110 }
111
112 if (mem->operation & MEM_OPERATION_STORE) {
113 e = perf_mem_events__ptr(PERF_MEM_EVENTS__STORE);
114 e->record = true;
115 }
116 }
117
118 e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD);
119 if (e->record)
120 rec_argv[i++] = "-W";
121
122 rec_argv[i++] = "-d";
123
124 if (mem->phys_addr)
125 rec_argv[i++] = "--phys-data";
126
127 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
128 e = perf_mem_events__ptr(j);
129 if (!e->record)
130 continue;
131
132 if (!e->supported) {
133 pr_err("failed: event '%s' not supported\n",
134 perf_mem_events__name(j));
135 free(rec_argv);
136 return -1;
137 }
138
139 rec_argv[i++] = "-e";
140 rec_argv[i++] = perf_mem_events__name(j);
141 }
142
143 if (all_user)
144 rec_argv[i++] = "--all-user";
145
146 if (all_kernel)
147 rec_argv[i++] = "--all-kernel";
148
149 for (j = 0; j < argc; j++, i++)
150 rec_argv[i] = argv[j];
151
152 if (verbose > 0) {
153 pr_debug("calling: record ");
154
155 while (rec_argv[j]) {
156 pr_debug("%s ", rec_argv[j]);
157 j++;
158 }
159 pr_debug("\n");
160 }
161
162 ret = cmd_record(i, rec_argv);
163 free(rec_argv);
164 return ret;
165}
166
167static int
168dump_raw_samples(struct perf_tool *tool,
169 union perf_event *event,
170 struct perf_sample *sample,
171 struct machine *machine)
172{
173 struct perf_mem *mem = container_of(tool, struct perf_mem, tool);
174 struct addr_location al;
175 const char *fmt;
176
177 if (machine__resolve(machine, &al, sample) < 0) {
178 fprintf(stderr, "problem processing %d event, skipping it.\n",
179 event->header.type);
180 return -1;
181 }
182
183 if (al.filtered || (mem->hide_unresolved && al.sym == NULL))
184 goto out_put;
185
186 if (al.map != NULL)
187 al.map->dso->hit = 1;
188
189 if (mem->phys_addr) {
190 if (symbol_conf.field_sep) {
191 fmt = "%d%s%d%s0x%"PRIx64"%s0x%"PRIx64"%s0x%016"PRIx64
192 "%s%"PRIu64"%s0x%"PRIx64"%s%s:%s\n";
193 } else {
194 fmt = "%5d%s%5d%s0x%016"PRIx64"%s0x016%"PRIx64
195 "%s0x%016"PRIx64"%s%5"PRIu64"%s0x%06"PRIx64
196 "%s%s:%s\n";
197 symbol_conf.field_sep = " ";
198 }
199
200 printf(fmt,
201 sample->pid,
202 symbol_conf.field_sep,
203 sample->tid,
204 symbol_conf.field_sep,
205 sample->ip,
206 symbol_conf.field_sep,
207 sample->addr,
208 symbol_conf.field_sep,
209 sample->phys_addr,
210 symbol_conf.field_sep,
211 sample->weight,
212 symbol_conf.field_sep,
213 sample->data_src,
214 symbol_conf.field_sep,
215 al.map ? (al.map->dso ? al.map->dso->long_name : "???") : "???",
216 al.sym ? al.sym->name : "???");
217 } else {
218 if (symbol_conf.field_sep) {
219 fmt = "%d%s%d%s0x%"PRIx64"%s0x%"PRIx64"%s%"PRIu64
220 "%s0x%"PRIx64"%s%s:%s\n";
221 } else {
222 fmt = "%5d%s%5d%s0x%016"PRIx64"%s0x016%"PRIx64
223 "%s%5"PRIu64"%s0x%06"PRIx64"%s%s:%s\n";
224 symbol_conf.field_sep = " ";
225 }
226
227 printf(fmt,
228 sample->pid,
229 symbol_conf.field_sep,
230 sample->tid,
231 symbol_conf.field_sep,
232 sample->ip,
233 symbol_conf.field_sep,
234 sample->addr,
235 symbol_conf.field_sep,
236 sample->weight,
237 symbol_conf.field_sep,
238 sample->data_src,
239 symbol_conf.field_sep,
240 al.map ? (al.map->dso ? al.map->dso->long_name : "???") : "???",
241 al.sym ? al.sym->name : "???");
242 }
243out_put:
244 addr_location__put(&al);
245 return 0;
246}
247
248static int process_sample_event(struct perf_tool *tool,
249 union perf_event *event,
250 struct perf_sample *sample,
251 struct evsel *evsel __maybe_unused,
252 struct machine *machine)
253{
254 return dump_raw_samples(tool, event, sample, machine);
255}
256
257static int report_raw_events(struct perf_mem *mem)
258{
259 struct itrace_synth_opts itrace_synth_opts = {
260 .set = true,
261 .mem = true,
262 .default_no_sample = true,
263 };
264
265 struct perf_data data = {
266 .path = input_name,
267 .mode = PERF_DATA_MODE_READ,
268 .force = mem->force,
269 };
270 int ret;
271 struct perf_session *session = perf_session__new(&data, false,
272 &mem->tool);
273
274 if (IS_ERR(session))
275 return PTR_ERR(session);
276
277 session->itrace_synth_opts = &itrace_synth_opts;
278
279 if (mem->cpu_list) {
280 ret = perf_session__cpu_bitmap(session, mem->cpu_list,
281 mem->cpu_bitmap);
282 if (ret < 0)
283 goto out_delete;
284 }
285
286 ret = symbol__init(&session->header.env);
287 if (ret < 0)
288 goto out_delete;
289
290 if (mem->phys_addr)
291 printf("# PID, TID, IP, ADDR, PHYS ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n");
292 else
293 printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n");
294
295 ret = perf_session__process_events(session);
296
297out_delete:
298 perf_session__delete(session);
299 return ret;
300}
301static char *get_sort_order(struct perf_mem *mem)
302{
303 bool has_extra_options = mem->phys_addr ? true : false;
304 char sort[128];
305
306
307
308
309
310 if (!(mem->operation & MEM_OPERATION_LOAD)) {
311 strcpy(sort, "--sort=mem,sym,dso,symbol_daddr,"
312 "dso_daddr,tlb,locked");
313 } else if (has_extra_options) {
314 strcpy(sort, "--sort=local_weight,mem,sym,dso,symbol_daddr,"
315 "dso_daddr,snoop,tlb,locked");
316 } else
317 return NULL;
318
319 if (mem->phys_addr)
320 strcat(sort, ",phys_daddr");
321
322 return strdup(sort);
323}
324
325static int report_events(int argc, const char **argv, struct perf_mem *mem)
326{
327 const char **rep_argv;
328 int ret, i = 0, j, rep_argc;
329 char *new_sort_order;
330
331 if (mem->dump_raw)
332 return report_raw_events(mem);
333
334 rep_argc = argc + 3;
335 rep_argv = calloc(rep_argc + 1, sizeof(char *));
336 if (!rep_argv)
337 return -1;
338
339 rep_argv[i++] = "report";
340 rep_argv[i++] = "--mem-mode";
341 rep_argv[i++] = "-n";
342
343 new_sort_order = get_sort_order(mem);
344 if (new_sort_order)
345 rep_argv[i++] = new_sort_order;
346
347 for (j = 1; j < argc; j++, i++)
348 rep_argv[i] = argv[j];
349
350 ret = cmd_report(i, rep_argv);
351 free(rep_argv);
352 return ret;
353}
354
355struct mem_mode {
356 const char *name;
357 int mode;
358};
359
360#define MEM_OPT(n, m) \
361 { .name = n, .mode = (m) }
362
363#define MEM_END { .name = NULL }
364
365static const struct mem_mode mem_modes[]={
366 MEM_OPT("load", MEM_OPERATION_LOAD),
367 MEM_OPT("store", MEM_OPERATION_STORE),
368 MEM_END
369};
370
371static int
372parse_mem_ops(const struct option *opt, const char *str, int unset)
373{
374 int *mode = (int *)opt->value;
375 const struct mem_mode *m;
376 char *s, *os = NULL, *p;
377 int ret = -1;
378
379 if (unset)
380 return 0;
381
382
383 if (str) {
384
385 s = os = strdup(str);
386 if (!s)
387 return -1;
388
389
390 *mode = 0;
391
392 for (;;) {
393 p = strchr(s, ',');
394 if (p)
395 *p = '\0';
396
397 for (m = mem_modes; m->name; m++) {
398 if (!strcasecmp(s, m->name))
399 break;
400 }
401 if (!m->name) {
402 fprintf(stderr, "unknown sampling op %s,"
403 " check man page\n", s);
404 goto error;
405 }
406
407 *mode |= m->mode;
408
409 if (!p)
410 break;
411
412 s = p + 1;
413 }
414 }
415 ret = 0;
416
417 if (*mode == 0)
418 *mode = MEM_OPERATION_LOAD;
419error:
420 free(os);
421 return ret;
422}
423
424int cmd_mem(int argc, const char **argv)
425{
426 struct stat st;
427 struct perf_mem mem = {
428 .tool = {
429 .sample = process_sample_event,
430 .mmap = perf_event__process_mmap,
431 .mmap2 = perf_event__process_mmap2,
432 .comm = perf_event__process_comm,
433 .lost = perf_event__process_lost,
434 .fork = perf_event__process_fork,
435 .attr = perf_event__process_attr,
436 .build_id = perf_event__process_build_id,
437 .namespaces = perf_event__process_namespaces,
438 .auxtrace_info = perf_event__process_auxtrace_info,
439 .auxtrace = perf_event__process_auxtrace,
440 .auxtrace_error = perf_event__process_auxtrace_error,
441 .ordered_events = true,
442 },
443 .input_name = "perf.data",
444
445
446
447 .operation = MEM_OPERATION_LOAD | MEM_OPERATION_STORE,
448 };
449 const struct option mem_options[] = {
450 OPT_CALLBACK('t', "type", &mem.operation,
451 "type", "memory operations(load,store) Default load,store",
452 parse_mem_ops),
453 OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw,
454 "dump raw samples in ASCII"),
455 OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved,
456 "Only display entries resolved to a symbol"),
457 OPT_STRING('i', "input", &input_name, "file",
458 "input file name"),
459 OPT_STRING('C', "cpu", &mem.cpu_list, "cpu",
460 "list of cpus to profile"),
461 OPT_STRING_NOEMPTY('x', "field-separator", &symbol_conf.field_sep,
462 "separator",
463 "separator for columns, no spaces will be added"
464 " between columns '.' is reserved."),
465 OPT_BOOLEAN('f', "force", &mem.force, "don't complain, do it"),
466 OPT_BOOLEAN('p', "phys-data", &mem.phys_addr, "Record/Report sample physical addresses"),
467 OPT_END()
468 };
469 const char *const mem_subcommands[] = { "record", "report", NULL };
470 const char *mem_usage[] = {
471 NULL,
472 NULL
473 };
474
475 argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands,
476 mem_usage, PARSE_OPT_KEEP_UNKNOWN);
477
478 if (!argc || !(strncmp(argv[0], "rec", 3) || mem.operation))
479 usage_with_options(mem_usage, mem_options);
480
481 if (!mem.input_name || !strlen(mem.input_name)) {
482 if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
483 mem.input_name = "-";
484 else
485 mem.input_name = "perf.data";
486 }
487
488 if (!strncmp(argv[0], "rec", 3))
489 return __cmd_record(argc, argv, &mem);
490 else if (!strncmp(argv[0], "rep", 3))
491 return report_events(argc, argv, &mem);
492 else
493 usage_with_options(mem_usage, mem_options);
494
495 return 0;
496}
497