perf_counter tools: clean up after introduction of the Git command framework
[linux-2.6] / Documentation / perf_counter / perf-report.cc
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, &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
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;
281
282         std::map<int, std::string>::const_iterator ci = comms.find(pid);
283         if (ci != comms.end()) {
284                 comm = ci->second;
285         } else {
286                 char pid_str[30];
287
288                 sprintf(pid_str, ":%d", pid);
289                 comm = pid_str;
290         }
291
292         return comm;
293 }
294
295 static 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
325 static 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
342 static 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
352 static 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
378 int 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
406 remap:
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
414 more:
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) {
432                         level = " [k] ";
433                         sym = resolve_kernel_symbol(event->ip.ip);
434                 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
435                         level = " [.] ";
436                         sym = resolve_user_symbol(event->ip.pid, event->ip.ip);
437                 } else {
438                         level = " [H] ";
439                 }
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