diff options
Diffstat (limited to 'tools/perf/util/hist.c')
-rw-r--r-- | tools/perf/util/hist.c | 259 |
1 files changed, 42 insertions, 217 deletions
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index be22ae6ef055..627a02e03c57 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #include "annotate.h" | ||
1 | #include "util.h" | 2 | #include "util.h" |
2 | #include "build-id.h" | 3 | #include "build-id.h" |
3 | #include "hist.h" | 4 | #include "hist.h" |
@@ -49,6 +50,15 @@ static void hists__calc_col_len(struct hists *self, struct hist_entry *h) | |||
49 | 50 | ||
50 | if (h->ms.sym) | 51 | if (h->ms.sym) |
51 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); | 52 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); |
53 | else { | ||
54 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | ||
55 | |||
56 | if (hists__col_len(self, HISTC_DSO) < unresolved_col_width && | ||
57 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
58 | !symbol_conf.dso_list) | ||
59 | hists__set_col_len(self, HISTC_DSO, | ||
60 | unresolved_col_width); | ||
61 | } | ||
52 | 62 | ||
53 | len = thread__comm_len(h->thread); | 63 | len = thread__comm_len(h->thread); |
54 | if (hists__new_col_len(self, HISTC_COMM, len)) | 64 | if (hists__new_col_len(self, HISTC_COMM, len)) |
@@ -87,7 +97,7 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self, | |||
87 | 97 | ||
88 | static struct hist_entry *hist_entry__new(struct hist_entry *template) | 98 | static struct hist_entry *hist_entry__new(struct hist_entry *template) |
89 | { | 99 | { |
90 | size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0; | 100 | size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; |
91 | struct hist_entry *self = malloc(sizeof(*self) + callchain_size); | 101 | struct hist_entry *self = malloc(sizeof(*self) + callchain_size); |
92 | 102 | ||
93 | if (self != NULL) { | 103 | if (self != NULL) { |
@@ -211,7 +221,9 @@ void hist_entry__free(struct hist_entry *he) | |||
211 | * collapse the histogram | 221 | * collapse the histogram |
212 | */ | 222 | */ |
213 | 223 | ||
214 | static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | 224 | static bool hists__collapse_insert_entry(struct hists *self, |
225 | struct rb_root *root, | ||
226 | struct hist_entry *he) | ||
215 | { | 227 | { |
216 | struct rb_node **p = &root->rb_node; | 228 | struct rb_node **p = &root->rb_node; |
217 | struct rb_node *parent = NULL; | 229 | struct rb_node *parent = NULL; |
@@ -226,6 +238,11 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | |||
226 | 238 | ||
227 | if (!cmp) { | 239 | if (!cmp) { |
228 | iter->period += he->period; | 240 | iter->period += he->period; |
241 | if (symbol_conf.use_callchain) { | ||
242 | callchain_cursor_reset(&self->callchain_cursor); | ||
243 | callchain_merge(&self->callchain_cursor, iter->callchain, | ||
244 | he->callchain); | ||
245 | } | ||
229 | hist_entry__free(he); | 246 | hist_entry__free(he); |
230 | return false; | 247 | return false; |
231 | } | 248 | } |
@@ -260,7 +277,7 @@ void hists__collapse_resort(struct hists *self) | |||
260 | next = rb_next(&n->rb_node); | 277 | next = rb_next(&n->rb_node); |
261 | 278 | ||
262 | rb_erase(&n->rb_node, &self->entries); | 279 | rb_erase(&n->rb_node, &self->entries); |
263 | if (collapse__insert_entry(&tmp, n)) | 280 | if (hists__collapse_insert_entry(self, &tmp, n)) |
264 | hists__inc_nr_entries(self, n); | 281 | hists__inc_nr_entries(self, n); |
265 | } | 282 | } |
266 | 283 | ||
@@ -354,7 +371,7 @@ static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | |||
354 | 371 | ||
355 | static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | 372 | static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, |
356 | int depth, int depth_mask, int period, | 373 | int depth, int depth_mask, int period, |
357 | u64 total_samples, int hits, | 374 | u64 total_samples, u64 hits, |
358 | int left_margin) | 375 | int left_margin) |
359 | { | 376 | { |
360 | int i; | 377 | int i; |
@@ -423,7 +440,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
423 | u64 cumul; | 440 | u64 cumul; |
424 | 441 | ||
425 | child = rb_entry(node, struct callchain_node, rb_node); | 442 | child = rb_entry(node, struct callchain_node, rb_node); |
426 | cumul = cumul_hits(child); | 443 | cumul = callchain_cumul_hits(child); |
427 | remaining -= cumul; | 444 | remaining -= cumul; |
428 | 445 | ||
429 | /* | 446 | /* |
@@ -583,6 +600,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
583 | { | 600 | { |
584 | struct sort_entry *se; | 601 | struct sort_entry *se; |
585 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; | 602 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; |
603 | u64 nr_events; | ||
586 | const char *sep = symbol_conf.field_sep; | 604 | const char *sep = symbol_conf.field_sep; |
587 | int ret; | 605 | int ret; |
588 | 606 | ||
@@ -591,6 +609,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
591 | 609 | ||
592 | if (pair_hists) { | 610 | if (pair_hists) { |
593 | period = self->pair ? self->pair->period : 0; | 611 | period = self->pair ? self->pair->period : 0; |
612 | nr_events = self->pair ? self->pair->nr_events : 0; | ||
594 | total = pair_hists->stats.total_period; | 613 | total = pair_hists->stats.total_period; |
595 | period_sys = self->pair ? self->pair->period_sys : 0; | 614 | period_sys = self->pair ? self->pair->period_sys : 0; |
596 | period_us = self->pair ? self->pair->period_us : 0; | 615 | period_us = self->pair ? self->pair->period_us : 0; |
@@ -598,6 +617,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
598 | period_guest_us = self->pair ? self->pair->period_guest_us : 0; | 617 | period_guest_us = self->pair ? self->pair->period_guest_us : 0; |
599 | } else { | 618 | } else { |
600 | period = self->period; | 619 | period = self->period; |
620 | nr_events = self->nr_events; | ||
601 | total = session_total; | 621 | total = session_total; |
602 | period_sys = self->period_sys; | 622 | period_sys = self->period_sys; |
603 | period_us = self->period_us; | 623 | period_us = self->period_us; |
@@ -634,13 +654,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
634 | } | 654 | } |
635 | } | 655 | } |
636 | } else | 656 | } else |
637 | ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period); | 657 | ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period); |
638 | 658 | ||
639 | if (symbol_conf.show_nr_samples) { | 659 | if (symbol_conf.show_nr_samples) { |
640 | if (sep) | 660 | if (sep) |
641 | ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period); | 661 | ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events); |
642 | else | 662 | else |
643 | ret += snprintf(s + ret, size - ret, "%11lld", period); | 663 | ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); |
644 | } | 664 | } |
645 | 665 | ||
646 | if (pair_hists) { | 666 | if (pair_hists) { |
@@ -942,216 +962,14 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
942 | } | 962 | } |
943 | } | 963 | } |
944 | 964 | ||
945 | static int symbol__alloc_hist(struct symbol *self) | 965 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) |
946 | { | ||
947 | struct sym_priv *priv = symbol__priv(self); | ||
948 | const int size = (sizeof(*priv->hist) + | ||
949 | (self->end - self->start) * sizeof(u64)); | ||
950 | |||
951 | priv->hist = zalloc(size); | ||
952 | return priv->hist == NULL ? -1 : 0; | ||
953 | } | ||
954 | |||
955 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) | ||
956 | { | ||
957 | unsigned int sym_size, offset; | ||
958 | struct symbol *sym = self->ms.sym; | ||
959 | struct sym_priv *priv; | ||
960 | struct sym_hist *h; | ||
961 | |||
962 | if (!sym || !self->ms.map) | ||
963 | return 0; | ||
964 | |||
965 | priv = symbol__priv(sym); | ||
966 | if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) | ||
967 | return -ENOMEM; | ||
968 | |||
969 | sym_size = sym->end - sym->start; | ||
970 | offset = ip - sym->start; | ||
971 | |||
972 | pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); | ||
973 | |||
974 | if (offset >= sym_size) | ||
975 | return 0; | ||
976 | |||
977 | h = priv->hist; | ||
978 | h->sum++; | ||
979 | h->ip[offset]++; | ||
980 | |||
981 | pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, | ||
982 | self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); | ||
983 | return 0; | ||
984 | } | ||
985 | |||
986 | static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) | ||
987 | { | ||
988 | struct objdump_line *self = malloc(sizeof(*self) + privsize); | ||
989 | |||
990 | if (self != NULL) { | ||
991 | self->offset = offset; | ||
992 | self->line = line; | ||
993 | } | ||
994 | |||
995 | return self; | ||
996 | } | ||
997 | |||
998 | void objdump_line__free(struct objdump_line *self) | ||
999 | { | ||
1000 | free(self->line); | ||
1001 | free(self); | ||
1002 | } | ||
1003 | |||
1004 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
1005 | { | ||
1006 | list_add_tail(&line->node, head); | ||
1007 | } | ||
1008 | |||
1009 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
1010 | struct objdump_line *pos) | ||
1011 | { | ||
1012 | list_for_each_entry_continue(pos, head, node) | ||
1013 | if (pos->offset >= 0) | ||
1014 | return pos; | ||
1015 | |||
1016 | return NULL; | ||
1017 | } | ||
1018 | |||
1019 | static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, | ||
1020 | struct list_head *head, size_t privsize) | ||
1021 | { | 966 | { |
1022 | struct symbol *sym = self->ms.sym; | 967 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); |
1023 | struct objdump_line *objdump_line; | ||
1024 | char *line = NULL, *tmp, *tmp2, *c; | ||
1025 | size_t line_len; | ||
1026 | s64 line_ip, offset = -1; | ||
1027 | |||
1028 | if (getline(&line, &line_len, file) < 0) | ||
1029 | return -1; | ||
1030 | |||
1031 | if (!line) | ||
1032 | return -1; | ||
1033 | |||
1034 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
1035 | line[--line_len] = '\0'; | ||
1036 | |||
1037 | c = strchr(line, '\n'); | ||
1038 | if (c) | ||
1039 | *c = 0; | ||
1040 | |||
1041 | line_ip = -1; | ||
1042 | |||
1043 | /* | ||
1044 | * Strip leading spaces: | ||
1045 | */ | ||
1046 | tmp = line; | ||
1047 | while (*tmp) { | ||
1048 | if (*tmp != ' ') | ||
1049 | break; | ||
1050 | tmp++; | ||
1051 | } | ||
1052 | |||
1053 | if (*tmp) { | ||
1054 | /* | ||
1055 | * Parse hexa addresses followed by ':' | ||
1056 | */ | ||
1057 | line_ip = strtoull(tmp, &tmp2, 16); | ||
1058 | if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') | ||
1059 | line_ip = -1; | ||
1060 | } | ||
1061 | |||
1062 | if (line_ip != -1) { | ||
1063 | u64 start = map__rip_2objdump(self->ms.map, sym->start), | ||
1064 | end = map__rip_2objdump(self->ms.map, sym->end); | ||
1065 | |||
1066 | offset = line_ip - start; | ||
1067 | if (offset < 0 || (u64)line_ip > end) | ||
1068 | offset = -1; | ||
1069 | } | ||
1070 | |||
1071 | objdump_line = objdump_line__new(offset, line, privsize); | ||
1072 | if (objdump_line == NULL) { | ||
1073 | free(line); | ||
1074 | return -1; | ||
1075 | } | ||
1076 | objdump__add_line(head, objdump_line); | ||
1077 | |||
1078 | return 0; | ||
1079 | } | 968 | } |
1080 | 969 | ||
1081 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head, | 970 | int hist_entry__annotate(struct hist_entry *he, size_t privsize) |
1082 | size_t privsize) | ||
1083 | { | 971 | { |
1084 | struct symbol *sym = self->ms.sym; | 972 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); |
1085 | struct map *map = self->ms.map; | ||
1086 | struct dso *dso = map->dso; | ||
1087 | char *filename = dso__build_id_filename(dso, NULL, 0); | ||
1088 | bool free_filename = true; | ||
1089 | char command[PATH_MAX * 2]; | ||
1090 | FILE *file; | ||
1091 | int err = 0; | ||
1092 | u64 len; | ||
1093 | |||
1094 | if (filename == NULL) { | ||
1095 | if (dso->has_build_id) { | ||
1096 | pr_err("Can't annotate %s: not enough memory\n", | ||
1097 | sym->name); | ||
1098 | return -ENOMEM; | ||
1099 | } | ||
1100 | goto fallback; | ||
1101 | } else if (readlink(filename, command, sizeof(command)) < 0 || | ||
1102 | strstr(command, "[kernel.kallsyms]") || | ||
1103 | access(filename, R_OK)) { | ||
1104 | free(filename); | ||
1105 | fallback: | ||
1106 | /* | ||
1107 | * If we don't have build-ids or the build-id file isn't in the | ||
1108 | * cache, or is just a kallsyms file, well, lets hope that this | ||
1109 | * DSO is the same as when 'perf record' ran. | ||
1110 | */ | ||
1111 | filename = dso->long_name; | ||
1112 | free_filename = false; | ||
1113 | } | ||
1114 | |||
1115 | if (dso->origin == DSO__ORIG_KERNEL) { | ||
1116 | if (dso->annotate_warned) | ||
1117 | goto out_free_filename; | ||
1118 | err = -ENOENT; | ||
1119 | dso->annotate_warned = 1; | ||
1120 | pr_err("Can't annotate %s: No vmlinux file was found in the " | ||
1121 | "path\n", sym->name); | ||
1122 | goto out_free_filename; | ||
1123 | } | ||
1124 | |||
1125 | pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, | ||
1126 | filename, sym->name, map->unmap_ip(map, sym->start), | ||
1127 | map->unmap_ip(map, sym->end)); | ||
1128 | |||
1129 | len = sym->end - sym->start; | ||
1130 | |||
1131 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | ||
1132 | dso, dso->long_name, sym, sym->name); | ||
1133 | |||
1134 | snprintf(command, sizeof(command), | ||
1135 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", | ||
1136 | map__rip_2objdump(map, sym->start), | ||
1137 | map__rip_2objdump(map, sym->end), | ||
1138 | filename, filename); | ||
1139 | |||
1140 | pr_debug("Executing: %s\n", command); | ||
1141 | |||
1142 | file = popen(command, "r"); | ||
1143 | if (!file) | ||
1144 | goto out_free_filename; | ||
1145 | |||
1146 | while (!feof(file)) | ||
1147 | if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0) | ||
1148 | break; | ||
1149 | |||
1150 | pclose(file); | ||
1151 | out_free_filename: | ||
1152 | if (free_filename) | ||
1153 | free(filename); | ||
1154 | return err; | ||
1155 | } | 973 | } |
1156 | 974 | ||
1157 | void hists__inc_nr_events(struct hists *self, u32 type) | 975 | void hists__inc_nr_events(struct hists *self, u32 type) |
@@ -1166,10 +984,17 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
1166 | size_t ret = 0; | 984 | size_t ret = 0; |
1167 | 985 | ||
1168 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 986 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
1169 | if (!event__name[i]) | 987 | const char *name; |
988 | |||
989 | if (self->stats.nr_events[i] == 0) | ||
990 | continue; | ||
991 | |||
992 | name = perf_event__name(i); | ||
993 | if (!strcmp(name, "UNKNOWN")) | ||
1170 | continue; | 994 | continue; |
1171 | ret += fprintf(fp, "%10s events: %10d\n", | 995 | |
1172 | event__name[i], self->stats.nr_events[i]); | 996 | ret += fprintf(fp, "%16s events: %10d\n", name, |
997 | self->stats.nr_events[i]); | ||
1173 | } | 998 | } |
1174 | 999 | ||
1175 | return ret; | 1000 | return ret; |