]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
perf hist: Support multi-line header
authorNamhyung Kim <namhyung@kernel.org>
Wed, 30 Apr 2025 20:55:40 +0000 (13:55 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Fri, 2 May 2025 18:36:14 +0000 (15:36 -0300)
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>
tools/perf/ui/browsers/hists.c
tools/perf/ui/hist.c
tools/perf/ui/stdio/hist.c
tools/perf/util/sort.c

index cf022e92d06b9b28e821682e4b2951d83f819487..67cbdec90d0bf0ea83221c86b40e6c20fee8a3a4 100644 (file)
@@ -1686,7 +1686,8 @@ hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
        return ret;
 }
 
-static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
+static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser,
+                                                     char *buf, size_t size, int line)
 {
        struct hists *hists = browser->hists;
        struct perf_hpp dummy_hpp = {
@@ -1712,7 +1713,7 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
                if (column++ < browser->b.horiz_scroll)
                        continue;
 
-               ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
+               ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL);
                if (advance_hpp_check(&dummy_hpp, ret))
                        break;
 
@@ -1723,6 +1724,9 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
                first_node = false;
        }
 
+       if (line < hists->hpp_list->nr_header_lines - 1)
+               return ret;
+
        if (!first_node) {
                ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
                                indent * HIERARCHY_INDENT, "");
@@ -1753,7 +1757,7 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
                        }
                        first_col = false;
 
-                       ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
+                       ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL);
                        dummy_hpp.buf[ret] = '\0';
 
                        start = strim(dummy_hpp.buf);
@@ -1772,14 +1776,18 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
 
 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
 {
+       struct perf_hpp_list *hpp_list = browser->hists->hpp_list;
        char headers[1024];
+       int line;
 
-       hists_browser__scnprintf_hierarchy_headers(browser, headers,
-                                                  sizeof(headers));
+       for (line = 0; line < hpp_list->nr_header_lines; line++) {
+               hists_browser__scnprintf_hierarchy_headers(browser, headers,
+                                                          sizeof(headers), line);
 
-       ui_browser__gotorc_title(&browser->b, 0, 0);
-       ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
-       ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
+               ui_browser__gotorc_title(&browser->b, line, 0);
+               ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
+               ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
+       }
 }
 
 static void hists_browser__headers(struct hist_browser *browser)
index bc0689fceeb18bdea1047a29e13bb93d30b0422d..ec44633207aa3abaa370ba9853b0b77a81ea60a4 100644 (file)
@@ -321,11 +321,16 @@ static int hpp__width_fn(struct perf_hpp_fmt *fmt,
 }
 
 static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
-                         struct hists *hists, int line __maybe_unused,
+                         struct hists *hists, int line,
                          int *span __maybe_unused)
 {
        int len = hpp__width_fn(fmt, hpp, hists);
-       return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name);
+       const char *hdr = "";
+
+       if (line == hists->hpp_list->nr_header_lines - 1)
+               hdr = fmt->name;
+
+       return scnprintf(hpp->buf, hpp->size, "%*s", len, hdr);
 }
 
 int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
index 7ac4b98e28bca82ed55b04f7d01800d6d942312d..8c4c8925df2c22fcd938194a716cad071956eef1 100644 (file)
@@ -643,45 +643,58 @@ static int hists__fprintf_hierarchy_headers(struct hists *hists,
        unsigned header_width = 0;
        struct perf_hpp_fmt *fmt;
        struct perf_hpp_list_node *fmt_node;
+       struct perf_hpp_list *hpp_list = hists->hpp_list;
        const char *sep = symbol_conf.field_sep;
 
        indent = hists->nr_hpp_node;
 
-       /* preserve max indent depth for column headers */
-       print_hierarchy_indent(sep, indent, " ", fp);
-
        /* the first hpp_list_node is for overhead columns */
        fmt_node = list_first_entry(&hists->hpp_formats,
                                    struct perf_hpp_list_node, list);
 
-       perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
-               fmt->header(fmt, hpp, hists, 0, NULL);
-               fprintf(fp, "%s%s", hpp->buf, sep ?: "  ");
-       }
+       for (int line = 0; line < hpp_list->nr_header_lines; line++) {
+               /* first # is displayed one level up */
+               if (line)
+                       fprintf(fp, "# ");
 
-       /* combine sort headers with ' / ' */
-       first_node = true;
-       list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
-               if (!first_node)
-                       header_width += fprintf(fp, " / ");
-               first_node = false;
+               /* preserve max indent depth for column headers */
+               print_hierarchy_indent(sep, indent, " ", fp);
 
-               first_col = true;
                perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
-                       if (perf_hpp__should_skip(fmt, hists))
-                               continue;
+                       fmt->header(fmt, hpp, hists, line, NULL);
+                       fprintf(fp, "%s%s", hpp->buf, sep ?: "  ");
+               }
 
-                       if (!first_col)
-                               header_width += fprintf(fp, "+");
-                       first_col = false;
+               if (line < hpp_list->nr_header_lines - 1)
+                       goto next_line;
+
+               /* combine sort headers with ' / ' */
+               first_node = true;
+               list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
+                       if (!first_node)
+                               header_width += fprintf(fp, " / ");
+                       first_node = false;
 
-                       fmt->header(fmt, hpp, hists, 0, NULL);
+                       first_col = true;
+                       perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
+                               if (perf_hpp__should_skip(fmt, hists))
+                                       continue;
 
-                       header_width += fprintf(fp, "%s", strim(hpp->buf));
+                               if (!first_col)
+                                       header_width += fprintf(fp, "+");
+                               first_col = false;
+
+                               fmt->header(fmt, hpp, hists, line, NULL);
+
+                               header_width += fprintf(fp, "%s", strim(hpp->buf));
+                       }
                }
+
+next_line:
+               fprintf(fp, "\n");
        }
 
-       fprintf(fp, "\n# ");
+       fprintf(fp, "# ");
 
        /* preserve max indent depth for initial dots */
        print_hierarchy_indent(sep, indent, dots, fp);
index 594b75ca95bf72b264762961d82a1bf4e2048961..ae8b8ceb82f3d00b46bb53614e638854ecf50f8f 100644 (file)
@@ -2641,18 +2641,22 @@ void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
 }
 
 static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
-                             struct hists *hists, int line __maybe_unused,
+                             struct hists *hists, int line,
                              int *span __maybe_unused)
 {
        struct hpp_sort_entry *hse;
        size_t len = fmt->user_len;
+       const char *hdr = "";
+
+       if (line == hists->hpp_list->nr_header_lines - 1)
+               hdr = fmt->name;
 
        hse = container_of(fmt, struct hpp_sort_entry, hpp);
 
        if (!len)
                len = hists__col_len(hists, hse->se->se_width_idx);
 
-       return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name);
+       return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, hdr);
 }
 
 static int __sort__hpp_width(struct perf_hpp_fmt *fmt,