xref: /openbmc/linux/tools/perf/builtin-mem.c (revision 5bd8e16d)
1 #include "builtin.h"
2 #include "perf.h"
3 
4 #include "util/parse-options.h"
5 #include "util/trace-event.h"
6 #include "util/tool.h"
7 #include "util/session.h"
8 
9 #define MEM_OPERATION_LOAD	"load"
10 #define MEM_OPERATION_STORE	"store"
11 
12 static const char	*mem_operation		= MEM_OPERATION_LOAD;
13 
14 struct perf_mem {
15 	struct perf_tool	tool;
16 	char const		*input_name;
17 	bool			hide_unresolved;
18 	bool			dump_raw;
19 	const char		*cpu_list;
20 	DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
21 };
22 
23 static const char * const mem_usage[] = {
24 	"perf mem [<options>] {record <command> |report}",
25 	NULL
26 };
27 
28 static int __cmd_record(int argc, const char **argv)
29 {
30 	int rec_argc, i = 0, j;
31 	const char **rec_argv;
32 	char event[64];
33 	int ret;
34 
35 	rec_argc = argc + 4;
36 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
37 	if (!rec_argv)
38 		return -1;
39 
40 	rec_argv[i++] = strdup("record");
41 	if (!strcmp(mem_operation, MEM_OPERATION_LOAD))
42 		rec_argv[i++] = strdup("-W");
43 	rec_argv[i++] = strdup("-d");
44 	rec_argv[i++] = strdup("-e");
45 
46 	if (strcmp(mem_operation, MEM_OPERATION_LOAD))
47 		sprintf(event, "cpu/mem-stores/pp");
48 	else
49 		sprintf(event, "cpu/mem-loads/pp");
50 
51 	rec_argv[i++] = strdup(event);
52 	for (j = 1; j < argc; j++, i++)
53 		rec_argv[i] = argv[j];
54 
55 	ret = cmd_record(i, rec_argv, NULL);
56 	free(rec_argv);
57 	return ret;
58 }
59 
60 static int
61 dump_raw_samples(struct perf_tool *tool,
62 		 union perf_event *event,
63 		 struct perf_sample *sample,
64 		 struct perf_evsel *evsel __maybe_unused,
65 		 struct machine *machine)
66 {
67 	struct perf_mem *mem = container_of(tool, struct perf_mem, tool);
68 	struct addr_location al;
69 	const char *fmt;
70 
71 	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
72 		fprintf(stderr, "problem processing %d event, skipping it.\n",
73 				event->header.type);
74 		return -1;
75 	}
76 
77 	if (al.filtered || (mem->hide_unresolved && al.sym == NULL))
78 		return 0;
79 
80 	if (al.map != NULL)
81 		al.map->dso->hit = 1;
82 
83 	if (symbol_conf.field_sep) {
84 		fmt = "%d%s%d%s0x%"PRIx64"%s0x%"PRIx64"%s%"PRIu64
85 		      "%s0x%"PRIx64"%s%s:%s\n";
86 	} else {
87 		fmt = "%5d%s%5d%s0x%016"PRIx64"%s0x016%"PRIx64
88 		      "%s%5"PRIu64"%s0x%06"PRIx64"%s%s:%s\n";
89 		symbol_conf.field_sep = " ";
90 	}
91 
92 	printf(fmt,
93 		sample->pid,
94 		symbol_conf.field_sep,
95 		sample->tid,
96 		symbol_conf.field_sep,
97 		sample->ip,
98 		symbol_conf.field_sep,
99 		sample->addr,
100 		symbol_conf.field_sep,
101 		sample->weight,
102 		symbol_conf.field_sep,
103 		sample->data_src,
104 		symbol_conf.field_sep,
105 		al.map ? (al.map->dso ? al.map->dso->long_name : "???") : "???",
106 		al.sym ? al.sym->name : "???");
107 
108 	return 0;
109 }
110 
111 static int process_sample_event(struct perf_tool *tool,
112 				union perf_event *event,
113 				struct perf_sample *sample,
114 				struct perf_evsel *evsel,
115 				struct machine *machine)
116 {
117 	return dump_raw_samples(tool, event, sample, evsel, machine);
118 }
119 
120 static int report_raw_events(struct perf_mem *mem)
121 {
122 	int err = -EINVAL;
123 	int ret;
124 	struct perf_session *session = perf_session__new(input_name, O_RDONLY,
125 							 0, false, &mem->tool);
126 
127 	if (session == NULL)
128 		return -ENOMEM;
129 
130 	if (mem->cpu_list) {
131 		ret = perf_session__cpu_bitmap(session, mem->cpu_list,
132 					       mem->cpu_bitmap);
133 		if (ret)
134 			goto out_delete;
135 	}
136 
137 	if (symbol__init() < 0)
138 		return -1;
139 
140 	printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n");
141 
142 	err = perf_session__process_events(session, &mem->tool);
143 	if (err)
144 		return err;
145 
146 	return 0;
147 
148 out_delete:
149 	perf_session__delete(session);
150 	return err;
151 }
152 
153 static int report_events(int argc, const char **argv, struct perf_mem *mem)
154 {
155 	const char **rep_argv;
156 	int ret, i = 0, j, rep_argc;
157 
158 	if (mem->dump_raw)
159 		return report_raw_events(mem);
160 
161 	rep_argc = argc + 3;
162 	rep_argv = calloc(rep_argc + 1, sizeof(char *));
163 	if (!rep_argv)
164 		return -1;
165 
166 	rep_argv[i++] = strdup("report");
167 	rep_argv[i++] = strdup("--mem-mode");
168 	rep_argv[i++] = strdup("-n"); /* display number of samples */
169 
170 	/*
171 	 * there is no weight (cost) associated with stores, so don't print
172 	 * the column
173 	 */
174 	if (strcmp(mem_operation, MEM_OPERATION_LOAD))
175 		rep_argv[i++] = strdup("--sort=mem,sym,dso,symbol_daddr,"
176 				       "dso_daddr,tlb,locked");
177 
178 	for (j = 1; j < argc; j++, i++)
179 		rep_argv[i] = argv[j];
180 
181 	ret = cmd_report(i, rep_argv, NULL);
182 	free(rep_argv);
183 	return ret;
184 }
185 
186 int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
187 {
188 	struct stat st;
189 	struct perf_mem mem = {
190 		.tool = {
191 			.sample		= process_sample_event,
192 			.mmap		= perf_event__process_mmap,
193 			.mmap2		= perf_event__process_mmap2,
194 			.comm		= perf_event__process_comm,
195 			.lost		= perf_event__process_lost,
196 			.fork		= perf_event__process_fork,
197 			.build_id	= perf_event__process_build_id,
198 			.ordered_samples = true,
199 		},
200 		.input_name		 = "perf.data",
201 	};
202 	const struct option mem_options[] = {
203 	OPT_STRING('t', "type", &mem_operation,
204 		   "type", "memory operations(load/store)"),
205 	OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw,
206 		    "dump raw samples in ASCII"),
207 	OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved,
208 		    "Only display entries resolved to a symbol"),
209 	OPT_STRING('i', "input", &input_name, "file",
210 		   "input file name"),
211 	OPT_STRING('C', "cpu", &mem.cpu_list, "cpu",
212 		   "list of cpus to profile"),
213 	OPT_STRING('x', "field-separator", &symbol_conf.field_sep,
214 		   "separator",
215 		   "separator for columns, no spaces will be added"
216 		   " between columns '.' is reserved."),
217 	OPT_END()
218 	};
219 
220 	argc = parse_options(argc, argv, mem_options, mem_usage,
221 			     PARSE_OPT_STOP_AT_NON_OPTION);
222 
223 	if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation))
224 		usage_with_options(mem_usage, mem_options);
225 
226 	if (!mem.input_name || !strlen(mem.input_name)) {
227 		if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
228 			mem.input_name = "-";
229 		else
230 			mem.input_name = "perf.data";
231 	}
232 
233 	if (!strncmp(argv[0], "rec", 3))
234 		return __cmd_record(argc, argv);
235 	else if (!strncmp(argv[0], "rep", 3))
236 		return report_events(argc, argv, &mem);
237 	else
238 		usage_with_options(mem_usage, mem_options);
239 
240 	return 0;
241 }
242