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