]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
perf stat: Improve handling of termination by signal
authorIan Rogers <irogers@google.com>
Wed, 3 Dec 2025 21:47:06 +0000 (13:47 -0800)
committerNamhyung Kim <namhyung@kernel.org>
Thu, 4 Dec 2025 23:44:39 +0000 (15:44 -0800)
When interrupting perf stat in repeat mode with a signal the signal is
passed to the child process but the repeat doesn't terminate:
```
$ perf stat -v --null --repeat 10 sleep 1
Control descriptor is not initialized
[ perf stat: executing run #1 ... ]
[ perf stat: executing run #2 ... ]
^Csleep: Interrupt
[ perf stat: executing run #3 ... ]
[ perf stat: executing run #4 ... ]
[ perf stat: executing run #5 ... ]
[ perf stat: executing run #6 ... ]
[ perf stat: executing run #7 ... ]
[ perf stat: executing run #8 ... ]
[ perf stat: executing run #9 ... ]
[ perf stat: executing run #10 ... ]

 Performance counter stats for 'sleep 1' (10 runs):

            0.9500 +- 0.0512 seconds time elapsed  ( +-  5.39% )

0.01user 0.02system 0:09.53elapsed 0%CPU (0avgtext+0avgdata 18940maxresident)k
29944inputs+0outputs (0major+2629minor)pagefaults 0swaps
```

Terminate the repeated run and give a reasonable exit value:
```
$ perf stat -v --null --repeat 10 sleep 1
Control descriptor is not initialized
[ perf stat: executing run #1 ... ]
[ perf stat: executing run #2 ... ]
[ perf stat: executing run #3 ... ]
^Csleep: Interrupt

 Performance counter stats for 'sleep 1' (10 runs):

             0.680 +- 0.321 seconds time elapsed  ( +- 47.16% )

Command exited with non-zero status 130
0.00user 0.01system 0:02.05elapsed 0%CPU (0avgtext+0avgdata 70688maxresident)k
0inputs+0outputs (0major+5002minor)pagefaults 0swaps
```

Note, this also changes the exit value for non-repeat runs when
interrupted by a signal.

Reported-by: Ingo Molnar <mingo@kernel.org>
Closes: https://lore.kernel.org/lkml/aS5wjmbAM9ka3M2g@gmail.com/
Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
tools/perf/builtin-stat.c

index bd3c3de8d2009d11d5129a78344b645d0c8fda41..ab40d85fb1259f4e0aed6502570f4b22cf33b46d 100644 (file)
@@ -1007,10 +1007,20 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
                        goto err_out;
                }
 
-               if (WIFSIGNALED(status))
+               if (WIFSIGNALED(status)) {
+                       /*
+                        * We want to indicate failure to stop a repeat run,
+                        * hence negative. We want the value to be the exit code
+                        * of perf, which for termination by a signal is 128
+                        * plus the signal number.
+                        */
+                       err = 0 - (128 + WTERMSIG(status));
                        psignal(WTERMSIG(status), argv[0]);
+               } else {
+                       err = WEXITSTATUS(status);
+               }
        } else {
-               status = dispatch_events(forks, timeout, interval, &times);
+               err = dispatch_events(forks, timeout, interval, &times);
        }
 
        disable_counters();
@@ -1050,7 +1060,7 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
        if (!STAT_RECORD)
                evlist__close(evsel_list);
 
-       return WEXITSTATUS(status);
+       return err;
 
 err_out:
        if (forks)
@@ -2969,7 +2979,7 @@ int cmd_stat(int argc, const char **argv)
                        evlist__reset_prev_raw_counts(evsel_list);
 
                status = run_perf_stat(argc, argv, run_idx);
-               if (status == -1)
+               if (status < 0)
                        break;
 
                if (forever && !interval) {
@@ -3039,5 +3049,6 @@ out:
 
        evlist__close_control(stat_config.ctl_fd, stat_config.ctl_fd_ack, &stat_config.ctl_fd_close);
 
-       return status;
+       /* Only the low byte of status becomes the exit code. */
+       return abs(status);
 }