aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/ui/stdio/hist.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/ui/stdio/hist.c')
-rw-r--r--tools/perf/ui/stdio/hist.c302
1 files changed, 260 insertions, 42 deletions
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index 387110d50b00..7aff5acf3265 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -165,8 +165,28 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
165 return ret; 165 return ret;
166} 166}
167 167
168/*
169 * If have one single callchain root, don't bother printing
170 * its percentage (100 % in fractal mode and the same percentage
171 * than the hist in graph mode). This also avoid one level of column.
172 *
173 * However when percent-limit applied, it's possible that single callchain
174 * node have different (non-100% in fractal mode) percentage.
175 */
176static bool need_percent_display(struct rb_node *node, u64 parent_samples)
177{
178 struct callchain_node *cnode;
179
180 if (rb_next(node))
181 return true;
182
183 cnode = rb_entry(node, struct callchain_node, rb_node);
184 return callchain_cumul_hits(cnode) != parent_samples;
185}
186
168static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, 187static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
169 u64 total_samples, int left_margin) 188 u64 total_samples, u64 parent_samples,
189 int left_margin)
170{ 190{
171 struct callchain_node *cnode; 191 struct callchain_node *cnode;
172 struct callchain_list *chain; 192 struct callchain_list *chain;
@@ -177,13 +197,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
177 int ret = 0; 197 int ret = 0;
178 char bf[1024]; 198 char bf[1024];
179 199
180 /*
181 * If have one single callchain root, don't bother printing
182 * its percentage (100 % in fractal mode and the same percentage
183 * than the hist in graph mode). This also avoid one level of column.
184 */
185 node = rb_first(root); 200 node = rb_first(root);
186 if (node && !rb_next(node)) { 201 if (node && !need_percent_display(node, parent_samples)) {
187 cnode = rb_entry(node, struct callchain_node, rb_node); 202 cnode = rb_entry(node, struct callchain_node, rb_node);
188 list_for_each_entry(chain, &cnode->val, list) { 203 list_for_each_entry(chain, &cnode->val, list) {
189 /* 204 /*
@@ -213,9 +228,15 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
213 root = &cnode->rb_root; 228 root = &cnode->rb_root;
214 } 229 }
215 230
231 if (callchain_param.mode == CHAIN_GRAPH_REL)
232 total_samples = parent_samples;
233
216 ret += __callchain__fprintf_graph(fp, root, total_samples, 234 ret += __callchain__fprintf_graph(fp, root, total_samples,
217 1, 1, left_margin); 235 1, 1, left_margin);
218 ret += fprintf(fp, "\n"); 236 if (ret) {
237 /* do not add a blank line if it printed nothing */
238 ret += fprintf(fp, "\n");
239 }
219 240
220 return ret; 241 return ret;
221} 242}
@@ -323,16 +344,19 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
323 u64 total_samples, int left_margin, 344 u64 total_samples, int left_margin,
324 FILE *fp) 345 FILE *fp)
325{ 346{
347 u64 parent_samples = he->stat.period;
348
349 if (symbol_conf.cumulate_callchain)
350 parent_samples = he->stat_acc->period;
351
326 switch (callchain_param.mode) { 352 switch (callchain_param.mode) {
327 case CHAIN_GRAPH_REL: 353 case CHAIN_GRAPH_REL:
328 return callchain__fprintf_graph(fp, &he->sorted_chain, 354 return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
329 symbol_conf.cumulate_callchain ? 355 parent_samples, left_margin);
330 he->stat_acc->period : he->stat.period,
331 left_margin);
332 break; 356 break;
333 case CHAIN_GRAPH_ABS: 357 case CHAIN_GRAPH_ABS:
334 return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, 358 return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
335 left_margin); 359 parent_samples, left_margin);
336 break; 360 break;
337 case CHAIN_FLAT: 361 case CHAIN_FLAT:
338 return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples); 362 return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
@@ -349,45 +373,66 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
349 return 0; 373 return 0;
350} 374}
351 375
352static size_t hist_entry__callchain_fprintf(struct hist_entry *he, 376static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
353 struct hists *hists,
354 FILE *fp)
355{ 377{
356 int left_margin = 0; 378 const char *sep = symbol_conf.field_sep;
357 u64 total_period = hists->stats.total_period; 379 struct perf_hpp_fmt *fmt;
380 char *start = hpp->buf;
381 int ret;
382 bool first = true;
358 383
359 if (field_order == NULL && (sort_order == NULL || 384 if (symbol_conf.exclude_other && !he->parent)
360 !prefixcmp(sort_order, "comm"))) { 385 return 0;
361 struct perf_hpp_fmt *fmt;
362 386
363 perf_hpp__for_each_format(fmt) { 387 hists__for_each_format(he->hists, fmt) {
364 if (!perf_hpp__is_sort_entry(fmt)) 388 if (perf_hpp__should_skip(fmt, he->hists))
365 continue; 389 continue;
366 390
367 /* must be 'comm' sort entry */ 391 /*
368 left_margin = fmt->width(fmt, NULL, hists_to_evsel(hists)); 392 * If there's no field_sep, we still need
369 left_margin -= thread__comm_len(he->thread); 393 * to display initial ' '.
370 break; 394 */
371 } 395 if (!sep || !first) {
396 ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " ");
397 advance_hpp(hpp, ret);
398 } else
399 first = false;
400
401 if (perf_hpp__use_color() && fmt->color)
402 ret = fmt->color(fmt, hpp, he);
403 else
404 ret = fmt->entry(fmt, hpp, he);
405
406 ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
407 advance_hpp(hpp, ret);
372 } 408 }
373 return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); 409
410 return hpp->buf - start;
374} 411}
375 412
376static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp) 413static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
414 struct perf_hpp *hpp,
415 struct hists *hists,
416 FILE *fp)
377{ 417{
378 const char *sep = symbol_conf.field_sep; 418 const char *sep = symbol_conf.field_sep;
379 struct perf_hpp_fmt *fmt; 419 struct perf_hpp_fmt *fmt;
380 char *start = hpp->buf; 420 struct perf_hpp_list_node *fmt_node;
381 int ret; 421 char *buf = hpp->buf;
422 size_t size = hpp->size;
423 int ret, printed = 0;
382 bool first = true; 424 bool first = true;
383 425
384 if (symbol_conf.exclude_other && !he->parent) 426 if (symbol_conf.exclude_other && !he->parent)
385 return 0; 427 return 0;
386 428
387 perf_hpp__for_each_format(fmt) { 429 ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, "");
388 if (perf_hpp__should_skip(fmt, he->hists)) 430 advance_hpp(hpp, ret);
389 continue;
390 431
432 /* the first hpp_list_node is for overhead columns */
433 fmt_node = list_first_entry(&hists->hpp_formats,
434 struct perf_hpp_list_node, list);
435 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
391 /* 436 /*
392 * If there's no field_sep, we still need 437 * If there's no field_sep, we still need
393 * to display initial ' '. 438 * to display initial ' '.
@@ -403,10 +448,47 @@ static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
403 else 448 else
404 ret = fmt->entry(fmt, hpp, he); 449 ret = fmt->entry(fmt, hpp, he);
405 450
451 ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
406 advance_hpp(hpp, ret); 452 advance_hpp(hpp, ret);
407 } 453 }
408 454
409 return hpp->buf - start; 455 if (!sep)
456 ret = scnprintf(hpp->buf, hpp->size, "%*s",
457 (hists->nr_hpp_node - 2) * HIERARCHY_INDENT, "");
458 advance_hpp(hpp, ret);
459
460 printed += fprintf(fp, "%s", buf);
461
462 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
463 hpp->buf = buf;
464 hpp->size = size;
465
466 /*
467 * No need to call hist_entry__snprintf_alignment() since this
468 * fmt is always the last column in the hierarchy mode.
469 */
470 if (perf_hpp__use_color() && fmt->color)
471 fmt->color(fmt, hpp, he);
472 else
473 fmt->entry(fmt, hpp, he);
474
475 /*
476 * dynamic entries are right-aligned but we want left-aligned
477 * in the hierarchy mode
478 */
479 printed += fprintf(fp, "%s%s", sep ?: " ", ltrim(buf));
480 }
481 printed += putc('\n', fp);
482
483 if (symbol_conf.use_callchain && he->leaf) {
484 u64 total = hists__total_period(hists);
485
486 printed += hist_entry_callchain__fprintf(he, total, 0, fp);
487 goto out;
488 }
489
490out:
491 return printed;
410} 492}
411 493
412static int hist_entry__fprintf(struct hist_entry *he, size_t size, 494static int hist_entry__fprintf(struct hist_entry *he, size_t size,
@@ -418,24 +500,134 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
418 .buf = bf, 500 .buf = bf,
419 .size = size, 501 .size = size,
420 }; 502 };
503 u64 total_period = hists->stats.total_period;
421 504
422 if (size == 0 || size > bfsz) 505 if (size == 0 || size > bfsz)
423 size = hpp.size = bfsz; 506 size = hpp.size = bfsz;
424 507
508 if (symbol_conf.report_hierarchy)
509 return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp);
510
425 hist_entry__snprintf(he, &hpp); 511 hist_entry__snprintf(he, &hpp);
426 512
427 ret = fprintf(fp, "%s\n", bf); 513 ret = fprintf(fp, "%s\n", bf);
428 514
429 if (symbol_conf.use_callchain) 515 if (symbol_conf.use_callchain)
430 ret += hist_entry__callchain_fprintf(he, hists, fp); 516 ret += hist_entry_callchain__fprintf(he, total_period, 0, fp);
431 517
432 return ret; 518 return ret;
433} 519}
434 520
521static int print_hierarchy_indent(const char *sep, int indent,
522 const char *line, FILE *fp)
523{
524 if (sep != NULL || indent < 2)
525 return 0;
526
527 return fprintf(fp, "%-.*s", (indent - 2) * HIERARCHY_INDENT, line);
528}
529
530static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp,
531 const char *sep, FILE *fp)
532{
533 bool first_node, first_col;
534 int indent;
535 int depth;
536 unsigned width = 0;
537 unsigned header_width = 0;
538 struct perf_hpp_fmt *fmt;
539 struct perf_hpp_list_node *fmt_node;
540
541 indent = hists->nr_hpp_node;
542
543 /* preserve max indent depth for column headers */
544 print_hierarchy_indent(sep, indent, spaces, fp);
545
546 /* the first hpp_list_node is for overhead columns */
547 fmt_node = list_first_entry(&hists->hpp_formats,
548 struct perf_hpp_list_node, list);
549
550 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
551 fmt->header(fmt, hpp, hists_to_evsel(hists));
552 fprintf(fp, "%s%s", hpp->buf, sep ?: " ");
553 }
554
555 /* combine sort headers with ' / ' */
556 first_node = true;
557 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
558 if (!first_node)
559 header_width += fprintf(fp, " / ");
560 first_node = false;
561
562 first_col = true;
563 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
564 if (perf_hpp__should_skip(fmt, hists))
565 continue;
566
567 if (!first_col)
568 header_width += fprintf(fp, "+");
569 first_col = false;
570
571 fmt->header(fmt, hpp, hists_to_evsel(hists));
572 rtrim(hpp->buf);
573
574 header_width += fprintf(fp, "%s", ltrim(hpp->buf));
575 }
576 }
577
578 fprintf(fp, "\n# ");
579
580 /* preserve max indent depth for initial dots */
581 print_hierarchy_indent(sep, indent, dots, fp);
582
583 /* the first hpp_list_node is for overhead columns */
584 fmt_node = list_first_entry(&hists->hpp_formats,
585 struct perf_hpp_list_node, list);
586
587 first_col = true;
588 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
589 if (!first_col)
590 fprintf(fp, "%s", sep ?: "..");
591 first_col = false;
592
593 width = fmt->width(fmt, hpp, hists_to_evsel(hists));
594 fprintf(fp, "%.*s", width, dots);
595 }
596
597 depth = 0;
598 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
599 first_col = true;
600 width = depth * HIERARCHY_INDENT;
601
602 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
603 if (perf_hpp__should_skip(fmt, hists))
604 continue;
605
606 if (!first_col)
607 width++; /* for '+' sign between column header */
608 first_col = false;
609
610 width += fmt->width(fmt, hpp, hists_to_evsel(hists));
611 }
612
613 if (width > header_width)
614 header_width = width;
615
616 depth++;
617 }
618
619 fprintf(fp, "%s%-.*s", sep ?: " ", header_width, dots);
620
621 fprintf(fp, "\n#\n");
622
623 return 2;
624}
625
435size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, 626size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
436 int max_cols, float min_pcnt, FILE *fp) 627 int max_cols, float min_pcnt, FILE *fp)
437{ 628{
438 struct perf_hpp_fmt *fmt; 629 struct perf_hpp_fmt *fmt;
630 struct perf_hpp_list_node *fmt_node;
439 struct rb_node *nd; 631 struct rb_node *nd;
440 size_t ret = 0; 632 size_t ret = 0;
441 unsigned int width; 633 unsigned int width;
@@ -449,10 +641,11 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
449 bool first = true; 641 bool first = true;
450 size_t linesz; 642 size_t linesz;
451 char *line = NULL; 643 char *line = NULL;
644 unsigned indent;
452 645
453 init_rem_hits(); 646 init_rem_hits();
454 647
455 perf_hpp__for_each_format(fmt) 648 hists__for_each_format(hists, fmt)
456 perf_hpp__reset_width(fmt, hists); 649 perf_hpp__reset_width(fmt, hists);
457 650
458 if (symbol_conf.col_width_list_str) 651 if (symbol_conf.col_width_list_str)
@@ -463,7 +656,16 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
463 656
464 fprintf(fp, "# "); 657 fprintf(fp, "# ");
465 658
466 perf_hpp__for_each_format(fmt) { 659 if (symbol_conf.report_hierarchy) {
660 list_for_each_entry(fmt_node, &hists->hpp_formats, list) {
661 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
662 perf_hpp__reset_width(fmt, hists);
663 }
664 nr_rows += print_hierarchy_header(hists, &dummy_hpp, sep, fp);
665 goto print_entries;
666 }
667
668 hists__for_each_format(hists, fmt) {
467 if (perf_hpp__should_skip(fmt, hists)) 669 if (perf_hpp__should_skip(fmt, hists))
468 continue; 670 continue;
469 671
@@ -487,7 +689,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
487 689
488 fprintf(fp, "# "); 690 fprintf(fp, "# ");
489 691
490 perf_hpp__for_each_format(fmt) { 692 hists__for_each_format(hists, fmt) {
491 unsigned int i; 693 unsigned int i;
492 694
493 if (perf_hpp__should_skip(fmt, hists)) 695 if (perf_hpp__should_skip(fmt, hists))
@@ -520,7 +722,9 @@ print_entries:
520 goto out; 722 goto out;
521 } 723 }
522 724
523 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 725 indent = hists__overhead_width(hists) + 4;
726
727 for (nd = rb_first(&hists->entries); nd; nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) {
524 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 728 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
525 float percent; 729 float percent;
526 730
@@ -536,6 +740,20 @@ print_entries:
536 if (max_rows && ++nr_rows >= max_rows) 740 if (max_rows && ++nr_rows >= max_rows)
537 break; 741 break;
538 742
743 /*
744 * If all children are filtered out or percent-limited,
745 * display "no entry >= x.xx%" message.
746 */
747 if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) {
748 int depth = hists->nr_hpp_node + h->depth + 1;
749
750 print_hierarchy_indent(sep, depth, spaces, fp);
751 fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt);
752
753 if (max_rows && ++nr_rows >= max_rows)
754 break;
755 }
756
539 if (h->ms.map == NULL && verbose > 1) { 757 if (h->ms.map == NULL && verbose > 1) {
540 __map_groups__fprintf_maps(h->thread->mg, 758 __map_groups__fprintf_maps(h->thread->mg,
541 MAP__FUNCTION, fp); 759 MAP__FUNCTION, fp);