diff options
Diffstat (limited to 'Documentation/perf_counter/perf-report.cc')
-rw-r--r-- | Documentation/perf_counter/perf-report.cc | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/Documentation/perf_counter/perf-report.cc b/Documentation/perf_counter/perf-report.cc new file mode 100644 index 000000000000..09da0ba482cd --- /dev/null +++ b/Documentation/perf_counter/perf-report.cc | |||
@@ -0,0 +1,472 @@ | |||
1 | #define _GNU_SOURCE | ||
2 | #include <sys/types.h> | ||
3 | #include <sys/stat.h> | ||
4 | #include <sys/time.h> | ||
5 | #include <unistd.h> | ||
6 | #include <stdint.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <string.h> | ||
9 | #include <limits.h> | ||
10 | #include <fcntl.h> | ||
11 | #include <stdio.h> | ||
12 | #include <errno.h> | ||
13 | #include <ctype.h> | ||
14 | #include <time.h> | ||
15 | #include <getopt.h> | ||
16 | |||
17 | #include <sys/ioctl.h> | ||
18 | #include <sys/poll.h> | ||
19 | #include <sys/prctl.h> | ||
20 | #include <sys/wait.h> | ||
21 | #include <sys/mman.h> | ||
22 | #include <sys/types.h> | ||
23 | #include <sys/stat.h> | ||
24 | |||
25 | #include <linux/unistd.h> | ||
26 | #include <linux/types.h> | ||
27 | |||
28 | #include "../../include/linux/perf_counter.h" | ||
29 | |||
30 | #include <set> | ||
31 | #include <map> | ||
32 | #include <string> | ||
33 | |||
34 | |||
35 | static char const *input_name = "output.perf"; | ||
36 | static int input; | ||
37 | |||
38 | static unsigned long page_size; | ||
39 | static unsigned long mmap_window = 32; | ||
40 | |||
41 | struct ip_event { | ||
42 | struct perf_event_header header; | ||
43 | __u64 ip; | ||
44 | __u32 pid, tid; | ||
45 | }; | ||
46 | struct mmap_event { | ||
47 | struct perf_event_header header; | ||
48 | __u32 pid, tid; | ||
49 | __u64 start; | ||
50 | __u64 len; | ||
51 | __u64 pgoff; | ||
52 | char filename[PATH_MAX]; | ||
53 | }; | ||
54 | struct comm_event { | ||
55 | struct perf_event_header header; | ||
56 | __u32 pid,tid; | ||
57 | char comm[16]; | ||
58 | }; | ||
59 | |||
60 | typedef union event_union { | ||
61 | struct perf_event_header header; | ||
62 | struct ip_event ip; | ||
63 | struct mmap_event mmap; | ||
64 | struct comm_event comm; | ||
65 | } event_t; | ||
66 | |||
67 | struct section { | ||
68 | uint64_t start; | ||
69 | uint64_t end; | ||
70 | |||
71 | uint64_t offset; | ||
72 | |||
73 | std::string name; | ||
74 | |||
75 | section() { }; | ||
76 | |||
77 | section(uint64_t stab) : end(stab) { }; | ||
78 | |||
79 | section(uint64_t start, uint64_t size, uint64_t offset, std::string name) : | ||
80 | start(start), end(start + size), offset(offset), name(name) | ||
81 | { }; | ||
82 | |||
83 | bool operator < (const struct section &s) const { | ||
84 | return end < s.end; | ||
85 | }; | ||
86 | }; | ||
87 | |||
88 | typedef std::set<struct section> sections_t; | ||
89 | |||
90 | struct symbol { | ||
91 | uint64_t start; | ||
92 | uint64_t end; | ||
93 | |||
94 | std::string name; | ||
95 | |||
96 | symbol() { }; | ||
97 | |||
98 | symbol(uint64_t ip) : start(ip) { } | ||
99 | |||
100 | symbol(uint64_t start, uint64_t len, std::string name) : | ||
101 | start(start), end(start + len), name(name) | ||
102 | { }; | ||
103 | |||
104 | bool operator < (const struct symbol &s) const { | ||
105 | return start < s.start; | ||
106 | }; | ||
107 | }; | ||
108 | |||
109 | typedef std::set<struct symbol> symbols_t; | ||
110 | |||
111 | struct dso { | ||
112 | sections_t sections; | ||
113 | symbols_t syms; | ||
114 | }; | ||
115 | |||
116 | static std::map<std::string, struct dso> dsos; | ||
117 | |||
118 | static void load_dso_sections(std::string dso_name) | ||
119 | { | ||
120 | struct dso &dso = dsos[dso_name]; | ||
121 | |||
122 | std::string cmd = "readelf -DSW " + dso_name; | ||
123 | |||
124 | FILE *file = popen(cmd.c_str(), "r"); | ||
125 | if (!file) { | ||
126 | perror("failed to open pipe"); | ||
127 | exit(-1); | ||
128 | } | ||
129 | |||
130 | char *line = NULL; | ||
131 | size_t n = 0; | ||
132 | |||
133 | while (!feof(file)) { | ||
134 | uint64_t addr, off, size; | ||
135 | char name[32]; | ||
136 | |||
137 | if (getline(&line, &n, file) < 0) | ||
138 | break; | ||
139 | if (!line) | ||
140 | break; | ||
141 | |||
142 | if (sscanf(line, " [%*2d] %16s %*14s %Lx %Lx %Lx", | ||
143 | name, &addr, &off, &size) == 4) { | ||
144 | |||
145 | dso.sections.insert(section(addr, size, addr - off, name)); | ||
146 | } | ||
147 | #if 0 | ||
148 | /* | ||
149 | * for reading readelf symbols (-s), however these don't seem | ||
150 | * to include nearly everything, so use nm for that. | ||
151 | */ | ||
152 | if (sscanf(line, " %*4d %*3d: %Lx %5Lu %*7s %*6s %*7s %3d %s", | ||
153 | &start, &size, §ion, sym) == 4) { | ||
154 | |||
155 | start -= dso.section_offsets[section]; | ||
156 | |||
157 | dso.syms.insert(symbol(start, size, std::string(sym))); | ||
158 | } | ||
159 | #endif | ||
160 | } | ||
161 | pclose(file); | ||
162 | } | ||
163 | |||
164 | static void load_dso_symbols(std::string dso_name, std::string args) | ||
165 | { | ||
166 | struct dso &dso = dsos[dso_name]; | ||
167 | |||
168 | std::string cmd = "nm -nSC " + args + " " + dso_name; | ||
169 | |||
170 | FILE *file = popen(cmd.c_str(), "r"); | ||
171 | if (!file) { | ||
172 | perror("failed to open pipe"); | ||
173 | exit(-1); | ||
174 | } | ||
175 | |||
176 | char *line = NULL; | ||
177 | size_t n = 0; | ||
178 | |||
179 | while (!feof(file)) { | ||
180 | uint64_t start, size; | ||
181 | char c; | ||
182 | char sym[1024]; | ||
183 | |||
184 | if (getline(&line, &n, file) < 0) | ||
185 | break; | ||
186 | if (!line) | ||
187 | break; | ||
188 | |||
189 | |||
190 | if (sscanf(line, "%Lx %Lx %c %s", &start, &size, &c, sym) == 4) { | ||
191 | sections_t::const_iterator si = | ||
192 | dso.sections.upper_bound(section(start)); | ||
193 | if (si == dso.sections.end()) { | ||
194 | printf("symbol in unknown section: %s\n", sym); | ||
195 | continue; | ||
196 | } | ||
197 | |||
198 | start -= si->offset; | ||
199 | |||
200 | dso.syms.insert(symbol(start, size, sym)); | ||
201 | } | ||
202 | } | ||
203 | pclose(file); | ||
204 | } | ||
205 | |||
206 | static void load_dso(std::string dso_name) | ||
207 | { | ||
208 | load_dso_sections(dso_name); | ||
209 | load_dso_symbols(dso_name, "-D"); /* dynamic symbols */ | ||
210 | load_dso_symbols(dso_name, ""); /* regular ones */ | ||
211 | } | ||
212 | |||
213 | void load_kallsyms(void) | ||
214 | { | ||
215 | struct dso &dso = dsos["[kernel]"]; | ||
216 | |||
217 | FILE *file = fopen("/proc/kallsyms", "r"); | ||
218 | if (!file) { | ||
219 | perror("failed to open kallsyms"); | ||
220 | exit(-1); | ||
221 | } | ||
222 | |||
223 | char *line; | ||
224 | size_t n; | ||
225 | |||
226 | while (!feof(file)) { | ||
227 | uint64_t start; | ||
228 | char c; | ||
229 | char sym[1024]; | ||
230 | |||
231 | if (getline(&line, &n, file) < 0) | ||
232 | break; | ||
233 | if (!line) | ||
234 | break; | ||
235 | |||
236 | if (sscanf(line, "%Lx %c %s", &start, &c, sym) == 3) | ||
237 | dso.syms.insert(symbol(start, 0x1000000, std::string(sym))); | ||
238 | } | ||
239 | fclose(file); | ||
240 | } | ||
241 | |||
242 | struct map { | ||
243 | uint64_t start; | ||
244 | uint64_t end; | ||
245 | uint64_t pgoff; | ||
246 | |||
247 | std::string dso; | ||
248 | |||
249 | map() { }; | ||
250 | |||
251 | map(uint64_t ip) : end(ip) { } | ||
252 | |||
253 | map(mmap_event *mmap) { | ||
254 | start = mmap->start; | ||
255 | end = mmap->start + mmap->len; | ||
256 | pgoff = mmap->pgoff; | ||
257 | |||
258 | dso = std::string(mmap->filename); | ||
259 | |||
260 | if (dsos.find(dso) == dsos.end()) | ||
261 | load_dso(dso); | ||
262 | }; | ||
263 | |||
264 | bool operator < (const struct map &m) const { | ||
265 | return end < m.end; | ||
266 | }; | ||
267 | }; | ||
268 | |||
269 | typedef std::set<struct map> maps_t; | ||
270 | |||
271 | static std::map<int, maps_t> maps; | ||
272 | |||
273 | static std::map<int, std::string> comms; | ||
274 | |||
275 | static std::map<std::string, int> hist; | ||
276 | static std::multimap<int, std::string> rev_hist; | ||
277 | |||
278 | static std::string resolve_comm(int pid) | ||
279 | { | ||
280 | std::string comm = "<unknown>"; | ||
281 | std::map<int, std::string>::const_iterator ci = comms.find(pid); | ||
282 | if (ci != comms.end()) | ||
283 | comm = ci->second; | ||
284 | |||
285 | return comm; | ||
286 | } | ||
287 | |||
288 | static std::string resolve_user_symbol(int pid, uint64_t ip) | ||
289 | { | ||
290 | std::string sym = "<unknown>"; | ||
291 | |||
292 | maps_t &m = maps[pid]; | ||
293 | maps_t::const_iterator mi = m.upper_bound(map(ip)); | ||
294 | if (mi == m.end()) | ||
295 | return sym; | ||
296 | |||
297 | ip -= mi->start + mi->pgoff; | ||
298 | |||
299 | symbols_t &s = dsos[mi->dso].syms; | ||
300 | symbols_t::const_iterator si = s.upper_bound(symbol(ip)); | ||
301 | |||
302 | sym = mi->dso + ": <unknown>"; | ||
303 | |||
304 | if (si == s.begin()) | ||
305 | return sym; | ||
306 | si--; | ||
307 | |||
308 | if (si->start <= ip && ip < si->end) | ||
309 | sym = mi->dso + ": " + si->name; | ||
310 | #if 0 | ||
311 | else if (si->start <= ip) | ||
312 | sym = mi->dso + ": ?" + si->name; | ||
313 | #endif | ||
314 | |||
315 | return sym; | ||
316 | } | ||
317 | |||
318 | static std::string resolve_kernel_symbol(uint64_t ip) | ||
319 | { | ||
320 | std::string sym = "<unknown>"; | ||
321 | |||
322 | symbols_t &s = dsos["[kernel]"].syms; | ||
323 | symbols_t::const_iterator si = s.upper_bound(symbol(ip)); | ||
324 | |||
325 | if (si == s.begin()) | ||
326 | return sym; | ||
327 | si--; | ||
328 | |||
329 | if (si->start <= ip && ip < si->end) | ||
330 | sym = si->name; | ||
331 | |||
332 | return sym; | ||
333 | } | ||
334 | |||
335 | static void display_help(void) | ||
336 | { | ||
337 | printf( | ||
338 | "Usage: perf-report [<options>]\n" | ||
339 | " -i file --input=<file> # input file\n" | ||
340 | ); | ||
341 | |||
342 | exit(0); | ||
343 | } | ||
344 | |||
345 | static void process_options(int argc, char *argv[]) | ||
346 | { | ||
347 | int error = 0; | ||
348 | |||
349 | for (;;) { | ||
350 | int option_index = 0; | ||
351 | /** Options for getopt */ | ||
352 | static struct option long_options[] = { | ||
353 | {"input", required_argument, NULL, 'i'}, | ||
354 | {NULL, 0, NULL, 0 } | ||
355 | }; | ||
356 | int c = getopt_long(argc, argv, "+:i:", | ||
357 | long_options, &option_index); | ||
358 | if (c == -1) | ||
359 | break; | ||
360 | |||
361 | switch (c) { | ||
362 | case 'i': input_name = strdup(optarg); break; | ||
363 | default: error = 1; break; | ||
364 | } | ||
365 | } | ||
366 | |||
367 | if (error) | ||
368 | display_help(); | ||
369 | } | ||
370 | |||
371 | int main(int argc, char *argv[]) | ||
372 | { | ||
373 | unsigned long offset = 0; | ||
374 | unsigned long head = 0; | ||
375 | struct stat stat; | ||
376 | char *buf; | ||
377 | event_t *event; | ||
378 | int ret; | ||
379 | unsigned long total = 0; | ||
380 | |||
381 | page_size = getpagesize(); | ||
382 | |||
383 | process_options(argc, argv); | ||
384 | |||
385 | input = open(input_name, O_RDONLY); | ||
386 | if (input < 0) { | ||
387 | perror("failed to open file"); | ||
388 | exit(-1); | ||
389 | } | ||
390 | |||
391 | ret = fstat(input, &stat); | ||
392 | if (ret < 0) { | ||
393 | perror("failed to stat file"); | ||
394 | exit(-1); | ||
395 | } | ||
396 | |||
397 | load_kallsyms(); | ||
398 | |||
399 | remap: | ||
400 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | ||
401 | MAP_SHARED, input, offset); | ||
402 | if (buf == MAP_FAILED) { | ||
403 | perror("failed to mmap file"); | ||
404 | exit(-1); | ||
405 | } | ||
406 | |||
407 | more: | ||
408 | event = (event_t *)(buf + head); | ||
409 | |||
410 | if (head + event->header.size >= page_size * mmap_window) { | ||
411 | unsigned long shift = page_size * (head / page_size); | ||
412 | |||
413 | munmap(buf, page_size * mmap_window); | ||
414 | offset += shift; | ||
415 | head -= shift; | ||
416 | goto remap; | ||
417 | } | ||
418 | head += event->header.size; | ||
419 | |||
420 | if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) { | ||
421 | std::string comm, sym, level; | ||
422 | char output[1024]; | ||
423 | |||
424 | if (event->header.misc & PERF_EVENT_MISC_KERNEL) { | ||
425 | level = "[kernel]"; | ||
426 | sym = resolve_kernel_symbol(event->ip.ip); | ||
427 | } else if (event->header.misc & PERF_EVENT_MISC_USER) { | ||
428 | level = "[ user ]"; | ||
429 | sym = resolve_user_symbol(event->ip.pid, event->ip.ip); | ||
430 | } else { | ||
431 | level = "[ hv ]"; | ||
432 | } | ||
433 | comm = resolve_comm(event->ip.pid); | ||
434 | |||
435 | snprintf(output, sizeof(output), "%16s %s %s", | ||
436 | comm.c_str(), level.c_str(), sym.c_str()); | ||
437 | hist[output]++; | ||
438 | |||
439 | total++; | ||
440 | |||
441 | } else switch (event->header.type) { | ||
442 | case PERF_EVENT_MMAP: | ||
443 | maps[event->mmap.pid].insert(map(&event->mmap)); | ||
444 | break; | ||
445 | |||
446 | case PERF_EVENT_COMM: | ||
447 | comms[event->comm.pid] = std::string(event->comm.comm); | ||
448 | break; | ||
449 | } | ||
450 | |||
451 | if (offset + head < stat.st_size) | ||
452 | goto more; | ||
453 | |||
454 | close(input); | ||
455 | |||
456 | std::map<std::string, int>::iterator hi = hist.begin(); | ||
457 | |||
458 | while (hi != hist.end()) { | ||
459 | rev_hist.insert(std::pair<int, std::string>(hi->second, hi->first)); | ||
460 | hist.erase(hi++); | ||
461 | } | ||
462 | |||
463 | std::multimap<int, std::string>::const_iterator ri = rev_hist.begin(); | ||
464 | |||
465 | while (ri != rev_hist.end()) { | ||
466 | printf(" %5.2f %s\n", (100.0 * ri->first)/total, ri->second.c_str()); | ||
467 | ri++; | ||
468 | } | ||
469 | |||
470 | return 0; | ||
471 | } | ||
472 | |||