diff options
Diffstat (limited to 'tools/perf/util/hist.c')
| -rw-r--r-- | tools/perf/util/hist.c | 613 |
1 files changed, 501 insertions, 112 deletions
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 2be33c7dbf03..9a71c94f057a 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | #include "util.h" | ||
| 1 | #include "hist.h" | 2 | #include "hist.h" |
| 2 | #include "session.h" | 3 | #include "session.h" |
| 3 | #include "sort.h" | 4 | #include "sort.h" |
| @@ -8,25 +9,69 @@ struct callchain_param callchain_param = { | |||
| 8 | .min_percent = 0.5 | 9 | .min_percent = 0.5 |
| 9 | }; | 10 | }; |
| 10 | 11 | ||
| 12 | static void hist_entry__add_cpumode_period(struct hist_entry *self, | ||
| 13 | unsigned int cpumode, u64 period) | ||
| 14 | { | ||
| 15 | switch (cpumode) { | ||
| 16 | case PERF_RECORD_MISC_KERNEL: | ||
| 17 | self->period_sys += period; | ||
| 18 | break; | ||
| 19 | case PERF_RECORD_MISC_USER: | ||
| 20 | self->period_us += period; | ||
| 21 | break; | ||
| 22 | case PERF_RECORD_MISC_GUEST_KERNEL: | ||
| 23 | self->period_guest_sys += period; | ||
| 24 | break; | ||
| 25 | case PERF_RECORD_MISC_GUEST_USER: | ||
| 26 | self->period_guest_us += period; | ||
| 27 | break; | ||
| 28 | default: | ||
| 29 | break; | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 11 | /* | 33 | /* |
| 12 | * histogram, sorted on item, collects counts | 34 | * histogram, sorted on item, collects periods |
| 13 | */ | 35 | */ |
| 14 | 36 | ||
| 15 | struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, | 37 | static struct hist_entry *hist_entry__new(struct hist_entry *template) |
| 16 | struct addr_location *al, | 38 | { |
| 17 | struct symbol *sym_parent, | 39 | size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0; |
| 18 | u64 count, bool *hit) | 40 | struct hist_entry *self = malloc(sizeof(*self) + callchain_size); |
| 41 | |||
| 42 | if (self != NULL) { | ||
| 43 | *self = *template; | ||
| 44 | self->nr_events = 1; | ||
| 45 | if (symbol_conf.use_callchain) | ||
| 46 | callchain_init(self->callchain); | ||
| 47 | } | ||
| 48 | |||
| 49 | return self; | ||
| 50 | } | ||
| 51 | |||
| 52 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) | ||
| 19 | { | 53 | { |
| 20 | struct rb_node **p = &hists->rb_node; | 54 | if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) |
| 55 | self->max_sym_namelen = entry->ms.sym->namelen; | ||
| 56 | ++self->nr_entries; | ||
| 57 | } | ||
| 58 | |||
| 59 | struct hist_entry *__hists__add_entry(struct hists *self, | ||
| 60 | struct addr_location *al, | ||
| 61 | struct symbol *sym_parent, u64 period) | ||
| 62 | { | ||
| 63 | struct rb_node **p = &self->entries.rb_node; | ||
| 21 | struct rb_node *parent = NULL; | 64 | struct rb_node *parent = NULL; |
| 22 | struct hist_entry *he; | 65 | struct hist_entry *he; |
| 23 | struct hist_entry entry = { | 66 | struct hist_entry entry = { |
| 24 | .thread = al->thread, | 67 | .thread = al->thread, |
| 25 | .map = al->map, | 68 | .ms = { |
| 26 | .sym = al->sym, | 69 | .map = al->map, |
| 70 | .sym = al->sym, | ||
| 71 | }, | ||
| 27 | .ip = al->addr, | 72 | .ip = al->addr, |
| 28 | .level = al->level, | 73 | .level = al->level, |
| 29 | .count = count, | 74 | .period = period, |
| 30 | .parent = sym_parent, | 75 | .parent = sym_parent, |
| 31 | }; | 76 | }; |
| 32 | int cmp; | 77 | int cmp; |
| @@ -38,8 +83,9 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, | |||
| 38 | cmp = hist_entry__cmp(&entry, he); | 83 | cmp = hist_entry__cmp(&entry, he); |
| 39 | 84 | ||
| 40 | if (!cmp) { | 85 | if (!cmp) { |
| 41 | *hit = true; | 86 | he->period += period; |
| 42 | return he; | 87 | ++he->nr_events; |
| 88 | goto out; | ||
| 43 | } | 89 | } |
| 44 | 90 | ||
| 45 | if (cmp < 0) | 91 | if (cmp < 0) |
| @@ -48,13 +94,14 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, | |||
| 48 | p = &(*p)->rb_right; | 94 | p = &(*p)->rb_right; |
| 49 | } | 95 | } |
| 50 | 96 | ||
| 51 | he = malloc(sizeof(*he)); | 97 | he = hist_entry__new(&entry); |
| 52 | if (!he) | 98 | if (!he) |
| 53 | return NULL; | 99 | return NULL; |
| 54 | *he = entry; | ||
| 55 | rb_link_node(&he->rb_node, parent, p); | 100 | rb_link_node(&he->rb_node, parent, p); |
| 56 | rb_insert_color(&he->rb_node, hists); | 101 | rb_insert_color(&he->rb_node, &self->entries); |
| 57 | *hit = false; | 102 | hists__inc_nr_entries(self, he); |
| 103 | out: | ||
| 104 | hist_entry__add_cpumode_period(he, al->cpumode, period); | ||
| 58 | return he; | 105 | return he; |
| 59 | } | 106 | } |
| 60 | 107 | ||
| @@ -65,7 +112,7 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 65 | int64_t cmp = 0; | 112 | int64_t cmp = 0; |
| 66 | 113 | ||
| 67 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 114 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 68 | cmp = se->cmp(left, right); | 115 | cmp = se->se_cmp(left, right); |
| 69 | if (cmp) | 116 | if (cmp) |
| 70 | break; | 117 | break; |
| 71 | } | 118 | } |
| @@ -82,7 +129,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
| 82 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 129 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 83 | int64_t (*f)(struct hist_entry *, struct hist_entry *); | 130 | int64_t (*f)(struct hist_entry *, struct hist_entry *); |
| 84 | 131 | ||
| 85 | f = se->collapse ?: se->cmp; | 132 | f = se->se_collapse ?: se->se_cmp; |
| 86 | 133 | ||
| 87 | cmp = f(left, right); | 134 | cmp = f(left, right); |
| 88 | if (cmp) | 135 | if (cmp) |
| @@ -101,7 +148,7 @@ void hist_entry__free(struct hist_entry *he) | |||
| 101 | * collapse the histogram | 148 | * collapse the histogram |
| 102 | */ | 149 | */ |
| 103 | 150 | ||
| 104 | static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | 151 | static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) |
| 105 | { | 152 | { |
| 106 | struct rb_node **p = &root->rb_node; | 153 | struct rb_node **p = &root->rb_node; |
| 107 | struct rb_node *parent = NULL; | 154 | struct rb_node *parent = NULL; |
| @@ -115,9 +162,9 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | |||
| 115 | cmp = hist_entry__collapse(iter, he); | 162 | cmp = hist_entry__collapse(iter, he); |
| 116 | 163 | ||
| 117 | if (!cmp) { | 164 | if (!cmp) { |
| 118 | iter->count += he->count; | 165 | iter->period += he->period; |
| 119 | hist_entry__free(he); | 166 | hist_entry__free(he); |
| 120 | return; | 167 | return false; |
| 121 | } | 168 | } |
| 122 | 169 | ||
| 123 | if (cmp < 0) | 170 | if (cmp < 0) |
| @@ -128,9 +175,10 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | |||
| 128 | 175 | ||
| 129 | rb_link_node(&he->rb_node, parent, p); | 176 | rb_link_node(&he->rb_node, parent, p); |
| 130 | rb_insert_color(&he->rb_node, root); | 177 | rb_insert_color(&he->rb_node, root); |
| 178 | return true; | ||
| 131 | } | 179 | } |
| 132 | 180 | ||
| 133 | void perf_session__collapse_resort(struct rb_root *hists) | 181 | void hists__collapse_resort(struct hists *self) |
| 134 | { | 182 | { |
| 135 | struct rb_root tmp; | 183 | struct rb_root tmp; |
| 136 | struct rb_node *next; | 184 | struct rb_node *next; |
| @@ -140,72 +188,77 @@ void perf_session__collapse_resort(struct rb_root *hists) | |||
| 140 | return; | 188 | return; |
| 141 | 189 | ||
| 142 | tmp = RB_ROOT; | 190 | tmp = RB_ROOT; |
| 143 | next = rb_first(hists); | 191 | next = rb_first(&self->entries); |
| 192 | self->nr_entries = 0; | ||
| 193 | self->max_sym_namelen = 0; | ||
| 144 | 194 | ||
| 145 | while (next) { | 195 | while (next) { |
| 146 | n = rb_entry(next, struct hist_entry, rb_node); | 196 | n = rb_entry(next, struct hist_entry, rb_node); |
| 147 | next = rb_next(&n->rb_node); | 197 | next = rb_next(&n->rb_node); |
| 148 | 198 | ||
| 149 | rb_erase(&n->rb_node, hists); | 199 | rb_erase(&n->rb_node, &self->entries); |
| 150 | collapse__insert_entry(&tmp, n); | 200 | if (collapse__insert_entry(&tmp, n)) |
| 201 | hists__inc_nr_entries(self, n); | ||
| 151 | } | 202 | } |
| 152 | 203 | ||
| 153 | *hists = tmp; | 204 | self->entries = tmp; |
| 154 | } | 205 | } |
| 155 | 206 | ||
| 156 | /* | 207 | /* |
| 157 | * reverse the map, sort on count. | 208 | * reverse the map, sort on period. |
| 158 | */ | 209 | */ |
| 159 | 210 | ||
| 160 | static void perf_session__insert_output_hist_entry(struct rb_root *root, | 211 | static void __hists__insert_output_entry(struct rb_root *entries, |
| 161 | struct hist_entry *he, | 212 | struct hist_entry *he, |
| 162 | u64 min_callchain_hits) | 213 | u64 min_callchain_hits) |
| 163 | { | 214 | { |
| 164 | struct rb_node **p = &root->rb_node; | 215 | struct rb_node **p = &entries->rb_node; |
| 165 | struct rb_node *parent = NULL; | 216 | struct rb_node *parent = NULL; |
| 166 | struct hist_entry *iter; | 217 | struct hist_entry *iter; |
| 167 | 218 | ||
| 168 | if (symbol_conf.use_callchain) | 219 | if (symbol_conf.use_callchain) |
| 169 | callchain_param.sort(&he->sorted_chain, &he->callchain, | 220 | callchain_param.sort(&he->sorted_chain, he->callchain, |
| 170 | min_callchain_hits, &callchain_param); | 221 | min_callchain_hits, &callchain_param); |
| 171 | 222 | ||
| 172 | while (*p != NULL) { | 223 | while (*p != NULL) { |
| 173 | parent = *p; | 224 | parent = *p; |
| 174 | iter = rb_entry(parent, struct hist_entry, rb_node); | 225 | iter = rb_entry(parent, struct hist_entry, rb_node); |
| 175 | 226 | ||
| 176 | if (he->count > iter->count) | 227 | if (he->period > iter->period) |
| 177 | p = &(*p)->rb_left; | 228 | p = &(*p)->rb_left; |
| 178 | else | 229 | else |
| 179 | p = &(*p)->rb_right; | 230 | p = &(*p)->rb_right; |
| 180 | } | 231 | } |
| 181 | 232 | ||
| 182 | rb_link_node(&he->rb_node, parent, p); | 233 | rb_link_node(&he->rb_node, parent, p); |
| 183 | rb_insert_color(&he->rb_node, root); | 234 | rb_insert_color(&he->rb_node, entries); |
| 184 | } | 235 | } |
| 185 | 236 | ||
| 186 | void perf_session__output_resort(struct rb_root *hists, u64 total_samples) | 237 | void hists__output_resort(struct hists *self) |
| 187 | { | 238 | { |
| 188 | struct rb_root tmp; | 239 | struct rb_root tmp; |
| 189 | struct rb_node *next; | 240 | struct rb_node *next; |
| 190 | struct hist_entry *n; | 241 | struct hist_entry *n; |
| 191 | u64 min_callchain_hits; | 242 | u64 min_callchain_hits; |
| 192 | 243 | ||
| 193 | min_callchain_hits = | 244 | min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); |
| 194 | total_samples * (callchain_param.min_percent / 100); | ||
| 195 | 245 | ||
| 196 | tmp = RB_ROOT; | 246 | tmp = RB_ROOT; |
| 197 | next = rb_first(hists); | 247 | next = rb_first(&self->entries); |
| 248 | |||
| 249 | self->nr_entries = 0; | ||
| 250 | self->max_sym_namelen = 0; | ||
| 198 | 251 | ||
| 199 | while (next) { | 252 | while (next) { |
| 200 | n = rb_entry(next, struct hist_entry, rb_node); | 253 | n = rb_entry(next, struct hist_entry, rb_node); |
| 201 | next = rb_next(&n->rb_node); | 254 | next = rb_next(&n->rb_node); |
| 202 | 255 | ||
| 203 | rb_erase(&n->rb_node, hists); | 256 | rb_erase(&n->rb_node, &self->entries); |
| 204 | perf_session__insert_output_hist_entry(&tmp, n, | 257 | __hists__insert_output_entry(&tmp, n, min_callchain_hits); |
| 205 | min_callchain_hits); | 258 | hists__inc_nr_entries(self, n); |
| 206 | } | 259 | } |
| 207 | 260 | ||
| 208 | *hists = tmp; | 261 | self->entries = tmp; |
| 209 | } | 262 | } |
| 210 | 263 | ||
| 211 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | 264 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) |
| @@ -237,7 +290,7 @@ static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | |||
| 237 | } | 290 | } |
| 238 | 291 | ||
| 239 | static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | 292 | static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, |
| 240 | int depth, int depth_mask, int count, | 293 | int depth, int depth_mask, int period, |
| 241 | u64 total_samples, int hits, | 294 | u64 total_samples, int hits, |
| 242 | int left_margin) | 295 | int left_margin) |
| 243 | { | 296 | { |
| @@ -250,7 +303,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | |||
| 250 | ret += fprintf(fp, "|"); | 303 | ret += fprintf(fp, "|"); |
| 251 | else | 304 | else |
| 252 | ret += fprintf(fp, " "); | 305 | ret += fprintf(fp, " "); |
| 253 | if (!count && i == depth - 1) { | 306 | if (!period && i == depth - 1) { |
| 254 | double percent; | 307 | double percent; |
| 255 | 308 | ||
| 256 | percent = hits * 100.0 / total_samples; | 309 | percent = hits * 100.0 / total_samples; |
| @@ -258,8 +311,8 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | |||
| 258 | } else | 311 | } else |
| 259 | ret += fprintf(fp, "%s", " "); | 312 | ret += fprintf(fp, "%s", " "); |
| 260 | } | 313 | } |
| 261 | if (chain->sym) | 314 | if (chain->ms.sym) |
| 262 | ret += fprintf(fp, "%s\n", chain->sym->name); | 315 | ret += fprintf(fp, "%s\n", chain->ms.sym->name); |
| 263 | else | 316 | else |
| 264 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | 317 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); |
| 265 | 318 | ||
| @@ -278,7 +331,7 @@ static void init_rem_hits(void) | |||
| 278 | } | 331 | } |
| 279 | 332 | ||
| 280 | strcpy(rem_sq_bracket->name, "[...]"); | 333 | strcpy(rem_sq_bracket->name, "[...]"); |
| 281 | rem_hits.sym = rem_sq_bracket; | 334 | rem_hits.ms.sym = rem_sq_bracket; |
| 282 | } | 335 | } |
| 283 | 336 | ||
| 284 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 337 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, |
| @@ -293,6 +346,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 293 | u64 remaining; | 346 | u64 remaining; |
| 294 | size_t ret = 0; | 347 | size_t ret = 0; |
| 295 | int i; | 348 | int i; |
| 349 | uint entries_printed = 0; | ||
| 296 | 350 | ||
| 297 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 351 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
| 298 | new_total = self->children_hit; | 352 | new_total = self->children_hit; |
| @@ -328,8 +382,6 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 328 | left_margin); | 382 | left_margin); |
| 329 | i = 0; | 383 | i = 0; |
| 330 | list_for_each_entry(chain, &child->val, list) { | 384 | list_for_each_entry(chain, &child->val, list) { |
| 331 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 332 | continue; | ||
| 333 | ret += ipchain__fprintf_graph(fp, chain, depth, | 385 | ret += ipchain__fprintf_graph(fp, chain, depth, |
| 334 | new_depth_mask, i++, | 386 | new_depth_mask, i++, |
| 335 | new_total, | 387 | new_total, |
| @@ -341,6 +393,8 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 341 | new_depth_mask | (1 << depth), | 393 | new_depth_mask | (1 << depth), |
| 342 | left_margin); | 394 | left_margin); |
| 343 | node = next; | 395 | node = next; |
| 396 | if (++entries_printed == callchain_param.print_limit) | ||
| 397 | break; | ||
| 344 | } | 398 | } |
| 345 | 399 | ||
| 346 | if (callchain_param.mode == CHAIN_GRAPH_REL && | 400 | if (callchain_param.mode == CHAIN_GRAPH_REL && |
| @@ -366,11 +420,9 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 366 | bool printed = false; | 420 | bool printed = false; |
| 367 | int i = 0; | 421 | int i = 0; |
| 368 | int ret = 0; | 422 | int ret = 0; |
| 423 | u32 entries_printed = 0; | ||
| 369 | 424 | ||
| 370 | list_for_each_entry(chain, &self->val, list) { | 425 | list_for_each_entry(chain, &self->val, list) { |
| 371 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 372 | continue; | ||
| 373 | |||
| 374 | if (!i++ && sort__first_dimension == SORT_SYM) | 426 | if (!i++ && sort__first_dimension == SORT_SYM) |
| 375 | continue; | 427 | continue; |
| 376 | 428 | ||
| @@ -385,10 +437,13 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 385 | } else | 437 | } else |
| 386 | ret += callchain__fprintf_left_margin(fp, left_margin); | 438 | ret += callchain__fprintf_left_margin(fp, left_margin); |
| 387 | 439 | ||
| 388 | if (chain->sym) | 440 | if (chain->ms.sym) |
| 389 | ret += fprintf(fp, " %s\n", chain->sym->name); | 441 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); |
| 390 | else | 442 | else |
| 391 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | 443 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); |
| 444 | |||
| 445 | if (++entries_printed == callchain_param.print_limit) | ||
| 446 | break; | ||
| 392 | } | 447 | } |
| 393 | 448 | ||
| 394 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | 449 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); |
| @@ -411,8 +466,8 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |||
| 411 | list_for_each_entry(chain, &self->val, list) { | 466 | list_for_each_entry(chain, &self->val, list) { |
| 412 | if (chain->ip >= PERF_CONTEXT_MAX) | 467 | if (chain->ip >= PERF_CONTEXT_MAX) |
| 413 | continue; | 468 | continue; |
| 414 | if (chain->sym) | 469 | if (chain->ms.sym) |
| 415 | ret += fprintf(fp, " %s\n", chain->sym->name); | 470 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); |
| 416 | else | 471 | else |
| 417 | ret += fprintf(fp, " %p\n", | 472 | ret += fprintf(fp, " %p\n", |
| 418 | (void *)(long)chain->ip); | 473 | (void *)(long)chain->ip); |
| @@ -427,6 +482,7 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
| 427 | struct rb_node *rb_node; | 482 | struct rb_node *rb_node; |
| 428 | struct callchain_node *chain; | 483 | struct callchain_node *chain; |
| 429 | size_t ret = 0; | 484 | size_t ret = 0; |
| 485 | u32 entries_printed = 0; | ||
| 430 | 486 | ||
| 431 | rb_node = rb_first(&self->sorted_chain); | 487 | rb_node = rb_first(&self->sorted_chain); |
| 432 | while (rb_node) { | 488 | while (rb_node) { |
| @@ -449,55 +505,88 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
| 449 | break; | 505 | break; |
| 450 | } | 506 | } |
| 451 | ret += fprintf(fp, "\n"); | 507 | ret += fprintf(fp, "\n"); |
| 508 | if (++entries_printed == callchain_param.print_limit) | ||
| 509 | break; | ||
| 452 | rb_node = rb_next(rb_node); | 510 | rb_node = rb_next(rb_node); |
| 453 | } | 511 | } |
| 454 | 512 | ||
| 455 | return ret; | 513 | return ret; |
| 456 | } | 514 | } |
| 457 | 515 | ||
| 458 | static size_t hist_entry__fprintf(struct hist_entry *self, | 516 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, |
| 459 | struct perf_session *pair_session, | 517 | struct hists *pair_hists, bool show_displacement, |
| 460 | bool show_displacement, | 518 | long displacement, bool color, u64 session_total) |
| 461 | long displacement, FILE *fp, | ||
| 462 | u64 session_total) | ||
| 463 | { | 519 | { |
| 464 | struct sort_entry *se; | 520 | struct sort_entry *se; |
| 465 | u64 count, total; | 521 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; |
| 466 | const char *sep = symbol_conf.field_sep; | 522 | const char *sep = symbol_conf.field_sep; |
| 467 | size_t ret; | 523 | int ret; |
| 468 | 524 | ||
| 469 | if (symbol_conf.exclude_other && !self->parent) | 525 | if (symbol_conf.exclude_other && !self->parent) |
| 470 | return 0; | 526 | return 0; |
| 471 | 527 | ||
| 472 | if (pair_session) { | 528 | if (pair_hists) { |
| 473 | count = self->pair ? self->pair->count : 0; | 529 | period = self->pair ? self->pair->period : 0; |
| 474 | total = pair_session->events_stats.total; | 530 | total = pair_hists->stats.total_period; |
| 531 | period_sys = self->pair ? self->pair->period_sys : 0; | ||
| 532 | period_us = self->pair ? self->pair->period_us : 0; | ||
| 533 | period_guest_sys = self->pair ? self->pair->period_guest_sys : 0; | ||
| 534 | period_guest_us = self->pair ? self->pair->period_guest_us : 0; | ||
| 475 | } else { | 535 | } else { |
| 476 | count = self->count; | 536 | period = self->period; |
| 477 | total = session_total; | 537 | total = session_total; |
| 538 | period_sys = self->period_sys; | ||
| 539 | period_us = self->period_us; | ||
| 540 | period_guest_sys = self->period_guest_sys; | ||
| 541 | period_guest_us = self->period_guest_us; | ||
| 478 | } | 542 | } |
| 479 | 543 | ||
| 480 | if (total) | 544 | if (total) { |
| 481 | ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%", | 545 | if (color) |
| 482 | (count * 100.0) / total); | 546 | ret = percent_color_snprintf(s, size, |
| 483 | else | 547 | sep ? "%.2f" : " %6.2f%%", |
| 484 | ret = fprintf(fp, sep ? "%lld" : "%12lld ", count); | 548 | (period * 100.0) / total); |
| 549 | else | ||
| 550 | ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", | ||
| 551 | (period * 100.0) / total); | ||
| 552 | if (symbol_conf.show_cpu_utilization) { | ||
| 553 | ret += percent_color_snprintf(s + ret, size - ret, | ||
| 554 | sep ? "%.2f" : " %6.2f%%", | ||
| 555 | (period_sys * 100.0) / total); | ||
| 556 | ret += percent_color_snprintf(s + ret, size - ret, | ||
| 557 | sep ? "%.2f" : " %6.2f%%", | ||
| 558 | (period_us * 100.0) / total); | ||
| 559 | if (perf_guest) { | ||
| 560 | ret += percent_color_snprintf(s + ret, | ||
| 561 | size - ret, | ||
| 562 | sep ? "%.2f" : " %6.2f%%", | ||
| 563 | (period_guest_sys * 100.0) / | ||
| 564 | total); | ||
| 565 | ret += percent_color_snprintf(s + ret, | ||
| 566 | size - ret, | ||
| 567 | sep ? "%.2f" : " %6.2f%%", | ||
| 568 | (period_guest_us * 100.0) / | ||
| 569 | total); | ||
| 570 | } | ||
| 571 | } | ||
| 572 | } else | ||
| 573 | ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period); | ||
| 485 | 574 | ||
| 486 | if (symbol_conf.show_nr_samples) { | 575 | if (symbol_conf.show_nr_samples) { |
| 487 | if (sep) | 576 | if (sep) |
| 488 | fprintf(fp, "%c%lld", *sep, count); | 577 | ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period); |
| 489 | else | 578 | else |
| 490 | fprintf(fp, "%11lld", count); | 579 | ret += snprintf(s + ret, size - ret, "%11lld", period); |
| 491 | } | 580 | } |
| 492 | 581 | ||
| 493 | if (pair_session) { | 582 | if (pair_hists) { |
| 494 | char bf[32]; | 583 | char bf[32]; |
| 495 | double old_percent = 0, new_percent = 0, diff; | 584 | double old_percent = 0, new_percent = 0, diff; |
| 496 | 585 | ||
| 497 | if (total > 0) | 586 | if (total > 0) |
| 498 | old_percent = (count * 100.0) / total; | 587 | old_percent = (period * 100.0) / total; |
| 499 | if (session_total > 0) | 588 | if (session_total > 0) |
| 500 | new_percent = (self->count * 100.0) / session_total; | 589 | new_percent = (self->period * 100.0) / session_total; |
| 501 | 590 | ||
| 502 | diff = new_percent - old_percent; | 591 | diff = new_percent - old_percent; |
| 503 | 592 | ||
| @@ -507,9 +596,9 @@ static size_t hist_entry__fprintf(struct hist_entry *self, | |||
| 507 | snprintf(bf, sizeof(bf), " "); | 596 | snprintf(bf, sizeof(bf), " "); |
| 508 | 597 | ||
| 509 | if (sep) | 598 | if (sep) |
| 510 | ret += fprintf(fp, "%c%s", *sep, bf); | 599 | ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); |
| 511 | else | 600 | else |
| 512 | ret += fprintf(fp, "%11.11s", bf); | 601 | ret += snprintf(s + ret, size - ret, "%11.11s", bf); |
| 513 | 602 | ||
| 514 | if (show_displacement) { | 603 | if (show_displacement) { |
| 515 | if (displacement) | 604 | if (displacement) |
| @@ -518,9 +607,9 @@ static size_t hist_entry__fprintf(struct hist_entry *self, | |||
| 518 | snprintf(bf, sizeof(bf), " "); | 607 | snprintf(bf, sizeof(bf), " "); |
| 519 | 608 | ||
| 520 | if (sep) | 609 | if (sep) |
| 521 | fprintf(fp, "%c%s", *sep, bf); | 610 | ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); |
| 522 | else | 611 | else |
| 523 | fprintf(fp, "%6.6s", bf); | 612 | ret += snprintf(s + ret, size - ret, "%6.6s", bf); |
| 524 | } | 613 | } |
| 525 | } | 614 | } |
| 526 | 615 | ||
| @@ -528,33 +617,43 @@ static size_t hist_entry__fprintf(struct hist_entry *self, | |||
| 528 | if (se->elide) | 617 | if (se->elide) |
| 529 | continue; | 618 | continue; |
| 530 | 619 | ||
| 531 | fprintf(fp, "%s", sep ?: " "); | 620 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); |
| 532 | ret += se->print(fp, self, se->width ? *se->width : 0); | 621 | ret += se->se_snprintf(self, s + ret, size - ret, |
| 622 | se->se_width ? *se->se_width : 0); | ||
| 533 | } | 623 | } |
| 534 | 624 | ||
| 535 | ret += fprintf(fp, "\n"); | 625 | return ret; |
| 626 | } | ||
| 536 | 627 | ||
| 537 | if (symbol_conf.use_callchain) { | 628 | int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, |
| 538 | int left_margin = 0; | 629 | bool show_displacement, long displacement, FILE *fp, |
| 630 | u64 session_total) | ||
| 631 | { | ||
| 632 | char bf[512]; | ||
| 633 | hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, | ||
| 634 | show_displacement, displacement, | ||
| 635 | true, session_total); | ||
| 636 | return fprintf(fp, "%s\n", bf); | ||
| 637 | } | ||
| 539 | 638 | ||
| 540 | if (sort__first_dimension == SORT_COMM) { | 639 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, |
| 541 | se = list_first_entry(&hist_entry__sort_list, typeof(*se), | 640 | u64 session_total) |
| 542 | list); | 641 | { |
| 543 | left_margin = se->width ? *se->width : 0; | 642 | int left_margin = 0; |
| 544 | left_margin -= thread__comm_len(self->thread); | ||
| 545 | } | ||
| 546 | 643 | ||
| 547 | hist_entry_callchain__fprintf(fp, self, session_total, | 644 | if (sort__first_dimension == SORT_COMM) { |
| 548 | left_margin); | 645 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, |
| 646 | typeof(*se), list); | ||
| 647 | left_margin = se->se_width ? *se->se_width : 0; | ||
| 648 | left_margin -= thread__comm_len(self->thread); | ||
| 549 | } | 649 | } |
| 550 | 650 | ||
| 551 | return ret; | 651 | return hist_entry_callchain__fprintf(fp, self, session_total, |
| 652 | left_margin); | ||
| 552 | } | 653 | } |
| 553 | 654 | ||
| 554 | size_t perf_session__fprintf_hists(struct rb_root *hists, | 655 | size_t hists__fprintf(struct hists *self, struct hists *pair, |
| 555 | struct perf_session *pair, | 656 | bool show_displacement, FILE *fp) |
| 556 | bool show_displacement, FILE *fp, | ||
| 557 | u64 session_total) | ||
| 558 | { | 657 | { |
| 559 | struct sort_entry *se; | 658 | struct sort_entry *se; |
| 560 | struct rb_node *nd; | 659 | struct rb_node *nd; |
| @@ -563,7 +662,7 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, | |||
| 563 | long displacement = 0; | 662 | long displacement = 0; |
| 564 | unsigned int width; | 663 | unsigned int width; |
| 565 | const char *sep = symbol_conf.field_sep; | 664 | const char *sep = symbol_conf.field_sep; |
| 566 | char *col_width = symbol_conf.col_width_list_str; | 665 | const char *col_width = symbol_conf.col_width_list_str; |
| 567 | 666 | ||
| 568 | init_rem_hits(); | 667 | init_rem_hits(); |
| 569 | 668 | ||
| @@ -576,6 +675,24 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, | |||
| 576 | fputs(" Samples ", fp); | 675 | fputs(" Samples ", fp); |
| 577 | } | 676 | } |
| 578 | 677 | ||
| 678 | if (symbol_conf.show_cpu_utilization) { | ||
| 679 | if (sep) { | ||
| 680 | ret += fprintf(fp, "%csys", *sep); | ||
| 681 | ret += fprintf(fp, "%cus", *sep); | ||
| 682 | if (perf_guest) { | ||
| 683 | ret += fprintf(fp, "%cguest sys", *sep); | ||
| 684 | ret += fprintf(fp, "%cguest us", *sep); | ||
| 685 | } | ||
| 686 | } else { | ||
| 687 | ret += fprintf(fp, " sys "); | ||
| 688 | ret += fprintf(fp, " us "); | ||
| 689 | if (perf_guest) { | ||
| 690 | ret += fprintf(fp, " guest sys "); | ||
| 691 | ret += fprintf(fp, " guest us "); | ||
| 692 | } | ||
| 693 | } | ||
| 694 | } | ||
| 695 | |||
| 579 | if (pair) { | 696 | if (pair) { |
| 580 | if (sep) | 697 | if (sep) |
| 581 | ret += fprintf(fp, "%cDelta", *sep); | 698 | ret += fprintf(fp, "%cDelta", *sep); |
| @@ -594,22 +711,22 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, | |||
| 594 | if (se->elide) | 711 | if (se->elide) |
| 595 | continue; | 712 | continue; |
| 596 | if (sep) { | 713 | if (sep) { |
| 597 | fprintf(fp, "%c%s", *sep, se->header); | 714 | fprintf(fp, "%c%s", *sep, se->se_header); |
| 598 | continue; | 715 | continue; |
| 599 | } | 716 | } |
| 600 | width = strlen(se->header); | 717 | width = strlen(se->se_header); |
| 601 | if (se->width) { | 718 | if (se->se_width) { |
| 602 | if (symbol_conf.col_width_list_str) { | 719 | if (symbol_conf.col_width_list_str) { |
| 603 | if (col_width) { | 720 | if (col_width) { |
| 604 | *se->width = atoi(col_width); | 721 | *se->se_width = atoi(col_width); |
| 605 | col_width = strchr(col_width, ','); | 722 | col_width = strchr(col_width, ','); |
| 606 | if (col_width) | 723 | if (col_width) |
| 607 | ++col_width; | 724 | ++col_width; |
| 608 | } | 725 | } |
| 609 | } | 726 | } |
| 610 | width = *se->width = max(*se->width, width); | 727 | width = *se->se_width = max(*se->se_width, width); |
| 611 | } | 728 | } |
| 612 | fprintf(fp, " %*s", width, se->header); | 729 | fprintf(fp, " %*s", width, se->se_header); |
| 613 | } | 730 | } |
| 614 | fprintf(fp, "\n"); | 731 | fprintf(fp, "\n"); |
| 615 | 732 | ||
| @@ -631,10 +748,10 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, | |||
| 631 | continue; | 748 | continue; |
| 632 | 749 | ||
| 633 | fprintf(fp, " "); | 750 | fprintf(fp, " "); |
| 634 | if (se->width) | 751 | if (se->se_width) |
| 635 | width = *se->width; | 752 | width = *se->se_width; |
| 636 | else | 753 | else |
| 637 | width = strlen(se->header); | 754 | width = strlen(se->se_header); |
| 638 | for (i = 0; i < width; i++) | 755 | for (i = 0; i < width; i++) |
| 639 | fprintf(fp, "."); | 756 | fprintf(fp, "."); |
| 640 | } | 757 | } |
| @@ -642,7 +759,7 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, | |||
| 642 | fprintf(fp, "\n#\n"); | 759 | fprintf(fp, "\n#\n"); |
| 643 | 760 | ||
| 644 | print_entries: | 761 | print_entries: |
| 645 | for (nd = rb_first(hists); nd; nd = rb_next(nd)) { | 762 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
| 646 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 763 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
| 647 | 764 | ||
| 648 | if (show_displacement) { | 765 | if (show_displacement) { |
| @@ -654,10 +771,14 @@ print_entries: | |||
| 654 | ++position; | 771 | ++position; |
| 655 | } | 772 | } |
| 656 | ret += hist_entry__fprintf(h, pair, show_displacement, | 773 | ret += hist_entry__fprintf(h, pair, show_displacement, |
| 657 | displacement, fp, session_total); | 774 | displacement, fp, self->stats.total_period); |
| 658 | if (h->map == NULL && verbose > 1) { | 775 | |
| 776 | if (symbol_conf.use_callchain) | ||
| 777 | ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); | ||
| 778 | |||
| 779 | if (h->ms.map == NULL && verbose > 1) { | ||
| 659 | __map_groups__fprintf_maps(&h->thread->mg, | 780 | __map_groups__fprintf_maps(&h->thread->mg, |
| 660 | MAP__FUNCTION, fp); | 781 | MAP__FUNCTION, verbose, fp); |
| 661 | fprintf(fp, "%.10s end\n", graph_dotted_line); | 782 | fprintf(fp, "%.10s end\n", graph_dotted_line); |
| 662 | } | 783 | } |
| 663 | } | 784 | } |
| @@ -666,3 +787,271 @@ print_entries: | |||
| 666 | 787 | ||
| 667 | return ret; | 788 | return ret; |
| 668 | } | 789 | } |
| 790 | |||
| 791 | enum hist_filter { | ||
| 792 | HIST_FILTER__DSO, | ||
| 793 | HIST_FILTER__THREAD, | ||
| 794 | }; | ||
| 795 | |||
| 796 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) | ||
| 797 | { | ||
| 798 | struct rb_node *nd; | ||
| 799 | |||
| 800 | self->nr_entries = self->stats.total_period = 0; | ||
| 801 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | ||
| 802 | self->max_sym_namelen = 0; | ||
| 803 | |||
| 804 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | ||
| 805 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
| 806 | |||
| 807 | if (symbol_conf.exclude_other && !h->parent) | ||
| 808 | continue; | ||
| 809 | |||
| 810 | if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { | ||
| 811 | h->filtered |= (1 << HIST_FILTER__DSO); | ||
| 812 | continue; | ||
| 813 | } | ||
| 814 | |||
| 815 | h->filtered &= ~(1 << HIST_FILTER__DSO); | ||
| 816 | if (!h->filtered) { | ||
| 817 | ++self->nr_entries; | ||
| 818 | self->stats.total_period += h->period; | ||
| 819 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | ||
| 820 | if (h->ms.sym && | ||
| 821 | self->max_sym_namelen < h->ms.sym->namelen) | ||
| 822 | self->max_sym_namelen = h->ms.sym->namelen; | ||
| 823 | } | ||
| 824 | } | ||
| 825 | } | ||
| 826 | |||
| 827 | void hists__filter_by_thread(struct hists *self, const struct thread *thread) | ||
| 828 | { | ||
| 829 | struct rb_node *nd; | ||
| 830 | |||
| 831 | self->nr_entries = self->stats.total_period = 0; | ||
| 832 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | ||
| 833 | self->max_sym_namelen = 0; | ||
| 834 | |||
| 835 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | ||
| 836 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
| 837 | |||
| 838 | if (thread != NULL && h->thread != thread) { | ||
| 839 | h->filtered |= (1 << HIST_FILTER__THREAD); | ||
| 840 | continue; | ||
| 841 | } | ||
| 842 | h->filtered &= ~(1 << HIST_FILTER__THREAD); | ||
| 843 | if (!h->filtered) { | ||
| 844 | ++self->nr_entries; | ||
| 845 | self->stats.total_period += h->period; | ||
| 846 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | ||
| 847 | if (h->ms.sym && | ||
| 848 | self->max_sym_namelen < h->ms.sym->namelen) | ||
| 849 | self->max_sym_namelen = h->ms.sym->namelen; | ||
| 850 | } | ||
| 851 | } | ||
| 852 | } | ||
| 853 | |||
| 854 | static int symbol__alloc_hist(struct symbol *self) | ||
| 855 | { | ||
| 856 | struct sym_priv *priv = symbol__priv(self); | ||
| 857 | const int size = (sizeof(*priv->hist) + | ||
| 858 | (self->end - self->start) * sizeof(u64)); | ||
| 859 | |||
| 860 | priv->hist = zalloc(size); | ||
| 861 | return priv->hist == NULL ? -1 : 0; | ||
| 862 | } | ||
| 863 | |||
| 864 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) | ||
| 865 | { | ||
| 866 | unsigned int sym_size, offset; | ||
| 867 | struct symbol *sym = self->ms.sym; | ||
| 868 | struct sym_priv *priv; | ||
| 869 | struct sym_hist *h; | ||
| 870 | |||
| 871 | if (!sym || !self->ms.map) | ||
| 872 | return 0; | ||
| 873 | |||
| 874 | priv = symbol__priv(sym); | ||
| 875 | if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) | ||
| 876 | return -ENOMEM; | ||
| 877 | |||
| 878 | sym_size = sym->end - sym->start; | ||
| 879 | offset = ip - sym->start; | ||
| 880 | |||
| 881 | pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); | ||
| 882 | |||
| 883 | if (offset >= sym_size) | ||
| 884 | return 0; | ||
| 885 | |||
| 886 | h = priv->hist; | ||
| 887 | h->sum++; | ||
| 888 | h->ip[offset]++; | ||
| 889 | |||
| 890 | pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, | ||
| 891 | self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); | ||
| 892 | return 0; | ||
| 893 | } | ||
| 894 | |||
| 895 | static struct objdump_line *objdump_line__new(s64 offset, char *line) | ||
| 896 | { | ||
| 897 | struct objdump_line *self = malloc(sizeof(*self)); | ||
| 898 | |||
| 899 | if (self != NULL) { | ||
| 900 | self->offset = offset; | ||
| 901 | self->line = line; | ||
| 902 | } | ||
| 903 | |||
| 904 | return self; | ||
| 905 | } | ||
| 906 | |||
| 907 | void objdump_line__free(struct objdump_line *self) | ||
| 908 | { | ||
| 909 | free(self->line); | ||
| 910 | free(self); | ||
| 911 | } | ||
| 912 | |||
| 913 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
| 914 | { | ||
| 915 | list_add_tail(&line->node, head); | ||
| 916 | } | ||
| 917 | |||
| 918 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
| 919 | struct objdump_line *pos) | ||
| 920 | { | ||
| 921 | list_for_each_entry_continue(pos, head, node) | ||
| 922 | if (pos->offset >= 0) | ||
| 923 | return pos; | ||
| 924 | |||
| 925 | return NULL; | ||
| 926 | } | ||
| 927 | |||
| 928 | static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, | ||
| 929 | struct list_head *head) | ||
| 930 | { | ||
| 931 | struct symbol *sym = self->ms.sym; | ||
| 932 | struct objdump_line *objdump_line; | ||
| 933 | char *line = NULL, *tmp, *tmp2, *c; | ||
| 934 | size_t line_len; | ||
| 935 | s64 line_ip, offset = -1; | ||
| 936 | |||
| 937 | if (getline(&line, &line_len, file) < 0) | ||
| 938 | return -1; | ||
| 939 | |||
| 940 | if (!line) | ||
| 941 | return -1; | ||
| 942 | |||
| 943 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
| 944 | line[--line_len] = '\0'; | ||
| 945 | |||
| 946 | c = strchr(line, '\n'); | ||
| 947 | if (c) | ||
| 948 | *c = 0; | ||
| 949 | |||
| 950 | line_ip = -1; | ||
| 951 | |||
| 952 | /* | ||
| 953 | * Strip leading spaces: | ||
| 954 | */ | ||
| 955 | tmp = line; | ||
| 956 | while (*tmp) { | ||
| 957 | if (*tmp != ' ') | ||
| 958 | break; | ||
| 959 | tmp++; | ||
| 960 | } | ||
| 961 | |||
| 962 | if (*tmp) { | ||
| 963 | /* | ||
| 964 | * Parse hexa addresses followed by ':' | ||
| 965 | */ | ||
| 966 | line_ip = strtoull(tmp, &tmp2, 16); | ||
| 967 | if (*tmp2 != ':') | ||
| 968 | line_ip = -1; | ||
| 969 | } | ||
| 970 | |||
| 971 | if (line_ip != -1) { | ||
| 972 | u64 start = map__rip_2objdump(self->ms.map, sym->start); | ||
| 973 | offset = line_ip - start; | ||
| 974 | } | ||
| 975 | |||
| 976 | objdump_line = objdump_line__new(offset, line); | ||
| 977 | if (objdump_line == NULL) { | ||
| 978 | free(line); | ||
| 979 | return -1; | ||
| 980 | } | ||
| 981 | objdump__add_line(head, objdump_line); | ||
| 982 | |||
| 983 | return 0; | ||
| 984 | } | ||
| 985 | |||
| 986 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head) | ||
| 987 | { | ||
| 988 | struct symbol *sym = self->ms.sym; | ||
| 989 | struct map *map = self->ms.map; | ||
| 990 | struct dso *dso = map->dso; | ||
| 991 | const char *filename = dso->long_name; | ||
| 992 | char command[PATH_MAX * 2]; | ||
| 993 | FILE *file; | ||
| 994 | u64 len; | ||
| 995 | |||
| 996 | if (!filename) | ||
| 997 | return -1; | ||
| 998 | |||
| 999 | if (dso->origin == DSO__ORIG_KERNEL) { | ||
| 1000 | if (dso->annotate_warned) | ||
| 1001 | return 0; | ||
| 1002 | dso->annotate_warned = 1; | ||
| 1003 | pr_err("Can't annotate %s: No vmlinux file was found in the " | ||
| 1004 | "path:\n", sym->name); | ||
| 1005 | vmlinux_path__fprintf(stderr); | ||
| 1006 | return -1; | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, | ||
| 1010 | filename, sym->name, map->unmap_ip(map, sym->start), | ||
| 1011 | map->unmap_ip(map, sym->end)); | ||
| 1012 | |||
| 1013 | len = sym->end - sym->start; | ||
| 1014 | |||
| 1015 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | ||
| 1016 | dso, dso->long_name, sym, sym->name); | ||
| 1017 | |||
| 1018 | snprintf(command, sizeof(command), | ||
| 1019 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", | ||
| 1020 | map__rip_2objdump(map, sym->start), | ||
| 1021 | map__rip_2objdump(map, sym->end), | ||
| 1022 | filename, filename); | ||
| 1023 | |||
| 1024 | pr_debug("Executing: %s\n", command); | ||
| 1025 | |||
| 1026 | file = popen(command, "r"); | ||
| 1027 | if (!file) | ||
| 1028 | return -1; | ||
| 1029 | |||
| 1030 | while (!feof(file)) | ||
| 1031 | if (hist_entry__parse_objdump_line(self, file, head) < 0) | ||
| 1032 | break; | ||
| 1033 | |||
| 1034 | pclose(file); | ||
| 1035 | return 0; | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | void hists__inc_nr_events(struct hists *self, u32 type) | ||
| 1039 | { | ||
| 1040 | ++self->stats.nr_events[0]; | ||
| 1041 | ++self->stats.nr_events[type]; | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | ||
| 1045 | { | ||
| 1046 | int i; | ||
| 1047 | size_t ret = 0; | ||
| 1048 | |||
| 1049 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | ||
| 1050 | if (!event__name[i]) | ||
| 1051 | continue; | ||
| 1052 | ret += fprintf(fp, "%10s events: %10d\n", | ||
| 1053 | event__name[i], self->stats.nr_events[i]); | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | return ret; | ||
| 1057 | } | ||
