]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
perf evlist: Improve default event for s390
authorIan Rogers <irogers@google.com>
Wed, 18 Mar 2026 23:46:00 +0000 (16:46 -0700)
committerNamhyung Kim <namhyung@kernel.org>
Thu, 19 Mar 2026 21:42:46 +0000 (14:42 -0700)
Frame pointer callchains are not supported on s390 and dwarf
callchains are only supported on software events.

Switch the default event from the hardware 'cycles' event to the
software 'cpu-clock' or 'task-clock' on s390 if callchains are
enabled. Move some of the target initialization earlier in builtin-top
and builtin-record, so it is ready for use by evlist__new_default.

If frame pointer callchains are requested on s390 show a
warning. Modify the '-g' option of `perf top` and `perf record` to
default to dwarf callchains on s390.

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-record.c
tools/perf/builtin-top.c
tools/perf/tests/event_update.c
tools/perf/tests/expand-cgroup.c
tools/perf/tests/perf-record.c
tools/perf/tests/topology.c
tools/perf/util/evlist.c
tools/perf/util/evlist.h
tools/perf/util/evsel.c

index 59b8125d1b13a79ed22b9ddde79ff65ab1de0182..3276ffdc3141aa28d37b351022e63b22014a2b2d 100644 (file)
@@ -55,6 +55,7 @@
 #include "asm/bug.h"
 #include "perf.h"
 #include "cputopo.h"
+#include "dwarf-regs.h"
 
 #include <errno.h>
 #include <inttypes.h>
@@ -2995,7 +2996,9 @@ static int record_callchain_opt(const struct option *opt,
                return 0;
        }
 
-       return record_opts__parse_callchain(opt->value, &callchain_param, "fp", unset);
+       return record_opts__parse_callchain(opt->value, &callchain_param,
+                                           EM_HOST != EM_S390 ? "fp" : "dwarf",
+                                           unset);
 }
 
 
@@ -4095,8 +4098,11 @@ int cmd_record(int argc, const char **argv)
 
        perf_debuginfod_setup(&record.debuginfod);
 
-       /* Make system wide (-a) the default target. */
-       if (!argc && target__none(&rec->opts.target))
+       /*
+        * Use system wide (-a) for the default target (ie. when no
+        * workload). User ID filtering also implies system-wide.
+        */
+       if ((!argc && target__none(&rec->opts.target)) || rec->uid_str)
                rec->opts.target.system_wide = true;
 
        if (nr_cgroups && !rec->opts.target.system_wide) {
@@ -4274,7 +4280,8 @@ int cmd_record(int argc, const char **argv)
                record.opts.tail_synthesize = true;
 
        if (rec->evlist->core.nr_entries == 0) {
-               struct evlist *def_evlist = evlist__new_default();
+               struct evlist *def_evlist = evlist__new_default(&rec->opts.target,
+                                                               callchain_param.enabled);
 
                if (!def_evlist)
                        goto out;
@@ -4303,9 +4310,6 @@ int cmd_record(int argc, const char **argv)
                err = parse_uid_filter(rec->evlist, uid);
                if (err)
                        goto out;
-
-               /* User ID filtering implies system wide. */
-               rec->opts.target.system_wide = true;
        }
 
        /* Enable ignoring missing threads when -p option is defined. */
index b6726f4dffb3a2b75bf50699c007e015b020afb9..37950efb28aca8ccea0be1c2e3f65fc69eff1aff 100644 (file)
@@ -56,6 +56,7 @@
 #include "util/debug.h"
 #include "util/ordered-events.h"
 #include "util/pfm.h"
+#include "dwarf-regs.h"
 
 #include <assert.h>
 #include <elf.h>
@@ -1420,7 +1421,7 @@ callchain_opt(const struct option *opt, const char *arg __maybe_unused, int unse
                return 0;
        }
 
-       return parse_callchain_opt(opt, "fp", unset);
+       return parse_callchain_opt(opt, EM_HOST != EM_S390 ? "fp" : "dwarf", unset);
 }
 
 
@@ -1705,8 +1706,17 @@ int cmd_top(int argc, const char **argv)
        if (annotate_check_args() < 0)
                goto out_delete_evlist;
 
