aboutsummaryrefslogtreecommitdiffstats
path: root/Documentation/perf_counter/perf-report.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Documentation/perf_counter/perf-report.cc')
-rw-r--r--Documentation/perf_counter/perf-report.cc472
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
35static char const *input_name = "output.perf";
36static int input;
37
38static unsigned long page_size;
39static unsigned long mmap_window = 32;
40
41struct ip_event {
42 struct perf_event_header header;
43 __u64 ip;
44 __u32 pid, tid;
45};
46struct 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};
54struct comm_event {
55 struct perf_event_header header;
56 __u32 pid,tid;
57 char comm[16];
58};
59
60typedef 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
67struct 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
88typedef std::set<struct section> sections_t;
89
90struct 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
109typedef std::set<struct symbol> symbols_t;
110
111struct dso {
112 sections_t sections;
113 symbols_t syms;
114};
115
116static std::map<std::string, struct dso> dsos;
117
118static 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, &section, 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
164static 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
206static 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
213void 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
242struct 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
269typedef std::set<struct map> maps_t;
270
271static std::map<int, maps_t> maps;
272
273static std::map<int, std::string> comms;
274
275static std::map<std::string, int> hist;
276static std::multimap<int, std::string> rev_hist;
277
278static 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
288static 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
318static 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
335static 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
345static 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
371int 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
399remap:
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
407more:
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