From: Ian Rogers Date: Wed, 18 Mar 2026 23:46:00 +0000 (-0700) Subject: perf evlist: Improve default event for s390 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ca76fb67ebdd5e1a30a242d06dc096fddd670734;p=thirdparty%2Fkernel%2Flinux.git perf evlist: Improve default event for s390 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 Tested-by: Thomas Richter Signed-off-by: Namhyung Kim --- diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 59b8125d1b13a..3276ffdc3141a 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -55,6 +55,7 @@ #include "asm/bug.h" #include "perf.h" #include "cputopo.h" +#include "dwarf-regs.h" #include #include @@ -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. */ diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index b6726f4dffb3a..37950efb28aca 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -56,6 +56,7 @@ #include "util/debug.h" #include "util/ordered-events.h" #include "util/pfm.h" +#include "dwarf-regs.h" #include #include @@ -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))); diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index cb9e6de2e0338..facc65e29f20c 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -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); diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c index c7b32a220ca1c..dd547f2f77cc8 100644 --- a/tools/perf/tests/expand-cgroup.c +++ b/tools/perf/tests/expand-cgroup.c @@ -8,6 +8,7 @@ #include "parse-events.h" #include "pmu-events/pmu-events.h" #include "pfm.h" +#include "target.h" #include #include #include @@ -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); diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index efbd9cd60c630..c6e31ab8a6b8f 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -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"); diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index ec01150d208de..a34a7ab19a80c 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -9,6 +9,7 @@ #include "evlist.h" #include "debug.h" #include "pmus.h" +#include "target.h" #include #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; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 591bdf0b3e2a1..c702741a91738 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -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) diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index d17c3b57a409c..e507f5f20ef61 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -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); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 54c8922a8e473..5a294595a6776 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -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;