aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/hist.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/hist.c')
-rw-r--r--tools/perf/util/hist.c613
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
12static 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
15struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, 37static 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
52static 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
59struct 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);
103out:
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
104static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) 151static 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
133void perf_session__collapse_resort(struct rb_root *hists) 181void 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
160static void perf_session__insert_output_hist_entry(struct rb_root *root, 211static 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
186void perf_session__output_resort(struct rb_root *hists, u64 total_samples) 237void 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
211static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) 264static 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
239static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, 292static 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
284static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, 337static 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
458static size_t hist_entry__fprintf(struct hist_entry *self, 516int 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) { 628int 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) { 639static 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
554size_t perf_session__fprintf_hists(struct rb_root *hists, 655size_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
644print_entries: 761print_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
791enum hist_filter {
792 HIST_FILTER__DSO,
793 HIST_FILTER__THREAD,
794};
795
796void 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
827void 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
854static 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
864int 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
895static 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
907void objdump_line__free(struct objdump_line *self)
908{
909 free(self->line);
910 free(self);
911}
912
913static void objdump__add_line(struct list_head *head, struct objdump_line *line)
914{
915 list_add_tail(&line->node, head);
916}
917
918struct 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
928static 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
986int 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
1038void hists__inc_nr_events(struct hists *self, u32 type)
1039{
1040 ++self->stats.nr_events[0];
1041 ++self->stats.nr_events[type];
1042}
1043
1044size_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}