18 #include <sys/ioctl.h>
20 #include <sys/prctl.h>
23 #include <sys/types.h>
26 #include <linux/unistd.h>
27 #include <linux/types.h>
29 #include "../../include/linux/perf_counter.h"
40 static char const *input_name
= "output.perf";
42 static int show_mask
= SHOW_KERNEL
| SHOW_USER
| SHOW_HV
;
44 static unsigned long page_size
;
45 static unsigned long mmap_window
= 32;
48 struct perf_event_header header
;
53 struct perf_event_header header
;
58 char filename
[PATH_MAX
];
61 struct perf_event_header header
;
66 typedef union event_union
{
67 struct perf_event_header header
;
69 struct mmap_event mmap
;
70 struct comm_event comm
;
83 section(uint64_t stab
) : end(stab
) { };
85 section(uint64_t start
, uint64_t size
, uint64_t offset
, std::string name
) :
86 start(start
), end(start
+ size
), offset(offset
), name(name
)
89 bool operator < (const struct section
&s
) const {
94 typedef std::set
<struct section
> sections_t
;
104 symbol(uint64_t ip
) : start(ip
) { }
106 symbol(uint64_t start
, uint64_t len
, std::string name
) :
107 start(start
), end(start
+ len
), name(name
)
110 bool operator < (const struct symbol
&s
) const {
111 return start
< s
.start
;
115 typedef std::set
<struct symbol
> symbols_t
;
122 static std::map
<std::string
, struct dso
> dsos
;
124 static void load_dso_sections(std::string dso_name
)
126 struct dso
&dso
= dsos
[dso_name
];
128 std::string cmd
= "readelf -DSW " + dso_name
;
130 FILE *file
= popen(cmd
.c_str(), "r");
132 perror("failed to open pipe");
139 while (!feof(file
)) {
140 uint64_t addr
, off
, size
;
143 if (getline(&line
, &n
, file
) < 0)
148 if (sscanf(line
, " [%*2d] %16s %*14s %Lx %Lx %Lx",
149 name
, &addr
, &off
, &size
) == 4) {
151 dso
.sections
.insert(section(addr
, size
, addr
- off
, name
));
155 * for reading readelf symbols (-s), however these don't seem
156 * to include nearly everything, so use nm for that.
158 if (sscanf(line
, " %*4d %*3d: %Lx %5Lu %*7s %*6s %*7s %3d %s",
159 &start
, &size
, §ion
, sym
) == 4) {
161 start
-= dso
.section_offsets
[section
];
163 dso
.syms
.insert(symbol(start
, size
, std::string(sym
)));
170 static void load_dso_symbols(std::string dso_name
, std::string args
)
172 struct dso
&dso
= dsos
[dso_name
];
174 std::string cmd
= "nm -nSC " + args
+ " " + dso_name
;
176 FILE *file
= popen(cmd
.c_str(), "r");
178 perror("failed to open pipe");
185 while (!feof(file
)) {
186 uint64_t start
, size
;
190 if (getline(&line
, &n
, file
) < 0)
196 if (sscanf(line
, "%Lx %Lx %c %s", &start
, &size
, &c
, sym
) == 4) {
197 sections_t::const_iterator si
=
198 dso
.sections
.upper_bound(section(start
));
199 if (si
== dso
.sections
.end()) {
200 printf("symbol in unknown section: %s\n", sym
);
206 dso
.syms
.insert(symbol(start
, size
, sym
));
212 static void load_dso(std::string dso_name
)
214 load_dso_sections(dso_name
);
215 load_dso_symbols(dso_name
, "-D"); /* dynamic symbols */
216 load_dso_symbols(dso_name
, ""); /* regular ones */
219 void load_kallsyms(void)
221 struct dso
&dso
= dsos
["[kernel]"];
223 FILE *file
= fopen("/proc/kallsyms", "r");
225 perror("failed to open kallsyms");
232 while (!feof(file
)) {
237 if (getline(&line
, &n
, file
) < 0)
242 if (sscanf(line
, "%Lx %c %s", &start
, &c
, sym
) == 3)
243 dso
.syms
.insert(symbol(start
, 0x1000000, std::string(sym
)));
257 map(uint64_t ip
) : end(ip
) { }
259 map(mmap_event
*mmap
) {
261 end
= mmap
->start
+ mmap
->len
;
264 dso
= std::string(mmap
->filename
);
266 if (dsos
.find(dso
) == dsos
.end())
270 bool operator < (const struct map
&m
) const {
275 typedef std::set
<struct map
> maps_t
;
277 static std::map
<int, maps_t
> maps
;
279 static std::map
<int, std::string
> comms
;
281 static std::map
<std::string
, int> hist
;
282 static std::multimap
<int, std::string
> rev_hist
;
284 static std::string
resolve_comm(int pid
)
288 std::map
<int, std::string
>::const_iterator ci
= comms
.find(pid
);
289 if (ci
!= comms
.end()) {
294 sprintf(pid_str
, ":%d", pid
);
301 static std::string
resolve_user_symbol(int pid
, uint64_t ip
)
303 std::string sym
= "<unknown>";
305 maps_t
&m
= maps
[pid
];
306 maps_t::const_iterator mi
= m
.upper_bound(map(ip
));
310 ip
-= mi
->start
+ mi
->pgoff
;
312 symbols_t
&s
= dsos
[mi
->dso
].syms
;
313 symbols_t::const_iterator si
= s
.upper_bound(symbol(ip
));
315 sym
= mi
->dso
+ ": <unknown>";
321 if (si
->start
<= ip
&& ip
< si
->end
)
322 sym
= mi
->dso
+ ": " + si
->name
;
324 else if (si
->start
<= ip
)
325 sym
= mi
->dso
+ ": ?" + si
->name
;
331 static std::string
resolve_kernel_symbol(uint64_t ip
)
333 std::string sym
= "<unknown>";
335 symbols_t
&s
= dsos
["[kernel]"].syms
;
336 symbols_t::const_iterator si
= s
.upper_bound(symbol(ip
));
342 if (si
->start
<= ip
&& ip
< si
->end
)
348 static void display_help(void)
351 "Usage: perf-report [<options>]\n"
352 " -i file --input=<file> # input file\n"
358 static void process_options(int argc
, char *argv
[])
363 int option_index
= 0;
364 /** Options for getopt */
365 static struct option long_options
[] = {
366 {"input", required_argument
, NULL
, 'i'},
367 {"no-user", no_argument
, NULL
, 'u'},
368 {"no-kernel", no_argument
, NULL
, 'k'},
369 {"no-hv", no_argument
, NULL
, 'h'},
372 int c
= getopt_long(argc
, argv
, "+:i:kuh",
373 long_options
, &option_index
);
378 case 'i': input_name
= strdup(optarg
); break;
379 case 'k': show_mask
&= ~SHOW_KERNEL
; break;
380 case 'u': show_mask
&= ~SHOW_USER
; break;
381 case 'h': show_mask
&= ~SHOW_HV
; break;
382 default: error
= 1; break;
390 int main(int argc
, char *argv
[])
392 unsigned long offset
= 0;
393 unsigned long head
= 0;
398 unsigned long total
= 0;
400 page_size
= getpagesize();
402 process_options(argc
, argv
);
404 input
= open(input_name
, O_RDONLY
);
406 perror("failed to open file");
410 ret
= fstat(input
, &stat
);
412 perror("failed to stat file");
417 fprintf(stderr
, "zero-sized file, nothing to do!\n");
424 buf
= (char *)mmap(NULL
, page_size
* mmap_window
, PROT_READ
,
425 MAP_SHARED
, input
, offset
);
426 if (buf
== MAP_FAILED
) {
427 perror("failed to mmap file");
432 event
= (event_t
*)(buf
+ head
);
434 if (head
+ event
->header
.size
>= page_size
* mmap_window
) {
435 unsigned long shift
= page_size
* (head
/ page_size
);
438 ret
= munmap(buf
, page_size
* mmap_window
);
447 if (!event
->header
.size
) {
448 fprintf(stderr
, "zero-sized event at file offset %ld\n", offset
+ head
);
449 fprintf(stderr
, "skipping %ld bytes of events.\n", stat
.st_size
- offset
- head
);
453 head
+= event
->header
.size
;
455 if (event
->header
.misc
& PERF_EVENT_MISC_OVERFLOW
) {
456 std::string comm
, sym
, level
;
460 if (event
->header
.misc
& PERF_EVENT_MISC_KERNEL
) {
463 sym
= resolve_kernel_symbol(event
->ip
.ip
);
464 } else if (event
->header
.misc
& PERF_EVENT_MISC_USER
) {
467 sym
= resolve_user_symbol(event
->ip
.pid
, event
->ip
.ip
);
473 if (show
& show_mask
) {
474 comm
= resolve_comm(event
->ip
.pid
);
475 snprintf(output
, sizeof(output
), "%16s %s %s",
476 comm
.c_str(), level
.c_str(), sym
.c_str());
482 } else switch (event
->header
.type
) {
483 case PERF_EVENT_MMAP
:
484 maps
[event
->mmap
.pid
].insert(map(&event
->mmap
));
487 case PERF_EVENT_COMM
:
488 comms
[event
->comm
.pid
] = std::string(event
->comm
.comm
);
492 if (offset
+ head
< stat
.st_size
)
499 std::map
<std::string
, int>::iterator hi
= hist
.begin();
501 while (hi
!= hist
.end()) {
502 rev_hist
.insert(std::pair
<int, std::string
>(hi
->second
, hi
->first
));
506 std::multimap
<int, std::string
>::const_iterator ri
= rev_hist
.begin();
508 while (ri
!= rev_hist
.end()) {
509 printf(" %5.2f %s\n", (100.0 * ri
->first
)/total
, ri
->second
.c_str());