[PATCH v8 00/18] perf test: Parallel harness optimizations, summary, JUnit XML & PMU fixes

Ian Rogers posted 18 patches 5 days, 10 hours ago
tools/lib/subcmd/run-command.c                |   69 +-
tools/perf/pmu-events/empty-pmu-events.c      | 8821 +++++++++++------
tools/perf/pmu-events/jevents.py              |  847 +-
tools/perf/pmu-events/pmu-events.h            |    5 +
tools/perf/tests/builtin-test.c               |  788 +-
tools/perf/tests/pmu-events.c                 |  156 +-
tools/perf/tests/shell/test_intel_pt.sh       |  169 +-
.../tests/shell/test_test_junit_output.sh     |   63 +
tools/perf/tests/tests-scripts.c              |   82 +-
tools/perf/tests/tests.h                      |    3 +
tools/perf/tests/util.c                       |   20 +-
tools/perf/tests/workloads/Build              |    1 +
tools/perf/tests/workloads/jitdump.c          |  210 +
tools/perf/util/intel-tpebs.c                 |   92 +-
tools/perf/util/jitdump.h                     |    3 +-
tools/perf/util/pmu.c                         |   19 +-
16 files changed, 7619 insertions(+), 3729 deletions(-)
create mode 100755 tools/perf/tests/shell/test_test_junit_output.sh
create mode 100644 tools/perf/tests/workloads/jitdump.c
[PATCH v8 00/18] perf test: Parallel harness optimizations, summary, JUnit XML & PMU fixes
Posted by Ian Rogers 5 days, 10 hours ago
This patch series dramatically improves the speed, usability, and test
reporting of the Linux perf test suite (`perf test`), and introduces PMU
matching optimizations. It optimizes the parallel execution harness,
introduces automated summary reporting, adds standardized JUnit XML report
generation, removes runtime external C compiler dependencies for continuous
integration environments, and avoids unnecessary PMU scanning.

1. Parallel execution optimizations:
   - When running in parallel verbose mode (-v), the parent test harness
     previously only polled the pipe of the current active test wait-loop,
     blocking other children once they filled their 64KB pipe buffer.
     This series refactors the loop to drain all children's output pipes
     simultaneously, reducing parallel execution times for high-output
     suites (like PMU events) from ~35 seconds down to ~5.9 seconds.
   - Hardens `check_if_command_finished()` and `wait_or_whine()` inside
     `tools/lib/subcmd` to safely handle invalid PIDs, using a robust
     waitpid fallback when procfs limits are hit to prevent zombie leak
     loops and PID reuse hazards.
   - Fixes race conditions and concurrent process reaping hazards inside
     the Intel TPEBS driver (`intel-tpebs.c`).

2. Improved Console Summary and Output Layout:
   - Shows context failure snippets for failed tests in moderate verbose
     mode (-v) without requiring extremely verbose output (-vv).
   - Prints a concise, colored console execution summary showing passed,
     skipped, and failed counts for main tests and individual subtests at
     the absolute tail of the run, together with the indices of all
     failed tests.
   - Dynamically truncates long test descriptions to fit within the
     terminal columns to prevent visual wrapping and duplicate printing
     glitches, while ensuring suite headers are printed without trailing
     padding that causes wrapping.

3. Automated JUnit XML Reports:
   - Adds a `-j/--junit [filename]` option to generate standardized XML
     test reports, capturing execution duration and XML-escaped failure
     logs or skip reasons for each test.
   - Includes a dedicated shell validation test validating XML report
     structure via Python's ElementTree parser.

4. PMU Events & Monolithic Suite Splitting:
   - Generates dynamic sub-tests for each PMU event metric table instead
     of a single monolithic sanity test, enabling fine-grained tracing.
   - Splits the monolithic `util` test suite into independent sub-tests
     for easier failure isolation.

