diff options
Diffstat (limited to 'tools/perf/util/hist.c')
-rw-r--r-- | tools/perf/util/hist.c | 258 |
1 files changed, 35 insertions, 223 deletions
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c749ba6136a0..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)) |
@@ -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,8 +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; |
229 | if (symbol_conf.use_callchain) | 241 | if (symbol_conf.use_callchain) { |
230 | callchain_merge(iter->callchain, he->callchain); | 242 | callchain_cursor_reset(&self->callchain_cursor); |
243 | callchain_merge(&self->callchain_cursor, iter->callchain, | ||
244 | he->callchain); | ||
245 | } | ||
231 | hist_entry__free(he); | 246 | hist_entry__free(he); |
232 | return false; | 247 | return false; |
233 | } | 248 | } |
@@ -262,7 +277,7 @@ void hists__collapse_resort(struct hists *self) | |||
262 | next = rb_next(&n->rb_node); | 277 | next = rb_next(&n->rb_node); |
263 | 278 | ||
264 | rb_erase(&n->rb_node, &self->entries); | 279 | rb_erase(&n->rb_node, &self->entries); |
265 | if (collapse__insert_entry(&tmp, n)) | 280 | if (hists__collapse_insert_entry(self, &tmp, n)) |
266 | hists__inc_nr_entries(self, n); | 281 | hists__inc_nr_entries(self, n); |
267 | } | 282 | } |
268 | 283 | ||
@@ -425,7 +440,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
425 | u64 cumul; | 440 | u64 cumul; |
426 | 441 | ||
427 | child = rb_entry(node, struct callchain_node, rb_node); | 442 | child = rb_entry(node, struct callchain_node, rb_node); |
428 | cumul = cumul_hits(child); | 443 | cumul = callchain_cumul_hits(child); |
429 | remaining -= cumul; | 444 | remaining -= cumul; |
430 | 445 | ||
431 | /* | 446 | /* |
@@ -585,6 +600,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
585 | { | 600 | { |
586 | struct sort_entry *se; | 601 | struct sort_entry *se; |
587 | 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; | ||
588 | const char *sep = symbol_conf.field_sep; | 604 | const char *sep = symbol_conf.field_sep; |
589 | int ret; | 605 | int ret; |
590 | 606 | ||
@@ -593,6 +609,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
593 | 609 | ||
594 | if (pair_hists) { | 610 | if (pair_hists) { |
595 | 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; | ||
596 | total = pair_hists->stats.total_period; | 613 | total = pair_hists->stats.total_period; |
597 | period_sys = self->pair ? self->pair->period_sys : 0; | 614 | period_sys = self->pair ? self->pair->period_sys : 0; |
598 | period_us = self->pair ? self->pair->period_us : 0; | 615 | period_us = self->pair ? self->pair->period_us : 0; |
@@ -600,6 +617,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
600 | period_guest_us = self->pair ? self->pair->period_guest_us : 0; | 617 | period_guest_us = self->pair ? self->pair->period_guest_us : 0; |
601 | } else { | 618 | } else { |
602 | period = self->period; | 619 | period = self->period; |
620 | nr_events = self->nr_events; | ||
603 | total = session_total; | 621 | total = session_total; |
604 | period_sys = self->period_sys; | 622 | period_sys = self->period_sys; |
605 | period_us = self->period_us; | 623 | period_us = self->period_us; |
@@ -636,13 +654,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
636 | } | 654 | } |
637 | } | 655 | } |
638 | } else | 656 | } else |
639 | ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period); | 657 | ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period); |
640 | 658 | ||
641 | if (symbol_conf.show_nr_samples) { | 659 | if (symbol_conf.show_nr_samples) { |
642 | if (sep) | 660 | if (sep) |
643 | ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period); | 661 | ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events); |
644 | else | 662 | else |
645 | ret += snprintf(s + ret, size - ret, "%11lld", period); | 663 | ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); |
646 | } | 664 | } |
647 | 665 | ||
648 | if (pair_hists) { | 666 | if (pair_hists) { |
@@ -944,224 +962,14 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
944 | } | 962 | } |
945 | } | 963 | } |
946 | 964 | ||
947 | static int symbol__alloc_hist(struct symbol *self) | 965 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) |
948 | { | ||
949 | struct sym_priv *priv = symbol__priv(self); | ||
950 | const int size = (sizeof(*priv->hist) + | ||
951 | (self->end - self->start) * sizeof(u64)); | ||
952 | |||
953 | priv->hist = zalloc(size); | ||
954 | return priv->hist == NULL ? -1 : 0; | ||
955 | } | ||
956 | |||
957 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) | ||
958 | { | ||
959 | unsigned int sym_size, offset; | ||
960 | struct symbol *sym = self->ms.sym; | ||
961 | struct sym_priv *priv; | ||
962 | struct sym_hist *h; | ||
963 | |||
964 | if (!sym || !self->ms.map) | ||
965 | return 0; | ||
966 | |||
967 | priv = symbol__priv(sym); | ||
968 | if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) | ||
969 | return -ENOMEM; | ||
970 | |||
971 | sym_size = sym->end - sym->start; | ||
972 | offset = ip - sym->start; | ||
973 | |||
974 | pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); | ||
975 | |||
976 | if (offset >= sym_size) | ||
977 | return 0; | ||
978 | |||
979 | h = priv->hist; | ||
980 | h->sum++; | ||
981 | h->ip[offset]++; | ||
982 | |||
983 | pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, | ||
984 | self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); | ||
985 | return 0; | ||
986 | } | ||
987 | |||
988 | static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) | ||
989 | { | ||
990 | struct objdump_line *self = malloc(sizeof(*self) + privsize); | ||
991 | |||
992 | if (self != NULL) { | ||
993 | self->offset = offset; | ||
994 | self->line = line; | ||
995 | } | ||
996 | |||
997 | return self; | ||
998 | } | ||
999 | |||
1000 | void objdump_line__free(struct objdump_line *self) | ||
1001 | { | ||
1002 | free(self->line); | ||
1003 | free(self); | ||
1004 | } | ||
1005 | |||
1006 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
1007 | { | ||
1008 | list_add_tail(&line->node, head); | ||
1009 | } | ||
1010 | |||
1011 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
1012 | struct objdump_line *pos) | ||
1013 | { | ||
1014 | list_for_each_entry_continue(pos, head, node) | ||
1015 | if (pos->offset >= 0) | ||
1016 | return pos; | ||
1017 | |||
1018 | return NULL; | ||
1019 | } | ||
1020 | |||
1021 | static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, | ||
1022 | struct list_head *head, size_t privsize) | ||
1023 | { | 966 | { |
1024 | struct symbol *sym = self->ms.sym; | 967 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); |
1025 | struct objdump_line *objdump_line; | ||
1026 | char *line = NULL, *tmp, *tmp2, *c; | ||
1027 | size_t line_len; | ||
1028 | s64 line_ip, offset = -1; | ||
1029 | |||
1030 | if (getline(&line, &line_len, file) < 0) | ||
1031 | return -1; | ||
1032 | |||
1033 | if (!line) | ||
1034 | return -1; | ||
1035 | |||
1036 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
1037 | line[--line_len] = '\0'; | ||
1038 | |||
1039 | c = strchr(line, '\n'); | ||
1040 | if (c) | ||
1041 | *c = 0; | ||
1042 | |||
1043 | line_ip = -1; | ||
1044 | |||
1045 | /* | ||
1046 | * Strip leading spaces: | ||
1047 | */ | ||
1048 | tmp = line; | ||
1049 | while (*tmp) { | ||
1050 | if (*tmp != ' ') | ||
1051 | break; | ||
1052 | tmp++; | ||
1053 | } | ||
1054 | |||
1055 | if (*tmp) { | ||
1056 | /* | ||
1057 | * Parse hexa addresses followed by ':' | ||
1058 | */ | ||
1059 | line_ip = strtoull(tmp, &tmp2, 16); | ||
1060 | if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') | ||
1061 | line_ip = -1; | ||
1062 | } | ||
1063 | |||
1064 | if (line_ip != -1) { | ||
1065 | u64 start = map__rip_2objdump(self->ms.map, sym->start), | ||
1066 | end = map__rip_2objdump(self->ms.map, sym->end); | ||
1067 | |||
1068 | offset = line_ip - start; | ||
1069 | if (offset < 0 || (u64)line_ip > end) | ||
1070 | offset = -1; | ||
1071 | } | ||
1072 | |||
1073 | objdump_line = objdump_line__new(offset, line, privsize); | ||
1074 | if (objdump_line == NULL) { | ||
1075 | free(line); | ||
1076 | return -1; | ||
1077 | } | ||
1078 | objdump__add_line(head, objdump_line); | ||
1079 | |||
1080 | return 0; | ||
1081 | } | 968 | } |
1082 | 969 | ||
1083 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head, | 970 | int hist_entry__annotate(struct hist_entry *he, size_t privsize) |
1084 | size_t privsize) | ||
1085 | { | 971 | { |
1086 | struct symbol *sym = self->ms.sym; | 972 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); |
1087 | struct map *map = self->ms.map; | ||
1088 | struct dso *dso = map->dso; | ||
1089 | char *filename = dso__build_id_filename(dso, NULL, 0); | ||
1090 | bool free_filename = true; | ||
1091 | char command[PATH_MAX * 2]; | ||
1092 | FILE *file; | ||
1093 | int err = 0; | ||
1094 | u64 len; | ||
1095 | char symfs_filename[PATH_MAX]; | ||
1096 | |||
1097 | if (filename) { | ||
1098 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
1099 | symbol_conf.symfs, filename); | ||
1100 | } | ||
1101 | |||
1102 | if (filename == NULL) { | ||
1103 | if (dso->has_build_id) { | ||
1104 | pr_err("Can't annotate %s: not enough memory\n", | ||
1105 | sym->name); | ||
1106 | return -ENOMEM; | ||
1107 | } | ||
1108 | goto fallback; | ||
1109 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | ||
1110 | strstr(command, "[kernel.kallsyms]") || | ||
1111 | access(symfs_filename, R_OK)) { | ||
1112 | free(filename); | ||
1113 | fallback: | ||
1114 | /* | ||
1115 | * If we don't have build-ids or the build-id file isn't in the | ||
1116 | * cache, or is just a kallsyms file, well, lets hope that this | ||
1117 | * DSO is the same as when 'perf record' ran. | ||
1118 | */ | ||
1119 | filename = dso->long_name; | ||
1120 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
1121 | symbol_conf.symfs, filename); | ||
1122 | free_filename = false; | ||
1123 | } | ||
1124 | |||
1125 | if (dso->origin == DSO__ORIG_KERNEL) { | ||
1126 | if (dso->annotate_warned) | ||
1127 | goto out_free_filename; | ||
1128 | err = -ENOENT; | ||
1129 | dso->annotate_warned = 1; | ||
1130 | pr_err("Can't annotate %s: No vmlinux file was found in the " | ||
1131 | "path\n", sym->name); | ||
1132 | goto out_free_filename; | ||
1133 | } | ||
1134 | |||
1135 | pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, | ||
1136 | filename, sym->name, map->unmap_ip(map, sym->start), | ||
1137 | map->unmap_ip(map, sym->end)); | ||
1138 | |||
1139 | len = sym->end - sym->start; | ||
1140 | |||
1141 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | ||
1142 | dso, dso->long_name, sym, sym->name); | ||
1143 | |||
1144 | snprintf(command, sizeof(command), | ||
1145 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", | ||
1146 | map__rip_2objdump(map, sym->start), | ||
1147 | map__rip_2objdump(map, sym->end), | ||
1148 | symfs_filename, filename); | ||
1149 | |||
1150 | pr_debug("Executing: %s\n", command); | ||
1151 | |||
1152 | file = popen(command, "r"); | ||
1153 | if (!file) | ||
1154 | goto out_free_filename; | ||
1155 | |||
1156 | while (!feof(file)) | ||
1157 | if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0) | ||
1158 | break; | ||
1159 | |||
1160 | pclose(file); | ||
1161 | out_free_filename: | ||
1162 | if (free_filename) | ||
1163 | free(filename); | ||
1164 | return err; | ||
1165 | } | 973 | } |
1166 | 974 | ||
1167 | void hists__inc_nr_events(struct hists *self, u32 type) | 975 | void hists__inc_nr_events(struct hists *self, u32 type) |
@@ -1176,8 +984,12 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
1176 | size_t ret = 0; | 984 | size_t ret = 0; |
1177 | 985 | ||
1178 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 986 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
1179 | const char *name = event__get_event_name(i); | 987 | const char *name; |
988 | |||
989 | if (self->stats.nr_events[i] == 0) | ||
990 | continue; | ||
1180 | 991 | ||
992 | name = perf_event__name(i); | ||
1181 | if (!strcmp(name, "UNKNOWN")) | 993 | if (!strcmp(name, "UNKNOWN")) |
1182 | continue; | 994 | continue; |
1183 | 995 | ||