Re: [PATCH v8 18/18] perf test: Truncate printed test descriptions dynamically to avoid terminal wrapping
From: Arnaldo Carvalho de Melo
Date: Thu Jun 04 2026 - 11:06:37 EST
On Thu, Jun 04, 2026 at 11:45:34AM -0300, Arnaldo Carvalho de Melo wrote:
> On Tue, Jun 02, 2026 at 10:41:29AM -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.
> >
> > 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.
> >
> > JUnit XML output and the failure summary report still print the full,
> > untruncated test descriptions.
> >
> > Assisted-by: Gemini-CLI:Google Gemini 3
> > Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
>
> I processed the first 17 patches this one has this problem, please take
> a look:
I pushed what I have to tmp.perf-tools-next, please continue from there.
- Arnaldo
>
> GENSKEL /tmp/build/perf-tools-next/util/bpf_skel/lock_contention.skel.h
> 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:420:40: error: ‘%-*s’ directive output may be truncated writing between 20 and 65505 bytes into a region of size 256 [-Werror=format-truncation=]
> 420 | 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:420:3,
> inlined from ‘print_test_result.isra’ at tests/builtin-test.c:459:3:
> /usr/include/bits/stdio2.h:68:10: note: ‘__builtin___snprintf_chk’ output between 21 and 65506 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 ());
> | ~~~~~~~~~~~~~~~~~
> tests/builtin-test.c: In function ‘print_test_result.isra’:
> tests/builtin-test.c:420:40: error: ‘%-*s’ directive output may be truncated writing between 20 and 65507 bytes into a region of size 256 [-Werror=format-truncation=]
> 420 | snprintf(buf, buf_sz, "%-*s", width, desc);
> | ^~~~
> In function ‘snprintf’,
> inlined from ‘format_test_description’ at tests/builtin-test.c:420:3,
> inlined from ‘print_test_result.isra’ at tests/builtin-test.c:466: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]$
> acme@number:~/git/perf-tools-next$
>
> acme@number:~/git/perf-tools-next$ gcc -v
> Using built-in specs.
> COLLECT_GCC=gcc
> COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/16/lto-wrapper
> OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
> OFFLOAD_TARGET_DEFAULT=1
> Target: x86_64-redhat-linux
> Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,objc,obj-c++,ada,go,d,m2,cobol,algol68,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugzilla.redhat.com/ --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-libstdcxx-backtrace --with-libstdcxx-zoneinfo=/usr/share/zoneinfo --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl=/builddir/build/BUILD/gcc-16.1.1-build/gcc-16.1.1-20260515/obj-x86_64-redhat-linux/isl-install --enable-offload-targets=nvptx-none,amdgcn-amdhsa --enable-offload-defaulted --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-tls=gnu2 --with-arch_32=i686 --build=x86_64-redhat-linux --with-build-config=bootstrap-lto --enable-link-serialization=1 --disable-libssp
> Thread model: posix
> Supported LTO compression algorithms: zlib zstd
> gcc version 16.1.1 20260515 (Red Hat 16.1.1-2) (GCC)
> acme@number:~/git/perf-tools-next$
>
>
> > ---
> > tools/perf/tests/builtin-test.c | 63 +++++++++++++++++++++++++++++----
> > 1 file changed, 56 insertions(+), 7 deletions(-)
> >
> > diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> > index b64fc2204f22..edb4eaa695f1 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,66 @@ 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);
> > +
> > + 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 +749,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 +1025,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 +1089,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.1013.g208068f2d8-goog