5. CI & Environmental Hardening:
   - Removes runtime dependency on `/usr/bin/cc` inside the Intel PT
     shell test by introducing a built-in `jitdump` C workload that
     generates JIT self-modifying code natively across x86, ARM, RISC-V,
     MIPS, and others.

6. PMU Core Matching Optimization:
   - Treats "default_core" as a core PMU name in `is_pmu_core`, avoiding
     slow and unnecessary scanning of other PMUs which always misses.
   - Documents different types of core PMU matching approaches (x86 cpu,
     s390 cpum_cf, ARM sysfs).

v8 Changes
----------
- Treat "default_core" as a core PMU to avoid slow and unnecessary scanning of
  other PMUs.
- Dynamically truncate long test descriptions to fit within terminal columns
  to prevent visual wrapping.
- Remove unnecessary padding from suite header prints to prevent colon wrapping
  on newlines (resolves visual glitch in `perf test -v`).


Ian Rogers (18):
  perf tpebs: Fix concurrent stop races and PID reuse hazards in
    tpebs_stop
  perf jevents.py: Make generated C code more kernel style
  perf pmu-events: Add API to get metric table name and iterate tables
  perf test: Drain pipe after child finishes to avoid losing output
  perf test: Support dynamic test suites with setup callback and private
    data
  perf test pmu-events: A sub-test per metric table
  tools subcmd: Robust fallback and existence checks for process reaping
  perf test: Refactor parallel poll loop to drain all pipes
    simultaneously
  perf test: Show snippet failure output for verbose=1
  perf test: Add summary reporting
  perf test: Fix subtest status alignment for multi-digit indexes
  perf test: Skip shebang and SPDX comments in shell test descriptions
  perf test: Split monolithic 'util' test suite into sub-tests
  perf test: Add -j/--junit option for JUnit XML test reports
  perf test: Add shell test to validate JUnit XML reporting output
  perf test: Remove /usr/bin/cc dependency from Intel PT shell test
  perf pmu: Recognize 'default_core' as a core PMU and document matching
  perf test: Truncate printed test descriptions dynamically to avoid
    terminal wrapping

 tools/lib/subcmd/run-command.c                |   69 +-
 tools/perf/pmu-events/empty-pmu-events.c      | 8821 +++++++++++------
 tools/perf/pmu-events/jevents.py              |  847 +-
 tools/perf/pmu-events/pmu-events.h            |    5 +
 tools/perf/tests/builtin-test.c               |  788 +-
 tools/perf/tests/pmu-events.c                 |  156 +-
 tools/perf/tests/shell/test_intel_pt.sh       |  169 +-
 .../tests/shell/test_test_junit_output.sh     |   63 +
 tools/perf/tests/tests-scripts.c              |   82 +-
 tools/perf/tests/tests.h                      |    3 +
 tools/perf/tests/util.c                       |   20 +-
 tools/perf/tests/workloads/Build              |    1 +
 tools/perf/tests/workloads/jitdump.c          |  210 +
 tools/perf/util/intel-tpebs.c                 |   92 +-
 tools/perf/util/jitdump.h                     |    3 +-
 tools/perf/util/pmu.c                         |   19 +-
 16 files changed, 7619 insertions(+), 3729 deletions(-)
 create mode 100755 tools/perf/tests/shell/test_test_junit_output.sh
 create mode 100644 tools/perf/tests/workloads/jitdump.c

-- 
2.54.0.1013.g208068f2d8-goog
[PATCH v9 0/2] perf test & PMU metric resolution improvements
Posted by Ian Rogers 3 days, 11 hours ago
This series contains the final remaining unmerged patch for the perf test
improvements sent as v8 in:
https://lore.kernel.org/lkml/20260602174129.3192312-1-irogers@google.com/

v9 Changes
----------
- Patch 1: Resubmitted the "perf pmu: Recognize 'default_core' as a core PMU..."
  patch, but this time with a squashed fix for a metric resolution bug due to
  its use of is_pmu_core.
