Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf hist: Support multi-line header

This is a preparation to support multi-line headers in 'perf mem report'.

Normal sort keys and output fields that don't have contents for multi-
line will print the header string at the last line only.

As we don't use multi-line headers normally, it should not have any
changes in the output.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Link: https://lore.kernel.org/r/20250430205548.789750-4-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
29e6392e 43a64469

+67 -37
+16 -8
tools/perf/ui/browsers/hists.c
··· 1686 1686 return ret; 1687 1687 } 1688 1688 1689 - static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size) 1689 + static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, 1690 + char *buf, size_t size, int line) 1690 1691 { 1691 1692 struct hists *hists = browser->hists; 1692 1693 struct perf_hpp dummy_hpp = { ··· 1713 1712 if (column++ < browser->b.horiz_scroll) 1714 1713 continue; 1715 1714 1716 - ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL); 1715 + ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL); 1717 1716 if (advance_hpp_check(&dummy_hpp, ret)) 1718 1717 break; 1719 1718 ··· 1723 1722 1724 1723 first_node = false; 1725 1724 } 1725 + 1726 + if (line < hists->hpp_list->nr_header_lines - 1) 1727 + return ret; 1726 1728 1727 1729 if (!first_node) { 1728 1730 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s", ··· 1757 1753 } 1758 1754 first_col = false; 1759 1755 1760 - ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL); 1756 + ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL); 1761 1757 dummy_hpp.buf[ret] = '\0'; 1762 1758 1763 1759 start = strim(dummy_hpp.buf); ··· 1776 1772 1777 1773 static void hists_browser__hierarchy_headers(struct hist_browser *browser) 1778 1774 { 1775 + struct perf_hpp_list *hpp_list = browser->hists->hpp_list; 1779 1776 char headers[1024]; 1777 + int line; 1780 1778 1781 - hists_browser__scnprintf_hierarchy_headers(browser, headers, 1782 - sizeof(headers)); 1779 + for (line = 0; line < hpp_list->nr_header_lines; line++) { 1780 + hists_browser__scnprintf_hierarchy_headers(browser, headers, 1781 + sizeof(headers), line); 1783 1782 1784 - ui_browser__gotorc_title(&browser->b, 0, 0); 1785 - ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); 1786 - ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); 1783 + ui_browser__gotorc_title(&browser->b, line, 0); 1784 + ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); 1785 + ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); 1786 + } 1787 1787 } 1788 1788 1789 1789 static void hists_browser__headers(struct hist_browser *browser)
+7 -2
tools/perf/ui/hist.c
··· 321 321 } 322 322 323 323 static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 324 - struct hists *hists, int line __maybe_unused, 324 + struct hists *hists, int line, 325 325 int *span __maybe_unused) 326 326 { 327 327 int len = hpp__width_fn(fmt, hpp, hists); 328 - return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name); 328 + const char *hdr = ""; 329 + 330 + if (line == hists->hpp_list->nr_header_lines - 1) 331 + hdr = fmt->name; 332 + 333 + return scnprintf(hpp->buf, hpp->size, "%*s", len, hdr); 329 334 } 330 335 331 336 int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
+38 -25
tools/perf/ui/stdio/hist.c
··· 643 643 unsigned header_width = 0; 644 644 struct perf_hpp_fmt *fmt; 645 645 struct perf_hpp_list_node *fmt_node; 646 + struct perf_hpp_list *hpp_list = hists->hpp_list; 646 647 const char *sep = symbol_conf.field_sep; 647 648 648 649 indent = hists->nr_hpp_node; 649 - 650 - /* preserve max indent depth for column headers */ 651 - print_hierarchy_indent(sep, indent, " ", fp); 652 650 653 651 /* the first hpp_list_node is for overhead columns */ 654 652 fmt_node = list_first_entry(&hists->hpp_formats, 655 653 struct perf_hpp_list_node, list); 656 654 657 - perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 658 - fmt->header(fmt, hpp, hists, 0, NULL); 659 - fprintf(fp, "%s%s", hpp->buf, sep ?: " "); 660 - } 655 + for (int line = 0; line < hpp_list->nr_header_lines; line++) { 656 + /* first # is displayed one level up */ 657 + if (line) 658 + fprintf(fp, "# "); 661 659 662 - /* combine sort headers with ' / ' */ 663 - first_node = true; 664 - list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { 665 - if (!first_node) 666 - header_width += fprintf(fp, " / "); 667 - first_node = false; 660 + /* preserve max indent depth for column headers */ 661 + print_hierarchy_indent(sep, indent, " ", fp); 668 662 669 - first_col = true; 670 663 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 671 - if (perf_hpp__should_skip(fmt, hists)) 672 - continue; 673 - 674 - if (!first_col) 675 - header_width += fprintf(fp, "+"); 676 - first_col = false; 677 - 678 - fmt->header(fmt, hpp, hists, 0, NULL); 679 - 680 - header_width += fprintf(fp, "%s", strim(hpp->buf)); 664 + fmt->header(fmt, hpp, hists, line, NULL); 665 + fprintf(fp, "%s%s", hpp->buf, sep ?: " "); 681 666 } 667 + 668 + if (line < hpp_list->nr_header_lines - 1) 669 + goto next_line; 670 + 671 + /* combine sort headers with ' / ' */ 672 + first_node = true; 673 + list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { 674 + if (!first_node) 675 + header_width += fprintf(fp, " / "); 676 + first_node = false; 677 + 678 + first_col = true; 679 + perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 680 + if (perf_hpp__should_skip(fmt, hists)) 681 + continue; 682 + 683 + if (!first_col) 684 + header_width += fprintf(fp, "+"); 685 + first_col = false; 686 + 687 + fmt->header(fmt, hpp, hists, line, NULL); 688 + 689 + header_width += fprintf(fp, "%s", strim(hpp->buf)); 690 + } 691 + } 692 + 693 + next_line: 694 + fprintf(fp, "\n"); 682 695 } 683 696 684 - fprintf(fp, "\n# "); 697 + fprintf(fp, "# "); 685 698 686 699 /* preserve max indent depth for initial dots */ 687 700 print_hierarchy_indent(sep, indent, dots, fp);
+6 -2
tools/perf/util/sort.c
··· 2641 2641 } 2642 2642 2643 2643 static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 2644 - struct hists *hists, int line __maybe_unused, 2644 + struct hists *hists, int line, 2645 2645 int *span __maybe_unused) 2646 2646 { 2647 2647 struct hpp_sort_entry *hse; 2648 2648 size_t len = fmt->user_len; 2649 + const char *hdr = ""; 2650 + 2651 + if (line == hists->hpp_list->nr_header_lines - 1) 2652 + hdr = fmt->name; 2649 2653 2650 2654 hse = container_of(fmt, struct hpp_sort_entry, hpp); 2651 2655 2652 2656 if (!len) 2653 2657 len = hists__col_len(hists, hse->se->se_width_idx); 2654 2658 2655 - return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name); 2659 + return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, hdr); 2656 2660 } 2657 2661 2658 2662 static int __sort__hpp_width(struct perf_hpp_fmt *fmt,