blob: 1727317352bf71e4afc2d5c5bda18cdb19014163 [file] [log] [blame]
Peter Zijlstrade9ac072009-04-08 15:01:31 +02001#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{
Ingo Molnarb3828eb2009-04-09 09:50:04 +0200280 std::string comm;
281
Peter Zijlstrade9ac072009-04-08 15:01:31 +0200282 std::map<int, std::string>::const_iterator ci = comms.find(pid);
Ingo Molnarb3828eb2009-04-09 09:50:04 +0200283 if (ci != comms.end()) {
Peter Zijlstrade9ac072009-04-08 15:01:31 +0200284 comm = ci->second;
Ingo Molnarb3828eb2009-04-09 09:50:04 +0200285 } else {
286 char pid_str[30];
287
288 sprintf(pid_str, ":%d", pid);
289 comm = pid_str;
290 }
Peter Zijlstrade9ac072009-04-08 15:01:31 +0200291
292 return comm;
293}
294
295static std::string resolve_user_symbol(int pid, uint64_t ip)
296{
297 std::string sym = "<unknown>";
298
299 maps_t &m = maps[pid];
300 maps_t::const_iterator mi = m.upper_bound(map(ip));
301 if (mi == m.end())
302 return sym;
303
304 ip -= mi->start + mi->pgoff;
305
306 symbols_t &s = dsos[mi->dso].syms;
307 symbols_t::const_iterator si = s.upper_bound(symbol(ip));
308
309 sym = mi->dso + ": <unknown>";
310
311 if (si == s.begin())
312 return sym;
313 si--;
314
315 if (si->start <= ip && ip < si->end)
316 sym = mi->dso + ": " + si->name;
317#if 0
318 else if (si->start <= ip)
319 sym = mi->dso + ": ?" + si->name;
320#endif
321
322 return sym;
323}
324
325static std::string resolve_kernel_symbol(uint64_t ip)
326{
327 std::string sym = "<unknown>";
328
329 symbols_t &s = dsos["[kernel]"].syms;
330 symbols_t::const_iterator si = s.upper_bound(symbol(ip));
331
332 if (si == s.begin())
333 return sym;
334 si--;
335
336 if (si->start <= ip && ip < si->end)
337 sym = si->name;
338
339 return sym;
340}
341
342static void display_help(void)
343{
344 printf(
345 "Usage: perf-report [<options>]\n"
346 " -i file --input=<file> # input file\n"
347 );
348
349 exit(0);
350}
351
352static void process_options(int argc, char *argv[])
353{
354 int error = 0;
355
356 for (;;) {
357 int option_index = 0;
358 /** Options for getopt */
359 static struct option long_options[] = {
360 {"input", required_argument, NULL, 'i'},
361 {NULL, 0, NULL, 0 }
362 };
363 int c = getopt_long(argc, argv, "+:i:",
364 long_options, &option_index);
365 if (c == -1)
366 break;
367
368 switch (c) {
369 case 'i': input_name = strdup(optarg); break;
370 default: error = 1; break;
371 }
372 }
373
374 if (error)
375 display_help();
376}
377
378int main(int argc, char *argv[])
379{
380 unsigned long offset = 0;
381 unsigned long head = 0;
382 struct stat stat;
383 char *buf;
384 event_t *event;
385 int ret;
386 unsigned long total = 0;
387
388 page_size = getpagesize();
389
390 process_options(argc, argv);
391
392 input = open(input_name, O_RDONLY);
393 if (input < 0) {
394 perror("failed to open file");
395 exit(-1);
396 }
397
398 ret = fstat(input, &stat);
399 if (ret < 0) {
400 perror("failed to stat file");
401 exit(-1);
402 }
403
404 load_kallsyms();
405
406remap:
407 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
408 MAP_SHARED, input, offset);
409 if (buf == MAP_FAILED) {
410 perror("failed to mmap file");
411 exit(-1);
412 }
413
414more:
415 event = (event_t *)(buf + head);
416
417 if (head + event->header.size >= page_size * mmap_window) {
418 unsigned long shift = page_size * (head / page_size);
419
420 munmap(buf, page_size * mmap_window);
421 offset += shift;
422 head -= shift;
423 goto remap;
424 }
425 head += event->header.size;
426
427 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
428 std::string comm, sym, level;
429 char output[1024];
430
431 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
Ingo Molnarb3828eb2009-04-09 09:50:04 +0200432 level = " [k] ";
Peter Zijlstrade9ac072009-04-08 15:01:31 +0200433 sym = resolve_kernel_symbol(event->ip.ip);
434 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
Ingo Molnarb3828eb2009-04-09 09:50:04 +0200435 level = " [.] ";
Peter Zijlstrade9ac072009-04-08 15:01:31 +0200436 sym = resolve_user_symbol(event->ip.pid, event->ip.ip);
437 } else {
Ingo Molnarb3828eb2009-04-09 09:50:04 +0200438 level = " [H] ";
Peter Zijlstrade9ac072009-04-08 15:01:31 +0200439 }
440 comm = resolve_comm(event->ip.pid);
441
442 snprintf(output, sizeof(output), "%16s %s %s",
443 comm.c_str(), level.c_str(), sym.c_str());
444 hist[output]++;
445
446 total++;
447
448 } else switch (event->header.type) {
449 case PERF_EVENT_MMAP:
450 maps[event->mmap.pid].insert(map(&event->mmap));
451 break;
452
453 case PERF_EVENT_COMM:
454 comms[event->comm.pid] = std::string(event->comm.comm);
455 break;
456 }
457
458 if (offset + head < stat.st_size)
459 goto more;
460
461 close(input);
462
463 std::map<std::string, int>::iterator hi = hist.begin();
464
465 while (hi != hist.end()) {
466 rev_hist.insert(std::pair<int, std::string>(hi->second, hi->first));
467 hist.erase(hi++);
468 }
469
470 std::multimap<int, std::string>::const_iterator ri = rev_hist.begin();
471
472 while (ri != rev_hist.end()) {
473 printf(" %5.2f %s\n", (100.0 * ri->first)/total, ri->second.c_str());
474 ri++;
475 }
476
477 return 0;
478}
479