- Patch 2: Added explicit clamping for max_desc_width and width based on the
  buffer size in format_test_description() to satisfy GCC 16 and prevent the
  -Wformat-truncation warning reported by the maintainer.

Ian Rogers (2):
  perf pmu: Recognize 'default_core' as a core PMU and document matching
  perf test: Truncate printed test descriptions dynamically to avoid
    terminal wrapping

 tools/perf/tests/builtin-test.c | 70 +++++++++++++++++++++++++++++----
 tools/perf/util/metricgroup.c   |  3 +-
 tools/perf/util/pmu.c           | 19 ++++++++-
 3 files changed, 83 insertions(+), 9 deletions(-)

-- 
2.54.0.1032.g2f8565e1d1-goog
Re: [PATCH v9 0/2] perf test & PMU metric resolution improvements
Posted by Arnaldo Carvalho de Melo 3 days, 8 hours ago
On Thu, Jun 04, 2026 at 09:36:25AM -0700, Ian Rogers wrote:
> This series contains the final remaining unmerged patch for the perf test
> improvements sent as v8 in:
> https://lore.kernel.org/lkml/20260602174129.3192312-1-irogers@google.com/
> 
> v9 Changes
> ----------
> - Patch 1: Resubmitted the "perf pmu: Recognize 'default_core' as a core PMU..."
>   patch, but this time with a squashed fix for a metric resolution bug due to
>   its use of is_pmu_core.
> - Patch 2: Added explicit clamping for max_desc_width and width based on the
>   buffer size in format_test_description() to satisfy GCC 16 and prevent the
>   -Wformat-truncation warning reported by the maintainer.
> 
> Ian Rogers (2):
>   perf pmu: Recognize 'default_core' as a core PMU and document matching
>   perf test: Truncate printed test descriptions dynamically to avoid
>     terminal wrapping

Applying, will ask local sashiko to give another look,

- Arnaldo
[PATCH v9 1/2] perf pmu: Recognize 'default_core' as a core PMU and document matching
Posted by Ian Rogers 3 days, 11 hours ago
The is_pmu_core function checks if a PMU name corresponds to a core
CPU PMU. However, it currently fails to recognize "default_core" as
a core PMU.

When "default_core" is used, the PMU scanning fallback in pmus.c
scans the "other_pmus" list. This scan is slow and always misses because
"default_core" is a core PMU, leading to unnecessary overhead.

Update is_pmu_core to recognize "default_core" directly. Additionally,
document the different matching approaches (exact name for x86/s390,
sysfs-based cpus file check for ARM/hybrid) to clarify how core PMUs are
classified.

Also, explicitly treat "default_core" as `all_pmus` in `setup_metric_events()`
to preserve the original metric resolution behavior for this pseudo-PMU.

Assisted-by: Gemini-CLI:Google Gemini 3.1 Pro
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/metricgroup.c |  3 ++-
 tools/perf/util/pmu.c         | 19 ++++++++++++++++++-
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 5a489e97c413..c2ce3e53aaee 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -295,7 +295,8 @@ static int setup_metric_events(const char *pmu, struct hashmap *ids,
 	const char *metric_id;
 	struct evsel *ev;
 	size_t ids_size, matched_events, i;
-	bool all_pmus = !strcmp(pmu, "all") || perf_pmus__num_core_pmus() == 1 || !is_pmu_core(pmu);
+	bool all_pmus = !strcmp(pmu, "all") || !strcmp(pmu, "default_core") ||
+			perf_pmus__num_core_pmus() == 1 || !is_pmu_core(pmu);
 
 	*out_metric_events = NULL;
 	ids_size = hashmap__size(ids);
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 23337d2fa281..9994709ef12b 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -2029,9 +2029,26 @@ int perf_pmu__for_each_format(struct perf_pmu *pmu, void *state, pmu_format_call
 	return 0;
 }
 
+/**
+ * is_pmu_core() - Check if the given PMU name corresponds to a core CPU PMU.
+ * @name: The PMU name to check.
+ *
+ * Core PMUs can be identified by:
+ * 1. Exact name match:
+ *    - "cpu": Typically used on x86 architectures.
+ *    - "cpum_cf": Typically used on s390 architectures (CPU Measurement Counter Facility).
+ *    - "default_core": A generic name used to refer to the default core PMU.
+ * 2. Sysfs file existence check (is_sysfs_pmu_core):
+ *    - Typically used on ARM systems or Intel hybrid architectures (e.g., "cpu_atom",
+ *      "cpu_core"). This approach checks if the sysfs directory for the PMU
+ *      contains a "cpus" file.
+ */
 bool is_pmu_core(const char *name)
 {
-	return !strcmp(name, "cpu") || !strcmp(name, "cpum_cf") || is_sysfs_pmu_core(name);
+	return !strcmp(name, "cpu") ||
+	       !strcmp(name, "cpum_cf") ||
+	       !strcmp(name, "default_core") ||
+	       is_sysfs_pmu_core(name);
 }
 
 bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu)
