diff options
Diffstat (limited to 'tools/perf')
| -rw-r--r-- | tools/perf/Makefile | 10 | ||||
| -rw-r--r-- | tools/perf/builtin-annotate.c | 262 | ||||
| -rw-r--r-- | tools/perf/builtin-record.c | 163 | ||||
| -rw-r--r-- | tools/perf/builtin-report.c | 439 | ||||
| -rw-r--r-- | tools/perf/builtin-stat.c | 308 | ||||
| -rw-r--r-- | tools/perf/builtin-top.c | 24 | ||||
| -rw-r--r-- | tools/perf/perf.h | 13 | ||||
| -rw-r--r-- | tools/perf/types.h | 17 | ||||
| -rw-r--r-- | tools/perf/util/ctype.c | 17 | ||||
| -rw-r--r-- | tools/perf/util/parse-events.c | 14 | ||||
| -rw-r--r-- | tools/perf/util/string.c | 2 | ||||
| -rw-r--r-- | tools/perf/util/string.h | 4 | ||||
| -rw-r--r-- | tools/perf/util/symbol.c | 20 | ||||
| -rw-r--r-- | tools/perf/util/symbol.h | 16 | ||||
| -rw-r--r-- | tools/perf/util/util.h | 18 |
15 files changed, 998 insertions, 329 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0cbd5d6874ec..36d7eef49913 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
| @@ -157,10 +157,15 @@ uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') | |||
| 157 | uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') | 157 | uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') |
| 158 | uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') | 158 | uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') |
| 159 | 159 | ||
| 160 | # If we're on a 64-bit kernel, use -m64 | ||
| 161 | ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M)) | ||
| 162 | M64 := -m64 | ||
| 163 | endif | ||
| 164 | |||
| 160 | # CFLAGS and LDFLAGS are for the users to override from the command line. | 165 | # CFLAGS and LDFLAGS are for the users to override from the command line. |
| 161 | 166 | ||
| 162 | CFLAGS = -ggdb3 -Wall -Werror -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -std=gnu99 -Wdeclaration-after-statement -O6 | 167 | CFLAGS = $(M64) -ggdb3 -Wall -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -std=gnu99 -Wdeclaration-after-statement -Werror -O6 |
| 163 | LDFLAGS = -lpthread -lrt -lelf | 168 | LDFLAGS = -lpthread -lrt -lelf -lm |
| 164 | ALL_CFLAGS = $(CFLAGS) | 169 | ALL_CFLAGS = $(CFLAGS) |
| 165 | ALL_LDFLAGS = $(LDFLAGS) | 170 | ALL_LDFLAGS = $(LDFLAGS) |
| 166 | STRIP ?= strip | 171 | STRIP ?= strip |
| @@ -285,6 +290,7 @@ LIB_FILE=libperf.a | |||
| 285 | 290 | ||
| 286 | LIB_H += ../../include/linux/perf_counter.h | 291 | LIB_H += ../../include/linux/perf_counter.h |
| 287 | LIB_H += perf.h | 292 | LIB_H += perf.h |
| 293 | LIB_H += types.h | ||
| 288 | LIB_H += util/list.h | 294 | LIB_H += util/list.h |
| 289 | LIB_H += util/rbtree.h | 295 | LIB_H += util/rbtree.h |
| 290 | LIB_H += util/levenshtein.h | 296 | LIB_H += util/levenshtein.h |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index b1ed5f766cb3..7e58e3ad1508 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -25,6 +25,10 @@ | |||
| 25 | #define SHOW_USER 2 | 25 | #define SHOW_USER 2 |
| 26 | #define SHOW_HV 4 | 26 | #define SHOW_HV 4 |
| 27 | 27 | ||
| 28 | #define MIN_GREEN 0.5 | ||
| 29 | #define MIN_RED 5.0 | ||
| 30 | |||
| 31 | |||
| 28 | static char const *input_name = "perf.data"; | 32 | static char const *input_name = "perf.data"; |
| 29 | static char *vmlinux = "vmlinux"; | 33 | static char *vmlinux = "vmlinux"; |
| 30 | 34 | ||
| @@ -39,40 +43,42 @@ static int dump_trace = 0; | |||
| 39 | 43 | ||
| 40 | static int verbose; | 44 | static int verbose; |
| 41 | 45 | ||
| 46 | static int print_line; | ||
| 47 | |||
| 42 | static unsigned long page_size; | 48 | static unsigned long page_size; |
| 43 | static unsigned long mmap_window = 32; | 49 | static unsigned long mmap_window = 32; |
| 44 | 50 | ||
| 45 | struct ip_event { | 51 | struct ip_event { |
| 46 | struct perf_event_header header; | 52 | struct perf_event_header header; |
| 47 | __u64 ip; | 53 | u64 ip; |
| 48 | __u32 pid, tid; | 54 | u32 pid, tid; |
| 49 | }; | 55 | }; |
| 50 | 56 | ||
| 51 | struct mmap_event { | 57 | struct mmap_event { |
| 52 | struct perf_event_header header; | 58 | struct perf_event_header header; |
| 53 | __u32 pid, tid; | 59 | u32 pid, tid; |
| 54 | __u64 start; | 60 | u64 start; |
| 55 | __u64 len; | 61 | u64 len; |
| 56 | __u64 pgoff; | 62 | u64 pgoff; |
| 57 | char filename[PATH_MAX]; | 63 | char filename[PATH_MAX]; |
| 58 | }; | 64 | }; |
| 59 | 65 | ||
| 60 | struct comm_event { | 66 | struct comm_event { |
| 61 | struct perf_event_header header; | 67 | struct perf_event_header header; |
| 62 | __u32 pid, tid; | 68 | u32 pid, tid; |
| 63 | char comm[16]; | 69 | char comm[16]; |
| 64 | }; | 70 | }; |
| 65 | 71 | ||
| 66 | struct fork_event { | 72 | struct fork_event { |
| 67 | struct perf_event_header header; | 73 | struct perf_event_header header; |
| 68 | __u32 pid, ppid; | 74 | u32 pid, ppid; |
| 69 | }; | 75 | }; |
| 70 | 76 | ||
| 71 | struct period_event { | 77 | struct period_event { |
| 72 | struct perf_event_header header; | 78 | struct perf_event_header header; |
| 73 | __u64 time; | 79 | u64 time; |
| 74 | __u64 id; | 80 | u64 id; |
| 75 | __u64 sample_period; | 81 | u64 sample_period; |
| 76 | }; | 82 | }; |
| 77 | 83 | ||
| 78 | typedef union event_union { | 84 | typedef union event_union { |
| @@ -84,6 +90,13 @@ typedef union event_union { | |||
| 84 | struct period_event period; | 90 | struct period_event period; |
| 85 | } event_t; | 91 | } event_t; |
| 86 | 92 | ||
| 93 | |||
| 94 | struct sym_ext { | ||
| 95 | struct rb_node node; | ||
| 96 | double percent; | ||
| 97 | char *path; | ||
| 98 | }; | ||
| 99 | |||
| 87 | static LIST_HEAD(dsos); | 100 | static LIST_HEAD(dsos); |
| 88 | static struct dso *kernel_dso; | 101 | static struct dso *kernel_dso; |
| 89 | static struct dso *vdso; | 102 | static struct dso *vdso; |
| @@ -145,7 +158,7 @@ static void dsos__fprintf(FILE *fp) | |||
| 145 | dso__fprintf(pos, fp); | 158 | dso__fprintf(pos, fp); |
| 146 | } | 159 | } |
| 147 | 160 | ||
| 148 | static struct symbol *vdso__find_symbol(struct dso *dso, __u64 ip) | 161 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) |
| 149 | { | 162 | { |
| 150 | return dso__find_symbol(kernel_dso, ip); | 163 | return dso__find_symbol(kernel_dso, ip); |
| 151 | } | 164 | } |
| @@ -178,19 +191,19 @@ static int load_kernel(void) | |||
| 178 | 191 | ||
| 179 | struct map { | 192 | struct map { |
| 180 | struct list_head node; | 193 | struct list_head node; |
| 181 | __u64 start; | 194 | u64 start; |
| 182 | __u64 end; | 195 | u64 end; |
| 183 | __u64 pgoff; | 196 | u64 pgoff; |
| 184 | __u64 (*map_ip)(struct map *, __u64); | 197 | u64 (*map_ip)(struct map *, u64); |
| 185 | struct dso *dso; | 198 | struct dso *dso; |
| 186 | }; | 199 | }; |
| 187 | 200 | ||
| 188 | static __u64 map__map_ip(struct map *map, __u64 ip) | 201 | static u64 map__map_ip(struct map *map, u64 ip) |
| 189 | { | 202 | { |
| 190 | return ip - map->start + map->pgoff; | 203 | return ip - map->start + map->pgoff; |
| 191 | } | 204 | } |
| 192 | 205 | ||
| 193 | static __u64 vdso__map_ip(struct map *map, __u64 ip) | 206 | static u64 vdso__map_ip(struct map *map, u64 ip) |
| 194 | { | 207 | { |
| 195 | return ip; | 208 | return ip; |
| 196 | } | 209 | } |
| @@ -373,7 +386,7 @@ static int thread__fork(struct thread *self, struct thread *parent) | |||
| 373 | return 0; | 386 | return 0; |
| 374 | } | 387 | } |
| 375 | 388 | ||
| 376 | static struct map *thread__find_map(struct thread *self, __u64 ip) | 389 | static struct map *thread__find_map(struct thread *self, u64 ip) |
| 377 | { | 390 | { |
| 378 | struct map *pos; | 391 | struct map *pos; |
| 379 | 392 | ||
| @@ -414,7 +427,7 @@ struct hist_entry { | |||
| 414 | struct map *map; | 427 | struct map *map; |
| 415 | struct dso *dso; | 428 | struct dso *dso; |
| 416 | struct symbol *sym; | 429 | struct symbol *sym; |
| 417 | __u64 ip; | 430 | u64 ip; |
| 418 | char level; | 431 | char level; |
| 419 | 432 | ||
| 420 | uint32_t count; | 433 | uint32_t count; |
| @@ -519,7 +532,7 @@ sort__dso_print(FILE *fp, struct hist_entry *self) | |||
| 519 | if (self->dso) | 532 | if (self->dso) |
| 520 | return fprintf(fp, "%-25s", self->dso->name); | 533 | return fprintf(fp, "%-25s", self->dso->name); |
| 521 | 534 | ||
| 522 | return fprintf(fp, "%016llx ", (__u64)self->ip); | 535 | return fprintf(fp, "%016llx ", (u64)self->ip); |
| 523 | } | 536 | } |
| 524 | 537 | ||
| 525 | static struct sort_entry sort_dso = { | 538 | static struct sort_entry sort_dso = { |
| @@ -533,7 +546,7 @@ static struct sort_entry sort_dso = { | |||
| 533 | static int64_t | 546 | static int64_t |
| 534 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | 547 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
| 535 | { | 548 | { |
| 536 | __u64 ip_l, ip_r; | 549 | u64 ip_l, ip_r; |
| 537 | 550 | ||
| 538 | if (left->sym == right->sym) | 551 | if (left->sym == right->sym) |
| 539 | return 0; | 552 | return 0; |
| @@ -550,13 +563,13 @@ sort__sym_print(FILE *fp, struct hist_entry *self) | |||
| 550 | size_t ret = 0; | 563 | size_t ret = 0; |
| 551 | 564 | ||
| 552 | if (verbose) | 565 | if (verbose) |
| 553 | ret += fprintf(fp, "%#018llx ", (__u64)self->ip); | 566 | ret += fprintf(fp, "%#018llx ", (u64)self->ip); |
| 554 | 567 | ||
| 555 | if (self->sym) { | 568 | if (self->sym) { |
| 556 | ret += fprintf(fp, "[%c] %s", | 569 | ret += fprintf(fp, "[%c] %s", |
| 557 | self->dso == kernel_dso ? 'k' : '.', self->sym->name); | 570 | self->dso == kernel_dso ? 'k' : '.', self->sym->name); |
| 558 | } else { | 571 | } else { |
| 559 | ret += fprintf(fp, "%#016llx", (__u64)self->ip); | 572 | ret += fprintf(fp, "%#016llx", (u64)self->ip); |
| 560 | } | 573 | } |
| 561 | 574 | ||
| 562 | return ret; | 575 | return ret; |
| @@ -647,7 +660,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
| 647 | /* | 660 | /* |
| 648 | * collect histogram counts | 661 | * collect histogram counts |
| 649 | */ | 662 | */ |
| 650 | static void hist_hit(struct hist_entry *he, __u64 ip) | 663 | static void hist_hit(struct hist_entry *he, u64 ip) |
| 651 | { | 664 | { |
| 652 | unsigned int sym_size, offset; | 665 | unsigned int sym_size, offset; |
| 653 | struct symbol *sym = he->sym; | 666 | struct symbol *sym = he->sym; |
| @@ -676,7 +689,7 @@ static void hist_hit(struct hist_entry *he, __u64 ip) | |||
| 676 | 689 | ||
| 677 | static int | 690 | static int |
| 678 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | 691 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, |
| 679 | struct symbol *sym, __u64 ip, char level) | 692 | struct symbol *sym, u64 ip, char level) |
| 680 | { | 693 | { |
| 681 | struct rb_node **p = &hist.rb_node; | 694 | struct rb_node **p = &hist.rb_node; |
| 682 | struct rb_node *parent = NULL; | 695 | struct rb_node *parent = NULL; |
| @@ -848,7 +861,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 848 | int show = 0; | 861 | int show = 0; |
| 849 | struct dso *dso = NULL; | 862 | struct dso *dso = NULL; |
| 850 | struct thread *thread = threads__findnew(event->ip.pid); | 863 | struct thread *thread = threads__findnew(event->ip.pid); |
| 851 | __u64 ip = event->ip.ip; | 864 | u64 ip = event->ip.ip; |
| 852 | struct map *map = NULL; | 865 | struct map *map = NULL; |
| 853 | 866 | ||
| 854 | dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", | 867 | dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", |
| @@ -1030,13 +1043,33 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1030 | return 0; | 1043 | return 0; |
| 1031 | } | 1044 | } |
| 1032 | 1045 | ||
| 1046 | static char *get_color(double percent) | ||
| 1047 | { | ||
| 1048 | char *color = PERF_COLOR_NORMAL; | ||
| 1049 | |||
| 1050 | /* | ||
| 1051 | * We color high-overhead entries in red, mid-overhead | ||
| 1052 | * entries in green - and keep the low overhead places | ||
| 1053 | * normal: | ||
| 1054 | */ | ||
| 1055 | if (percent >= MIN_RED) | ||
| 1056 | color = PERF_COLOR_RED; | ||
| 1057 | else { | ||
| 1058 | if (percent > MIN_GREEN) | ||
| 1059 | color = PERF_COLOR_GREEN; | ||
| 1060 | } | ||
| 1061 | return color; | ||
| 1062 | } | ||
| 1063 | |||
| 1033 | static int | 1064 | static int |
| 1034 | parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | 1065 | parse_line(FILE *file, struct symbol *sym, u64 start, u64 len) |
| 1035 | { | 1066 | { |
| 1036 | char *line = NULL, *tmp, *tmp2; | 1067 | char *line = NULL, *tmp, *tmp2; |
| 1068 | static const char *prev_line; | ||
| 1069 | static const char *prev_color; | ||
| 1037 | unsigned int offset; | 1070 | unsigned int offset; |
| 1038 | size_t line_len; | 1071 | size_t line_len; |
| 1039 | __u64 line_ip; | 1072 | u64 line_ip; |
| 1040 | int ret; | 1073 | int ret; |
| 1041 | char *c; | 1074 | char *c; |
| 1042 | 1075 | ||
| @@ -1073,27 +1106,36 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | |||
| 1073 | } | 1106 | } |
| 1074 | 1107 | ||
| 1075 | if (line_ip != -1) { | 1108 | if (line_ip != -1) { |
| 1109 | const char *path = NULL; | ||
| 1076 | unsigned int hits = 0; | 1110 | unsigned int hits = 0; |
| 1077 | double percent = 0.0; | 1111 | double percent = 0.0; |
| 1078 | char *color = PERF_COLOR_NORMAL; | 1112 | char *color; |
| 1113 | struct sym_ext *sym_ext = sym->priv; | ||
| 1079 | 1114 | ||
| 1080 | offset = line_ip - start; | 1115 | offset = line_ip - start; |
| 1081 | if (offset < len) | 1116 | if (offset < len) |
| 1082 | hits = sym->hist[offset]; | 1117 | hits = sym->hist[offset]; |
| 1083 | 1118 | ||
| 1084 | if (sym->hist_sum) | 1119 | if (offset < len && sym_ext) { |
| 1120 | path = sym_ext[offset].path; | ||
| 1121 | percent = sym_ext[offset].percent; | ||
| 1122 | } else if (sym->hist_sum) | ||
| 1085 | percent = 100.0 * hits / sym->hist_sum; | 1123 | percent = 100.0 * hits / sym->hist_sum; |
| 1086 | 1124 | ||
| 1125 | color = get_color(percent); | ||
| 1126 | |||
| 1087 | /* | 1127 | /* |
| 1088 | * We color high-overhead entries in red, mid-overhead | 1128 | * Also color the filename and line if needed, with |
| 1089 | * entries in green - and keep the low overhead places | 1129 | * the same color than the percentage. Don't print it |
| 1090 | * normal: | 1130 | * twice for close colored ip with the same filename:line |
| 1091 | */ | 1131 | */ |
| 1092 | if (percent >= 5.0) | 1132 | if (path) { |
| 1093 | color = PERF_COLOR_RED; | 1133 | if (!prev_line || strcmp(prev_line, path) |
| 1094 | else { | 1134 | || color != prev_color) { |
| 1095 | if (percent > 0.5) | 1135 | color_fprintf(stdout, color, " %s", path); |
| 1096 | color = PERF_COLOR_GREEN; | 1136 | prev_line = path; |
| 1137 | prev_color = color; | ||
| 1138 | } | ||
| 1097 | } | 1139 | } |
| 1098 | 1140 | ||
| 1099 | color_fprintf(stdout, color, " %7.2f", percent); | 1141 | color_fprintf(stdout, color, " %7.2f", percent); |
| @@ -1109,10 +1151,125 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | |||
| 1109 | return 0; | 1151 | return 0; |
| 1110 | } | 1152 | } |
| 1111 | 1153 | ||
| 1154 | static struct rb_root root_sym_ext; | ||
| 1155 | |||
| 1156 | static void insert_source_line(struct sym_ext *sym_ext) | ||
| 1157 | { | ||
| 1158 | struct sym_ext *iter; | ||
| 1159 | struct rb_node **p = &root_sym_ext.rb_node; | ||
| 1160 | struct rb_node *parent = NULL; | ||
| 1161 | |||
| 1162 | while (*p != NULL) { | ||
| 1163 | parent = *p; | ||
| 1164 | iter = rb_entry(parent, struct sym_ext, node); | ||
| 1165 | |||
| 1166 | if (sym_ext->percent > iter->percent) | ||
| 1167 | p = &(*p)->rb_left; | ||
| 1168 | else | ||
| 1169 | p = &(*p)->rb_right; | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | rb_link_node(&sym_ext->node, parent, p); | ||
| 1173 | rb_insert_color(&sym_ext->node, &root_sym_ext); | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | static void free_source_line(struct symbol *sym, int len) | ||
| 1177 | { | ||
| 1178 | struct sym_ext *sym_ext = sym->priv; | ||
| 1179 | int i; | ||
| 1180 | |||
| 1181 | if (!sym_ext) | ||
| 1182 | return; | ||
| 1183 | |||
| 1184 | for (i = 0; i < len; i++) | ||
| 1185 | free(sym_ext[i].path); | ||
| 1186 | free(sym_ext); | ||
| 1187 | |||
| 1188 | sym->priv = NULL; | ||
| 1189 | root_sym_ext = RB_ROOT; | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | /* Get the filename:line for the colored entries */ | ||
| 1193 | static void | ||
| 1194 | get_source_line(struct symbol *sym, u64 start, int len, char *filename) | ||
| 1195 | { | ||
| 1196 | int i; | ||
| 1197 | char cmd[PATH_MAX * 2]; | ||
| 1198 | struct sym_ext *sym_ext; | ||
| 1199 | |||
| 1200 | if (!sym->hist_sum) | ||
| 1201 | return; | ||
| 1202 | |||
| 1203 | sym->priv = calloc(len, sizeof(struct sym_ext)); | ||
| 1204 | if (!sym->priv) | ||
| 1205 | return; | ||
| 1206 | |||
| 1207 | sym_ext = sym->priv; | ||
| 1208 | |||
| 1209 | for (i = 0; i < len; i++) { | ||
| 1210 | char *path = NULL; | ||
| 1211 | size_t line_len; | ||
| 1212 | u64 offset; | ||
| 1213 | FILE *fp; | ||
| 1214 | |||
| 1215 | sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum; | ||
| 1216 | if (sym_ext[i].percent <= 0.5) | ||
| 1217 | continue; | ||
| 1218 | |||
| 1219 | offset = start + i; | ||
| 1220 | sprintf(cmd, "addr2line -e %s %016llx", filename, offset); | ||
| 1221 | fp = popen(cmd, "r"); | ||
| 1222 | if (!fp) | ||
| 1223 | continue; | ||
| 1224 | |||
| 1225 | if (getline(&path, &line_len, fp) < 0 || !line_len) | ||
| 1226 | goto next; | ||
| 1227 | |||
| 1228 | sym_ext[i].path = malloc(sizeof(char) * line_len + 1); | ||
| 1229 | if (!sym_ext[i].path) | ||
| 1230 | goto next; | ||
| 1231 | |||
| 1232 | strcpy(sym_ext[i].path, path); | ||
| 1233 | insert_source_line(&sym_ext[i]); | ||
| 1234 | |||
| 1235 | next: | ||
| 1236 | pclose(fp); | ||
| 1237 | } | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | static void print_summary(char *filename) | ||
| 1241 | { | ||
| 1242 | struct sym_ext *sym_ext; | ||
| 1243 | struct rb_node *node; | ||
| 1244 | |||
| 1245 | printf("\nSorted summary for file %s\n", filename); | ||
| 1246 | printf("----------------------------------------------\n\n"); | ||
| 1247 | |||
| 1248 | if (RB_EMPTY_ROOT(&root_sym_ext)) { | ||
| 1249 | printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); | ||
| 1250 | return; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | node = rb_first(&root_sym_ext); | ||
| 1254 | while (node) { | ||
| 1255 | double percent; | ||
| 1256 | char *color; | ||
| 1257 | char *path; | ||
| 1258 | |||
| 1259 | sym_ext = rb_entry(node, struct sym_ext, node); | ||
| 1260 | percent = sym_ext->percent; | ||
| 1261 | color = get_color(percent); | ||
| 1262 | path = sym_ext->path; | ||
| 1263 | |||
| 1264 | color_fprintf(stdout, color, " %7.2f %s", percent, path); | ||
| 1265 | node = rb_next(node); | ||
| 1266 | } | ||
| 1267 | } | ||
| 1268 | |||
| 1112 | static void annotate_sym(struct dso *dso, struct symbol *sym) | 1269 | static void annotate_sym(struct dso *dso, struct symbol *sym) |
| 1113 | { | 1270 | { |
| 1114 | char *filename = dso->name; | 1271 | char *filename = dso->name; |
| 1115 | __u64 start, end, len; | 1272 | u64 start, end, len; |
| 1116 | char command[PATH_MAX*2]; | 1273 | char command[PATH_MAX*2]; |
| 1117 | FILE *file; | 1274 | FILE *file; |
| 1118 | 1275 | ||
| @@ -1121,13 +1278,6 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
| 1121 | if (dso == kernel_dso) | 1278 | if (dso == kernel_dso) |
| 1122 | filename = vmlinux; | 1279 | filename = vmlinux; |
| 1123 | 1280 | ||
| 1124 | printf("\n------------------------------------------------\n"); | ||
| 1125 | printf(" Percent | Source code & Disassembly of %s\n", filename); | ||
| 1126 | printf("------------------------------------------------\n"); | ||
| 1127 | |||
| 1128 | if (verbose >= 2) | ||
| 1129 | printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name); | ||
| 1130 | |||
| 1131 | start = sym->obj_start; | 1281 | start = sym->obj_start; |
| 1132 | if (!start) | 1282 | if (!start) |
| 1133 | start = sym->start; | 1283 | start = sym->start; |
| @@ -1135,7 +1285,19 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
| 1135 | end = start + sym->end - sym->start + 1; | 1285 | end = start + sym->end - sym->start + 1; |
| 1136 | len = sym->end - sym->start; | 1286 | len = sym->end - sym->start; |
| 1137 | 1287 | ||
| 1138 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (__u64)start, (__u64)end, filename); | 1288 | if (print_line) { |
| 1289 | get_source_line(sym, start, len, filename); | ||
| 1290 | print_summary(filename); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | printf("\n\n------------------------------------------------\n"); | ||
| 1294 | printf(" Percent | Source code & Disassembly of %s\n", filename); | ||
| 1295 | printf("------------------------------------------------\n"); | ||
| 1296 | |||
| 1297 | if (verbose >= 2) | ||
| 1298 | printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name); | ||
| 1299 | |||
| 1300 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (u64)start, (u64)end, filename); | ||
| 1139 | 1301 | ||
| 1140 | if (verbose >= 3) | 1302 | if (verbose >= 3) |
| 1141 | printf("doing: %s\n", command); | 1303 | printf("doing: %s\n", command); |
| @@ -1150,6 +1312,8 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
| 1150 | } | 1312 | } |
| 1151 | 1313 | ||
| 1152 | pclose(file); | 1314 | pclose(file); |
| 1315 | if (print_line) | ||
| 1316 | free_source_line(sym, len); | ||
| 1153 | } | 1317 | } |
| 1154 | 1318 | ||
| 1155 | static void find_annotations(void) | 1319 | static void find_annotations(void) |
| @@ -1308,6 +1472,8 @@ static const struct option options[] = { | |||
| 1308 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1472 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
| 1309 | "dump raw trace in ASCII"), | 1473 | "dump raw trace in ASCII"), |
| 1310 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 1474 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
| 1475 | OPT_BOOLEAN('l', "print-line", &print_line, | ||
| 1476 | "print matching source lines (may be slow)"), | ||
| 1311 | OPT_END() | 1477 | OPT_END() |
| 1312 | }; | 1478 | }; |
| 1313 | 1479 | ||
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 0f5771f615da..d7ebbd757543 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -37,33 +37,37 @@ static pid_t target_pid = -1; | |||
| 37 | static int inherit = 1; | 37 | static int inherit = 1; |
| 38 | static int force = 0; | 38 | static int force = 0; |
| 39 | static int append_file = 0; | 39 | static int append_file = 0; |
| 40 | static int call_graph = 0; | ||
| 40 | static int verbose = 0; | 41 | static int verbose = 0; |
| 41 | 42 | ||
| 42 | static long samples; | 43 | static long samples; |
| 43 | static struct timeval last_read; | 44 | static struct timeval last_read; |
| 44 | static struct timeval this_read; | 45 | static struct timeval this_read; |
| 45 | 46 | ||
| 46 | static __u64 bytes_written; | 47 | static u64 bytes_written; |
| 47 | 48 | ||
| 48 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; | 49 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; |
| 49 | 50 | ||
| 50 | static int nr_poll; | 51 | static int nr_poll; |
| 51 | static int nr_cpu; | 52 | static int nr_cpu; |
| 52 | 53 | ||
| 54 | static int file_new = 1; | ||
| 55 | static struct perf_file_header file_header; | ||
| 56 | |||
| 53 | struct mmap_event { | 57 | struct mmap_event { |
| 54 | struct perf_event_header header; | 58 | struct perf_event_header header; |
| 55 | __u32 pid; | 59 | u32 pid; |
| 56 | __u32 tid; | 60 | u32 tid; |
| 57 | __u64 start; | 61 | u64 start; |
| 58 | __u64 len; | 62 | u64 len; |
| 59 | __u64 pgoff; | 63 | u64 pgoff; |
| 60 | char filename[PATH_MAX]; | 64 | char filename[PATH_MAX]; |
| 61 | }; | 65 | }; |
| 62 | 66 | ||
| 63 | struct comm_event { | 67 | struct comm_event { |
| 64 | struct perf_event_header header; | 68 | struct perf_event_header header; |
| 65 | __u32 pid; | 69 | u32 pid; |
| 66 | __u32 tid; | 70 | u32 tid; |
| 67 | char comm[16]; | 71 | char comm[16]; |
| 68 | }; | 72 | }; |
| 69 | 73 | ||
| @@ -77,10 +81,10 @@ struct mmap_data { | |||
| 77 | 81 | ||
| 78 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; | 82 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; |
| 79 | 83 | ||
| 80 | static unsigned int mmap_read_head(struct mmap_data *md) | 84 | static unsigned long mmap_read_head(struct mmap_data *md) |
| 81 | { | 85 | { |
| 82 | struct perf_counter_mmap_page *pc = md->base; | 86 | struct perf_counter_mmap_page *pc = md->base; |
| 83 | int head; | 87 | long head; |
| 84 | 88 | ||
| 85 | head = pc->data_head; | 89 | head = pc->data_head; |
| 86 | rmb(); | 90 | rmb(); |
| @@ -88,6 +92,32 @@ static unsigned int mmap_read_head(struct mmap_data *md) | |||
| 88 | return head; | 92 | return head; |
| 89 | } | 93 | } |
| 90 | 94 | ||
| 95 | static void mmap_write_tail(struct mmap_data *md, unsigned long tail) | ||
| 96 | { | ||
| 97 | struct perf_counter_mmap_page *pc = md->base; | ||
| 98 | |||
| 99 | /* | ||
| 100 | * ensure all reads are done before we write the tail out. | ||
| 101 | */ | ||
| 102 | /* mb(); */ | ||
| 103 | pc->data_tail = tail; | ||
| 104 | } | ||
| 105 | |||
| 106 | static void write_output(void *buf, size_t size) | ||
| 107 | { | ||
| 108 | while (size) { | ||
| 109 | int ret = write(output, buf, size); | ||
| 110 | |||
| 111 | if (ret < 0) | ||
| 112 | die("failed to write"); | ||
| 113 | |||
| 114 | size -= ret; | ||
| 115 | buf += ret; | ||
| 116 | |||
| 117 | bytes_written += ret; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 91 | static void mmap_read(struct mmap_data *md) | 121 | static void mmap_read(struct mmap_data *md) |
| 92 | { | 122 | { |
| 93 | unsigned int head = mmap_read_head(md); | 123 | unsigned int head = mmap_read_head(md); |
| @@ -108,7 +138,7 @@ static void mmap_read(struct mmap_data *md) | |||
| 108 | * In either case, truncate and restart at head. | 138 | * In either case, truncate and restart at head. |
| 109 | */ | 139 | */ |
| 110 | diff = head - old; | 140 | diff = head - old; |
| 111 | if (diff > md->mask / 2 || diff < 0) { | 141 | if (diff < 0) { |
| 112 | struct timeval iv; | 142 | struct timeval iv; |
| 113 | unsigned long msecs; | 143 | unsigned long msecs; |
| 114 | 144 | ||
| @@ -136,36 +166,17 @@ static void mmap_read(struct mmap_data *md) | |||
| 136 | size = md->mask + 1 - (old & md->mask); | 166 | size = md->mask + 1 - (old & md->mask); |
| 137 | old += size; | 167 | old += size; |
| 138 | 168 | ||
| 139 | while (size) { | 169 | write_output(buf, size); |
| 140 | int ret = write(output, buf, size); | ||
| 141 | |||
| 142 | if (ret < 0) | ||
| 143 | die("failed to write"); | ||
| 144 | |||
| 145 | size -= ret; | ||
| 146 | buf += ret; | ||
| 147 | |||
| 148 | bytes_written += ret; | ||
| 149 | } | ||
| 150 | } | 170 | } |
| 151 | 171 | ||
| 152 | buf = &data[old & md->mask]; | 172 | buf = &data[old & md->mask]; |
| 153 | size = head - old; | 173 | size = head - old; |
| 154 | old += size; | 174 | old += size; |
| 155 | 175 | ||
| 156 | while (size) { | 176 | write_output(buf, size); |
| 157 | int ret = write(output, buf, size); | ||
| 158 | |||
| 159 | if (ret < 0) | ||
| 160 | die("failed to write"); | ||
| 161 | |||
| 162 | size -= ret; | ||
| 163 | buf += ret; | ||
| 164 | |||
| 165 | bytes_written += ret; | ||
| 166 | } | ||
| 167 | 177 | ||
| 168 | md->prev = old; | 178 | md->prev = old; |
| 179 | mmap_write_tail(md, old); | ||
| 169 | } | 180 | } |
| 170 | 181 | ||
| 171 | static volatile int done = 0; | 182 | static volatile int done = 0; |
| @@ -191,7 +202,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
| 191 | struct comm_event comm_ev; | 202 | struct comm_event comm_ev; |
| 192 | char filename[PATH_MAX]; | 203 | char filename[PATH_MAX]; |
| 193 | char bf[BUFSIZ]; | 204 | char bf[BUFSIZ]; |
| 194 | int fd, ret; | 205 | int fd; |
| 195 | size_t size; | 206 | size_t size; |
| 196 | char *field, *sep; | 207 | char *field, *sep; |
| 197 | DIR *tasks; | 208 | DIR *tasks; |
| @@ -201,8 +212,12 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
| 201 | 212 | ||
| 202 | fd = open(filename, O_RDONLY); | 213 | fd = open(filename, O_RDONLY); |
| 203 | if (fd < 0) { | 214 | if (fd < 0) { |
| 204 | fprintf(stderr, "couldn't open %s\n", filename); | 215 | /* |
| 205 | exit(EXIT_FAILURE); | 216 | * We raced with a task exiting - just return: |
| 217 | */ | ||
| 218 | if (verbose) | ||
| 219 | fprintf(stderr, "couldn't open %s\n", filename); | ||
| 220 | return; | ||
| 206 | } | 221 | } |
| 207 | if (read(fd, bf, sizeof(bf)) < 0) { | 222 | if (read(fd, bf, sizeof(bf)) < 0) { |
| 208 | fprintf(stderr, "couldn't read %s\n", filename); | 223 | fprintf(stderr, "couldn't read %s\n", filename); |
| @@ -223,17 +238,13 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
| 223 | 238 | ||
| 224 | comm_ev.pid = pid; | 239 | comm_ev.pid = pid; |
| 225 | comm_ev.header.type = PERF_EVENT_COMM; | 240 | comm_ev.header.type = PERF_EVENT_COMM; |
| 226 | size = ALIGN(size, sizeof(__u64)); | 241 | size = ALIGN(size, sizeof(u64)); |
| 227 | comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); | 242 | comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); |
| 228 | 243 | ||
| 229 | if (!full) { | 244 | if (!full) { |
| 230 | comm_ev.tid = pid; | 245 | comm_ev.tid = pid; |
| 231 | 246 | ||
| 232 | ret = write(output, &comm_ev, comm_ev.header.size); | 247 | write_output(&comm_ev, comm_ev.header.size); |
| 233 | if (ret < 0) { | ||
| 234 | perror("failed to write"); | ||
| 235 | exit(-1); | ||
| 236 | } | ||
| 237 | return; | 248 | return; |
| 238 | } | 249 | } |
| 239 | 250 | ||
| @@ -248,11 +259,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
| 248 | 259 | ||
| 249 | comm_ev.tid = pid; | 260 | comm_ev.tid = pid; |
| 250 | 261 | ||
| 251 | ret = write(output, &comm_ev, comm_ev.header.size); | 262 | write_output(&comm_ev, comm_ev.header.size); |
| 252 | if (ret < 0) { | ||
| 253 | perror("failed to write"); | ||
| 254 | exit(-1); | ||
| 255 | } | ||
| 256 | } | 263 | } |
| 257 | closedir(tasks); | 264 | closedir(tasks); |
| 258 | return; | 265 | return; |
| @@ -272,8 +279,12 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
| 272 | 279 | ||
| 273 | fp = fopen(filename, "r"); | 280 | fp = fopen(filename, "r"); |
| 274 | if (fp == NULL) { | 281 | if (fp == NULL) { |
| 275 | fprintf(stderr, "couldn't open %s\n", filename); | 282 | /* |
| 276 | exit(EXIT_FAILURE); | 283 | * We raced with a task exiting - just return: |
| 284 | */ | ||
| 285 | if (verbose) | ||
| 286 | fprintf(stderr, "couldn't open %s\n", filename); | ||
| 287 | return; | ||
| 277 | } | 288 | } |
| 278 | while (1) { | 289 | while (1) { |
| 279 | char bf[BUFSIZ], *pbf = bf; | 290 | char bf[BUFSIZ], *pbf = bf; |
| @@ -304,17 +315,14 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
| 304 | size = strlen(execname); | 315 | size = strlen(execname); |
| 305 | execname[size - 1] = '\0'; /* Remove \n */ | 316 | execname[size - 1] = '\0'; /* Remove \n */ |
| 306 | memcpy(mmap_ev.filename, execname, size); | 317 | memcpy(mmap_ev.filename, execname, size); |
| 307 | size = ALIGN(size, sizeof(__u64)); | 318 | size = ALIGN(size, sizeof(u64)); |
| 308 | mmap_ev.len -= mmap_ev.start; | 319 | mmap_ev.len -= mmap_ev.start; |
| 309 | mmap_ev.header.size = (sizeof(mmap_ev) - | 320 | mmap_ev.header.size = (sizeof(mmap_ev) - |
| 310 | (sizeof(mmap_ev.filename) - size)); | 321 | (sizeof(mmap_ev.filename) - size)); |
| 311 | mmap_ev.pid = pid; | 322 | mmap_ev.pid = pid; |
| 312 | mmap_ev.tid = pid; | 323 | mmap_ev.tid = pid; |
| 313 | 324 | ||
| 314 | if (write(output, &mmap_ev, mmap_ev.header.size) < 0) { | 325 | write_output(&mmap_ev, mmap_ev.header.size); |
| 315 | perror("failed to write"); | ||
| 316 | exit(-1); | ||
| 317 | } | ||
| 318 | } | 326 | } |
| 319 | } | 327 | } |
| 320 | 328 | ||
| @@ -351,11 +359,25 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
| 351 | int track = 1; | 359 | int track = 1; |
| 352 | 360 | ||
| 353 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 361 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
| 362 | |||
| 354 | if (freq) { | 363 | if (freq) { |
| 355 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 364 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
| 356 | attr->freq = 1; | 365 | attr->freq = 1; |
| 357 | attr->sample_freq = freq; | 366 | attr->sample_freq = freq; |
| 358 | } | 367 | } |
| 368 | |||
| 369 | if (call_graph) | ||
| 370 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | ||
| 371 | |||
| 372 | if (file_new) { | ||
| 373 | file_header.sample_type = attr->sample_type; | ||
| 374 | } else { | ||
| 375 | if (file_header.sample_type != attr->sample_type) { | ||
| 376 | fprintf(stderr, "incompatible append\n"); | ||
| 377 | exit(-1); | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 359 | attr->mmap = track; | 381 | attr->mmap = track; |
| 360 | attr->comm = track; | 382 | attr->comm = track; |
| 361 | attr->inherit = (cpu < 0) && inherit; | 383 | attr->inherit = (cpu < 0) && inherit; |
| @@ -410,7 +432,7 @@ try_again: | |||
| 410 | mmap_array[nr_cpu][counter].prev = 0; | 432 | mmap_array[nr_cpu][counter].prev = 0; |
| 411 | mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1; | 433 | mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1; |
| 412 | mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size, | 434 | mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size, |
| 413 | PROT_READ, MAP_SHARED, fd[nr_cpu][counter], 0); | 435 | PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0); |
| 414 | if (mmap_array[nr_cpu][counter].base == MAP_FAILED) { | 436 | if (mmap_array[nr_cpu][counter].base == MAP_FAILED) { |
| 415 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 437 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
| 416 | exit(-1); | 438 | exit(-1); |
| @@ -435,6 +457,14 @@ static void open_counters(int cpu, pid_t pid) | |||
| 435 | nr_cpu++; | 457 | nr_cpu++; |
| 436 | } | 458 | } |
| 437 | 459 | ||
| 460 | static void atexit_header(void) | ||
| 461 | { | ||
| 462 | file_header.data_size += bytes_written; | ||
| 463 | |||
| 464 | if (pwrite(output, &file_header, sizeof(file_header), 0) == -1) | ||
| 465 | perror("failed to write on file headers"); | ||
| 466 | } | ||
| 467 | |||
| 438 | static int __cmd_record(int argc, const char **argv) | 468 | static int __cmd_record(int argc, const char **argv) |
| 439 | { | 469 | { |
| 440 | int i, counter; | 470 | int i, counter; |
| @@ -448,6 +478,10 @@ static int __cmd_record(int argc, const char **argv) | |||
| 448 | assert(nr_cpus <= MAX_NR_CPUS); | 478 | assert(nr_cpus <= MAX_NR_CPUS); |
| 449 | assert(nr_cpus >= 0); | 479 | assert(nr_cpus >= 0); |
| 450 | 480 | ||
| 481 | atexit(sig_atexit); | ||
| 482 | signal(SIGCHLD, sig_handler); | ||
| 483 | signal(SIGINT, sig_handler); | ||
| 484 | |||
| 451 | if (!stat(output_name, &st) && !force && !append_file) { | 485 | if (!stat(output_name, &st) && !force && !append_file) { |
| 452 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", | 486 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", |
| 453 | output_name); | 487 | output_name); |
| @@ -456,7 +490,7 @@ static int __cmd_record(int argc, const char **argv) | |||
| 456 | 490 | ||
| 457 | flags = O_CREAT|O_RDWR; | 491 | flags = O_CREAT|O_RDWR; |
| 458 | if (append_file) | 492 | if (append_file) |
| 459 | flags |= O_APPEND; | 493 | file_new = 0; |
| 460 | else | 494 | else |
| 461 | flags |= O_TRUNC; | 495 | flags |= O_TRUNC; |
| 462 | 496 | ||
| @@ -466,15 +500,22 @@ static int __cmd_record(int argc, const char **argv) | |||
| 466 | exit(-1); | 500 | exit(-1); |
| 467 | } | 501 | } |
| 468 | 502 | ||
| 503 | if (!file_new) { | ||
| 504 | if (read(output, &file_header, sizeof(file_header)) == -1) { | ||
| 505 | perror("failed to read file headers"); | ||
| 506 | exit(-1); | ||
| 507 | } | ||
| 508 | |||
| 509 | lseek(output, file_header.data_size, SEEK_CUR); | ||
| 510 | } | ||
| 511 | |||
| 512 | atexit(atexit_header); | ||
| 513 | |||
| 469 | if (!system_wide) { | 514 | if (!system_wide) { |
| 470 | open_counters(-1, target_pid != -1 ? target_pid : getpid()); | 515 | open_counters(-1, target_pid != -1 ? target_pid : getpid()); |
| 471 | } else for (i = 0; i < nr_cpus; i++) | 516 | } else for (i = 0; i < nr_cpus; i++) |
| 472 | open_counters(i, target_pid); | 517 | open_counters(i, target_pid); |
| 473 | 518 | ||
| 474 | atexit(sig_atexit); | ||
| 475 | signal(SIGCHLD, sig_handler); | ||
| 476 | signal(SIGINT, sig_handler); | ||
| 477 | |||
| 478 | if (target_pid == -1 && argc) { | 519 | if (target_pid == -1 && argc) { |
| 479 | pid = fork(); | 520 | pid = fork(); |
| 480 | if (pid < 0) | 521 | if (pid < 0) |
| @@ -555,6 +596,8 @@ static const struct option options[] = { | |||
| 555 | "profile at this frequency"), | 596 | "profile at this frequency"), |
| 556 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, | 597 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, |
| 557 | "number of mmap data pages"), | 598 | "number of mmap data pages"), |
| 599 | OPT_BOOLEAN('g', "call-graph", &call_graph, | ||
| 600 | "do call-graph (stack chain/backtrace) recording"), | ||
| 558 | OPT_BOOLEAN('v', "verbose", &verbose, | 601 | OPT_BOOLEAN('v', "verbose", &verbose, |
| 559 | "be more verbose (show counter open errors, etc)"), | 602 | "be more verbose (show counter open errors, etc)"), |
| 560 | OPT_END() | 603 | OPT_END() |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 82fa93b4db99..5eb5566f0c95 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -36,45 +36,65 @@ static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | |||
| 36 | 36 | ||
| 37 | static int dump_trace = 0; | 37 | static int dump_trace = 0; |
| 38 | #define dprintf(x...) do { if (dump_trace) printf(x); } while (0) | 38 | #define dprintf(x...) do { if (dump_trace) printf(x); } while (0) |
| 39 | #define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0) | ||
| 39 | 40 | ||
| 40 | static int verbose; | 41 | static int verbose; |
| 42 | #define eprintf(x...) do { if (verbose) fprintf(stderr, x); } while (0) | ||
| 43 | |||
| 41 | static int full_paths; | 44 | static int full_paths; |
| 42 | 45 | ||
| 43 | static unsigned long page_size; | 46 | static unsigned long page_size; |
| 44 | static unsigned long mmap_window = 32; | 47 | static unsigned long mmap_window = 32; |
| 45 | 48 | ||
| 49 | static char default_parent_pattern[] = "^sys_|^do_page_fault"; | ||
| 50 | static char *parent_pattern = default_parent_pattern; | ||
| 51 | static regex_t parent_regex; | ||
| 52 | |||
| 53 | static int exclude_other = 1; | ||
| 54 | |||
| 46 | struct ip_event { | 55 | struct ip_event { |
| 47 | struct perf_event_header header; | 56 | struct perf_event_header header; |
| 48 | __u64 ip; | 57 | u64 ip; |
| 49 | __u32 pid, tid; | 58 | u32 pid, tid; |
| 50 | __u64 period; | 59 | unsigned char __more_data[]; |
| 60 | }; | ||
| 61 | |||
| 62 | struct ip_callchain { | ||
| 63 | u64 nr; | ||
| 64 | u64 ips[0]; | ||
| 51 | }; | 65 | }; |
| 52 | 66 | ||
| 53 | struct mmap_event { | 67 | struct mmap_event { |
| 54 | struct perf_event_header header; | 68 | struct perf_event_header header; |
| 55 | __u32 pid, tid; | 69 | u32 pid, tid; |
| 56 | __u64 start; | 70 | u64 start; |
| 57 | __u64 len; | 71 | u64 len; |
| 58 | __u64 pgoff; | 72 | u64 pgoff; |
| 59 | char filename[PATH_MAX]; | 73 | char filename[PATH_MAX]; |
| 60 | }; | 74 | }; |
| 61 | 75 | ||
| 62 | struct comm_event { | 76 | struct comm_event { |
| 63 | struct perf_event_header header; | 77 | struct perf_event_header header; |
| 64 | __u32 pid, tid; | 78 | u32 pid, tid; |
| 65 | char comm[16]; | 79 | char comm[16]; |
| 66 | }; | 80 | }; |
| 67 | 81 | ||
| 68 | struct fork_event { | 82 | struct fork_event { |
| 69 | struct perf_event_header header; | 83 | struct perf_event_header header; |
| 70 | __u32 pid, ppid; | 84 | u32 pid, ppid; |
| 71 | }; | 85 | }; |
| 72 | 86 | ||
| 73 | struct period_event { | 87 | struct period_event { |
| 74 | struct perf_event_header header; | 88 | struct perf_event_header header; |
| 75 | __u64 time; | 89 | u64 time; |
| 76 | __u64 id; | 90 | u64 id; |
| 77 | __u64 sample_period; | 91 | u64 sample_period; |
| 92 | }; | ||
| 93 | |||
| 94 | struct lost_event { | ||
| 95 | struct perf_event_header header; | ||
| 96 | u64 id; | ||
| 97 | u64 lost; | ||
| 78 | }; | 98 | }; |
| 79 | 99 | ||
| 80 | typedef union event_union { | 100 | typedef union event_union { |
| @@ -84,6 +104,7 @@ typedef union event_union { | |||
| 84 | struct comm_event comm; | 104 | struct comm_event comm; |
| 85 | struct fork_event fork; | 105 | struct fork_event fork; |
| 86 | struct period_event period; | 106 | struct period_event period; |
| 107 | struct lost_event lost; | ||
| 87 | } event_t; | 108 | } event_t; |
| 88 | 109 | ||
| 89 | static LIST_HEAD(dsos); | 110 | static LIST_HEAD(dsos); |
| @@ -119,15 +140,11 @@ static struct dso *dsos__findnew(const char *name) | |||
| 119 | 140 | ||
| 120 | nr = dso__load(dso, NULL, verbose); | 141 | nr = dso__load(dso, NULL, verbose); |
| 121 | if (nr < 0) { | 142 | if (nr < 0) { |
| 122 | if (verbose) | 143 | eprintf("Failed to open: %s\n", name); |
| 123 | fprintf(stderr, "Failed to open: %s\n", name); | ||
| 124 | goto out_delete_dso; | 144 | goto out_delete_dso; |
| 125 | } | 145 | } |
| 126 | if (!nr && verbose) { | 146 | if (!nr) |
| 127 | fprintf(stderr, | 147 | eprintf("No symbols found in: %s, maybe install a debug package?\n", name); |
| 128 | "No symbols found in: %s, maybe install a debug package?\n", | ||
| 129 | name); | ||
| 130 | } | ||
| 131 | 148 | ||
| 132 | dsos__add(dso); | 149 | dsos__add(dso); |
| 133 | 150 | ||
| @@ -146,7 +163,7 @@ static void dsos__fprintf(FILE *fp) | |||
| 146 | dso__fprintf(pos, fp); | 163 | dso__fprintf(pos, fp); |
| 147 | } | 164 | } |
| 148 | 165 | ||
| 149 | static struct symbol *vdso__find_symbol(struct dso *dso, __u64 ip) | 166 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) |
| 150 | { | 167 | { |
| 151 | return dso__find_symbol(kernel_dso, ip); | 168 | return dso__find_symbol(kernel_dso, ip); |
| 152 | } | 169 | } |
| @@ -193,19 +210,19 @@ static int strcommon(const char *pathname) | |||
| 193 | 210 | ||
| 194 | struct map { | 211 | struct map { |
| 195 | struct list_head node; | 212 | struct list_head node; |
| 196 | __u64 start; | 213 | u64 start; |
| 197 | __u64 end; | 214 | u64 end; |
| 198 | __u64 pgoff; | 215 | u64 pgoff; |
| 199 | __u64 (*map_ip)(struct map *, __u64); | 216 | u64 (*map_ip)(struct map *, u64); |
| 200 | struct dso *dso; | 217 | struct dso *dso; |
| 201 | }; | 218 | }; |
| 202 | 219 | ||
| 203 | static __u64 map__map_ip(struct map *map, __u64 ip) | 220 | static u64 map__map_ip(struct map *map, u64 ip) |
| 204 | { | 221 | { |
| 205 | return ip - map->start + map->pgoff; | 222 | return ip - map->start + map->pgoff; |
| 206 | } | 223 | } |
| 207 | 224 | ||
| 208 | static __u64 vdso__map_ip(struct map *map, __u64 ip) | 225 | static u64 vdso__map_ip(struct map *map, u64 ip) |
| 209 | { | 226 | { |
| 210 | return ip; | 227 | return ip; |
| 211 | } | 228 | } |
| @@ -412,7 +429,7 @@ static int thread__fork(struct thread *self, struct thread *parent) | |||
| 412 | return 0; | 429 | return 0; |
| 413 | } | 430 | } |
| 414 | 431 | ||
| 415 | static struct map *thread__find_map(struct thread *self, __u64 ip) | 432 | static struct map *thread__find_map(struct thread *self, u64 ip) |
| 416 | { | 433 | { |
| 417 | struct map *pos; | 434 | struct map *pos; |
| 418 | 435 | ||
| @@ -453,10 +470,11 @@ struct hist_entry { | |||
| 453 | struct map *map; | 470 | struct map *map; |
| 454 | struct dso *dso; | 471 | struct dso *dso; |
| 455 | struct symbol *sym; | 472 | struct symbol *sym; |
| 456 | __u64 ip; | 473 | struct symbol *parent; |
| 474 | u64 ip; | ||
| 457 | char level; | 475 | char level; |
| 458 | 476 | ||
| 459 | __u64 count; | 477 | u64 count; |
| 460 | }; | 478 | }; |
| 461 | 479 | ||
| 462 | /* | 480 | /* |
| @@ -473,6 +491,16 @@ struct sort_entry { | |||
| 473 | size_t (*print)(FILE *fp, struct hist_entry *); | 491 | size_t (*print)(FILE *fp, struct hist_entry *); |
| 474 | }; | 492 | }; |
| 475 | 493 | ||
| 494 | static int64_t cmp_null(void *l, void *r) | ||
| 495 | { | ||
| 496 | if (!l && !r) | ||
| 497 | return 0; | ||
| 498 | else if (!l) | ||
| 499 | return -1; | ||
| 500 | else | ||
| 501 | return 1; | ||
| 502 | } | ||
| 503 | |||
| 476 | /* --sort pid */ | 504 | /* --sort pid */ |
| 477 | 505 | ||
| 478 | static int64_t | 506 | static int64_t |
| @@ -507,14 +535,8 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | |||
| 507 | char *comm_l = left->thread->comm; | 535 | char *comm_l = left->thread->comm; |
| 508 | char *comm_r = right->thread->comm; | 536 | char *comm_r = right->thread->comm; |
| 509 | 537 | ||
| 510 | if (!comm_l || !comm_r) { | 538 | if (!comm_l || !comm_r) |
| 511 | if (!comm_l && !comm_r) | 539 | return cmp_null(comm_l, comm_r); |
| 512 | return 0; | ||
| 513 | else if (!comm_l) | ||
| 514 | return -1; | ||
| 515 | else | ||
| 516 | return 1; | ||
| 517 | } | ||
| 518 | 540 | ||
| 519 | return strcmp(comm_l, comm_r); | 541 | return strcmp(comm_l, comm_r); |
| 520 | } | 542 | } |
| @@ -540,14 +562,8 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 540 | struct dso *dso_l = left->dso; | 562 | struct dso *dso_l = left->dso; |
| 541 | struct dso *dso_r = right->dso; | 563 | struct dso *dso_r = right->dso; |
| 542 | 564 | ||
| 543 | if (!dso_l || !dso_r) { | 565 | if (!dso_l || !dso_r) |
| 544 | if (!dso_l && !dso_r) | 566 | return cmp_null(dso_l, dso_r); |
| 545 | return 0; | ||
| 546 | else if (!dso_l) | ||
| 547 | return -1; | ||
| 548 | else | ||
| 549 | return 1; | ||
| 550 | } | ||
| 551 | 567 | ||
| 552 | return strcmp(dso_l->name, dso_r->name); | 568 | return strcmp(dso_l->name, dso_r->name); |
| 553 | } | 569 | } |
| @@ -558,7 +574,7 @@ sort__dso_print(FILE *fp, struct hist_entry *self) | |||
| 558 | if (self->dso) | 574 | if (self->dso) |
| 559 | return fprintf(fp, "%-25s", self->dso->name); | 575 | return fprintf(fp, "%-25s", self->dso->name); |
| 560 | 576 | ||
| 561 | return fprintf(fp, "%016llx ", (__u64)self->ip); | 577 | return fprintf(fp, "%016llx ", (u64)self->ip); |
| 562 | } | 578 | } |
| 563 | 579 | ||
| 564 | static struct sort_entry sort_dso = { | 580 | static struct sort_entry sort_dso = { |
| @@ -572,7 +588,7 @@ static struct sort_entry sort_dso = { | |||
| 572 | static int64_t | 588 | static int64_t |
| 573 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | 589 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
| 574 | { | 590 | { |
| 575 | __u64 ip_l, ip_r; | 591 | u64 ip_l, ip_r; |
| 576 | 592 | ||
| 577 | if (left->sym == right->sym) | 593 | if (left->sym == right->sym) |
| 578 | return 0; | 594 | return 0; |
| @@ -589,13 +605,13 @@ sort__sym_print(FILE *fp, struct hist_entry *self) | |||
| 589 | size_t ret = 0; | 605 | size_t ret = 0; |
| 590 | 606 | ||
| 591 | if (verbose) | 607 | if (verbose) |
| 592 | ret += fprintf(fp, "%#018llx ", (__u64)self->ip); | 608 | ret += fprintf(fp, "%#018llx ", (u64)self->ip); |
| 593 | 609 | ||
| 594 | if (self->sym) { | 610 | if (self->sym) { |
| 595 | ret += fprintf(fp, "[%c] %s", | 611 | ret += fprintf(fp, "[%c] %s", |
| 596 | self->dso == kernel_dso ? 'k' : '.', self->sym->name); | 612 | self->dso == kernel_dso ? 'k' : '.', self->sym->name); |
| 597 | } else { | 613 | } else { |
| 598 | ret += fprintf(fp, "%#016llx", (__u64)self->ip); | 614 | ret += fprintf(fp, "%#016llx", (u64)self->ip); |
| 599 | } | 615 | } |
| 600 | 616 | ||
| 601 | return ret; | 617 | return ret; |
| @@ -607,7 +623,38 @@ static struct sort_entry sort_sym = { | |||
| 607 | .print = sort__sym_print, | 623 | .print = sort__sym_print, |
| 608 | }; | 624 | }; |
| 609 | 625 | ||
| 626 | /* --sort parent */ | ||
| 627 | |||
| 628 | static int64_t | ||
| 629 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 630 | { | ||
| 631 | struct symbol *sym_l = left->parent; | ||
| 632 | struct symbol *sym_r = right->parent; | ||
| 633 | |||
| 634 | if (!sym_l || !sym_r) | ||
| 635 | return cmp_null(sym_l, sym_r); | ||
| 636 | |||
| 637 | return strcmp(sym_l->name, sym_r->name); | ||
| 638 | } | ||
| 639 | |||
| 640 | static size_t | ||
| 641 | sort__parent_print(FILE *fp, struct hist_entry *self) | ||
| 642 | { | ||
| 643 | size_t ret = 0; | ||
| 644 | |||
| 645 | ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]"); | ||
| 646 | |||
| 647 | return ret; | ||
| 648 | } | ||
| 649 | |||
| 650 | static struct sort_entry sort_parent = { | ||
| 651 | .header = "Parent symbol ", | ||
| 652 | .cmp = sort__parent_cmp, | ||
| 653 | .print = sort__parent_print, | ||
| 654 | }; | ||
| 655 | |||
| 610 | static int sort__need_collapse = 0; | 656 | static int sort__need_collapse = 0; |
| 657 | static int sort__has_parent = 0; | ||
| 611 | 658 | ||
| 612 | struct sort_dimension { | 659 | struct sort_dimension { |
| 613 | char *name; | 660 | char *name; |
| @@ -620,6 +667,7 @@ static struct sort_dimension sort_dimensions[] = { | |||
| 620 | { .name = "comm", .entry = &sort_comm, }, | 667 | { .name = "comm", .entry = &sort_comm, }, |
| 621 | { .name = "dso", .entry = &sort_dso, }, | 668 | { .name = "dso", .entry = &sort_dso, }, |
| 622 | { .name = "symbol", .entry = &sort_sym, }, | 669 | { .name = "symbol", .entry = &sort_sym, }, |
| 670 | { .name = "parent", .entry = &sort_parent, }, | ||
| 623 | }; | 671 | }; |
| 624 | 672 | ||
| 625 | static LIST_HEAD(hist_entry__sort_list); | 673 | static LIST_HEAD(hist_entry__sort_list); |
| @@ -640,6 +688,19 @@ static int sort_dimension__add(char *tok) | |||
| 640 | if (sd->entry->collapse) | 688 | if (sd->entry->collapse) |
| 641 | sort__need_collapse = 1; | 689 | sort__need_collapse = 1; |
| 642 | 690 | ||
| 691 | if (sd->entry == &sort_parent) { | ||
| 692 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | ||
| 693 | if (ret) { | ||
| 694 | char err[BUFSIZ]; | ||
| 695 | |||
| 696 | regerror(ret, &parent_regex, err, sizeof(err)); | ||
| 697 | fprintf(stderr, "Invalid regex: %s\n%s", | ||
| 698 | parent_pattern, err); | ||
| 699 | exit(-1); | ||
| 700 | } | ||
| 701 | sort__has_parent = 1; | ||
| 702 | } | ||
| 703 | |||
| 643 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | 704 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); |
| 644 | sd->taken = 1; | 705 | sd->taken = 1; |
| 645 | 706 | ||
| @@ -684,11 +745,14 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
| 684 | } | 745 | } |
| 685 | 746 | ||
| 686 | static size_t | 747 | static size_t |
| 687 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, __u64 total_samples) | 748 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) |
| 688 | { | 749 | { |
| 689 | struct sort_entry *se; | 750 | struct sort_entry *se; |
| 690 | size_t ret; | 751 | size_t ret; |
| 691 | 752 | ||
| 753 | if (exclude_other && !self->parent) | ||
| 754 | return 0; | ||
| 755 | |||
| 692 | if (total_samples) { | 756 | if (total_samples) { |
| 693 | double percent = self->count * 100.0 / total_samples; | 757 | double percent = self->count * 100.0 / total_samples; |
| 694 | char *color = PERF_COLOR_NORMAL; | 758 | char *color = PERF_COLOR_NORMAL; |
| @@ -711,6 +775,9 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, __u64 total_samples) | |||
| 711 | ret = fprintf(fp, "%12Ld ", self->count); | 775 | ret = fprintf(fp, "%12Ld ", self->count); |
| 712 | 776 | ||
| 713 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 777 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 778 | if (exclude_other && (se == &sort_parent)) | ||
| 779 | continue; | ||
| 780 | |||
| 714 | fprintf(fp, " "); | 781 | fprintf(fp, " "); |
| 715 | ret += se->print(fp, self); | 782 | ret += se->print(fp, self); |
| 716 | } | 783 | } |
| @@ -721,12 +788,72 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, __u64 total_samples) | |||
| 721 | } | 788 | } |
| 722 | 789 | ||
| 723 | /* | 790 | /* |
| 791 | * | ||
| 792 | */ | ||
| 793 | |||
| 794 | static struct symbol * | ||
| 795 | resolve_symbol(struct thread *thread, struct map **mapp, | ||
| 796 | struct dso **dsop, u64 *ipp) | ||
| 797 | { | ||
| 798 | struct dso *dso = dsop ? *dsop : NULL; | ||
| 799 | struct map *map = mapp ? *mapp : NULL; | ||
| 800 | uint64_t ip = *ipp; | ||
| 801 | |||
| 802 | if (!thread) | ||
| 803 | return NULL; | ||
| 804 | |||
| 805 | if (dso) | ||
| 806 | goto got_dso; | ||
| 807 | |||
| 808 | if (map) | ||
| 809 | goto got_map; | ||
| 810 | |||
| 811 | map = thread__find_map(thread, ip); | ||
| 812 | if (map != NULL) { | ||
| 813 | if (mapp) | ||
| 814 | *mapp = map; | ||
| 815 | got_map: | ||
| 816 | ip = map->map_ip(map, ip); | ||
| 817 | *ipp = ip; | ||
| 818 | |||
| 819 | dso = map->dso; | ||
| 820 | } else { | ||
| 821 | /* | ||
| 822 | * If this is outside of all known maps, | ||
| 823 | * and is a negative address, try to look it | ||
| 824 | * up in the kernel dso, as it might be a | ||
| 825 | * vsyscall (which executes in user-mode): | ||
| 826 | */ | ||
| 827 | if ((long long)ip < 0) | ||
| 828 | dso = kernel_dso; | ||
| 829 | } | ||
| 830 | dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); | ||
| 831 | |||
| 832 | if (dsop) | ||
| 833 | *dsop = dso; | ||
| 834 | |||
| 835 | if (!dso) | ||
| 836 | return NULL; | ||
| 837 | got_dso: | ||
| 838 | return dso->find_symbol(dso, ip); | ||
| 839 | } | ||
| 840 | |||
| 841 | static int call__match(struct symbol *sym) | ||
| 842 | { | ||
| 843 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
| 844 | return 1; | ||
| 845 | |||
| 846 | return 0; | ||
| 847 | } | ||
| 848 | |||
| 849 | /* | ||
| 724 | * collect histogram counts | 850 | * collect histogram counts |
| 725 | */ | 851 | */ |
| 726 | 852 | ||
| 727 | static int | 853 | static int |
| 728 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | 854 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, |
| 729 | struct symbol *sym, __u64 ip, char level, __u64 count) | 855 | struct symbol *sym, u64 ip, struct ip_callchain *chain, |
| 856 | char level, u64 count) | ||
| 730 | { | 857 | { |
| 731 | struct rb_node **p = &hist.rb_node; | 858 | struct rb_node **p = &hist.rb_node; |
| 732 | struct rb_node *parent = NULL; | 859 | struct rb_node *parent = NULL; |
| @@ -739,9 +866,41 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
| 739 | .ip = ip, | 866 | .ip = ip, |
| 740 | .level = level, | 867 | .level = level, |
| 741 | .count = count, | 868 | .count = count, |
| 869 | .parent = NULL, | ||
| 742 | }; | 870 | }; |
| 743 | int cmp; | 871 | int cmp; |
| 744 | 872 | ||
| 873 | if (sort__has_parent && chain) { | ||
| 874 | u64 context = PERF_CONTEXT_MAX; | ||
| 875 | int i; | ||
| 876 | |||
| 877 | for (i = 0; i < chain->nr; i++) { | ||
| 878 | u64 ip = chain->ips[i]; | ||
| 879 | struct dso *dso = NULL; | ||
| 880 | struct symbol *sym; | ||
| 881 | |||
| 882 | if (ip >= PERF_CONTEXT_MAX) { | ||
| 883 | context = ip; | ||
| 884 | continue; | ||
| 885 | } | ||
| 886 | |||
| 887 | switch (context) { | ||
| 888 | case PERF_CONTEXT_KERNEL: | ||
| 889 | dso = kernel_dso; | ||
| 890 | break; | ||
| 891 | default: | ||
| 892 | break; | ||
| 893 | } | ||
| 894 | |||
| 895 | sym = resolve_symbol(thread, NULL, &dso, &ip); | ||
| 896 | |||
| 897 | if (sym && call__match(sym)) { | ||
| 898 | entry.parent = sym; | ||
| 899 | break; | ||
| 900 | } | ||
| 901 | } | ||
| 902 | } | ||
| 903 | |||
| 745 | while (*p != NULL) { | 904 | while (*p != NULL) { |
| 746 | parent = *p; | 905 | parent = *p; |
| 747 | he = rb_entry(parent, struct hist_entry, rb_node); | 906 | he = rb_entry(parent, struct hist_entry, rb_node); |
| @@ -873,7 +1032,7 @@ static void output__resort(void) | |||
| 873 | } | 1032 | } |
| 874 | } | 1033 | } |
| 875 | 1034 | ||
| 876 | static size_t output__fprintf(FILE *fp, __u64 total_samples) | 1035 | static size_t output__fprintf(FILE *fp, u64 total_samples) |
| 877 | { | 1036 | { |
| 878 | struct hist_entry *pos; | 1037 | struct hist_entry *pos; |
| 879 | struct sort_entry *se; | 1038 | struct sort_entry *se; |
| @@ -882,18 +1041,24 @@ static size_t output__fprintf(FILE *fp, __u64 total_samples) | |||
| 882 | 1041 | ||
| 883 | fprintf(fp, "\n"); | 1042 | fprintf(fp, "\n"); |
| 884 | fprintf(fp, "#\n"); | 1043 | fprintf(fp, "#\n"); |
| 885 | fprintf(fp, "# (%Ld samples)\n", (__u64)total_samples); | 1044 | fprintf(fp, "# (%Ld samples)\n", (u64)total_samples); |
| 886 | fprintf(fp, "#\n"); | 1045 | fprintf(fp, "#\n"); |
| 887 | 1046 | ||
| 888 | fprintf(fp, "# Overhead"); | 1047 | fprintf(fp, "# Overhead"); |
| 889 | list_for_each_entry(se, &hist_entry__sort_list, list) | 1048 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 1049 | if (exclude_other && (se == &sort_parent)) | ||
| 1050 | continue; | ||
| 890 | fprintf(fp, " %s", se->header); | 1051 | fprintf(fp, " %s", se->header); |
| 1052 | } | ||
| 891 | fprintf(fp, "\n"); | 1053 | fprintf(fp, "\n"); |
| 892 | 1054 | ||
| 893 | fprintf(fp, "# ........"); | 1055 | fprintf(fp, "# ........"); |
| 894 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1056 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 895 | int i; | 1057 | int i; |
| 896 | 1058 | ||
| 1059 | if (exclude_other && (se == &sort_parent)) | ||
| 1060 | continue; | ||
| 1061 | |||
| 897 | fprintf(fp, " "); | 1062 | fprintf(fp, " "); |
| 898 | for (i = 0; i < strlen(se->header); i++) | 1063 | for (i = 0; i < strlen(se->header); i++) |
| 899 | fprintf(fp, "."); | 1064 | fprintf(fp, "."); |
| @@ -907,7 +1072,8 @@ static size_t output__fprintf(FILE *fp, __u64 total_samples) | |||
| 907 | ret += hist_entry__fprintf(fp, pos, total_samples); | 1072 | ret += hist_entry__fprintf(fp, pos, total_samples); |
| 908 | } | 1073 | } |
| 909 | 1074 | ||
| 910 | if (!strcmp(sort_order, default_sort_order)) { | 1075 | if (sort_order == default_sort_order && |
| 1076 | parent_pattern == default_parent_pattern) { | ||
| 911 | fprintf(fp, "#\n"); | 1077 | fprintf(fp, "#\n"); |
| 912 | fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n"); | 1078 | fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n"); |
| 913 | fprintf(fp, "#\n"); | 1079 | fprintf(fp, "#\n"); |
| @@ -932,7 +1098,21 @@ static unsigned long total = 0, | |||
| 932 | total_mmap = 0, | 1098 | total_mmap = 0, |
| 933 | total_comm = 0, | 1099 | total_comm = 0, |
| 934 | total_fork = 0, | 1100 | total_fork = 0, |
| 935 | total_unknown = 0; | 1101 | total_unknown = 0, |
| 1102 | total_lost = 0; | ||
| 1103 | |||
| 1104 | static int validate_chain(struct ip_callchain *chain, event_t *event) | ||
| 1105 | { | ||
| 1106 | unsigned int chain_size; | ||
| 1107 | |||
| 1108 | chain_size = event->header.size; | ||
| 1109 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; | ||
| 1110 | |||
| 1111 | if (chain->nr*sizeof(u64) > chain_size) | ||
| 1112 | return -1; | ||
| 1113 | |||
| 1114 | return 0; | ||
| 1115 | } | ||
| 936 | 1116 | ||
| 937 | static int | 1117 | static int |
| 938 | process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | 1118 | process_overflow_event(event_t *event, unsigned long offset, unsigned long head) |
| @@ -941,12 +1121,16 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 941 | int show = 0; | 1121 | int show = 0; |
| 942 | struct dso *dso = NULL; | 1122 | struct dso *dso = NULL; |
| 943 | struct thread *thread = threads__findnew(event->ip.pid); | 1123 | struct thread *thread = threads__findnew(event->ip.pid); |
| 944 | __u64 ip = event->ip.ip; | 1124 | u64 ip = event->ip.ip; |
| 945 | __u64 period = 1; | 1125 | u64 period = 1; |
| 946 | struct map *map = NULL; | 1126 | struct map *map = NULL; |
| 1127 | void *more_data = event->ip.__more_data; | ||
| 1128 | struct ip_callchain *chain = NULL; | ||
| 947 | 1129 | ||
| 948 | if (event->header.type & PERF_SAMPLE_PERIOD) | 1130 | if (event->header.type & PERF_SAMPLE_PERIOD) { |
| 949 | period = event->ip.period; | 1131 | period = *(u64 *)more_data; |
| 1132 | more_data += sizeof(u64); | ||
| 1133 | } | ||
| 950 | 1134 | ||
| 951 | dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p period: %Ld\n", | 1135 | dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p period: %Ld\n", |
| 952 | (void *)(offset + head), | 1136 | (void *)(offset + head), |
| @@ -956,10 +1140,28 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 956 | (void *)(long)ip, | 1140 | (void *)(long)ip, |
| 957 | (long long)period); | 1141 | (long long)period); |
| 958 | 1142 | ||
| 1143 | if (event->header.type & PERF_SAMPLE_CALLCHAIN) { | ||
| 1144 | int i; | ||
| 1145 | |||
| 1146 | chain = (void *)more_data; | ||
| 1147 | |||
| 1148 | dprintf("... chain: nr:%Lu\n", chain->nr); | ||
| 1149 | |||
| 1150 | if (validate_chain(chain, event) < 0) { | ||
| 1151 | eprintf("call-chain problem with event, skipping it.\n"); | ||
| 1152 | return 0; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | if (dump_trace) { | ||
| 1156 | for (i = 0; i < chain->nr; i++) | ||
| 1157 | dprintf("..... %2d: %016Lx\n", i, chain->ips[i]); | ||
| 1158 | } | ||
| 1159 | } | ||
| 1160 | |||
| 959 | dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 1161 | dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
| 960 | 1162 | ||
| 961 | if (thread == NULL) { | 1163 | if (thread == NULL) { |
| 962 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 1164 | eprintf("problem processing %d event, skipping it.\n", |
| 963 | event->header.type); | 1165 | event->header.type); |
| 964 | return -1; | 1166 | return -1; |
| 965 | } | 1167 | } |
| @@ -977,22 +1179,6 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 977 | show = SHOW_USER; | 1179 | show = SHOW_USER; |
| 978 | level = '.'; | 1180 | level = '.'; |
| 979 | 1181 | ||
| 980 | map = thread__find_map(thread, ip); | ||
| 981 | if (map != NULL) { | ||
| 982 | ip = map->map_ip(map, ip); | ||
| 983 | dso = map->dso; | ||
| 984 | } else { | ||
| 985 | /* | ||
| 986 | * If this is outside of all known maps, | ||
| 987 | * and is a negative address, try to look it | ||
| 988 | * up in the kernel dso, as it might be a | ||
| 989 | * vsyscall (which executes in user-mode): | ||
| 990 | */ | ||
| 991 | if ((long long)ip < 0) | ||
| 992 | dso = kernel_dso; | ||
| 993 | } | ||
| 994 | dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); | ||
| 995 | |||
| 996 | } else { | 1182 | } else { |
| 997 | show = SHOW_HV; | 1183 | show = SHOW_HV; |
| 998 | level = 'H'; | 1184 | level = 'H'; |
| @@ -1000,14 +1186,10 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1000 | } | 1186 | } |
| 1001 | 1187 | ||
| 1002 | if (show & show_mask) { | 1188 | if (show & show_mask) { |
| 1003 | struct symbol *sym = NULL; | 1189 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); |
| 1004 | |||
| 1005 | if (dso) | ||
| 1006 | sym = dso->find_symbol(dso, ip); | ||
| 1007 | 1190 | ||
| 1008 | if (hist_entry__add(thread, map, dso, sym, ip, level, period)) { | 1191 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { |
| 1009 | fprintf(stderr, | 1192 | eprintf("problem incrementing symbol count, skipping event\n"); |
| 1010 | "problem incrementing symbol count, skipping event\n"); | ||
| 1011 | return -1; | 1193 | return -1; |
| 1012 | } | 1194 | } |
| 1013 | } | 1195 | } |
| @@ -1096,8 +1278,60 @@ process_period_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1096 | } | 1278 | } |
| 1097 | 1279 | ||
| 1098 | static int | 1280 | static int |
| 1281 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) | ||
| 1282 | { | ||
| 1283 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", | ||
| 1284 | (void *)(offset + head), | ||
| 1285 | (void *)(long)(event->header.size), | ||
| 1286 | event->lost.id, | ||
| 1287 | event->lost.lost); | ||
| 1288 | |||
| 1289 | total_lost += event->lost.lost; | ||
| 1290 | |||
| 1291 | return 0; | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | static void trace_event(event_t *event) | ||
| 1295 | { | ||
| 1296 | unsigned char *raw_event = (void *)event; | ||
| 1297 | char *color = PERF_COLOR_BLUE; | ||
| 1298 | int i, j; | ||
| 1299 | |||
| 1300 | if (!dump_trace) | ||
| 1301 | return; | ||
| 1302 | |||
| 1303 | dprintf("."); | ||
| 1304 | cdprintf("\n. ... raw event: size %d bytes\n", event->header.size); | ||
| 1305 | |||
| 1306 | for (i = 0; i < event->header.size; i++) { | ||
| 1307 | if ((i & 15) == 0) { | ||
| 1308 | dprintf("."); | ||
| 1309 | cdprintf(" %04x: ", i); | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | cdprintf(" %02x", raw_event[i]); | ||
| 1313 | |||
| 1314 | if (((i & 15) == 15) || i == event->header.size-1) { | ||
| 1315 | cdprintf(" "); | ||
| 1316 | for (j = 0; j < 15-(i & 15); j++) | ||
| 1317 | cdprintf(" "); | ||
| 1318 | for (j = 0; j < (i & 15); j++) { | ||
| 1319 | if (isprint(raw_event[i-15+j])) | ||
| 1320 | cdprintf("%c", raw_event[i-15+j]); | ||
| 1321 | else | ||
| 1322 | cdprintf("."); | ||
| 1323 | } | ||
| 1324 | cdprintf("\n"); | ||
| 1325 | } | ||
| 1326 | } | ||
| 1327 | dprintf(".\n"); | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | static int | ||
| 1099 | process_event(event_t *event, unsigned long offset, unsigned long head) | 1331 | process_event(event_t *event, unsigned long offset, unsigned long head) |
| 1100 | { | 1332 | { |
| 1333 | trace_event(event); | ||
| 1334 | |||
| 1101 | if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) | 1335 | if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) |
| 1102 | return process_overflow_event(event, offset, head); | 1336 | return process_overflow_event(event, offset, head); |
| 1103 | 1337 | ||
| @@ -1113,6 +1347,10 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1113 | 1347 | ||
| 1114 | case PERF_EVENT_PERIOD: | 1348 | case PERF_EVENT_PERIOD: |
| 1115 | return process_period_event(event, offset, head); | 1349 | return process_period_event(event, offset, head); |
| 1350 | |||
| 1351 | case PERF_EVENT_LOST: | ||
| 1352 | return process_lost_event(event, offset, head); | ||
| 1353 | |||
| 1116 | /* | 1354 | /* |
| 1117 | * We dont process them right now but they are fine: | 1355 | * We dont process them right now but they are fine: |
| 1118 | */ | 1356 | */ |
| @@ -1128,11 +1366,13 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1128 | return 0; | 1366 | return 0; |
| 1129 | } | 1367 | } |
| 1130 | 1368 | ||
| 1369 | static struct perf_file_header file_header; | ||
| 1370 | |||
| 1131 | static int __cmd_report(void) | 1371 | static int __cmd_report(void) |
| 1132 | { | 1372 | { |
| 1133 | int ret, rc = EXIT_FAILURE; | 1373 | int ret, rc = EXIT_FAILURE; |
| 1134 | unsigned long offset = 0; | 1374 | unsigned long offset = 0; |
| 1135 | unsigned long head = 0; | 1375 | unsigned long head = sizeof(file_header); |
| 1136 | struct stat stat; | 1376 | struct stat stat; |
| 1137 | event_t *event; | 1377 | event_t *event; |
| 1138 | uint32_t size; | 1378 | uint32_t size; |
| @@ -1160,6 +1400,17 @@ static int __cmd_report(void) | |||
| 1160 | exit(0); | 1400 | exit(0); |
| 1161 | } | 1401 | } |
| 1162 | 1402 | ||
| 1403 | if (read(input, &file_header, sizeof(file_header)) == -1) { | ||
| 1404 | perror("failed to read file headers"); | ||
| 1405 | exit(-1); | ||
| 1406 | } | ||
| 1407 | |||
| 1408 | if (sort__has_parent && | ||
| 1409 | !(file_header.sample_type & PERF_SAMPLE_CALLCHAIN)) { | ||
| 1410 | fprintf(stderr, "selected --sort parent, but no callchain data\n"); | ||
| 1411 | exit(-1); | ||
| 1412 | } | ||
| 1413 | |||
| 1163 | if (load_kernel() < 0) { | 1414 | if (load_kernel() < 0) { |
| 1164 | perror("failed to load kernel symbols"); | 1415 | perror("failed to load kernel symbols"); |
| 1165 | return EXIT_FAILURE; | 1416 | return EXIT_FAILURE; |
| @@ -1204,7 +1455,7 @@ more: | |||
| 1204 | 1455 | ||
| 1205 | size = event->header.size; | 1456 | size = event->header.size; |
| 1206 | 1457 | ||
| 1207 | dprintf("%p [%p]: event: %d\n", | 1458 | dprintf("\n%p [%p]: event: %d\n", |
| 1208 | (void *)(offset + head), | 1459 | (void *)(offset + head), |
| 1209 | (void *)(long)event->header.size, | 1460 | (void *)(long)event->header.size, |
| 1210 | event->header.type); | 1461 | event->header.type); |
| @@ -1231,9 +1482,13 @@ more: | |||
| 1231 | 1482 | ||
| 1232 | head += size; | 1483 | head += size; |
| 1233 | 1484 | ||
| 1485 | if (offset + head >= sizeof(file_header) + file_header.data_size) | ||
| 1486 | goto done; | ||
| 1487 | |||
| 1234 | if (offset + head < stat.st_size) | 1488 | if (offset + head < stat.st_size) |
| 1235 | goto more; | 1489 | goto more; |
| 1236 | 1490 | ||
| 1491 | done: | ||
| 1237 | rc = EXIT_SUCCESS; | 1492 | rc = EXIT_SUCCESS; |
| 1238 | close(input); | 1493 | close(input); |
| 1239 | 1494 | ||
| @@ -1241,6 +1496,7 @@ more: | |||
| 1241 | dprintf(" mmap events: %10ld\n", total_mmap); | 1496 | dprintf(" mmap events: %10ld\n", total_mmap); |
| 1242 | dprintf(" comm events: %10ld\n", total_comm); | 1497 | dprintf(" comm events: %10ld\n", total_comm); |
| 1243 | dprintf(" fork events: %10ld\n", total_fork); | 1498 | dprintf(" fork events: %10ld\n", total_fork); |
| 1499 | dprintf(" lost events: %10ld\n", total_lost); | ||
| 1244 | dprintf(" unknown events: %10ld\n", total_unknown); | 1500 | dprintf(" unknown events: %10ld\n", total_unknown); |
| 1245 | 1501 | ||
| 1246 | if (dump_trace) | 1502 | if (dump_trace) |
| @@ -1273,9 +1529,13 @@ static const struct option options[] = { | |||
| 1273 | "dump raw trace in ASCII"), | 1529 | "dump raw trace in ASCII"), |
| 1274 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 1530 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
| 1275 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 1531 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
| 1276 | "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"), | 1532 | "sort by key(s): pid, comm, dso, symbol, parent"), |
| 1277 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 1533 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
| 1278 | "Don't shorten the pathnames taking into account the cwd"), | 1534 | "Don't shorten the pathnames taking into account the cwd"), |
| 1535 | OPT_STRING('p', "parent", &parent_pattern, "regex", | ||
| 1536 | "regex filter to identify parent, see: '--sort parent'"), | ||
| 1537 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, | ||
| 1538 | "Only display entries with parent-match"), | ||
| 1279 | OPT_END() | 1539 | OPT_END() |
| 1280 | }; | 1540 | }; |
| 1281 | 1541 | ||
| @@ -1304,6 +1564,11 @@ int cmd_report(int argc, const char **argv, const char *prefix) | |||
| 1304 | 1564 | ||
| 1305 | setup_sorting(); | 1565 | setup_sorting(); |
| 1306 | 1566 | ||
| 1567 | if (parent_pattern != default_parent_pattern) | ||
| 1568 | sort_dimension__add("parent"); | ||
| 1569 | else | ||
| 1570 | exclude_other = 0; | ||
| 1571 | |||
| 1307 | /* | 1572 | /* |
| 1308 | * Any (unrecognized) arguments left? | 1573 | * Any (unrecognized) arguments left? |
| 1309 | */ | 1574 | */ |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c43e4a97dc42..6d3eeac1ea25 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -43,6 +43,7 @@ | |||
| 43 | #include "util/parse-events.h" | 43 | #include "util/parse-events.h" |
| 44 | 44 | ||
| 45 | #include <sys/prctl.h> | 45 | #include <sys/prctl.h> |
| 46 | #include <math.h> | ||
| 46 | 47 | ||
| 47 | static struct perf_counter_attr default_attrs[MAX_COUNTERS] = { | 48 | static struct perf_counter_attr default_attrs[MAX_COUNTERS] = { |
| 48 | 49 | ||
| @@ -79,12 +80,34 @@ static const unsigned int default_count[] = { | |||
| 79 | 10000, | 80 | 10000, |
| 80 | }; | 81 | }; |
| 81 | 82 | ||
| 82 | static __u64 event_res[MAX_COUNTERS][3]; | 83 | #define MAX_RUN 100 |
| 83 | static __u64 event_scaled[MAX_COUNTERS]; | ||
| 84 | 84 | ||
| 85 | static __u64 runtime_nsecs; | 85 | static int run_count = 1; |
| 86 | static __u64 walltime_nsecs; | 86 | static int run_idx = 0; |
| 87 | static __u64 runtime_cycles; | 87 | |
| 88 | static u64 event_res[MAX_RUN][MAX_COUNTERS][3]; | ||
| 89 | static u64 event_scaled[MAX_RUN][MAX_COUNTERS]; | ||
| 90 | |||
| 91 | //static u64 event_hist[MAX_RUN][MAX_COUNTERS][3]; | ||
| 92 | |||
| 93 | |||
| 94 | static u64 runtime_nsecs[MAX_RUN]; | ||
| 95 | static u64 walltime_nsecs[MAX_RUN]; | ||
| 96 | static u64 runtime_cycles[MAX_RUN]; | ||
| 97 | |||
| 98 | static u64 event_res_avg[MAX_COUNTERS][3]; | ||
| 99 | static u64 event_res_noise[MAX_COUNTERS][3]; | ||
| 100 | |||
| 101 | static u64 event_scaled_avg[MAX_COUNTERS]; | ||
| 102 | |||
| 103 | static u64 runtime_nsecs_avg; | ||
| 104 | static u64 runtime_nsecs_noise; | ||
| 105 | |||
| 106 | static u64 walltime_nsecs_avg; | ||
| 107 | static u64 walltime_nsecs_noise; | ||
| 108 | |||
| 109 | static u64 runtime_cycles_avg; | ||
| 110 | static u64 runtime_cycles_noise; | ||
| 88 | 111 | ||
| 89 | static void create_perf_stat_counter(int counter) | 112 | static void create_perf_stat_counter(int counter) |
| 90 | { | 113 | { |
| @@ -135,12 +158,12 @@ static inline int nsec_counter(int counter) | |||
| 135 | */ | 158 | */ |
| 136 | static void read_counter(int counter) | 159 | static void read_counter(int counter) |
| 137 | { | 160 | { |
| 138 | __u64 *count, single_count[3]; | 161 | u64 *count, single_count[3]; |
| 139 | ssize_t res; | 162 | ssize_t res; |
| 140 | int cpu, nv; | 163 | int cpu, nv; |
| 141 | int scaled; | 164 | int scaled; |
| 142 | 165 | ||
| 143 | count = event_res[counter]; | 166 | count = event_res[run_idx][counter]; |
| 144 | 167 | ||
| 145 | count[0] = count[1] = count[2] = 0; | 168 | count[0] = count[1] = count[2] = 0; |
| 146 | 169 | ||
| @@ -149,8 +172,10 @@ static void read_counter(int counter) | |||
| 149 | if (fd[cpu][counter] < 0) | 172 | if (fd[cpu][counter] < 0) |
| 150 | continue; | 173 | continue; |
| 151 | 174 | ||
| 152 | res = read(fd[cpu][counter], single_count, nv * sizeof(__u64)); | 175 | res = read(fd[cpu][counter], single_count, nv * sizeof(u64)); |
| 153 | assert(res == nv * sizeof(__u64)); | 176 | assert(res == nv * sizeof(u64)); |
| 177 | close(fd[cpu][counter]); | ||
| 178 | fd[cpu][counter] = -1; | ||
| 154 | 179 | ||
| 155 | count[0] += single_count[0]; | 180 | count[0] += single_count[0]; |
| 156 | if (scale) { | 181 | if (scale) { |
| @@ -162,13 +187,13 @@ static void read_counter(int counter) | |||
| 162 | scaled = 0; | 187 | scaled = 0; |
| 163 | if (scale) { | 188 | if (scale) { |
| 164 | if (count[2] == 0) { | 189 | if (count[2] == 0) { |
| 165 | event_scaled[counter] = -1; | 190 | event_scaled[run_idx][counter] = -1; |
| 166 | count[0] = 0; | 191 | count[0] = 0; |
| 167 | return; | 192 | return; |
| 168 | } | 193 | } |
| 169 | 194 | ||
| 170 | if (count[2] < count[1]) { | 195 | if (count[2] < count[1]) { |
| 171 | event_scaled[counter] = 1; | 196 | event_scaled[run_idx][counter] = 1; |
| 172 | count[0] = (unsigned long long) | 197 | count[0] = (unsigned long long) |
| 173 | ((double)count[0] * count[1] / count[2] + 0.5); | 198 | ((double)count[0] * count[1] / count[2] + 0.5); |
| 174 | } | 199 | } |
| @@ -178,10 +203,94 @@ static void read_counter(int counter) | |||
| 178 | */ | 203 | */ |
| 179 | if (attrs[counter].type == PERF_TYPE_SOFTWARE && | 204 | if (attrs[counter].type == PERF_TYPE_SOFTWARE && |
| 180 | attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) | 205 | attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) |
| 181 | runtime_nsecs = count[0]; | 206 | runtime_nsecs[run_idx] = count[0]; |
| 182 | if (attrs[counter].type == PERF_TYPE_HARDWARE && | 207 | if (attrs[counter].type == PERF_TYPE_HARDWARE && |
| 183 | attrs[counter].config == PERF_COUNT_HW_CPU_CYCLES) | 208 | attrs[counter].config == PERF_COUNT_HW_CPU_CYCLES) |
| 184 | runtime_cycles = count[0]; | 209 | runtime_cycles[run_idx] = count[0]; |
| 210 | } | ||
| 211 | |||
| 212 | static int run_perf_stat(int argc, const char **argv) | ||
| 213 | { | ||
| 214 | unsigned long long t0, t1; | ||
| 215 | int status = 0; | ||
| 216 | int counter; | ||
| 217 | int pid; | ||
| 218 | |||
| 219 | if (!system_wide) | ||
| 220 | nr_cpus = 1; | ||
| 221 | |||
| 222 | for (counter = 0; counter < nr_counters; counter++) | ||
| 223 | create_perf_stat_counter(counter); | ||
| 224 | |||
| 225 | /* | ||
| 226 | * Enable counters and exec the command: | ||
| 227 | */ | ||
| 228 | t0 = rdclock(); | ||
| 229 | prctl(PR_TASK_PERF_COUNTERS_ENABLE); | ||
| 230 | |||
| 231 | if ((pid = fork()) < 0) | ||
| 232 | perror("failed to fork"); | ||
| 233 | |||
| 234 | if (!pid) { | ||
| 235 | if (execvp(argv[0], (char **)argv)) { | ||
| 236 | perror(argv[0]); | ||
| 237 | exit(-1); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | wait(&status); | ||
| 242 | |||
| 243 | prctl(PR_TASK_PERF_COUNTERS_DISABLE); | ||
| 244 | t1 = rdclock(); | ||
| 245 | |||
| 246 | walltime_nsecs[run_idx] = t1 - t0; | ||
| 247 | |||
| 248 | for (counter = 0; counter < nr_counters; counter++) | ||
| 249 | read_counter(counter); | ||
| 250 | |||
| 251 | return WEXITSTATUS(status); | ||
| 252 | } | ||
| 253 | |||
| 254 | static void print_noise(u64 *count, u64 *noise) | ||
| 255 | { | ||
| 256 | if (run_count > 1) | ||
| 257 | fprintf(stderr, " ( +- %7.3f%% )", | ||
| 258 | (double)noise[0]/(count[0]+1)*100.0); | ||
| 259 | } | ||
| 260 | |||
| 261 | static void nsec_printout(int counter, u64 *count, u64 *noise) | ||
| 262 | { | ||
| 263 | double msecs = (double)count[0] / 1000000; | ||
| 264 | |||
| 265 | fprintf(stderr, " %14.6f %-20s", msecs, event_name(counter)); | ||
| 266 | |||
| 267 | if (attrs[counter].type == PERF_TYPE_SOFTWARE && | ||
| 268 | attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) { | ||
| 269 | |||
| 270 | if (walltime_nsecs_avg) | ||
| 271 | fprintf(stderr, " # %10.3f CPUs ", | ||
| 272 | (double)count[0] / (double)walltime_nsecs_avg); | ||
| 273 | } | ||
| 274 | print_noise(count, noise); | ||
| 275 | } | ||
| 276 | |||
| 277 | static void abs_printout(int counter, u64 *count, u64 *noise) | ||
| 278 | { | ||
| 279 | fprintf(stderr, " %14Ld %-20s", count[0], event_name(counter)); | ||
| 280 | |||
| 281 | if (runtime_cycles_avg && | ||
| 282 | attrs[counter].type == PERF_TYPE_HARDWARE && | ||
| 283 | attrs[counter].config == PERF_COUNT_HW_INSTRUCTIONS) { | ||
| 284 | |||
| 285 | fprintf(stderr, " # %10.3f IPC ", | ||
| 286 | (double)count[0] / (double)runtime_cycles_avg); | ||
| 287 | } else { | ||
| 288 | if (runtime_nsecs_avg) { | ||
| 289 | fprintf(stderr, " # %10.3f M/sec", | ||
| 290 | (double)count[0]/runtime_nsecs_avg*1000.0); | ||
| 291 | } | ||
| 292 | } | ||
| 293 | print_noise(count, noise); | ||
| 185 | } | 294 | } |
| 186 | 295 | ||
| 187 | /* | 296 | /* |
| @@ -189,11 +298,12 @@ static void read_counter(int counter) | |||
| 189 | */ | 298 | */ |
| 190 | static void print_counter(int counter) | 299 | static void print_counter(int counter) |
| 191 | { | 300 | { |
| 192 | __u64 *count; | 301 | u64 *count, *noise; |
| 193 | int scaled; | 302 | int scaled; |
| 194 | 303 | ||
| 195 | count = event_res[counter]; | 304 | count = event_res_avg[counter]; |
| 196 | scaled = event_scaled[counter]; | 305 | noise = event_res_noise[counter]; |
| 306 | scaled = event_scaled_avg[counter]; | ||
| 197 | 307 | ||
| 198 | if (scaled == -1) { | 308 | if (scaled == -1) { |
| 199 | fprintf(stderr, " %14s %-20s\n", | 309 | fprintf(stderr, " %14s %-20s\n", |
| @@ -201,75 +311,107 @@ static void print_counter(int counter) | |||
| 201 | return; | 311 | return; |
| 202 | } | 312 | } |
| 203 | 313 | ||
| 204 | if (nsec_counter(counter)) { | 314 | if (nsec_counter(counter)) |
| 205 | double msecs = (double)count[0] / 1000000; | 315 | nsec_printout(counter, count, noise); |
| 206 | 316 | else | |
| 207 | fprintf(stderr, " %14.6f %-20s", | 317 | abs_printout(counter, count, noise); |
| 208 | msecs, event_name(counter)); | ||
| 209 | if (attrs[counter].type == PERF_TYPE_SOFTWARE && | ||
| 210 | attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) { | ||
| 211 | 318 | ||
| 212 | if (walltime_nsecs) | ||
| 213 | fprintf(stderr, " # %11.3f CPU utilization factor", | ||
| 214 | (double)count[0] / (double)walltime_nsecs); | ||
| 215 | } | ||
| 216 | } else { | ||
| 217 | fprintf(stderr, " %14Ld %-20s", | ||
| 218 | count[0], event_name(counter)); | ||
| 219 | if (runtime_nsecs) | ||
| 220 | fprintf(stderr, " # %11.3f M/sec", | ||
| 221 | (double)count[0]/runtime_nsecs*1000.0); | ||
| 222 | if (runtime_cycles && | ||
| 223 | attrs[counter].type == PERF_TYPE_HARDWARE && | ||
| 224 | attrs[counter].config == PERF_COUNT_HW_INSTRUCTIONS) { | ||
| 225 | |||
| 226 | fprintf(stderr, " # %1.3f per cycle", | ||
| 227 | (double)count[0] / (double)runtime_cycles); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | if (scaled) | 319 | if (scaled) |
| 231 | fprintf(stderr, " (scaled from %.2f%%)", | 320 | fprintf(stderr, " (scaled from %.2f%%)", |
| 232 | (double) count[2] / count[1] * 100); | 321 | (double) count[2] / count[1] * 100); |
| 322 | |||
| 233 | fprintf(stderr, "\n"); | 323 | fprintf(stderr, "\n"); |
| 234 | } | 324 | } |
| 235 | 325 | ||
| 236 | static int do_perf_stat(int argc, const char **argv) | 326 | /* |
| 327 | * normalize_noise noise values down to stddev: | ||
| 328 | */ | ||
| 329 | static void normalize_noise(u64 *val) | ||
| 237 | { | 330 | { |
| 238 | unsigned long long t0, t1; | 331 | double res; |
| 239 | int counter; | ||
| 240 | int status; | ||
| 241 | int pid; | ||
| 242 | int i; | ||
| 243 | 332 | ||
| 244 | if (!system_wide) | 333 | res = (double)*val / (run_count * sqrt((double)run_count)); |
| 245 | nr_cpus = 1; | ||
| 246 | 334 | ||
| 247 | for (counter = 0; counter < nr_counters; counter++) | 335 | *val = (u64)res; |
| 248 | create_perf_stat_counter(counter); | 336 | } |
| 249 | 337 | ||
| 250 | /* | 338 | static void update_avg(const char *name, int idx, u64 *avg, u64 *val) |
| 251 | * Enable counters and exec the command: | 339 | { |
| 252 | */ | 340 | *avg += *val; |
| 253 | t0 = rdclock(); | ||
| 254 | prctl(PR_TASK_PERF_COUNTERS_ENABLE); | ||
| 255 | 341 | ||
| 256 | if ((pid = fork()) < 0) | 342 | if (verbose > 1) |
| 257 | perror("failed to fork"); | 343 | fprintf(stderr, "debug: %20s[%d]: %Ld\n", name, idx, *val); |
| 344 | } | ||
| 345 | /* | ||
| 346 | * Calculate the averages and noises: | ||
| 347 | */ | ||
| 348 | static void calc_avg(void) | ||
| 349 | { | ||
| 350 | int i, j; | ||
| 351 | |||
| 352 | if (verbose > 1) | ||
| 353 | fprintf(stderr, "\n"); | ||
| 354 | |||
| 355 | for (i = 0; i < run_count; i++) { | ||
| 356 | update_avg("runtime", 0, &runtime_nsecs_avg, runtime_nsecs + i); | ||
| 357 | update_avg("walltime", 0, &walltime_nsecs_avg, walltime_nsecs + i); | ||
| 358 | update_avg("runtime_cycles", 0, &runtime_cycles_avg, runtime_cycles + i); | ||
| 359 | |||
| 360 | for (j = 0; j < nr_counters; j++) { | ||
| 361 | update_avg("counter/0", j, | ||
| 362 | event_res_avg[j]+0, event_res[i][j]+0); | ||
| 363 | update_avg("counter/1", j, | ||
| 364 | event_res_avg[j]+1, event_res[i][j]+1); | ||
| 365 | update_avg("counter/2", j, | ||
| 366 | event_res_avg[j]+2, event_res[i][j]+2); | ||
| 367 | update_avg("scaled", j, | ||
| 368 | event_scaled_avg + j, event_scaled[i]+j); | ||
| 369 | } | ||
| 370 | } | ||
| 371 | runtime_nsecs_avg /= run_count; | ||
| 372 | walltime_nsecs_avg /= run_count; | ||
| 373 | runtime_cycles_avg /= run_count; | ||
| 374 | |||
| 375 | for (j = 0; j < nr_counters; j++) { | ||
| 376 | event_res_avg[j][0] /= run_count; | ||
| 377 | event_res_avg[j][1] /= run_count; | ||
| 378 | event_res_avg[j][2] /= run_count; | ||
| 379 | } | ||
| 258 | 380 | ||
| 259 | if (!pid) { | 381 | for (i = 0; i < run_count; i++) { |
| 260 | if (execvp(argv[0], (char **)argv)) { | 382 | runtime_nsecs_noise += |
| 261 | perror(argv[0]); | 383 | abs((s64)(runtime_nsecs[i] - runtime_nsecs_avg)); |
| 262 | exit(-1); | 384 | walltime_nsecs_noise += |
| 385 | abs((s64)(walltime_nsecs[i] - walltime_nsecs_avg)); | ||
| 386 | runtime_cycles_noise += | ||
| 387 | abs((s64)(runtime_cycles[i] - runtime_cycles_avg)); | ||
| 388 | |||
| 389 | for (j = 0; j < nr_counters; j++) { | ||
| 390 | event_res_noise[j][0] += | ||
| 391 | abs((s64)(event_res[i][j][0] - event_res_avg[j][0])); | ||
| 392 | event_res_noise[j][1] += | ||
| 393 | abs((s64)(event_res[i][j][1] - event_res_avg[j][1])); | ||
| 394 | event_res_noise[j][2] += | ||
| 395 | abs((s64)(event_res[i][j][2] - event_res_avg[j][2])); | ||
| 263 | } | 396 | } |
| 264 | } | 397 | } |
| 265 | 398 | ||
| 266 | while (wait(&status) >= 0) | 399 | normalize_noise(&runtime_nsecs_noise); |
| 267 | ; | 400 | normalize_noise(&walltime_nsecs_noise); |
| 401 | normalize_noise(&runtime_cycles_noise); | ||
| 268 | 402 | ||
| 269 | prctl(PR_TASK_PERF_COUNTERS_DISABLE); | 403 | for (j = 0; j < nr_counters; j++) { |
| 270 | t1 = rdclock(); | 404 | normalize_noise(&event_res_noise[j][0]); |
| 405 | normalize_noise(&event_res_noise[j][1]); | ||
| 406 | normalize_noise(&event_res_noise[j][2]); | ||
| 407 | } | ||
| 408 | } | ||
| 271 | 409 | ||
| 272 | walltime_nsecs = t1 - t0; | 410 | static void print_stat(int argc, const char **argv) |
| 411 | { | ||
| 412 | int i, counter; | ||
| 413 | |||
| 414 | calc_avg(); | ||
| 273 | 415 | ||
| 274 | fflush(stdout); | 416 | fflush(stdout); |
| 275 | 417 | ||
| @@ -279,22 +421,19 @@ static int do_perf_stat(int argc, const char **argv) | |||
| 279 | for (i = 1; i < argc; i++) | 421 | for (i = 1; i < argc; i++) |
| 280 | fprintf(stderr, " %s", argv[i]); | 422 | fprintf(stderr, " %s", argv[i]); |
| 281 | 423 | ||
| 282 | fprintf(stderr, "\':\n"); | 424 | fprintf(stderr, "\'"); |
| 283 | fprintf(stderr, "\n"); | 425 | if (run_count > 1) |
| 284 | 426 | fprintf(stderr, " (%d runs)", run_count); | |
| 285 | for (counter = 0; counter < nr_counters; counter++) | 427 | fprintf(stderr, ":\n\n"); |
| 286 | read_counter(counter); | ||
| 287 | 428 | ||
| 288 | for (counter = 0; counter < nr_counters; counter++) | 429 | for (counter = 0; counter < nr_counters; counter++) |
| 289 | print_counter(counter); | 430 | print_counter(counter); |
| 290 | 431 | ||
| 291 | 432 | ||
| 292 | fprintf(stderr, "\n"); | 433 | fprintf(stderr, "\n"); |
| 293 | fprintf(stderr, " Wall-clock time elapsed: %12.6f msecs\n", | 434 | fprintf(stderr, " %14.9f seconds time elapsed.\n", |
| 294 | (double)(t1-t0)/1e6); | 435 | (double)walltime_nsecs_avg/1e9); |
| 295 | fprintf(stderr, "\n"); | 436 | fprintf(stderr, "\n"); |
| 296 | |||
| 297 | return 0; | ||
| 298 | } | 437 | } |
| 299 | 438 | ||
| 300 | static volatile int signr = -1; | 439 | static volatile int signr = -1; |
| @@ -332,11 +471,15 @@ static const struct option options[] = { | |||
| 332 | "scale/normalize counters"), | 471 | "scale/normalize counters"), |
| 333 | OPT_BOOLEAN('v', "verbose", &verbose, | 472 | OPT_BOOLEAN('v', "verbose", &verbose, |
| 334 | "be more verbose (show counter open errors, etc)"), | 473 | "be more verbose (show counter open errors, etc)"), |
| 474 | OPT_INTEGER('r', "repeat", &run_count, | ||
| 475 | "repeat command and print average + stddev (max: 100)"), | ||
| 335 | OPT_END() | 476 | OPT_END() |
| 336 | }; | 477 | }; |
| 337 | 478 | ||
| 338 | int cmd_stat(int argc, const char **argv, const char *prefix) | 479 | int cmd_stat(int argc, const char **argv, const char *prefix) |
| 339 | { | 480 | { |
| 481 | int status; | ||
| 482 | |||
| 340 | page_size = sysconf(_SC_PAGE_SIZE); | 483 | page_size = sysconf(_SC_PAGE_SIZE); |
| 341 | 484 | ||
| 342 | memcpy(attrs, default_attrs, sizeof(attrs)); | 485 | memcpy(attrs, default_attrs, sizeof(attrs)); |
| @@ -344,6 +487,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix) | |||
| 344 | argc = parse_options(argc, argv, options, stat_usage, 0); | 487 | argc = parse_options(argc, argv, options, stat_usage, 0); |
| 345 | if (!argc) | 488 | if (!argc) |
| 346 | usage_with_options(stat_usage, options); | 489 | usage_with_options(stat_usage, options); |
| 490 | if (run_count <= 0 || run_count > MAX_RUN) | ||
| 491 | usage_with_options(stat_usage, options); | ||
| 347 | 492 | ||
| 348 | if (!nr_counters) | 493 | if (!nr_counters) |
| 349 | nr_counters = 8; | 494 | nr_counters = 8; |
| @@ -363,5 +508,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix) | |||
| 363 | signal(SIGALRM, skip_signal); | 508 | signal(SIGALRM, skip_signal); |
| 364 | signal(SIGABRT, skip_signal); | 509 | signal(SIGABRT, skip_signal); |
| 365 | 510 | ||
| 366 | return do_perf_stat(argc, argv); | 511 | status = 0; |
| 512 | for (run_idx = 0; run_idx < run_count; run_idx++) { | ||
| 513 | if (run_count != 1 && verbose) | ||
| 514 | fprintf(stderr, "[ perf stat: executing run #%d ... ]\n", run_idx+1); | ||
| 515 | status = run_perf_stat(argc, argv); | ||
| 516 | } | ||
| 517 | |||
| 518 | print_stat(argc, argv); | ||
| 519 | |||
| 520 | return status; | ||
| 367 | } | 521 | } |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index fe338d3c5d7e..5352b5e352ed 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -54,7 +54,7 @@ static int system_wide = 0; | |||
| 54 | 54 | ||
| 55 | static int default_interval = 100000; | 55 | static int default_interval = 100000; |
| 56 | 56 | ||
| 57 | static __u64 count_filter = 5; | 57 | static u64 count_filter = 5; |
| 58 | static int print_entries = 15; | 58 | static int print_entries = 15; |
| 59 | 59 | ||
| 60 | static int target_pid = -1; | 60 | static int target_pid = -1; |
| @@ -79,8 +79,8 @@ static int dump_symtab; | |||
| 79 | * Symbols | 79 | * Symbols |
| 80 | */ | 80 | */ |
| 81 | 81 | ||
| 82 | static __u64 min_ip; | 82 | static u64 min_ip; |
| 83 | static __u64 max_ip = -1ll; | 83 | static u64 max_ip = -1ll; |
| 84 | 84 | ||
| 85 | struct sym_entry { | 85 | struct sym_entry { |
| 86 | struct rb_node rb_node; | 86 | struct rb_node rb_node; |
| @@ -194,7 +194,7 @@ static void print_sym_table(void) | |||
| 194 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); | 194 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); |
| 195 | 195 | ||
| 196 | if (nr_counters == 1) { | 196 | if (nr_counters == 1) { |
| 197 | printf("%Ld", attrs[0].sample_period); | 197 | printf("%Ld", (u64)attrs[0].sample_period); |
| 198 | if (freq) | 198 | if (freq) |
| 199 | printf("Hz "); | 199 | printf("Hz "); |
| 200 | else | 200 | else |
| @@ -372,7 +372,7 @@ out_delete_dso: | |||
| 372 | /* | 372 | /* |
| 373 | * Binary search in the histogram table and record the hit: | 373 | * Binary search in the histogram table and record the hit: |
| 374 | */ | 374 | */ |
| 375 | static void record_ip(__u64 ip, int counter) | 375 | static void record_ip(u64 ip, int counter) |
| 376 | { | 376 | { |
| 377 | struct symbol *sym = dso__find_symbol(kernel_dso, ip); | 377 | struct symbol *sym = dso__find_symbol(kernel_dso, ip); |
| 378 | 378 | ||
| @@ -392,7 +392,7 @@ static void record_ip(__u64 ip, int counter) | |||
| 392 | samples--; | 392 | samples--; |
| 393 | } | 393 | } |
| 394 | 394 | ||
| 395 | static void process_event(__u64 ip, int counter) | 395 | static void process_event(u64 ip, int counter) |
| 396 | { | 396 | { |
| 397 | samples++; | 397 | samples++; |
| 398 | 398 | ||
| @@ -463,15 +463,15 @@ static void mmap_read_counter(struct mmap_data *md) | |||
| 463 | for (; old != head;) { | 463 | for (; old != head;) { |
| 464 | struct ip_event { | 464 | struct ip_event { |
| 465 | struct perf_event_header header; | 465 | struct perf_event_header header; |
| 466 | __u64 ip; | 466 | u64 ip; |
| 467 | __u32 pid, target_pid; | 467 | u32 pid, target_pid; |
| 468 | }; | 468 | }; |
| 469 | struct mmap_event { | 469 | struct mmap_event { |
| 470 | struct perf_event_header header; | 470 | struct perf_event_header header; |
| 471 | __u32 pid, target_pid; | 471 | u32 pid, target_pid; |
| 472 | __u64 start; | 472 | u64 start; |
| 473 | __u64 len; | 473 | u64 len; |
| 474 | __u64 pgoff; | 474 | u64 pgoff; |
| 475 | char filename[PATH_MAX]; | 475 | char filename[PATH_MAX]; |
| 476 | }; | 476 | }; |
| 477 | 477 | ||
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 87a1aca4a424..ceb68aa51f7f 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
| @@ -13,12 +13,19 @@ | |||
| 13 | #define cpu_relax() asm volatile ("" ::: "memory"); | 13 | #define cpu_relax() asm volatile ("" ::: "memory"); |
| 14 | #endif | 14 | #endif |
| 15 | 15 | ||
| 16 | #ifdef __s390__ | ||
| 17 | #include "../../arch/s390/include/asm/unistd.h" | ||
| 18 | #define rmb() asm volatile("bcr 15,0" ::: "memory") | ||
| 19 | #define cpu_relax() asm volatile("" ::: "memory"); | ||
| 20 | #endif | ||
| 21 | |||
| 16 | #include <time.h> | 22 | #include <time.h> |
| 17 | #include <unistd.h> | 23 | #include <unistd.h> |
| 18 | #include <sys/types.h> | 24 | #include <sys/types.h> |
| 19 | #include <sys/syscall.h> | 25 | #include <sys/syscall.h> |
| 20 | 26 | ||
| 21 | #include "../../include/linux/perf_counter.h" | 27 | #include "../../include/linux/perf_counter.h" |
| 28 | #include "types.h" | ||
| 22 | 29 | ||
| 23 | /* | 30 | /* |
| 24 | * prctl(PR_TASK_PERF_COUNTERS_DISABLE) will (cheaply) disable all | 31 | * prctl(PR_TASK_PERF_COUNTERS_DISABLE) will (cheaply) disable all |
| @@ -65,4 +72,10 @@ sys_perf_counter_open(struct perf_counter_attr *attr, | |||
| 65 | #define MAX_COUNTERS 256 | 72 | #define MAX_COUNTERS 256 |
| 66 | #define MAX_NR_CPUS 256 | 73 | #define MAX_NR_CPUS 256 |
| 67 | 74 | ||
| 75 | struct perf_file_header { | ||
| 76 | u64 version; | ||
| 77 | u64 sample_type; | ||
| 78 | u64 data_size; | ||
| 79 | }; | ||
| 80 | |||
| 68 | #endif | 81 | #endif |
diff --git a/tools/perf/types.h b/tools/perf/types.h new file mode 100644 index 000000000000..5e75f9005940 --- /dev/null +++ b/tools/perf/types.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #ifndef _PERF_TYPES_H | ||
| 2 | #define _PERF_TYPES_H | ||
| 3 | |||
| 4 | /* | ||
| 5 | * We define u64 as unsigned long long for every architecture | ||
| 6 | * so that we can print it with %Lx without getting warnings. | ||
| 7 | */ | ||
| 8 | typedef unsigned long long u64; | ||
| 9 | typedef signed long long s64; | ||
| 10 | typedef unsigned int u32; | ||
| 11 | typedef signed int s32; | ||
| 12 | typedef unsigned short u16; | ||
| 13 | typedef signed short s16; | ||
| 14 | typedef unsigned char u8; | ||
| 15 | typedef signed char s8; | ||
| 16 | |||
| 17 | #endif /* _PERF_TYPES_H */ | ||
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c index b90ec004f29c..0b791bd346bc 100644 --- a/tools/perf/util/ctype.c +++ b/tools/perf/util/ctype.c | |||
| @@ -11,16 +11,21 @@ enum { | |||
| 11 | D = GIT_DIGIT, | 11 | D = GIT_DIGIT, |
| 12 | G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */ | 12 | G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */ |
| 13 | R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | * */ | 13 | R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | * */ |
| 14 | P = GIT_PRINT_EXTRA, /* printable - alpha - digit - glob - regex */ | ||
| 15 | |||
| 16 | PS = GIT_SPACE | GIT_PRINT_EXTRA, | ||
| 14 | }; | 17 | }; |
| 15 | 18 | ||
| 16 | unsigned char sane_ctype[256] = { | 19 | unsigned char sane_ctype[256] = { |
| 20 | /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ | ||
| 21 | |||
| 17 | 0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */ | 22 | 0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */ |
| 18 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */ | 23 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */ |
| 19 | S, 0, 0, 0, R, 0, 0, 0, R, R, G, R, 0, 0, R, 0, /* 32.. 47 */ | 24 | PS,P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */ |
| 20 | D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G, /* 48.. 63 */ | 25 | D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */ |
| 21 | 0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */ | 26 | P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */ |
| 22 | A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, 0, /* 80.. 95 */ | 27 | A, A, A, A, A, A, A, A, A, A, A, G, G, P, R, P, /* 80.. 95 */ |
| 23 | 0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */ | 28 | P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */ |
| 24 | A, A, A, A, A, A, A, A, A, A, A, R, R, 0, 0, 0, /* 112..127 */ | 29 | A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */ |
| 25 | /* Nothing in the 128.. range */ | 30 | /* Nothing in the 128.. range */ |
| 26 | }; | 31 | }; |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 5a72586e1df0..35d04da38d6a 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -13,8 +13,8 @@ int nr_counters; | |||
| 13 | struct perf_counter_attr attrs[MAX_COUNTERS]; | 13 | struct perf_counter_attr attrs[MAX_COUNTERS]; |
| 14 | 14 | ||
| 15 | struct event_symbol { | 15 | struct event_symbol { |
| 16 | __u8 type; | 16 | u8 type; |
| 17 | __u64 config; | 17 | u64 config; |
| 18 | char *symbol; | 18 | char *symbol; |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| @@ -63,8 +63,8 @@ static char *hw_event_names[] = { | |||
| 63 | }; | 63 | }; |
| 64 | 64 | ||
| 65 | static char *sw_event_names[] = { | 65 | static char *sw_event_names[] = { |
| 66 | "cpu-clock-ticks", | 66 | "cpu-clock-msecs", |
| 67 | "task-clock-ticks", | 67 | "task-clock-msecs", |
| 68 | "page-faults", | 68 | "page-faults", |
| 69 | "context-switches", | 69 | "context-switches", |
| 70 | "CPU-migrations", | 70 | "CPU-migrations", |
| @@ -96,7 +96,7 @@ static char *hw_cache_result [][MAX_ALIASES] = { | |||
| 96 | 96 | ||
| 97 | char *event_name(int counter) | 97 | char *event_name(int counter) |
| 98 | { | 98 | { |
| 99 | __u64 config = attrs[counter].config; | 99 | u64 config = attrs[counter].config; |
| 100 | int type = attrs[counter].type; | 100 | int type = attrs[counter].type; |
| 101 | static char buf[32]; | 101 | static char buf[32]; |
| 102 | 102 | ||
| @@ -112,7 +112,7 @@ char *event_name(int counter) | |||
| 112 | return "unknown-hardware"; | 112 | return "unknown-hardware"; |
| 113 | 113 | ||
| 114 | case PERF_TYPE_HW_CACHE: { | 114 | case PERF_TYPE_HW_CACHE: { |
| 115 | __u8 cache_type, cache_op, cache_result; | 115 | u8 cache_type, cache_op, cache_result; |
| 116 | static char name[100]; | 116 | static char name[100]; |
| 117 | 117 | ||
| 118 | cache_type = (config >> 0) & 0xff; | 118 | cache_type = (config >> 0) & 0xff; |
| @@ -202,7 +202,7 @@ static int parse_generic_hw_symbols(const char *str, struct perf_counter_attr *a | |||
| 202 | */ | 202 | */ |
| 203 | static int parse_event_symbols(const char *str, struct perf_counter_attr *attr) | 203 | static int parse_event_symbols(const char *str, struct perf_counter_attr *attr) |
| 204 | { | 204 | { |
| 205 | __u64 config, id; | 205 | u64 config, id; |
| 206 | int type; | 206 | int type; |
| 207 | unsigned int i; | 207 | unsigned int i; |
| 208 | const char *sep, *pstr; | 208 | const char *sep, *pstr; |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index ec33c0c7f4e2..c93eca9a7be3 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
| @@ -15,7 +15,7 @@ static int hex(char ch) | |||
| 15 | * While we find nice hex chars, build a long_val. | 15 | * While we find nice hex chars, build a long_val. |
| 16 | * Return number of chars processed. | 16 | * Return number of chars processed. |
| 17 | */ | 17 | */ |
| 18 | int hex2u64(const char *ptr, __u64 *long_val) | 18 | int hex2u64(const char *ptr, u64 *long_val) |
| 19 | { | 19 | { |
| 20 | const char *p = ptr; | 20 | const char *p = ptr; |
| 21 | *long_val = 0; | 21 | *long_val = 0; |
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index 72812c1c9a7a..37b03255b425 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | #ifndef _PERF_STRING_H_ | 1 | #ifndef _PERF_STRING_H_ |
| 2 | #define _PERF_STRING_H_ | 2 | #define _PERF_STRING_H_ |
| 3 | 3 | ||
| 4 | #include <linux/types.h> | 4 | #include "../types.h" |
| 5 | 5 | ||
| 6 | int hex2u64(const char *ptr, __u64 *val); | 6 | int hex2u64(const char *ptr, u64 *val); |
| 7 | 7 | ||
| 8 | #endif | 8 | #endif |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 49a55f813712..86e14375e74e 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -9,9 +9,9 @@ | |||
| 9 | 9 | ||
| 10 | const char *sym_hist_filter; | 10 | const char *sym_hist_filter; |
| 11 | 11 | ||
| 12 | static struct symbol *symbol__new(__u64 start, __u64 len, | 12 | static struct symbol *symbol__new(u64 start, u64 len, |
| 13 | const char *name, unsigned int priv_size, | 13 | const char *name, unsigned int priv_size, |
| 14 | __u64 obj_start, int verbose) | 14 | u64 obj_start, int verbose) |
| 15 | { | 15 | { |
| 16 | size_t namelen = strlen(name) + 1; | 16 | size_t namelen = strlen(name) + 1; |
| 17 | struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); | 17 | struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); |
| @@ -21,14 +21,14 @@ static struct symbol *symbol__new(__u64 start, __u64 len, | |||
| 21 | 21 | ||
| 22 | if (verbose >= 2) | 22 | if (verbose >= 2) |
| 23 | printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n", | 23 | printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n", |
| 24 | (__u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start); | 24 | (u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start); |
| 25 | 25 | ||
| 26 | self->obj_start= obj_start; | 26 | self->obj_start= obj_start; |
| 27 | self->hist = NULL; | 27 | self->hist = NULL; |
| 28 | self->hist_sum = 0; | 28 | self->hist_sum = 0; |
| 29 | 29 | ||
| 30 | if (sym_hist_filter && !strcmp(name, sym_hist_filter)) | 30 | if (sym_hist_filter && !strcmp(name, sym_hist_filter)) |
| 31 | self->hist = calloc(sizeof(__u64), len); | 31 | self->hist = calloc(sizeof(u64), len); |
| 32 | 32 | ||
| 33 | if (priv_size) { | 33 | if (priv_size) { |
| 34 | memset(self, 0, priv_size); | 34 | memset(self, 0, priv_size); |
| @@ -89,7 +89,7 @@ static void dso__insert_symbol(struct dso *self, struct symbol *sym) | |||
| 89 | { | 89 | { |
| 90 | struct rb_node **p = &self->syms.rb_node; | 90 | struct rb_node **p = &self->syms.rb_node; |
| 91 | struct rb_node *parent = NULL; | 91 | struct rb_node *parent = NULL; |
| 92 | const __u64 ip = sym->start; | 92 | const u64 ip = sym->start; |
| 93 | struct symbol *s; | 93 | struct symbol *s; |
| 94 | 94 | ||
| 95 | while (*p != NULL) { | 95 | while (*p != NULL) { |
| @@ -104,7 +104,7 @@ static void dso__insert_symbol(struct dso *self, struct symbol *sym) | |||
| 104 | rb_insert_color(&sym->rb_node, &self->syms); | 104 | rb_insert_color(&sym->rb_node, &self->syms); |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | struct symbol *dso__find_symbol(struct dso *self, __u64 ip) | 107 | struct symbol *dso__find_symbol(struct dso *self, u64 ip) |
| 108 | { | 108 | { |
| 109 | struct rb_node *n; | 109 | struct rb_node *n; |
| 110 | 110 | ||
| @@ -151,7 +151,7 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb | |||
| 151 | goto out_failure; | 151 | goto out_failure; |
| 152 | 152 | ||
| 153 | while (!feof(file)) { | 153 | while (!feof(file)) { |
| 154 | __u64 start; | 154 | u64 start; |
| 155 | struct symbol *sym; | 155 | struct symbol *sym; |
| 156 | int line_len, len; | 156 | int line_len, len; |
| 157 | char symbol_type; | 157 | char symbol_type; |
| @@ -232,7 +232,7 @@ static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int verb | |||
| 232 | goto out_failure; | 232 | goto out_failure; |
| 233 | 233 | ||
| 234 | while (!feof(file)) { | 234 | while (!feof(file)) { |
| 235 | __u64 start, size; | 235 | u64 start, size; |
| 236 | struct symbol *sym; | 236 | struct symbol *sym; |
| 237 | int line_len, len; | 237 | int line_len, len; |
| 238 | 238 | ||
| @@ -353,7 +353,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 353 | { | 353 | { |
| 354 | uint32_t nr_rel_entries, idx; | 354 | uint32_t nr_rel_entries, idx; |
| 355 | GElf_Sym sym; | 355 | GElf_Sym sym; |
| 356 | __u64 plt_offset; | 356 | u64 plt_offset; |
| 357 | GElf_Shdr shdr_plt; | 357 | GElf_Shdr shdr_plt; |
| 358 | struct symbol *f; | 358 | struct symbol *f; |
| 359 | GElf_Shdr shdr_rel_plt; | 359 | GElf_Shdr shdr_rel_plt; |
| @@ -523,7 +523,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 523 | 523 | ||
| 524 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { | 524 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { |
| 525 | struct symbol *f; | 525 | struct symbol *f; |
| 526 | __u64 obj_start; | 526 | u64 obj_start; |
| 527 | 527 | ||
| 528 | if (!elf_sym__is_function(&sym)) | 528 | if (!elf_sym__is_function(&sym)) |
| 529 | continue; | 529 | continue; |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 0d1292bd8270..ea332e56e458 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -2,16 +2,18 @@ | |||
| 2 | #define _PERF_SYMBOL_ 1 | 2 | #define _PERF_SYMBOL_ 1 |
| 3 | 3 | ||
| 4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
| 5 | #include "../types.h" | ||
| 5 | #include "list.h" | 6 | #include "list.h" |
| 6 | #include "rbtree.h" | 7 | #include "rbtree.h" |
| 7 | 8 | ||
| 8 | struct symbol { | 9 | struct symbol { |
| 9 | struct rb_node rb_node; | 10 | struct rb_node rb_node; |
| 10 | __u64 start; | 11 | u64 start; |
| 11 | __u64 end; | 12 | u64 end; |
| 12 | __u64 obj_start; | 13 | u64 obj_start; |
| 13 | __u64 hist_sum; | 14 | u64 hist_sum; |
| 14 | __u64 *hist; | 15 | u64 *hist; |
| 16 | void *priv; | ||
| 15 | char name[0]; | 17 | char name[0]; |
| 16 | }; | 18 | }; |
| 17 | 19 | ||
| @@ -19,7 +21,7 @@ struct dso { | |||
| 19 | struct list_head node; | 21 | struct list_head node; |
| 20 | struct rb_root syms; | 22 | struct rb_root syms; |
| 21 | unsigned int sym_priv_size; | 23 | unsigned int sym_priv_size; |
| 22 | struct symbol *(*find_symbol)(struct dso *, __u64 ip); | 24 | struct symbol *(*find_symbol)(struct dso *, u64 ip); |
| 23 | char name[0]; | 25 | char name[0]; |
| 24 | }; | 26 | }; |
| 25 | 27 | ||
| @@ -35,7 +37,7 @@ static inline void *dso__sym_priv(struct dso *self, struct symbol *sym) | |||
| 35 | return ((void *)sym) - self->sym_priv_size; | 37 | return ((void *)sym) - self->sym_priv_size; |
| 36 | } | 38 | } |
| 37 | 39 | ||
| 38 | struct symbol *dso__find_symbol(struct dso *self, __u64 ip); | 40 | struct symbol *dso__find_symbol(struct dso *self, u64 ip); |
| 39 | 41 | ||
| 40 | int dso__load_kernel(struct dso *self, const char *vmlinux, | 42 | int dso__load_kernel(struct dso *self, const char *vmlinux, |
| 41 | symbol_filter_t filter, int verbose); | 43 | symbol_filter_t filter, int verbose); |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 76590a16c271..b8cfed776d81 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
| @@ -100,11 +100,6 @@ | |||
| 100 | #include <iconv.h> | 100 | #include <iconv.h> |
| 101 | #endif | 101 | #endif |
| 102 | 102 | ||
| 103 | #ifndef NO_OPENSSL | ||
| 104 | #include <openssl/ssl.h> | ||
| 105 | #include <openssl/err.h> | ||
| 106 | #endif | ||
| 107 | |||
| 108 | /* On most systems <limits.h> would have given us this, but | 103 | /* On most systems <limits.h> would have given us this, but |
| 109 | * not on some systems (e.g. GNU/Hurd). | 104 | * not on some systems (e.g. GNU/Hurd). |
| 110 | */ | 105 | */ |
| @@ -332,17 +327,20 @@ static inline int has_extension(const char *filename, const char *ext) | |||
| 332 | #undef tolower | 327 | #undef tolower |
| 333 | #undef toupper | 328 | #undef toupper |
| 334 | extern unsigned char sane_ctype[256]; | 329 | extern unsigned char sane_ctype[256]; |
| 335 | #define GIT_SPACE 0x01 | 330 | #define GIT_SPACE 0x01 |
| 336 | #define GIT_DIGIT 0x02 | 331 | #define GIT_DIGIT 0x02 |
| 337 | #define GIT_ALPHA 0x04 | 332 | #define GIT_ALPHA 0x04 |
| 338 | #define GIT_GLOB_SPECIAL 0x08 | 333 | #define GIT_GLOB_SPECIAL 0x08 |
| 339 | #define GIT_REGEX_SPECIAL 0x10 | 334 | #define GIT_REGEX_SPECIAL 0x10 |
| 335 | #define GIT_PRINT_EXTRA 0x20 | ||
| 336 | #define GIT_PRINT 0x3E | ||
| 340 | #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0) | 337 | #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0) |
| 341 | #define isascii(x) (((x) & ~0x7f) == 0) | 338 | #define isascii(x) (((x) & ~0x7f) == 0) |
| 342 | #define isspace(x) sane_istest(x,GIT_SPACE) | 339 | #define isspace(x) sane_istest(x,GIT_SPACE) |
| 343 | #define isdigit(x) sane_istest(x,GIT_DIGIT) | 340 | #define isdigit(x) sane_istest(x,GIT_DIGIT) |
| 344 | #define isalpha(x) sane_istest(x,GIT_ALPHA) | 341 | #define isalpha(x) sane_istest(x,GIT_ALPHA) |
| 345 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) | 342 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) |
| 343 | #define isprint(x) sane_istest(x,GIT_PRINT) | ||
| 346 | #define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL) | 344 | #define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL) |
| 347 | #define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL) | 345 | #define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL) |
| 348 | #define tolower(x) sane_case((unsigned char)(x), 0x20) | 346 | #define tolower(x) sane_case((unsigned char)(x), 0x20) |
