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