+       status = target__validate(target);
+       if (status) {
+               target__strerror(target, status, errbuf, BUFSIZ);
+               ui__warning("%s\n", errbuf);
+       }
+
+       if (target__none(target))
+               target->system_wide = true;
+
        if (!top.evlist->core.nr_entries) {
-               struct evlist *def_evlist = evlist__new_default();
+               struct evlist *def_evlist = evlist__new_default(target, callchain_param.enabled);
 
                if (!def_evlist)
                        goto out_delete_evlist;
@@ -1799,12 +1809,6 @@ int cmd_top(int argc, const char **argv)
                goto out_delete_evlist;
        }
 
-       status = target__validate(target);
-       if (status) {
-               target__strerror(target, status, errbuf, BUFSIZ);
-               ui__warning("%s\n", errbuf);
-       }
-
        if (top.uid_str) {
                uid_t uid = parse_uid(top.uid_str);
 
@@ -1818,9 +1822,6 @@ int cmd_top(int argc, const char **argv)
                        goto out_delete_evlist;
        }
 
-       if (target__none(target))
-               target->system_wide = true;
-
        if (evlist__create_maps(top.evlist, target) < 0) {
                ui__error("Couldn't create thread/CPU maps: %s\n",
                          errno == ENOENT ? "No such process" : str_error_r(errno, errbuf, sizeof(errbuf)));
index cb9e6de2e03385b36bb319c99a7dbaf48f665546..facc65e29f20c2305e07f089462a4b0955f4f21e 100644 (file)
@@ -8,6 +8,7 @@
 #include "header.h"
 #include "machine.h"
 #include "util/synthetic-events.h"
+#include "target.h"
 #include "tool.h"
 #include "tests.h"
 #include "debug.h"
@@ -81,7 +82,8 @@ static int test__event_update(struct test_suite *test __maybe_unused, int subtes
 {
        struct evsel *evsel;
        struct event_name tmp;
-       struct evlist *evlist = evlist__new_default();
+       struct target target = {};
+       struct evlist *evlist = evlist__new_default(&target, /*sample_callchains=*/false);
 
        TEST_ASSERT_VAL("failed to get evlist", evlist);
 
index c7b32a220ca1cd6ccd101853d221d97c68c98d43..dd547f2f77cc832a81c827e859baad6780ef0e27 100644 (file)
@@ -8,6 +8,7 @@
 #include "parse-events.h"
 #include "pmu-events/pmu-events.h"
 #include "pfm.h"
+#include "target.h"
 #include <subcmd/parse-options.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -99,7 +100,8 @@ out: for (i = 0; i < nr_events; i++)
 static int expand_default_events(void)
 {
        int ret;
-       struct evlist *evlist = evlist__new_default();
+       struct target target = {};
+       struct evlist *evlist = evlist__new_default(&target, /*sample_callchains=*/false);
 
        TEST_ASSERT_VAL("failed to get evlist", evlist);
 
index efbd9cd60c6305c972135547ca8e7bc79e97e484..c6e31ab8a6b8f748175e2ecaed67bea79e9188e6 100644 (file)
@@ -84,8 +84,11 @@ static int test__PERF_RECORD(struct test_suite *test __maybe_unused, int subtest
        CPU_ZERO_S(cpu_mask_size, cpu_mask);
 
        perf_sample__init(&sample, /*all=*/false);
-       if (evlist == NULL) /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */
-               evlist = evlist__new_default();
+       if (evlist == NULL) { /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */
+               struct target target = {};
+
+               evlist = evlist__new_default(&target, /*sample_callchains=*/false);
+       }
 
        if (evlist == NULL) {
                pr_debug("Not enough memory to create evlist\n");
index ec01150d208de798c565a6fd4156aec72b54c5b1..a34a7ab19a80c6bdc877a9f1f24d035d862387af 100644 (file)
@@ -9,6 +9,7 @@
 #include "evlist.h"
 #include "debug.h"
 #include "pmus.h"
+#include "target.h"
 #include <linux/err.h>
 
 #define TEMPL "/tmp/perf-test-XXXXXX"
@@ -37,11 +38,12 @@ static int session_write_header(char *path)
                .path = path,
                .mode = PERF_DATA_MODE_WRITE,
        };
+       struct target target = {};
 
        session = perf_session__new(&data, NULL);
        TEST_ASSERT_VAL("can't get session", !IS_ERR(session));
 
-       session->evlist = evlist__new_default();
+       session->evlist = evlist__new_default(&target, /*sample_callchains=*/false);
        TEST_ASSERT_VAL("can't get evlist", session->evlist);
        session->evlist->session = session;
 
index 591bdf0b3e2a10623afd529706244105cc600c4d..c702741a917380b9c45c51576dc56fa3ebf97233 100644 (file)
@@ -13,6 +13,7 @@
 #include "util/mmap.h"
 #include "thread_map.h"
 #include "target.h"
+#include "dwarf-regs.h"
 #include "evlist.h"
 #include "evsel.h"
 #include "record.h"
@@ -98,38 +99,47 @@ struct evlist *evlist__new(void)
        return evlist;
 }
 
-struct evlist *evlist__new_default(void)
+struct evlist *evlist__new_default(const struct target *target, bool sample_callchains)
 {
        struct evlist *evlist = evlist__new();
        bool can_profile_kernel;
        struct perf_pmu *pmu = NULL;
+       struct evsel *evsel;
+       char buf[256];
+       int err;
 
        if (!evlist)
                return NULL;
 
        can_profile_kernel = perf_event_paranoid_check(1);
 
-       while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
-               char buf[256];
-               int err;
-
-               snprintf(buf, sizeof(buf), "%s/cycles/%s", pmu->name,
+       if (EM_HOST == EM_S390 && sample_callchains) {
+               snprintf(buf, sizeof(buf), "software/%s/%s",
+                        target__has_cpu(target) ? "cpu-clock" : "task-clock",
                         can_profile_kernel ? "P" : "Pu");
                err = parse_event(evlist, buf);
-               if (err) {
-                       evlist__delete(evlist);
-                       return NULL;
+               if (err)
+                       goto out_err;
+       } else {
+               while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
+                       snprintf(buf, sizeof(buf), "%s/cycles/%s", pmu->name,
+                               can_profile_kernel ? "P" : "Pu");
+                       err = parse_event(evlist, buf);
+                       if (err)
+                               goto out_err;
                }
        }
 
+       /* If there is only 1 event a sample identifier isn't necessary. */
        if (evlist->core.nr_entries > 1) {
-               struct evsel *evsel;
-
                evlist__for_each_entry(evlist, evsel)
                        evsel__set_sample_id(evsel, /*can_sample_identifier=*/false);
        }
 
        return evlist;
+out_err:
+       evlist__delete(evlist);
+       return NULL;
 }
 
 struct evlist *evlist__new_dummy(void)
index d17c3b57a409c34d8b08f35def539ec627f16d19..e507f5f20ef61b9e0ad040695d93dd5143d10419 100644 (file)
@@ -104,7 +104,7 @@ struct evsel_str_handler {
 };
 
 struct evlist *evlist__new(void);
-struct evlist *evlist__new_default(void);
+struct evlist *evlist__new_default(const struct target *target, bool sample_callchains);
 struct evlist *evlist__new_dummy(void);
 void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus,
                  struct perf_thread_map *threads);
index 54c8922a8e473c08a30e8a4a574d3d3b03083113..5a294595a677620eb1866f94cb765c1911fd8afb 100644 (file)
@@ -1021,6 +1021,11 @@ static void __evsel__config_callchain(struct evsel *evsel, const struct record_o
        bool function = evsel__is_function_event(evsel);
        struct perf_event_attr *attr = &evsel->core.attr;
 
+       if (EM_HOST == EM_S390 && param->record_mode == CALLCHAIN_FP) {
+               pr_warning_once(
+                       "Framepointer unwinding lacks kernel support. Use '--call-graph dwarf'\n");
+       }
+
        evsel__set_sample_bit(evsel, CALLCHAIN);
 
        attr->sample_max_stack = param->max_stack;