-- 
2.54.0.1032.g2f8565e1d1-goog
[PATCH v9 2/2] perf test: Truncate printed test descriptions dynamically to avoid terminal wrapping
Posted by Ian Rogers 3 days, 11 hours ago
When test descriptions are extremely long (e.g., the truncated perf.data
graceful handling test is 103 characters long), they wrap across terminal
boundaries.

Because the ANSI escape code to delete the line (PERF_COLOR_DELETE_LINE)
only clears a single terminal line, visual wrapping leaves orphan
wrapped lines on the screen, which results in the test description being
printed multiple times.

Resolve this by checking the terminal width (get_term_dimensions) and
dynamically truncating the printed test description to fit within the
available columns, leaving safety space for the prefix index and status
suffix.

Also, remove the width padding from the test suite headers which do not
display inline status messages. This prevents their trailing colons from
wrapping onto new lines on standard width terminals.

Finally, explicitly clamp the dynamically calculated description width to
the formatting buffer's size to satisfy GCC 16's bounds checking and
prevent a -Wformat-truncation warning during compilation.

JUnit XML output and the failure summary report still print the full,
untruncated test descriptions.

Assisted-by: Gemini-CLI:Google Gemini 3.1 Pro
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/tests/builtin-test.c | 70 +++++++++++++++++++++++++++++----
 1 file changed, 63 insertions(+), 7 deletions(-)

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index b64fc2204f22..b7eebfcf7700 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -20,6 +20,8 @@
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/time.h>
+#include <sys/ioctl.h>
+#include "util/term.h"
 #include "builtin.h"
 #include "config.h"
 #include "hist.h"
@@ -404,19 +406,73 @@ static char *xml_escape(const char *str)
 	return res ? res : strdup("");
 }
 
+static const char *format_test_description(const char *desc, int width, int max_desc_width,
+					  char *buf, size_t buf_sz)
+{
+	int len = strlen(desc);
+
+	/*
+	 * Clamp to buf_sz to prevent GCC format-truncation warnings
+	 * when terminal width is very large.
+	 */
+	if (max_desc_width >= (int)buf_sz)
+		max_desc_width = buf_sz - 1;
+
+	if (width > max_desc_width)
+		width = max_desc_width;
+
+	if (len > max_desc_width) {
+		snprintf(buf, buf_sz, "%.*s...", max_desc_width - 3, desc);
+	} else {
+		snprintf(buf, buf_sz, "%-*s", width, desc);
+	}
+	return buf;
+}
+
 static int print_test_result(struct test_suite *t, int curr_suite, int curr_test_case,
 			     int result, int width, int running,
 			     const char *err_output, double elapsed)
 {
+	char desc_buf[256];
+	const char *desc = test_description(t, curr_test_case);
+	struct winsize ws;
+	int max_desc_area_width;
+	int target_desc_area_width;
+	int desc_padding;
+
+	get_term_dimensions(&ws);
+	/*
+	 * Total terminal columns minus space for status e.g. " Running (12 active)"
+	 * which is 20 chars, plus a margin of 3 chars = 23 chars.
+	 */
+	max_desc_area_width = ws.ws_col - 23;
+	if (max_desc_area_width < 40)
+		max_desc_area_width = 40;
+
+	/* Standard test has prefix "%3d: " which is 5 chars */
+	target_desc_area_width = width + 5;
+	if (target_desc_area_width > max_desc_area_width)
+		target_desc_area_width = max_desc_area_width;
+
 	if (test_suite__num_test_cases(t) > 1) {
 		char prefix[32];
 		int len = snprintf(prefix, sizeof(prefix), "%3d.%1d:",
 				   curr_suite + 1, curr_test_case + 1);
-		int subw = len >= 4 ? width + 4 - len : width;
 
-		pr_info("%s %-*s:", prefix, subw, test_description(t, curr_test_case));
-	} else
-		pr_info("%3d: %-*s:", curr_suite + 1, width, test_description(t, curr_test_case));
+		desc_padding = target_desc_area_width - (len + 1);
+		if (desc_padding < 20)
+			desc_padding = 20;
+
+		format_test_description(desc, desc_padding, desc_padding, desc_buf, sizeof(desc_buf));
+		pr_info("%s %s:", prefix, desc_buf);
+	} else {
+		desc_padding = target_desc_area_width - 5;
+		if (desc_padding < 20)
+			desc_padding = 20;
+
+		format_test_description(desc, desc_padding, desc_padding, desc_buf, sizeof(desc_buf));
+		pr_info("%3d: %s:", curr_suite + 1, desc_buf);
+	}
 
 	switch (result) {
 	case TEST_RUNNING:
@@ -700,7 +756,7 @@ static void finish_test(struct child_test **child_tests, int running_test, int c
 	 * sub test names.
 	 */
 	if (test_suite__num_test_cases(t) > 1 && curr_test_case == 0)
-		pr_info("%3d: %-*s:\n", curr_suite + 1, width, test_description(t, -1));
+		pr_info("%3d: %s:\n", curr_suite + 1, test_description(t, -1));
 
 	/*
 	 * Busy loop reading from the child's stdout/stderr that are set to be
@@ -976,7 +1032,7 @@ static int finish_tests_parallel(struct child_test **child_tests, size_t num_tes
 			if (next_child) {
 				if (test_suite__num_test_cases(next_child->test) > 1 &&
 				    last_suite_printed != next_child->suite_num) {
-					pr_info("%3d: %-*s:\n", next_child->suite_num + 1, width,
+					pr_info("%3d: %s:\n", next_child->suite_num + 1,
 						test_description(next_child->test, -1));
 					last_suite_printed = next_child->suite_num;
 				}
@@ -1040,7 +1096,7 @@ static int finish_tests_parallel(struct child_test **child_tests, size_t num_tes
 
 			if (test_suite__num_test_cases(child->test) > 1 &&
 			    last_suite_printed != child->suite_num) {
-				pr_info("%3d: %-*s:\n", child->suite_num + 1, width,
+				pr_info("%3d: %s:\n", child->suite_num + 1,
 					test_description(child->test, -1));
 				last_suite_printed = child->suite_num;
 			}
-- 
2.54.0.1032.g2f8565e1d1-goog
Re: [PATCH v9 2/2] perf test: Truncate printed test descriptions dynamically to avoid terminal wrapping
Posted by Arnaldo Carvalho de Melo 3 days, 7 hours ago
On Thu, Jun 04, 2026 at 09:36:27AM -0700, Ian Rogers wrote:
> When test descriptions are extremely long (e.g., the truncated perf.data
> graceful handling test is 103 characters long), they wrap across terminal
> boundaries.
> 
> Because the ANSI escape code to delete the line (PERF_COLOR_DELETE_LINE)
> only clears a single terminal line, visual wrapping leaves orphan
> wrapped lines on the screen, which results in the test description being
> printed multiple times.

  CC      /tmp/build/perf-tools-next/util/bpf_lock_contention.o
tests/builtin-test.c: In function ‘print_test_result.isra’:
tests/builtin-test.c:427:40: error: ‘%-*s’ directive output may be truncated writing between 20 and 65507 bytes into a region of size 256 [-Werror=format-truncation=]
  427 |                 snprintf(buf, buf_sz, "%-*s", width, desc);
      |                                        ^~~~
In file included from /usr/include/stdio.h:974,
                 from /home/acme/git/perf-tools-next/tools/include/linux/panic.h:6,
                 from /home/acme/git/perf-tools-next/tools/include/linux/kernel.h:11,
                 from /home/acme/git/perf-tools-next/tools/include/linux/list.h:7,
                 from /home/acme/git/perf-tools-next/tools/perf/util/config.h:6,
                 from tests/builtin-test.c:26:
In function ‘snprintf’,
    inlined from ‘format_test_description’ at tests/builtin-test.c:427:3,
    inlined from ‘print_test_result.isra’ at tests/builtin-test.c:473:3:
/usr/include/bits/stdio2.h:68:10: note: ‘__builtin___snprintf_chk’ output between 21 and 65508 bytes into a destination of size 256
   68 |   return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1,
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   69 |                                    __glibc_objsize (__s), __fmt,
      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   70 |                                    __va_arg_pack ());
      |                                    ~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[4]: *** [/home/acme/git/perf-tools-next/tools/build/Makefile.build:95: /tmp/build/perf-tools-next/tests/builtin-test.o] Error 1
make[3]: *** [/home/acme/git/perf-tools-next/tools/build/Makefile.build:158: tests] Error 2
make[2]: *** [Makefile.perf:566: /tmp/build/perf-tools-next/perf-test-in.o] Error 2
make[2]: *** Waiting for unfinished jobs....
  LD      /tmp/build/perf-tools-next/util/perf-util-in.o
  LD      /tmp/build/perf-tools-next/perf-util-in.o
make[1]: *** [Makefile.perf:288: sub-make] Error 2
make: *** [Makefile:122: install-bin] Error 2
make: Leaving directory '/home/acme/git/perf-tools-next/tools/perf'
⬢ [acme@toolbx perf-tools-next]$


I thought you had fixed this one?

The first patch is applied, testing with it now.

- Arnaldo
Re: [PATCH v9 2/2] perf test: Truncate printed test descriptions dynamically to avoid terminal wrapping
Posted by Ian Rogers 3 days, 7 hours ago
On Thu, Jun 4, 2026 at 1:26 PM Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
>
> On Thu, Jun 04, 2026 at 09:36:27AM -0700, Ian Rogers wrote:
> > When test descriptions are extremely long (e.g., the truncated perf.data
> > graceful handling test is 103 characters long), they wrap across terminal
> > boundaries.
> >
> > Because the ANSI escape code to delete the line (PERF_COLOR_DELETE_LINE)
> > only clears a single terminal line, visual wrapping leaves orphan
> > wrapped lines on the screen, which results in the test description being
> > printed multiple times.
>
>   CC      /tmp/build/perf-tools-next/util/bpf_lock_contention.o
> tests/builtin-test.c: In function ‘print_test_result.isra’:
> tests/builtin-test.c:427:40: error: ‘%-*s’ directive output may be truncated writing between 20 and 65507 bytes into a region of size 256 [-Werror=format-truncation=]
>   427 |                 snprintf(buf, buf_sz, "%-*s", width, desc);
>       |                                        ^~~~
> In file included from /usr/include/stdio.h:974,
>                  from /home/acme/git/perf-tools-next/tools/include/linux/panic.h:6,
>                  from /home/acme/git/perf-tools-next/tools/include/linux/kernel.h:11,
>                  from /home/acme/git/perf-tools-next/tools/include/linux/list.h:7,
>                  from /home/acme/git/perf-tools-next/tools/perf/util/config.h:6,
>                  from tests/builtin-test.c:26:
> In function ‘snprintf’,
>     inlined from ‘format_test_description’ at tests/builtin-test.c:427:3,
>     inlined from ‘print_test_result.isra’ at tests/builtin-test.c:473:3:
> /usr/include/bits/stdio2.h:68:10: note: ‘__builtin___snprintf_chk’ output between 21 and 65508 bytes into a destination of size 256
>    68 |   return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1,
>       |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    69 |                                    __glibc_objsize (__s), __fmt,
>       |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    70 |                                    __va_arg_pack ());
>       |                                    ~~~~~~~~~~~~~~~~~
> cc1: all warnings being treated as errors
> make[4]: *** [/home/acme/git/perf-tools-next/tools/build/Makefile.build:95: /tmp/build/perf-tools-next/tests/builtin-test.o] Error 1
> make[3]: *** [/home/acme/git/perf-tools-next/tools/build/Makefile.build:158: tests] Error 2
> make[2]: *** [Makefile.perf:566: /tmp/build/perf-tools-next/perf-test-in.o] Error 2
> make[2]: *** Waiting for unfinished jobs....
>   LD      /tmp/build/perf-tools-next/util/perf-util-in.o
>   LD      /tmp/build/perf-tools-next/perf-util-in.o
> make[1]: *** [Makefile.perf:288: sub-make] Error 2
> make: *** [Makefile:122: install-bin] Error 2
> make: Leaving directory '/home/acme/git/perf-tools-next/tools/perf'
> ⬢ [acme@toolbx perf-tools-next]$
>
>
> I thought you had fixed this one?

I thought so too with the clipped local variable. Let me look again,
sorry for the churn.

Thanks,
Ian

> The first patch is applied, testing with it now.
>
> - Arnaldo
[PATCH v10] perf test: Truncate printed test descriptions dynamically to avoid terminal wrapping
Posted by Ian Rogers 3 days, 6 hours ago
When test descriptions are extremely long (e.g., the truncated perf.data
graceful handling test is 103 characters long), they wrap across terminal
boundaries.

Because the ANSI escape code to delete the line (PERF_COLOR_DELETE_LINE)
only clears a single terminal line, visual wrapping leaves orphan
wrapped lines on the screen, which results in the test description being
printed multiple times.

Resolve this by checking the terminal width (get_term_dimensions) and
dynamically truncating the printed test description to fit within the
available columns, leaving safety space for the prefix index and status
suffix.

Also, remove the width padding from the test suite headers which do not
display inline status messages. This prevents their trailing colons from
wrapping onto new lines on standard width terminals.

Finally, avoid GCC 16's -Wformat-truncation warnings by delegating the
description padding to pr_info's %-*s format specifier instead of padding
within a temporary buffer, and clamp the truncation limit to the temporary
buffer's size.

JUnit XML output and the failure summary report still print the full,
untruncated test descriptions.

Assisted-by: Gemini-CLI:Google Gemini 3.1 Pro
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/tests/builtin-test.c | 66 +++++++++++++++++++++++++++++----
 1 file changed, 59 insertions(+), 7 deletions(-)

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index b64fc2204f22..fd83ca2bda12 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -20,6 +20,8 @@
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/time.h>
+#include <sys/ioctl.h>
+#include "util/term.h"
 #include "builtin.h"
 #include "config.h"
 #include "hist.h"
@@ -404,19 +406,69 @@ static char *xml_escape(const char *str)
 	return res ? res : strdup("");
 }
 
+static const char *format_test_description(const char *desc, int max_desc_width,
+					  char *buf, size_t buf_sz)
+{
+	int len = strlen(desc);
+
+	/*
+	 * Clamp to buf_sz to prevent GCC format-truncation warnings
+	 * when terminal width is very large.
+	 */
+	if (max_desc_width >= (int)buf_sz)
+		max_desc_width = buf_sz - 1;
+
+	if (len > max_desc_width) {
+		snprintf(buf, buf_sz, "%.*s...", max_desc_width - 3, desc);
+		return buf;
+	}
+	return desc;
+}
+
 static int print_test_result(struct test_suite *t, int curr_suite, int curr_test_case,
 			     int result, int width, int running,
 			     const char *err_output, double elapsed)
 {
+	char desc_buf[256];
+	const char *desc = test_description(t, curr_test_case);
+	struct winsize ws;
+	int max_desc_area_width;
+	int target_desc_area_width;
+	int desc_padding;
+
+	get_term_dimensions(&ws);
+	/*
+	 * Total terminal columns minus space for status e.g. " Running (12 active)"
+	 * which is 20 chars, plus a margin of 3 chars = 23 chars.
+	 */
+	max_desc_area_width = ws.ws_col - 23;
+	if (max_desc_area_width < 40)
+		max_desc_area_width = 40;
+
+	/* Standard test has prefix "%3d: " which is 5 chars */
+	target_desc_area_width = width + 5;
+	if (target_desc_area_width > max_desc_area_width)
+		target_desc_area_width = max_desc_area_width;
+
 	if (test_suite__num_test_cases(t) > 1) {
 		char prefix[32];
 		int len = snprintf(prefix, sizeof(prefix), "%3d.%1d:",
 				   curr_suite + 1, curr_test_case + 1);
-		int subw = len >= 4 ? width + 4 - len : width;
 
-		pr_info("%s %-*s:", prefix, subw, test_description(t, curr_test_case));
-	} else
-		pr_info("%3d: %-*s:", curr_suite + 1, width, test_description(t, curr_test_case));
+		desc_padding = target_desc_area_width - (len + 1);
+		if (desc_padding < 20)
+			desc_padding = 20;
+
+		desc = format_test_description(desc, desc_padding, desc_buf, sizeof(desc_buf));
+		pr_info("%s %-*s:", prefix, desc_padding, desc);
+	} else {
+		desc_padding = target_desc_area_width - 5;
+		if (desc_padding < 20)
+			desc_padding = 20;
+
+		desc = format_test_description(desc, desc_padding, desc_buf, sizeof(desc_buf));
+		pr_info("%3d: %-*s:", curr_suite + 1, desc_padding, desc);
+	}
 
 	switch (result) {
 	case TEST_RUNNING:
@@ -700,7 +752,7 @@ static void finish_test(struct child_test **child_tests, int running_test, int c
 	 * sub test names.
 	 */
 	if (test_suite__num_test_cases(t) > 1 && curr_test_case == 0)
-		pr_info("%3d: %-*s:\n", curr_suite + 1, width, test_description(t, -1));
+		pr_info("%3d: %s:\n", curr_suite + 1, test_description(t, -1));
 
 	/*
 	 * Busy loop reading from the child's stdout/stderr that are set to be
@@ -976,7 +1028,7 @@ static int finish_tests_parallel(struct child_test **child_tests, size_t num_tes
 			if (next_child) {
 				if (test_suite__num_test_cases(next_child->test) > 1 &&
 				    last_suite_printed != next_child->suite_num) {
-					pr_info("%3d: %-*s:\n", next_child->suite_num + 1, width,
+					pr_info("%3d: %s:\n", next_child->suite_num + 1,
 						test_description(next_child->test, -1));
 					last_suite_printed = next_child->suite_num;
 				}
@@ -1040,7 +1092,7 @@ static int finish_tests_parallel(struct child_test **child_tests, size_t num_tes
 
 			if (test_suite__num_test_cases(child->test) > 1 &&
 			    last_suite_printed != child->suite_num) {
-				pr_info("%3d: %-*s:\n", child->suite_num + 1, width,
+				pr_info("%3d: %s:\n", child->suite_num + 1,
 					test_description(child->test, -1));
 				last_suite_printed = child->suite_num;
 			}
-- 
2.54.0.1032.g2f8565e1d1-goog