From 924a221581db557f6c61b93fe5f25893a74bc3ea Mon Sep 17 00:00:00 2001 From: "Shawn M. Chapla" Date: Thu, 26 May 2022 16:14:47 -0400 Subject: perf data convert: Prefer sampled CPU when exporting JSON When CPU has been explicitly sampled (via --sample-cpu), prefer this sampled value over the thread CPU value when exporting to JSON. Signed-off-by: Shawn M. Chapla Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20220526201506.2028281-1-schapla@codeweavers.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data-convert-json.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index f1ab6edba446..613d6ae82663 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -149,6 +149,7 @@ static int process_sample_event(struct perf_tool *tool, struct convert_json *c = container_of(tool, struct convert_json, tool); FILE *out = c->out; struct addr_location al, tal; + u64 sample_type = __evlist__combined_sample_type(evsel->evlist); u8 cpumode = PERF_RECORD_MISC_USER; if (machine__resolve(machine, &al, sample) < 0) { @@ -168,7 +169,9 @@ static int process_sample_event(struct perf_tool *tool, output_json_key_format(out, true, 3, "pid", "%i", al.thread->pid_); output_json_key_format(out, true, 3, "tid", "%i", al.thread->tid); - if (al.thread->cpu >= 0) + if ((sample_type & PERF_SAMPLE_CPU)) + output_json_key_format(out, true, 3, "cpu", "%i", sample->cpu); + else if (al.thread->cpu >= 0) output_json_key_format(out, true, 3, "cpu", "%i", al.thread->cpu); output_json_key_string(out, true, 3, "comm", thread__comm_str(al.thread)); -- cgit v1.2.3 From f42c0ce573df79d1b8bd169008c994dcdd43585a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 10 Jun 2022 14:33:12 +0300 Subject: perf record: Always get text_poke events with --kcore option kcore provides a copy of the running kernel including any modified code. A trace that benefits from that also benefits from text_poke events, so enable them. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lore.kernel.org/r/20220610113316.6682-2-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 9a71f0330137..3959a1b86afb 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -3805,6 +3805,9 @@ int cmd_record(int argc, const char **argv) goto out_opts; } + if (rec->opts.kcore) + rec->opts.text_poke = true; + if (rec->opts.kcore || record__threads_enabled(rec)) rec->data.is_dir = true; -- cgit v1.2.3 From 6b080312fc821658a479e74bdb0c3f7d9ac5838f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 10 Jun 2022 14:33:13 +0300 Subject: perf record: Always record id index In preparation for recording sideband events in a virtual machine guest so that they can be injected into a host perf.data file. Adjust the logic so that if there are IDs then the id index is recorded. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lore.kernel.org/r/20220610113316.6682-3-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 12 +++++------- tools/perf/util/synthetic-events.c | 7 +++++-- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 3959a1b86afb..00c2a6cdf1be 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1834,13 +1834,11 @@ static int record__synthesize(struct record *rec, bool tail) goto out; /* Synthesize id_index before auxtrace_info */ - if (rec->opts.auxtrace_sample_mode || rec->opts.full_auxtrace) { - err = perf_event__synthesize_id_index(tool, - process_synthesized_event, - session->evlist, machine); - if (err) - goto out; - } + err = perf_event__synthesize_id_index(tool, + process_synthesized_event, + session->evlist, machine); + if (err) + goto out; if (rec->opts.full_auxtrace) { err = perf_event__synthesize_auxtrace_info(rec->itr, tool, diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 27acdc5e5723..d75074486a55 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -1719,14 +1719,17 @@ int perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_ size_t nr = 0, i = 0, sz, max_nr, n; int err; - pr_debug2("Synthesizing id index\n"); - max_nr = (UINT16_MAX - sizeof(struct perf_record_id_index)) / sizeof(struct id_index_entry); evlist__for_each_entry(evlist, evsel) nr += evsel->core.ids; + if (!nr) + return 0; + + pr_debug2("Synthesizing id index\n"); + n = nr > max_nr ? max_nr : nr; sz = sizeof(struct perf_record_id_index) + n * sizeof(struct id_index_entry); ev = zalloc(sz); -- cgit v1.2.3 From 61110883a02090cb5fd1f890978e238cc99f0164 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 15 Jun 2022 08:25:11 +0300 Subject: perf record: Add new option to sample identifier In preparation for recording sideband events in a virtual machine guest so that they can be injected into a host perf.data file. Add an option to always include sample type PERF_SAMPLE_IDENTIFIER. Committer testing: # perf record sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.020 MB perf.data (7 samples) ] # perf evlist -v cycles: size: 128, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD, read_format: ID, disabled: 1, inherit: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 # # # perf record --sample-identifier sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.022 MB perf.data (7 samples) ] # perf evlist -v cycles: size: 128, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|PERIOD|IDENTIFIER, read_format: ID, disabled: 1, inherit: 1, mmap: 1, comm: 1, freq: 1, enable_on_exec: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 # Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lore.kernel.org/r/20220615052511.4441-1-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 5 +++++ tools/perf/builtin-record.c | 2 ++ tools/perf/util/record.c | 2 +- tools/perf/util/record.h | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index cf8ad50f3de1..6bd6d07021ba 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -313,6 +313,11 @@ OPTIONS --sample-cpu:: Record the sample cpu. +--sample-identifier:: + Record the sample identifier i.e. PERF_SAMPLE_IDENTIFIER bit set in + the sample_type member of the struct perf_event_attr argument to the + perf_event_open system call. + -n:: --no-samples:: Don't sample. diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 00c2a6cdf1be..40dca1fba4e3 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -3191,6 +3191,8 @@ static struct option __record_options[] = { OPT_BOOLEAN(0, "code-page-size", &record.opts.sample_code_page_size, "Record the sampled code address (ip) page size"), OPT_BOOLEAN(0, "sample-cpu", &record.opts.sample_cpu, "Record the sample cpu"), + OPT_BOOLEAN(0, "sample-identifier", &record.opts.sample_identifier, + "Record the sample identifier"), OPT_BOOLEAN_SET('T', "timestamp", &record.opts.sample_time, &record.opts.sample_time_set, "Record the sample timestamps"), diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 5b09ecbb05dc..b529636ab3ea 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -121,7 +121,7 @@ void evlist__config(struct evlist *evlist, struct record_opts *opts, struct call evlist__for_each_entry(evlist, evsel) evsel__config_leader_sampling(evsel, evlist); - if (opts->full_auxtrace) { + if (opts->full_auxtrace || opts->sample_identifier) { /* * Need to be able to synthesize and parse selected events with * arbitrary sample types, which requires always being able to diff --git a/tools/perf/util/record.h b/tools/perf/util/record.h index be9a957501f4..4269e916f450 100644 --- a/tools/perf/util/record.h +++ b/tools/perf/util/record.h @@ -28,6 +28,7 @@ struct record_opts { bool sample_time; bool sample_time_set; bool sample_cpu; + bool sample_identifier; bool period; bool period_set; bool running_time; -- cgit v1.2.3 From 3812d2987733c5a00e103be4e23d63ec9342043a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 10 Jun 2022 14:33:15 +0300 Subject: perf record: Add finished init event In preparation for recording sideband events in a virtual machine guest so that they can be injected into a host perf.data file. This is needed to enable injecting events after the initial synthesized user events (that have an all zero id sample) but before regular events. Committer notes: Add entry about PERF_RECORD_FINISHED_INIT to tools/perf/Documentation/perf.data-file-format.txt. Committer testing: Before: # perf report -D | grep FINISHED 0 0x5910 [0x8]: PERF_RECORD_FINISHED_ROUND FINISHED_ROUND events: 1 ( 0.5%) # After: # perf record -- sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.020 MB perf.data (7 samples) ] # perf report -D | grep FINISHED 0 0x5068 [0x8]: PERF_RECORD_FINISHED_INIT: unhandled! 0 0x5390 [0x8]: PERF_RECORD_FINISHED_ROUND FINISHED_ROUND events: 1 ( 0.5%) FINISHED_INIT events: 1 ( 0.5%) # Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lore.kernel.org/r/20220610113316.6682-5-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/include/perf/event.h | 1 + tools/perf/Documentation/perf.data-file-format.txt | 10 ++++++++ tools/perf/builtin-inject.c | 1 + tools/perf/builtin-record.c | 27 ++++++++++++++++++++++ tools/perf/util/event.c | 1 + tools/perf/util/session.c | 4 ++++ tools/perf/util/tool.h | 3 ++- 7 files changed, 46 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h index e7758707cadd..9f7ca070da87 100644 --- a/tools/lib/perf/include/perf/event.h +++ b/tools/lib/perf/include/perf/event.h @@ -389,6 +389,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_TIME_CONV = 79, PERF_RECORD_HEADER_FEATURE = 80, PERF_RECORD_COMPRESSED = 81, + PERF_RECORD_FINISHED_INIT = 82, PERF_RECORD_HEADER_MAX }; diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt index f56d0e0fbff6..bc9f0aa113d8 100644 --- a/tools/perf/Documentation/perf.data-file-format.txt +++ b/tools/perf/Documentation/perf.data-file-format.txt @@ -607,6 +607,16 @@ struct compressed_event { char data[]; }; + PERF_RECORD_FINISHED_INIT = 82, + +Marks the end of records for the system, pre-existing threads in system wide +sessions, etc. Those are the ones prefixed PERF_RECORD_USER_*. + +This is used, for instance, to 'perf inject' events after init and before +regular events, those emitted by the kernel, to support combining guest and +host records. + + The header is followed by compressed data frame that can be decompressed into array of perf trace records. The size of the entire compressed event record including the header is limited by the max value of header.size. diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index a75bf11585b5..42e2918fd1cc 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -1059,6 +1059,7 @@ int cmd_inject(int argc, const char **argv) .stat = perf_event__repipe_op2_synth, .stat_round = perf_event__repipe_op2_synth, .feature = perf_event__repipe_op2_synth, + .finished_init = perf_event__repipe_op2_synth, .compressed = perf_event__repipe_op4_synth, .auxtrace = perf_event__repipe_auxtrace, }, diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 40dca1fba4e3..cf5c5379ceaa 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1388,6 +1388,11 @@ static struct perf_event_header finished_round_event = { .type = PERF_RECORD_FINISHED_ROUND, }; +static struct perf_event_header finished_init_event = { + .size = sizeof(struct perf_event_header), + .type = PERF_RECORD_FINISHED_INIT, +}; + static void record__adjust_affinity(struct record *rec, struct mmap *map) { if (rec->opts.affinity != PERF_AFFINITY_SYS && @@ -1696,6 +1701,14 @@ static int record__synthesize_workload(struct record *rec, bool tail) return err; } +static int write_finished_init(struct record *rec, bool tail) +{ + if (rec->opts.tail_synthesize != tail) + return 0; + + return record__write(rec, NULL, &finished_init_event, sizeof(finished_init_event)); +} + static int record__synthesize(struct record *rec, bool tail); static int @@ -1710,6 +1723,8 @@ record__switch_output(struct record *rec, bool at_exit) record__aio_mmap_read_sync(rec); + write_finished_init(rec, true); + record__synthesize(rec, true); if (target__none(&rec->opts.target)) record__synthesize_workload(rec, true); @@ -1764,6 +1779,7 @@ record__switch_output(struct record *rec, bool at_exit) */ if (target__none(&rec->opts.target)) record__synthesize_workload(rec, false); + write_finished_init(rec, false); } return fd; } @@ -2419,6 +2435,15 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) trigger_ready(&auxtrace_snapshot_trigger); trigger_ready(&switch_output_trigger); perf_hooks__invoke_record_start(); + + /* + * Must write FINISHED_INIT so it will be seen after all other + * synthesized user events, but before any regular events. + */ + err = write_finished_init(rec, false); + if (err < 0) + goto out_child; + for (;;) { unsigned long long hits = thread->samples; @@ -2563,6 +2588,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", record__waking(rec)); + write_finished_init(rec, true); + if (target__none(&rec->opts.target)) record__synthesize_workload(rec, true); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 0476bb3a4188..1fa14598b916 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -76,6 +76,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_TIME_CONV] = "TIME_CONV", [PERF_RECORD_HEADER_FEATURE] = "FEATURE", [PERF_RECORD_COMPRESSED] = "COMPRESSED", + [PERF_RECORD_FINISHED_INIT] = "FINISHED_INIT", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0aa818977d2b..37f833c3c81b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -562,6 +562,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->feature = process_event_op2_stub; if (tool->compressed == NULL) tool->compressed = perf_session__process_compressed_event; + if (tool->finished_init == NULL) + tool->finished_init = process_event_op2_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -1706,6 +1708,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, if (err) dump_event(session->evlist, event, file_offset, &sample, file_path); return err; + case PERF_RECORD_FINISHED_INIT: + return tool->finished_init(session, event); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index f2352dba1875..c957fb849ac6 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -76,7 +76,8 @@ struct perf_tool { stat_config, stat, stat_round, - feature; + feature, + finished_init; event_op4 compressed; event_op3 auxtrace; bool ordered_events; -- cgit v1.2.3 From 52f28b7bac75da9b8508f17438c9a8d83ab48e5d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 10 Jun 2022 14:33:16 +0300 Subject: perf script: Add some missing event dumps When the -D option is used, the details of thread-map, cpu-map and event-update events are not currently dumped. Add prints so that they are. Example: # perf record --kcore sleep 0.1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.021 MB perf.data (7 samples) ] # perf script -D | grep 'THREAD\|CPU' 0 0x4950 [0x28]: PERF_RECORD_THREAD_MAP nr: 1 thread: 35116 0 0x4978 [0x20]: PERF_RECORD_CPU_MAP: 0-7 # perf script -D | grep -A4 'UPDATE' 0 0x4920 [0x30]: PERF_RECORD_EVENT_UPDATE ... id: 147 ... 0-7 Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lore.kernel.org/r/20220610113316.6682-6-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 6 ++++++ tools/perf/util/header.c | 3 +++ 2 files changed, 9 insertions(+) (limited to 'tools') diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c689054002cc..7cf21ab16f4f 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -3633,6 +3633,9 @@ int process_thread_map_event(struct perf_session *session, struct perf_tool *tool = session->tool; struct perf_script *script = container_of(tool, struct perf_script, tool); + if (dump_trace) + perf_event__fprintf_thread_map(event, stdout); + if (script->threads) { pr_warning("Extra thread map event, ignoring.\n"); return 0; @@ -3652,6 +3655,9 @@ int process_cpu_map_event(struct perf_session *session, struct perf_tool *tool = session->tool; struct perf_script *script = container_of(tool, struct perf_script, tool); + if (dump_trace) + perf_event__fprintf_cpu_map(event, stdout); + if (script->cpus) { pr_warning("Extra cpu map event, ignoring.\n"); return 0; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 53332da100e8..de5b7a023e9e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -4349,6 +4349,9 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, struct evsel *evsel; struct perf_cpu_map *map; + if (dump_trace) + perf_event__fprintf_event_update(event, stdout); + if (!pevlist || *pevlist == NULL) return -EINVAL; -- cgit v1.2.3 From 9ab95b0b15a092abb7c3309f3580f572a041f9ab Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Sat, 4 Jun 2022 10:15:12 +0530 Subject: perf record ibs: Warn about sampling period skew Samples without an L3 miss are discarded and counter is reset with random value (between 1-15 for fetch PMU and 1-127 for op PMU) when IBS L3 miss filtering is enabled. This causes a sampling period skew but there is no way to reconstruct aggregated sampling period. So print a warning at perf record if user sets l3missonly=1. Ex: # perf record -c 10000 -C 0 -e ibs_op/l3missonly=1/ WARNING: Hw internally resets sampling period when L3 Miss Filtering is enabled and tagged operation does not cause L3 Miss. This causes sampling period skew. Signed-off-by: Ravi Bangoria Acked-by: Ian Rogers Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Andi Kleen Cc: Borislav Petkov Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Kim Phillips Cc: Leo Yan Cc: Mark Rutland Cc: Peter Zijlstra Cc: Robert Richter Cc: Sandipan Das Cc: Santosh Shukla Cc: Stephane Eranian Cc: Thomas Gleixner Cc: like.xu.linux@gmail.com Cc: x86@kernel.org Link: http://lore.kernel.org/lkml/20220604044519.594-2-ravi.bangoria@amd.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/util/evsel.c | 52 ++++++++++++++++++++++++++++++++++++++++ tools/perf/util/evsel.c | 7 ++++++ tools/perf/util/evsel.h | 1 + 3 files changed, 60 insertions(+) (limited to 'tools') diff --git a/tools/perf/arch/x86/util/evsel.c b/tools/perf/arch/x86/util/evsel.c index 3501399cef35..882c1a8c1ded 100644 --- a/tools/perf/arch/x86/util/evsel.c +++ b/tools/perf/arch/x86/util/evsel.c @@ -6,6 +6,10 @@ #include "util/pmu.h" #include "linux/string.h" #include "evsel.h" +#include "util/debug.h" + +#define IBS_FETCH_L3MISSONLY (1ULL << 59) +#define IBS_OP_L3MISSONLY (1ULL << 16) void arch_evsel__set_sample_weight(struct evsel *evsel) { @@ -61,3 +65,51 @@ bool arch_evsel__must_be_in_group(const struct evsel *evsel) (strcasestr(evsel->name, "slots") || strcasestr(evsel->name, "topdown")); } + +static void ibs_l3miss_warn(void) +{ + pr_warning( +"WARNING: Hw internally resets sampling period when L3 Miss Filtering is enabled\n" +"and tagged operation does not cause L3 Miss. This causes sampling period skew.\n"); +} + +void arch__post_evsel_config(struct evsel *evsel, struct perf_event_attr *attr) +{ + struct perf_pmu *evsel_pmu, *ibs_fetch_pmu, *ibs_op_pmu; + static int warned_once; + /* 0: Uninitialized, 1: Yes, -1: No */ + static int is_amd; + + if (warned_once || is_amd == -1) + return; + + if (!is_amd) { + struct perf_env *env = evsel__env(evsel); + + if (!perf_env__cpuid(env) || !env->cpuid || + !strstarts(env->cpuid, "AuthenticAMD")) { + is_amd = -1; + return; + } + is_amd = 1; + } + + evsel_pmu = evsel__find_pmu(evsel); + if (!evsel_pmu) + return; + + ibs_fetch_pmu = perf_pmu__find("ibs_fetch"); + ibs_op_pmu = perf_pmu__find("ibs_op"); + + if (ibs_fetch_pmu && ibs_fetch_pmu->type == evsel_pmu->type) { + if (attr->config & IBS_FETCH_L3MISSONLY) { + ibs_l3miss_warn(); + warned_once = 1; + } + } else if (ibs_op_pmu && ibs_op_pmu->type == evsel_pmu->type) { + if (attr->config & IBS_OP_L3MISSONLY) { + ibs_l3miss_warn(); + warned_once = 1; + } + } +} diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index ce499c5da8d7..8fea51a9cd90 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1091,6 +1091,11 @@ void __weak arch_evsel__fixup_new_cycles(struct perf_event_attr *attr __maybe_un { } +void __weak arch__post_evsel_config(struct evsel *evsel __maybe_unused, + struct perf_event_attr *attr __maybe_unused) +{ +} + static void evsel__set_default_freq_period(struct record_opts *opts, struct perf_event_attr *attr) { @@ -1366,6 +1371,8 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, */ if (evsel__is_dummy_event(evsel)) evsel__reset_sample_bit(evsel, BRANCH_STACK); + + arch__post_evsel_config(evsel, attr); } int evsel__set_filter(struct evsel *evsel, const char *filter) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 73ea48e94079..92bed8e2f7d8 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -297,6 +297,7 @@ void evsel__set_sample_id(struct evsel *evsel, bool use_sample_identifier); void arch_evsel__set_sample_weight(struct evsel *evsel); void arch_evsel__fixup_new_cycles(struct perf_event_attr *attr); +void arch__post_evsel_config(struct evsel *evsel, struct perf_event_attr *attr); int evsel__set_filter(struct evsel *evsel, const char *filter); int evsel__append_tp_filter(struct evsel *evsel, const char *filter); -- cgit v1.2.3 From 3339ec44be7f9963c82d5d21e163f16f96e5ca58 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Sat, 4 Jun 2022 10:15:13 +0530 Subject: perf pmu: Parse pmu caps sysfs only once In addition to returning nr_caps, cache it locally in struct perf_pmu. Similarly, cache status of whether caps sysfs has already been parsed or not. These will help to avoid parsing sysfs every time the function gets called. Reviewed-by: Kan Liang Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Andi Kleen Cc: Borislav Petkov Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kim Phillips Cc: Leo Yan Cc: Mark Rutland Cc: Peter Zijlstra Cc: Robert Richter Cc: Sandipan Das Cc: Santosh Shukla Cc: Stephane Eranian Cc: Thomas Gleixner Cc: like.xu.linux@gmail.com Cc: x86@kernel.org Link: https://lore.kernel.org/r/20220604044519.594-3-ravi.bangoria@amd.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/pmu.c | 15 +++++++++++---- tools/perf/util/pmu.h | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 9a1c7e63e663..0112e1c36418 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1890,7 +1890,11 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu) const char *sysfs = sysfs__mountpoint(); DIR *caps_dir; struct dirent *evt_ent; - int nr_caps = 0; + + if (pmu->caps_initialized) + return pmu->nr_caps; + + pmu->nr_caps = 0; if (!sysfs) return -1; @@ -1898,8 +1902,10 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu) snprintf(caps_path, PATH_MAX, "%s" EVENT_SOURCE_DEVICE_PATH "%s/caps", sysfs, pmu->name); - if (stat(caps_path, &st) < 0) + if (stat(caps_path, &st) < 0) { + pmu->caps_initialized = true; return 0; /* no error if caps does not exist */ + } caps_dir = opendir(caps_path); if (!caps_dir) @@ -1926,13 +1932,14 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu) continue; } - nr_caps++; + pmu->nr_caps++; fclose(file); } closedir(caps_dir); - return nr_caps; + pmu->caps_initialized = true; + return pmu->nr_caps; } void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config, diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 541889fa9f9c..4b45fd8da5a3 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -46,6 +46,8 @@ struct perf_pmu { struct perf_cpu_map *cpus; struct list_head format; /* HEAD struct perf_pmu_format -> list */ struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */ + bool caps_initialized; + u32 nr_caps; struct list_head caps; /* HEAD struct perf_pmu_caps -> list */ struct list_head list; /* ELEM */ struct list_head hybrid_list; -- cgit v1.2.3 From 2a12bef413bb278db202ecb6adbd9c18dec4b260 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Sat, 4 Jun 2022 10:15:14 +0530 Subject: perf header: Pass "cpu" pmu name while printing caps Avoid unnecessary conditional code to check if pmu name is NULL or not by passing "cpu" pmu name to the printing function. Reviewed-by: Kan Liang Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Andi Kleen Cc: Borislav Petkov Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kim Phillips Cc: Leo Yan Cc: Mark Rutland Cc: Peter Zijlstra Cc: Robert Richter Cc: Sandipan Das Cc: Santosh Shukla Cc: Stephane Eranian Cc: Thomas Gleixner Cc: like.xu.linux@gmail.com Cc: x86@kernel.org Link: https://lore.kernel.org/r/20220604044519.594-4-ravi.bangoria@amd.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index de5b7a023e9e..8c0b85e3851c 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2058,17 +2058,11 @@ static void print_per_cpu_pmu_caps(FILE *fp, int nr_caps, char *cpu_pmu_caps, char *str, buf[128]; if (!nr_caps) { - if (!pmu_name) - fprintf(fp, "# cpu pmu capabilities: not available\n"); - else - fprintf(fp, "# %s pmu capabilities: not available\n", pmu_name); + fprintf(fp, "# %s pmu capabilities: not available\n", pmu_name); return; } - if (!pmu_name) - scnprintf(buf, sizeof(buf), "# cpu pmu capabilities: "); - else - scnprintf(buf, sizeof(buf), "# %s pmu capabilities: ", pmu_name); + scnprintf(buf, sizeof(buf), "# %s pmu capabilities: ", pmu_name); delimiter = buf; @@ -2085,7 +2079,7 @@ static void print_per_cpu_pmu_caps(FILE *fp, int nr_caps, char *cpu_pmu_caps, static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp) { print_per_cpu_pmu_caps(fp, ff->ph->env.nr_cpu_pmu_caps, - ff->ph->env.cpu_pmu_caps, NULL); + ff->ph->env.cpu_pmu_caps, (char *)"cpu"); } static void print_hybrid_cpu_pmu_caps(struct feat_fd *ff, FILE *fp) -- cgit v1.2.3 From ff34eaa820231dfaecca9c048637bd86ba294bc7 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Sat, 4 Jun 2022 10:15:15 +0530 Subject: perf header: Store PMU caps in an array of strings Currently all capabilities are stored in a single string separated by NULL character. Instead, store them in an array which makes searching of capability easier. Reviewed-by: Kan Liang Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Andi Kleen Cc: Borislav Petkov Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kim Phillips Cc: Leo Yan Cc: Mark Rutland Cc: Peter Zijlstra Cc: Robert Richter Cc: Sandipan Das Cc: Santosh Shukla Cc: Stephane Eranian Cc: Thomas Gleixner Cc: like.xu.linux@gmail.com Cc: x86@kernel.org Link: https://lore.kernel.org/r/20220604044519.594-5-ravi.bangoria@amd.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/env.c | 6 ++++- tools/perf/util/env.h | 4 +-- tools/perf/util/header.c | 63 ++++++++++++++++++++++++++---------------------- 3 files changed, 41 insertions(+), 32 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 579e44c59914..7d3aeb2e4622 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -179,7 +179,7 @@ static void perf_env__purge_bpf(struct perf_env *env __maybe_unused) void perf_env__exit(struct perf_env *env) { - int i; + int i, j; perf_env__purge_bpf(env); perf_env__purge_cgroups(env); @@ -196,6 +196,8 @@ void perf_env__exit(struct perf_env *env) zfree(&env->sibling_threads); zfree(&env->pmu_mappings); zfree(&env->cpu); + for (i = 0; i < env->nr_cpu_pmu_caps; i++) + zfree(&env->cpu_pmu_caps[i]); zfree(&env->cpu_pmu_caps); zfree(&env->numa_map); @@ -218,6 +220,8 @@ void perf_env__exit(struct perf_env *env) zfree(&env->hybrid_nodes); for (i = 0; i < env->nr_hybrid_cpc_nodes; i++) { + for (j = 0; j < env->hybrid_cpc_nodes[i].nr_cpu_pmu_caps; j++) + zfree(&env->hybrid_cpc_nodes[i].cpu_pmu_caps[j]); zfree(&env->hybrid_cpc_nodes[i].cpu_pmu_caps); zfree(&env->hybrid_cpc_nodes[i].pmu_name); } diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index a3541f98e1fc..43aab59f7322 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -46,7 +46,7 @@ struct hybrid_node { struct hybrid_cpc_node { int nr_cpu_pmu_caps; unsigned int max_branches; - char *cpu_pmu_caps; + char **cpu_pmu_caps; char *pmu_name; }; @@ -81,7 +81,7 @@ struct perf_env { char *sibling_dies; char *sibling_threads; char *pmu_mappings; - char *cpu_pmu_caps; + char **cpu_pmu_caps; struct cpu_topology_map *cpu; struct cpu_cache_level *caches; int caches_cnt; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 8c0b85e3851c..c9d1f764aa68 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2051,26 +2051,21 @@ static void print_compressed(struct feat_fd *ff, FILE *fp) ff->ph->env.comp_level, ff->ph->env.comp_ratio); } -static void print_per_cpu_pmu_caps(FILE *fp, int nr_caps, char *cpu_pmu_caps, +static void print_per_cpu_pmu_caps(FILE *fp, int nr_caps, char **cpu_pmu_caps, char *pmu_name) { - const char *delimiter; - char *str, buf[128]; + const char *delimiter = ""; + int i; if (!nr_caps) { fprintf(fp, "# %s pmu capabilities: not available\n", pmu_name); return; } - scnprintf(buf, sizeof(buf), "# %s pmu capabilities: ", pmu_name); - - delimiter = buf; - - str = cpu_pmu_caps; - while (nr_caps--) { - fprintf(fp, "%s%s", delimiter, str); + fprintf(fp, "# %s pmu capabilities: ", pmu_name); + for (i = 0; i < nr_caps; i++) { + fprintf(fp, "%s%s", delimiter, cpu_pmu_caps[i]); delimiter = ", "; - str += strlen(str) + 1; } fprintf(fp, "\n"); @@ -3202,27 +3197,26 @@ static int process_compressed(struct feat_fd *ff, } static int process_per_cpu_pmu_caps(struct feat_fd *ff, int *nr_cpu_pmu_caps, - char **cpu_pmu_caps, + char ***cpu_pmu_caps, unsigned int *max_branches) { - char *name, *value; - struct strbuf sb; - u32 nr_caps; + char *name, *value, *ptr; + u32 nr_caps, i; + + *nr_cpu_pmu_caps = 0; + *cpu_pmu_caps = NULL; if (do_read_u32(ff, &nr_caps)) return -1; - if (!nr_caps) { - pr_debug("cpu pmu capabilities not available\n"); + if (!nr_caps) return 0; - } - - *nr_cpu_pmu_caps = nr_caps; - if (strbuf_init(&sb, 128) < 0) + *cpu_pmu_caps = zalloc(sizeof(char *) * nr_caps); + if (!*cpu_pmu_caps) return -1; - while (nr_caps--) { + for (i = 0; i < nr_caps; i++) { name = do_read_string(ff); if (!name) goto error; @@ -3231,12 +3225,10 @@ static int process_per_cpu_pmu_caps(struct feat_fd *ff, int *nr_cpu_pmu_caps, if (!value) goto free_name; - if (strbuf_addf(&sb, "%s=%s", name, value) < 0) + if (asprintf(&ptr, "%s=%s", name, value) < 0) goto free_value; - /* include a NULL character at the end */ - if (strbuf_add(&sb, "", 1) < 0) - goto free_value; + (*cpu_pmu_caps)[i] = ptr; if (!strcmp(name, "branches")) *max_branches = atoi(value); @@ -3244,7 +3236,7 @@ static int process_per_cpu_pmu_caps(struct feat_fd *ff, int *nr_cpu_pmu_caps, free(value); free(name); } - *cpu_pmu_caps = strbuf_detach(&sb, NULL); + *nr_cpu_pmu_caps = nr_caps; return 0; free_value: @@ -3252,16 +3244,24 @@ free_value: free_name: free(name); error: - strbuf_release(&sb); + for (; i > 0; i--) + free((*cpu_pmu_caps)[i - 1]); + free(*cpu_pmu_caps); + *cpu_pmu_caps = NULL; + *nr_cpu_pmu_caps = 0; return -1; } static int process_cpu_pmu_caps(struct feat_fd *ff, void *data __maybe_unused) { - return process_per_cpu_pmu_caps(ff, &ff->ph->env.nr_cpu_pmu_caps, + int ret = process_per_cpu_pmu_caps(ff, &ff->ph->env.nr_cpu_pmu_caps, &ff->ph->env.cpu_pmu_caps, &ff->ph->env.max_branches); + + if (!ret && !ff->ph->env.cpu_pmu_caps) + pr_debug("cpu pmu capabilities not available\n"); + return ret; } static int process_hybrid_cpu_pmu_caps(struct feat_fd *ff, @@ -3270,6 +3270,7 @@ static int process_hybrid_cpu_pmu_caps(struct feat_fd *ff, struct hybrid_cpc_node *nodes; u32 nr_pmu, i; int ret; + int j; if (do_read_u32(ff, &nr_pmu)) return -1; @@ -3297,6 +3298,8 @@ static int process_hybrid_cpu_pmu_caps(struct feat_fd *ff, ret = -1; goto err; } + if (!n->nr_cpu_pmu_caps) + pr_debug("%s pmu capabilities not available\n", n->pmu_name); } ff->ph->env.nr_hybrid_cpc_nodes = nr_pmu; @@ -3305,6 +3308,8 @@ static int process_hybrid_cpu_pmu_caps(struct feat_fd *ff, err: for (i = 0; i < nr_pmu; i++) { + for (j = 0; j < nodes[i].nr_cpu_pmu_caps; j++) + free(nodes[i].cpu_pmu_caps[j]); free(nodes[i].cpu_pmu_caps); free(nodes[i].pmu_name); } -- cgit v1.2.3 From 2139f7424819818afc25fbfd0effda4babe235f6 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Sat, 4 Jun 2022 10:15:16 +0530 Subject: perf header: Record non-CPU PMU capabilities PMUs advertise their capabilities via sysfs attribute files but the perf tool currently parses only core(CPU) or hybrid core PMU capabilities. Add support of recording non-core PMU capabilities int perf.data header. Note that a newly proposed HEADER_PMU_CAPS is replacing existing HEADER_HYBRID_CPU_PMU_CAPS. Special care is taken for hybrid core PMUs by writing their capabilities first in the perf.data header to make sure new perf.data file being read by old perf tool does not break. Reviewed-by: Kan Liang Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Andi Kleen Cc: Borislav Petkov Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kim Phillips Cc: Leo Yan Cc: Mark Rutland Cc: Peter Zijlstra Cc: Robert Richter Cc: Sandipan Das Cc: Santosh Shukla Cc: Stephane Eranian Cc: Thomas Gleixner Cc: like.xu.linux@gmail.com Cc: x86@kernel.org Link: https://lore.kernel.org/r/20220604044519.594-6-ravi.bangoria@amd.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf.data-file-format.txt | 10 +- tools/perf/builtin-inject.c | 2 +- tools/perf/util/env.c | 60 +++++++- tools/perf/util/env.h | 12 +- tools/perf/util/header.c | 160 ++++++++++++--------- tools/perf/util/header.h | 2 +- 6 files changed, 158 insertions(+), 88 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt index bc9f0aa113d8..635ba043fd7d 100644 --- a/tools/perf/Documentation/perf.data-file-format.txt +++ b/tools/perf/Documentation/perf.data-file-format.txt @@ -419,18 +419,20 @@ Example: cpu_core cpu list : 0-15 cpu_atom cpu list : 16-23 - HEADER_HYBRID_CPU_PMU_CAPS = 31, + HEADER_PMU_CAPS = 31, - A list of hybrid CPU PMU capabilities. + List of pmu capabilities (except cpu pmu which is already + covered by HEADER_CPU_PMU_CAPS). Note that hybrid cpu pmu + capabilities are also stored here. struct { u32 nr_pmu; struct { - u32 nr_cpu_pmu_caps; + u32 nr_caps; { char name[]; char value[]; - } [nr_cpu_pmu_caps]; + } [nr_caps]; char pmu_name[]; } [nr_pmu]; }; diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 42e2918fd1cc..b9022d408def 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -809,7 +809,7 @@ static bool keep_feat(int feat) case HEADER_CPU_PMU_CAPS: case HEADER_CLOCK_DATA: case HEADER_HYBRID_TOPOLOGY: - case HEADER_HYBRID_CPU_PMU_CAPS: + case HEADER_PMU_CAPS: return true; /* Information that can be updated */ case HEADER_BUILD_ID: diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 7d3aeb2e4622..5b8cf6a421a4 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -219,13 +219,13 @@ void perf_env__exit(struct perf_env *env) } zfree(&env->hybrid_nodes); - for (i = 0; i < env->nr_hybrid_cpc_nodes; i++) { - for (j = 0; j < env->hybrid_cpc_nodes[i].nr_cpu_pmu_caps; j++) - zfree(&env->hybrid_cpc_nodes[i].cpu_pmu_caps[j]); - zfree(&env->hybrid_cpc_nodes[i].cpu_pmu_caps); - zfree(&env->hybrid_cpc_nodes[i].pmu_name); + for (i = 0; i < env->nr_pmus_with_caps; i++) { + for (j = 0; j < env->pmu_caps[i].nr_caps; j++) + zfree(&env->pmu_caps[i].caps[j]); + zfree(&env->pmu_caps[i].caps); + zfree(&env->pmu_caps[i].pmu_name); } - zfree(&env->hybrid_cpc_nodes); + zfree(&env->pmu_caps); } void perf_env__init(struct perf_env *env) @@ -531,3 +531,51 @@ int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu) return cpu.cpu >= 0 && cpu.cpu < env->nr_numa_map ? env->numa_map[cpu.cpu] : -1; } + +char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name, + const char *cap) +{ + char *cap_eq; + int cap_size; + char **ptr; + int i, j; + + if (!pmu_name || !cap) + return NULL; + + cap_size = strlen(cap); + cap_eq = zalloc(cap_size + 2); + if (!cap_eq) + return NULL; + + memcpy(cap_eq, cap, cap_size); + cap_eq[cap_size] = '='; + + if (!strcmp(pmu_name, "cpu")) { + for (i = 0; i < env->nr_cpu_pmu_caps; i++) { + if (!strncmp(env->cpu_pmu_caps[i], cap_eq, cap_size + 1)) { + free(cap_eq); + return &env->cpu_pmu_caps[i][cap_size + 1]; + } + } + goto out; + } + + for (i = 0; i < env->nr_pmus_with_caps; i++) { + if (strcmp(env->pmu_caps[i].pmu_name, pmu_name)) + continue; + + ptr = env->pmu_caps[i].caps; + + for (j = 0; j < env->pmu_caps[i].nr_caps; j++) { + if (!strncmp(ptr[j], cap_eq, cap_size + 1)) { + free(cap_eq); + return &ptr[j][cap_size + 1]; + } + } + } + +out: + free(cap_eq); + return NULL; +} diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 43aab59f7322..4566c51f2fd9 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -43,10 +43,10 @@ struct hybrid_node { char *cpus; }; -struct hybrid_cpc_node { - int nr_cpu_pmu_caps; +struct pmu_caps { + int nr_caps; unsigned int max_branches; - char **cpu_pmu_caps; + char **caps; char *pmu_name; }; @@ -74,7 +74,7 @@ struct perf_env { int nr_groups; int nr_cpu_pmu_caps; int nr_hybrid_nodes; - int nr_hybrid_cpc_nodes; + int nr_pmus_with_caps; char *cmdline; const char **cmdline_argv; char *sibling_cores; @@ -94,7 +94,7 @@ struct perf_env { struct memory_node *memory_nodes; unsigned long long memory_bsize; struct hybrid_node *hybrid_nodes; - struct hybrid_cpc_node *hybrid_cpc_nodes; + struct pmu_caps *pmu_caps; #ifdef HAVE_LIBBPF_SUPPORT /* * bpf_info_lock protects bpf rbtrees. This is needed because the @@ -172,4 +172,6 @@ bool perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node); struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id); int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu); +char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name, + const char *cap); #endif /* __PERF_ENV_H */ diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index c9d1f764aa68..511478429463 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1512,18 +1512,13 @@ static int write_compressed(struct feat_fd *ff __maybe_unused, return do_write(ff, &(ff->ph->env.comp_mmap_len), sizeof(ff->ph->env.comp_mmap_len)); } -static int write_per_cpu_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu, - bool write_pmu) +static int __write_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu, + bool write_pmu) { struct perf_pmu_caps *caps = NULL; - int nr_caps; int ret; - nr_caps = perf_pmu__caps_parse(pmu); - if (nr_caps < 0) - return nr_caps; - - ret = do_write(ff, &nr_caps, sizeof(nr_caps)); + ret = do_write(ff, &pmu->nr_caps, sizeof(pmu->nr_caps)); if (ret < 0) return ret; @@ -1550,33 +1545,60 @@ static int write_cpu_pmu_caps(struct feat_fd *ff, struct evlist *evlist __maybe_unused) { struct perf_pmu *cpu_pmu = perf_pmu__find("cpu"); + int ret; if (!cpu_pmu) return -ENOENT; - return write_per_cpu_pmu_caps(ff, cpu_pmu, false); + ret = perf_pmu__caps_parse(cpu_pmu); + if (ret < 0) + return ret; + + return __write_pmu_caps(ff, cpu_pmu, false); } -static int write_hybrid_cpu_pmu_caps(struct feat_fd *ff, - struct evlist *evlist __maybe_unused) +static int write_pmu_caps(struct feat_fd *ff, + struct evlist *evlist __maybe_unused) { - struct perf_pmu *pmu; - u32 nr_pmu = perf_pmu__hybrid_pmu_num(); + struct perf_pmu *pmu = NULL; + int nr_pmu = 0; int ret; - if (nr_pmu == 0) - return -ENOENT; + while ((pmu = perf_pmu__scan(pmu))) { + if (!pmu->name || !strcmp(pmu->name, "cpu") || + perf_pmu__caps_parse(pmu) <= 0) + continue; + nr_pmu++; + } ret = do_write(ff, &nr_pmu, sizeof(nr_pmu)); if (ret < 0) return ret; + if (!nr_pmu) + return 0; + + /* + * Write hybrid pmu caps first to maintain compatibility with + * older perf tool. + */ + pmu = NULL; perf_pmu__for_each_hybrid_pmu(pmu) { - ret = write_per_cpu_pmu_caps(ff, pmu, true); + ret = __write_pmu_caps(ff, pmu, true); if (ret < 0) return ret; } + pmu = NULL; + while ((pmu = perf_pmu__scan(pmu))) { + if (!pmu->name || !strcmp(pmu->name, "cpu") || + !pmu->nr_caps || perf_pmu__is_hybrid(pmu->name)) + continue; + + ret = __write_pmu_caps(ff, pmu, true); + if (ret < 0) + return ret; + } return 0; } @@ -2051,8 +2073,7 @@ static void print_compressed(struct feat_fd *ff, FILE *fp) ff->ph->env.comp_level, ff->ph->env.comp_ratio); } -static void print_per_cpu_pmu_caps(FILE *fp, int nr_caps, char **cpu_pmu_caps, - char *pmu_name) +static void __print_pmu_caps(FILE *fp, int nr_caps, char **caps, char *pmu_name) { const char *delimiter = ""; int i; @@ -2064,7 +2085,7 @@ static void print_per_cpu_pmu_caps(FILE *fp, int nr_caps, char **cpu_pmu_caps, fprintf(fp, "# %s pmu capabilities: ", pmu_name); for (i = 0; i < nr_caps; i++) { - fprintf(fp, "%s%s", delimiter, cpu_pmu_caps[i]); + fprintf(fp, "%s%s", delimiter, caps[i]); delimiter = ", "; } @@ -2073,19 +2094,18 @@ static void print_per_cpu_pmu_caps(FILE *fp, int nr_caps, char **cpu_pmu_caps, static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp) { - print_per_cpu_pmu_caps(fp, ff->ph->env.nr_cpu_pmu_caps, - ff->ph->env.cpu_pmu_caps, (char *)"cpu"); + __print_pmu_caps(fp, ff->ph->env.nr_cpu_pmu_caps, + ff->ph->env.cpu_pmu_caps, (char *)"cpu"); } -static void print_hybrid_cpu_pmu_caps(struct feat_fd *ff, FILE *fp) +static void print_pmu_caps(struct feat_fd *ff, FILE *fp) { - struct hybrid_cpc_node *n; + struct pmu_caps *pmu_caps; - for (int i = 0; i < ff->ph->env.nr_hybrid_cpc_nodes; i++) { - n = &ff->ph->env.hybrid_cpc_nodes[i]; - print_per_cpu_pmu_caps(fp, n->nr_cpu_pmu_caps, - n->cpu_pmu_caps, - n->pmu_name); + for (int i = 0; i < ff->ph->env.nr_pmus_with_caps; i++) { + pmu_caps = &ff->ph->env.pmu_caps[i]; + __print_pmu_caps(fp, pmu_caps->nr_caps, pmu_caps->caps, + pmu_caps->pmu_name); } } @@ -3196,27 +3216,26 @@ static int process_compressed(struct feat_fd *ff, return 0; } -static int process_per_cpu_pmu_caps(struct feat_fd *ff, int *nr_cpu_pmu_caps, - char ***cpu_pmu_caps, - unsigned int *max_branches) +static int __process_pmu_caps(struct feat_fd *ff, int *nr_caps, + char ***caps, unsigned int *max_branches) { char *name, *value, *ptr; - u32 nr_caps, i; + u32 nr_pmu_caps, i; - *nr_cpu_pmu_caps = 0; - *cpu_pmu_caps = NULL; + *nr_caps = 0; + *caps = NULL; - if (do_read_u32(ff, &nr_caps)) + if (do_read_u32(ff, &nr_pmu_caps)) return -1; - if (!nr_caps) + if (!nr_pmu_caps) return 0; - *cpu_pmu_caps = zalloc(sizeof(char *) * nr_caps); - if (!*cpu_pmu_caps) + *caps = zalloc(sizeof(char *) * nr_pmu_caps); + if (!*caps) return -1; - for (i = 0; i < nr_caps; i++) { + for (i = 0; i < nr_pmu_caps; i++) { name = do_read_string(ff); if (!name) goto error; @@ -3228,7 +3247,7 @@ static int process_per_cpu_pmu_caps(struct feat_fd *ff, int *nr_cpu_pmu_caps, if (asprintf(&ptr, "%s=%s", name, value) < 0) goto free_value; - (*cpu_pmu_caps)[i] = ptr; + (*caps)[i] = ptr; if (!strcmp(name, "branches")) *max_branches = atoi(value); @@ -3236,7 +3255,7 @@ static int process_per_cpu_pmu_caps(struct feat_fd *ff, int *nr_cpu_pmu_caps, free(value); free(name); } - *nr_cpu_pmu_caps = nr_caps; + *nr_caps = nr_pmu_caps; return 0; free_value: @@ -3245,29 +3264,28 @@ free_name: free(name); error: for (; i > 0; i--) - free((*cpu_pmu_caps)[i - 1]); - free(*cpu_pmu_caps); - *cpu_pmu_caps = NULL; - *nr_cpu_pmu_caps = 0; + free((*caps)[i - 1]); + free(*caps); + *caps = NULL; + *nr_caps = 0; return -1; } static int process_cpu_pmu_caps(struct feat_fd *ff, void *data __maybe_unused) { - int ret = process_per_cpu_pmu_caps(ff, &ff->ph->env.nr_cpu_pmu_caps, - &ff->ph->env.cpu_pmu_caps, - &ff->ph->env.max_branches); + int ret = __process_pmu_caps(ff, &ff->ph->env.nr_cpu_pmu_caps, + &ff->ph->env.cpu_pmu_caps, + &ff->ph->env.max_branches); if (!ret && !ff->ph->env.cpu_pmu_caps) pr_debug("cpu pmu capabilities not available\n"); return ret; } -static int process_hybrid_cpu_pmu_caps(struct feat_fd *ff, - void *data __maybe_unused) +static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused) { - struct hybrid_cpc_node *nodes; + struct pmu_caps *pmu_caps; u32 nr_pmu, i; int ret; int j; @@ -3276,45 +3294,45 @@ static int process_hybrid_cpu_pmu_caps(struct feat_fd *ff, return -1; if (!nr_pmu) { - pr_debug("hybrid cpu pmu capabilities not available\n"); + pr_debug("pmu capabilities not available\n"); return 0; } - nodes = zalloc(sizeof(*nodes) * nr_pmu); - if (!nodes) + pmu_caps = zalloc(sizeof(*pmu_caps) * nr_pmu); + if (!pmu_caps) return -ENOMEM; for (i = 0; i < nr_pmu; i++) { - struct hybrid_cpc_node *n = &nodes[i]; - - ret = process_per_cpu_pmu_caps(ff, &n->nr_cpu_pmu_caps, - &n->cpu_pmu_caps, - &n->max_branches); + ret = __process_pmu_caps(ff, &pmu_caps[i].nr_caps, + &pmu_caps[i].caps, + &pmu_caps[i].max_branches); if (ret) goto err; - n->pmu_name = do_read_string(ff); - if (!n->pmu_name) { + pmu_caps[i].pmu_name = do_read_string(ff); + if (!pmu_caps[i].pmu_name) { ret = -1; goto err; } - if (!n->nr_cpu_pmu_caps) - pr_debug("%s pmu capabilities not available\n", n->pmu_name); + if (!pmu_caps[i].nr_caps) { + pr_debug("%s pmu capabilities not available\n", + pmu_caps[i].pmu_name); + } } - ff->ph->env.nr_hybrid_cpc_nodes = nr_pmu; - ff->ph->env.hybrid_cpc_nodes = nodes; + ff->ph->env.nr_pmus_with_caps = nr_pmu; + ff->ph->env.pmu_caps = pmu_caps; return 0; err: for (i = 0; i < nr_pmu; i++) { - for (j = 0; j < nodes[i].nr_cpu_pmu_caps; j++) - free(nodes[i].cpu_pmu_caps[j]); - free(nodes[i].cpu_pmu_caps); - free(nodes[i].pmu_name); + for (j = 0; j < pmu_caps[i].nr_caps; j++) + free(pmu_caps[i].caps[j]); + free(pmu_caps[i].caps); + free(pmu_caps[i].pmu_name); } - free(nodes); + free(pmu_caps); return ret; } @@ -3380,7 +3398,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = { FEAT_OPR(CPU_PMU_CAPS, cpu_pmu_caps, false), FEAT_OPR(CLOCK_DATA, clock_data, false), FEAT_OPN(HYBRID_TOPOLOGY, hybrid_topology, true), - FEAT_OPR(HYBRID_CPU_PMU_CAPS, hybrid_cpu_pmu_caps, false), + FEAT_OPR(PMU_CAPS, pmu_caps, false), }; struct header_print_data { diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 08563c1f1bff..5790bf556b90 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -46,7 +46,7 @@ enum { HEADER_CPU_PMU_CAPS, HEADER_CLOCK_DATA, HEADER_HYBRID_TOPOLOGY, - HEADER_HYBRID_CPU_PMU_CAPS, + HEADER_PMU_CAPS, HEADER_LAST_FEATURE, HEADER_FEAT_BITS = 256, }; -- cgit v1.2.3 From c1f4f92b7d5d3deeaae758a1a7ed263b1381dd1c Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Sat, 4 Jun 2022 10:15:18 +0530 Subject: perf tool ibs: Sync AMD IBS header file IBS support has been enhanced with two new features in upcoming uarch: 1. DataSrc extension 2. L3 miss filtering. Additional set of bits has been introduced in IBS registers to exploit these features. New bits are already defining in arch/x86/ header. Sync it with tools header file. Also rename existing ibs_op_data field 'data_src' to 'data_src_lo'. Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Andi Kleen Cc: Borislav Petkov Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Kim Phillips Cc: Leo Yan Cc: Mark Rutland Cc: Peter Zijlstra Cc: Robert Richter Cc: Sandipan Das Cc: Santosh Shukla Cc: Stephane Eranian Cc: Thomas Gleixner Cc: like.xu.linux@gmail.com Cc: x86@kernel.org Link: https://lore.kernel.org/r/20220604044519.594-8-ravi.bangoria@amd.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/x86/include/asm/amd-ibs.h | 16 ++++++++++------ tools/perf/util/amd-sample-raw.c | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/arch/x86/include/asm/amd-ibs.h b/tools/arch/x86/include/asm/amd-ibs.h index 765e9e752d03..9a3312e12e2e 100644 --- a/tools/arch/x86/include/asm/amd-ibs.h +++ b/tools/arch/x86/include/asm/amd-ibs.h @@ -29,7 +29,10 @@ union ibs_fetch_ctl { rand_en:1, /* 57: random tagging enable */ fetch_l2_miss:1,/* 58: L2 miss for sampled fetch * (needs IbsFetchComp) */ - reserved:5; /* 59-63: reserved */ + l3_miss_only:1, /* 59: Collect L3 miss samples only */ + fetch_oc_miss:1,/* 60: Op cache miss for the sampled fetch */ + fetch_l3_miss:1,/* 61: L3 cache miss for the sampled fetch */ + reserved:2; /* 62-63: reserved */ }; }; @@ -38,14 +41,14 @@ union ibs_op_ctl { __u64 val; struct { __u64 opmaxcnt:16, /* 0-15: periodic op max. count */ - reserved0:1, /* 16: reserved */ + l3_miss_only:1, /* 16: Collect L3 miss samples only */ op_en:1, /* 17: op sampling enable */ op_val:1, /* 18: op sample valid */ cnt_ctl:1, /* 19: periodic op counter control */ opmaxcnt_ext:7, /* 20-26: upper 7 bits of periodic op maximum count */ - reserved1:5, /* 27-31: reserved */ + reserved0:5, /* 27-31: reserved */ opcurcnt:27, /* 32-58: periodic op counter current count */ - reserved2:5; /* 59-63: reserved */ + reserved1:5; /* 59-63: reserved */ }; }; @@ -71,11 +74,12 @@ union ibs_op_data { union ibs_op_data2 { __u64 val; struct { - __u64 data_src:3, /* 0-2: data source */ + __u64 data_src_lo:3, /* 0-2: data source low */ reserved0:1, /* 3: reserved */ rmt_node:1, /* 4: destination node */ cache_hit_st:1, /* 5: cache hit state */ - reserved1:57; /* 5-63: reserved */ + data_src_hi:2, /* 6-7: data source high */ + reserved1:56; /* 8-63: reserved */ }; }; diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-raw.c index d19d765195c5..3b623ea6ee7e 100644 --- a/tools/perf/util/amd-sample-raw.c +++ b/tools/perf/util/amd-sample-raw.c @@ -98,9 +98,9 @@ static void pr_ibs_op_data2(union ibs_op_data2 reg) }; printf("ibs_op_data2:\t%016llx %sRmtNode %d%s\n", reg.val, - reg.data_src == 2 ? (reg.cache_hit_st ? "CacheHitSt 1=O-State " + reg.data_src_lo == 2 ? (reg.cache_hit_st ? "CacheHitSt 1=O-State " : "CacheHitSt 0=M-state ") : "", - reg.rmt_node, data_src_str[reg.data_src]); + reg.rmt_node, data_src_str[reg.data_src_lo]); } static void pr_ibs_op_data3(union ibs_op_data3 reg) -- cgit v1.2.3 From 0429796e45ec17eee26d7a59de92271c275d7666 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Sat, 4 Jun 2022 10:15:19 +0530 Subject: perf script ibs: Support new IBS bits in raw trace dump Interpret Additional set of IBS register bits while doing perf report/script raw dump. IBS op PMU ex: $ sudo ./perf record -c 130 -a -e ibs_op/l3missonly=1/ --raw-samples $ sudo ./perf report -D ... ibs_op_ctl: 0000004500070008 MaxCnt 128 L3MissOnly 1 En 1 Val 1 CntCtl 0=cycles CurCnt 69 ibs_op_data: 0000000000710002 CompToRetCtr 2 TagToRetCtr 113 BrnRet 0 RipInvalid 0 BrnFuse 0 Microcode 0 ibs_op_data2: 0000000000000002 CacheHitSt 0=M-state RmtNode 0 DataSrc 2=A peer cache in a near CCX ibs_op_data3: 000000681d1700a1 LdOp 1 StOp 0 DcL1TlbMiss 0 DcL2TlbMiss 0 DcL1TlbHit2M 0 DcL1TlbHit1G 1 DcL2TlbHit2M 0 DcMiss 1 DcMisAcc 0 DcWcMemAcc 0 DcUcMemAcc 0 DcLockedOp 0 DcMissNoMabAlloc 1 DcLinAddrValid 1 DcPhyAddrValid 1 DcL2TlbHit1G 0 L2Miss 1 SwPf 0 OpMemWidth 8 bytes OpDcMissOpenMemReqs 7 DcMissLat 104 TlbRefillLat 0 IBS Fetch PMU ex: $ sudo ./perf record -c 130 -a -e ibs_fetch/l3missonly=1/ --raw-samples $ sudo ./perf report -D ... ibs_fetch_ctl: 3c1f00c700080008 MaxCnt 128 Cnt 128 Lat 199 En 1 Val 1 Comp 1 IcMiss 1 PhyAddrValid 1 L1TlbPgSz 4KB L1TlbMiss 0 L2TlbMiss 0 RandEn 0 L2Miss 1 L3MissOnly 1 FetchOcMiss 1 FetchL3Miss 1 With the DataSrc extensions, the source of data can be decoded among: - Local L3 or other L1/L2 in CCX. - A peer cache in a near CCX. - Data returned from DRAM. - A peer cache in a far CCX. - DRAM address map with "long latency" bit set. - Data returned from MMIO/Config/PCI/APIC. - Extension Memory (S-Link, GenZ, etc - identified by the CS target and/or address map at DF's choice). - Peer Agent Memory. Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Andi Kleen Cc: Borislav Petkov Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Kim Phillips Cc: Leo Yan Cc: Mark Rutland Cc: Peter Zijlstra Cc: Robert Richter Cc: Sandipan Das Cc: Santosh Shukla Cc: Stephane Eranian Cc: Thomas Gleixner Cc: like.xu.linux@gmail.com Cc: x86@kernel.org Link: https://lore.kernel.org/r/20220604044519.594-9-ravi.bangoria@amd.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/amd-sample-raw.c | 64 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-raw.c index 3b623ea6ee7e..238305868644 100644 --- a/tools/perf/util/amd-sample-raw.c +++ b/tools/perf/util/amd-sample-raw.c @@ -18,6 +18,7 @@ #include "pmu-events/pmu-events.h" static u32 cpu_family, cpu_model, ibs_fetch_type, ibs_op_type; +static bool zen4_ibs_extensions; static void pr_ibs_fetch_ctl(union ibs_fetch_ctl reg) { @@ -39,6 +40,7 @@ static void pr_ibs_fetch_ctl(union ibs_fetch_ctl reg) }; const char *ic_miss_str = NULL; const char *l1tlb_pgsz_str = NULL; + char l3_miss_str[sizeof(" L3MissOnly _ FetchOcMiss _ FetchL3Miss _")] = ""; if (cpu_family == 0x19 && cpu_model < 0x10) { /* @@ -53,12 +55,19 @@ static void pr_ibs_fetch_ctl(union ibs_fetch_ctl reg) ic_miss_str = ic_miss_strs[reg.ic_miss]; } + if (zen4_ibs_extensions) { + snprintf(l3_miss_str, sizeof(l3_miss_str), + " L3MissOnly %d FetchOcMiss %d FetchL3Miss %d", + reg.l3_miss_only, reg.fetch_oc_miss, reg.fetch_l3_miss); + } + printf("ibs_fetch_ctl:\t%016llx MaxCnt %7d Cnt %7d Lat %5d En %d Val %d Comp %d%s " - "PhyAddrValid %d%s L1TlbMiss %d L2TlbMiss %d RandEn %d%s\n", + "PhyAddrValid %d%s L1TlbMiss %d L2TlbMiss %d RandEn %d%s%s\n", reg.val, reg.fetch_maxcnt << 4, reg.fetch_cnt << 4, reg.fetch_lat, reg.fetch_en, reg.fetch_val, reg.fetch_comp, ic_miss_str ? : "", reg.phy_addr_valid, l1tlb_pgsz_str ? : "", reg.l1tlb_miss, reg.l2tlb_miss, - reg.rand_en, reg.fetch_comp ? (reg.fetch_l2_miss ? " L2Miss 1" : " L2Miss 0") : ""); + reg.rand_en, reg.fetch_comp ? (reg.fetch_l2_miss ? " L2Miss 1" : " L2Miss 0") : "", + l3_miss_str); } static void pr_ic_ibs_extd_ctl(union ic_ibs_extd_ctl reg) @@ -68,9 +77,15 @@ static void pr_ic_ibs_extd_ctl(union ic_ibs_extd_ctl reg) static void pr_ibs_op_ctl(union ibs_op_ctl reg) { - printf("ibs_op_ctl:\t%016llx MaxCnt %9d En %d Val %d CntCtl %d=%s CurCnt %9d\n", - reg.val, ((reg.opmaxcnt_ext << 16) | reg.opmaxcnt) << 4, reg.op_en, reg.op_val, - reg.cnt_ctl, reg.cnt_ctl ? "uOps" : "cycles", reg.opcurcnt); + char l3_miss_only[sizeof(" L3MissOnly _")] = ""; + + if (zen4_ibs_extensions) + snprintf(l3_miss_only, sizeof(l3_miss_only), " L3MissOnly %d", reg.l3_miss_only); + + printf("ibs_op_ctl:\t%016llx MaxCnt %9d%s En %d Val %d CntCtl %d=%s CurCnt %9d\n", + reg.val, ((reg.opmaxcnt_ext << 16) | reg.opmaxcnt) << 4, l3_miss_only, + reg.op_en, reg.op_val, reg.cnt_ctl, + reg.cnt_ctl ? "uOps" : "cycles", reg.opcurcnt); } static void pr_ibs_op_data(union ibs_op_data reg) @@ -84,7 +99,34 @@ static void pr_ibs_op_data(union ibs_op_data reg) reg.op_brn_ret, reg.op_rip_invalid, reg.op_brn_fuse, reg.op_microcode); } -static void pr_ibs_op_data2(union ibs_op_data2 reg) +static void pr_ibs_op_data2_extended(union ibs_op_data2 reg) +{ + static const char * const data_src_str[] = { + "", + " DataSrc 1=Local L3 or other L1/L2 in CCX", + " DataSrc 2=A peer cache in a near CCX", + " DataSrc 3=Data returned from DRAM", + " DataSrc 4=(reserved)", + " DataSrc 5=A peer cache in a far CCX", + " DataSrc 6=DRAM address map with \"long latency\" bit set", + " DataSrc 7=Data returned from MMIO/Config/PCI/APIC", + " DataSrc 8=Extension Memory (S-Link, GenZ, etc)", + " DataSrc 9=(reserved)", + " DataSrc 10=(reserved)", + " DataSrc 11=(reserved)", + " DataSrc 12=Peer Agent Memory", + /* 13 to 31 are reserved. Avoid printing them. */ + }; + int data_src = (reg.data_src_hi << 3) | reg.data_src_lo; + + printf("ibs_op_data2:\t%016llx %sRmtNode %d%s\n", reg.val, + (data_src == 1 || data_src == 2 || data_src == 5) ? + (reg.cache_hit_st ? "CacheHitSt 1=O-State " : "CacheHitSt 0=M-state ") : "", + reg.rmt_node, + data_src < (int)ARRAY_SIZE(data_src_str) ? data_src_str[data_src] : ""); +} + +static void pr_ibs_op_data2_default(union ibs_op_data2 reg) { static const char * const data_src_str[] = { "", @@ -103,6 +145,13 @@ static void pr_ibs_op_data2(union ibs_op_data2 reg) reg.rmt_node, data_src_str[reg.data_src_lo]); } +static void pr_ibs_op_data2(union ibs_op_data2 reg) +{ + if (zen4_ibs_extensions) + return pr_ibs_op_data2_extended(reg); + pr_ibs_op_data2_default(reg); +} + static void pr_ibs_op_data3(union ibs_op_data3 reg) { char l2_miss_str[sizeof(" L2Miss _")] = ""; @@ -279,6 +328,9 @@ bool evlist__has_amd_ibs(struct evlist *evlist) pmu_mapping += strlen(pmu_mapping) + 1 /* '\0' */; } + if (perf_env__find_pmu_cap(env, "ibs_op", "zen4_ibs_extensions")) + zen4_ibs_extensions = 1; + if (ibs_fetch_type || ibs_op_type) { if (!cpu_family) parse_cpuid(env); -- cgit v1.2.3 From 4c41cb46a732fe82575df0015b7d0d8a52efb59b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 29 Jun 2022 11:25:02 -0700 Subject: perf python: Prefer python3 The PYTHON_AUTO code orders the preference for the PYTHON command to be python3, python and then python2. python3 makes a more logical preference as python2 is no longer supported: https://www.python.org/doc/sunset-python-2/ Reorder the priority of the PYTHON command to be python2, python and then python3. Reported-by: John Garry Signed-off-by: Ian Rogers Tested-by: John Garry Tested-by: Thomas Richter Tested-by: Xing Zhengjun Cc: Alexander Shishkin Cc: Ananth Narayan Cc: Andi Kleen Cc: Andrew Kilroy Cc: Caleb Biggers Cc: Felix Fietkau Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kajol Jain Cc: Kan Liang Cc: Kshipra Bopardikar Cc: Like Xu Cc: Mark Rutland Cc: Mathieu Poirier Cc: Namhyung Kim Cc: Nick Forrington Cc: Paul Clarke Cc: Perry Taylor Cc: Peter Zijlstra Cc: Qi Liu Cc: Ravi Bangoria Cc: Sandipan Das Cc: Santosh Shukla Cc: Stephane Eranian Cc: Will Deacon Link: https://lore.kernel.org/r/20220629182505.406269-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 73e0762092fe..16c1a87444b8 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -241,15 +241,15 @@ endif # Try different combinations to accommodate systems that only have # python[2][3]-config in weird combinations in the following order of # priority from lowest to highest: -# * python3-config -# * python-config # * python2-config as per pep-0394. +# * python-config +# * python3-config # * $(PYTHON)-config (If PYTHON is user supplied but PYTHON_CONFIG isn't) # PYTHON_AUTO := python-config -PYTHON_AUTO := $(if $(call get-executable,python3-config),python3-config,$(PYTHON_AUTO)) -PYTHON_AUTO := $(if $(call get-executable,python-config),python-config,$(PYTHON_AUTO)) PYTHON_AUTO := $(if $(call get-executable,python2-config),python2-config,$(PYTHON_AUTO)) +PYTHON_AUTO := $(if $(call get-executable,python-config),python-config,$(PYTHON_AUTO)) +PYTHON_AUTO := $(if $(call get-executable,python3-config),python3-config,$(PYTHON_AUTO)) # If PYTHON is defined but PYTHON_CONFIG isn't, then take $(PYTHON)-config as if it was the user # supplied value for PYTHON_CONFIG. Because it's "user supplied", error out if it doesn't exist. -- cgit v1.2.3 From ffc606ada3d7d8b200a27fc0c3a1cf0e000d75b2 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 29 Jun 2022 11:25:03 -0700 Subject: perf jevents: Add python converter script jevents.c is large, has a dependency on an old forked version of jsmn, and is challenging to work upon. A lot of jevents.c's complexity comes from needing to write json and csv parsing from first principles. In contrast python has this functionality in standard libraries and is already a build pre-requisite for tools like asciidoc (that builds all of the perf man pages). Introduce jevents.py that produces identical output to jevents.c. Add a test that runs both converter tools and validates there are no output differences. The test can be invoked with a phony build target like: $ make -C tools/perf jevents-py-test The python code deliberately tries to replicate the behavior of jevents.c so that the output matches and transitioning tools shouldn't introduce regressions. In some cases the code isn't as elegant as hoped, but fixing this can be done as follow up. Committer testing: $ make -C tools/perf jevents-py-test make: Entering directory '/var/home/acme/git/perf/tools/perf' BUILD: Doing 'make -j32' parallel build HOSTCC fixdep.o HOSTLD fixdep-in.o LINK fixdep Auto-detecting system features: ... dwarf: [ on ] ... dwarf_getlocations: [ on ] ... glibc: [ on ] ... libbfd: [ on ] ... libbfd-buildid: [ on ] ... libcap: [ on ] ... libelf: [ on ] ... libnuma: [ on ] ... numa_num_possible_cpus: [ on ] ... libperl: [ on ] ... libpython: [ on ] ... libcrypto: [ OFF ] ... libunwind: [ on ] ... libdw-dwarf-unwind: [ on ] ... zlib: [ on ] ... lzma: [ on ] ... get_cpuid: [ on ] ... bpf: [ on ] ... libaio: [ on ] ... libzstd: [ on ] ... disassembler-four-args: [ on ] HOSTCC pmu-events/json.o HOSTCC pmu-events/jsmn.o HOSTCC pmu-events/jevents.o HOSTLD pmu-events/jevents-in.o LINK pmu-events/jevents Checking architecture: arm64 Generating using jevents.c Generating using jevents.py Diffing Checking architecture: nds32 Generating using jevents.c Generating using jevents.py Diffing Checking architecture: powerpc Generating using jevents.c Generating using jevents.py Diffing Checking architecture: s390 Generating using jevents.c Generating using jevents.py Diffing Checking architecture: x86 Generating using jevents.c Generating using jevents.py Diffing make: Leaving directory '/var/home/acme/git/perf/tools/perf' $ Signed-off-by: Ian Rogers Tested-by: John Garry Tested-by: Thomas Richter Tested-by: Xing Zhengjun Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Ananth Narayan Cc: Andi Kleen Cc: Andrew Kilroy Cc: Caleb Biggers Cc: Felix Fietkau Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kajol Jain Cc: Kan Liang Cc: Kshipra Bopardikar Cc: Like Xu Cc: Mark Rutland Cc: Mathieu Poirier Cc: Namhyung Kim Cc: Nick Forrington Cc: Paul Clarke Cc: Perry Taylor Cc: Peter Zijlstra Cc: Qi Liu Cc: Ravi Bangoria Cc: Sandipan Das Cc: Santosh Shukla Cc: Stephane Eranian Cc: Will Deacon Link: https://lore.kernel.org/r/20220629182505.406269-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 6 + tools/perf/pmu-events/jevents-test.sh | 33 +++ tools/perf/pmu-events/jevents.py | 409 ++++++++++++++++++++++++++++++++++ 3 files changed, 448 insertions(+) create mode 100755 tools/perf/pmu-events/jevents-test.sh create mode 100755 tools/perf/pmu-events/jevents.py (limited to 'tools') diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 8f738e11356d..1e29c8936f71 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -669,6 +669,12 @@ $(JEVENTS_IN): FORCE $(JEVENTS): $(JEVENTS_IN) $(QUIET_LINK)$(HOSTCC) $(JEVENTS_IN) -o $@ +JEVENTS_PY := pmu-events/jevents.py +JEVENTS_PY_TEST := pmu-events/jevents-test.sh +.PHONY: jevents-py-test +jevents-py-test: $(JEVENTS) + $(Q)$(call echo-cmd,gen)$(JEVENTS_PY_TEST) $(JEVENTS) $(JEVENTS_PY) pmu-events/arch + $(PMU_EVENTS_IN): $(JEVENTS) FORCE $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=pmu-events diff --git a/tools/perf/pmu-events/jevents-test.sh b/tools/perf/pmu-events/jevents-test.sh new file mode 100755 index 000000000000..9ae852292576 --- /dev/null +++ b/tools/perf/pmu-events/jevents-test.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +# Validate that the legacy jevents and jevents.py produce identical output. +set -e + +JEVENTS="$1" +JEVENTS_PY="$2" +ARCH_PATH="$3" +JEVENTS_C_GENERATED=$(mktemp /tmp/jevents_c.XXXXX.c) +JEVENTS_PY_GENERATED=$(mktemp /tmp/jevents_py.XXXXX.c) + +cleanup() { + rm "$JEVENTS_C_GENERATED" "$JEVENTS_PY_GENERATED" + trap - exit term int +} +trap cleanup exit term int + +for path in "$ARCH_PATH"/* +do + arch=$(basename $path) + if [ "$arch" = "test" ] + then + continue + fi + echo "Checking architecture: $arch" + echo "Generating using jevents.c" + "$JEVENTS" "$arch" "$ARCH_PATH" "$JEVENTS_C_GENERATED" + echo "Generating using jevents.py" + "$JEVENTS_PY" "$arch" "$ARCH_PATH" "$JEVENTS_PY_GENERATED" + echo "Diffing" + diff -u "$JEVENTS_C_GENERATED" "$JEVENTS_PY_GENERATED" +done +cleanup diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py new file mode 100755 index 000000000000..83e0dcbeac9a --- /dev/null +++ b/tools/perf/pmu-events/jevents.py @@ -0,0 +1,409 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +"""Convert directories of JSON events to C code.""" +import argparse +import csv +import json +import os +import sys +from typing import Callable +from typing import Sequence + +# Global command line arguments. +_args = None +# List of event tables generated from "/sys" directories. +_sys_event_tables = [] +# Map from an event name to an architecture standard +# JsonEvent. Architecture standard events are in json files in the top +# f'{_args.starting_dir}/{_args.arch}' directory. +_arch_std_events = {} +# Track whether an events table is currently being defined and needs closing. +_close_table = False + + +def removesuffix(s: str, suffix: str) -> str: + """Remove the suffix from a string + + The removesuffix function is added to str in Python 3.9. We aim for 3.6 + compatibility and so provide our own function here. + """ + return s[0:-len(suffix)] if s.endswith(suffix) else s + + +def file_name_to_table_name(parents: Sequence[str], dirname: str) -> str: + """Generate a C table name from directory names.""" + tblname = 'pme' + for p in parents: + tblname += '_' + p + tblname += '_' + dirname + return tblname.replace('-', '_') + + +class JsonEvent: + """Representation of an event loaded from a json file dictionary.""" + + def __init__(self, jd: dict): + """Constructor passed the dictionary of parsed json values.""" + + def llx(x: int) -> str: + """Convert an int to a string similar to a printf modifier of %#llx.""" + return '0' if x == 0 else hex(x) + + def fixdesc(s: str) -> str: + """Fix formatting issue for the desc string.""" + if s is None: + return None + return removesuffix(removesuffix(removesuffix(s, '. '), + '. '), '.').replace('\n', '\\n').replace( + '\"', '\\"').replace('\r', '\\r') + + def convert_aggr_mode(aggr_mode: str) -> str: + """Returns the aggr_mode_class enum value associated with the JSON string.""" + if not aggr_mode: + return None + aggr_mode_to_enum = { + 'PerChip': '1', + 'PerCore': '2', + } + return aggr_mode_to_enum[aggr_mode] + + def lookup_msr(num: str) -> str: + """Converts the msr number, or first in a list to the appropriate event field.""" + if not num: + return None + msrmap = { + 0x3F6: 'ldlat=', + 0x1A6: 'offcore_rsp=', + 0x1A7: 'offcore_rsp=', + 0x3F7: 'frontend=', + } + return msrmap[int(num.split(',', 1)[0], 0)] + + def real_event(name: str, event: str) -> str: + """Convert well known event names to an event string otherwise use the event argument.""" + fixed = { + 'inst_retired.any': 'event=0xc0,period=2000003', + 'inst_retired.any_p': 'event=0xc0,period=2000003', + 'cpu_clk_unhalted.ref': 'event=0x0,umask=0x03,period=2000003', + 'cpu_clk_unhalted.thread': 'event=0x3c,period=2000003', + 'cpu_clk_unhalted.core': 'event=0x3c,period=2000003', + 'cpu_clk_unhalted.thread_any': 'event=0x3c,any=1,period=2000003', + } + if not name: + return None + if name.lower() in fixed: + return fixed[name.lower()] + return event + + def unit_to_pmu(unit: str) -> str: + """Convert a JSON Unit to Linux PMU name.""" + if not unit: + return None + # Comment brought over from jevents.c: + # it's not realistic to keep adding these, we need something more scalable ... + table = { + 'CBO': 'uncore_cbox', + 'QPI LL': 'uncore_qpi', + 'SBO': 'uncore_sbox', + 'iMPH-U': 'uncore_arb', + 'CPU-M-CF': 'cpum_cf', + 'CPU-M-SF': 'cpum_sf', + 'UPI LL': 'uncore_upi', + 'hisi_sicl,cpa': 'hisi_sicl,cpa', + 'hisi_sccl,ddrc': 'hisi_sccl,ddrc', + 'hisi_sccl,hha': 'hisi_sccl,hha', + 'hisi_sccl,l3c': 'hisi_sccl,l3c', + 'imx8_ddr': 'imx8_ddr', + 'L3PMC': 'amd_l3', + 'DFPMC': 'amd_df', + 'cpu_core': 'cpu_core', + 'cpu_atom': 'cpu_atom', + } + return table[unit] if unit in table else f'uncore_{unit.lower()}' + + eventcode = 0 + if 'EventCode' in jd: + eventcode = int(jd['EventCode'].split(',', 1)[0], 0) + if 'ExtSel' in jd: + eventcode |= int(jd['ExtSel']) << 8 + configcode = int(jd['ConfigCode'], 0) if 'ConfigCode' in jd else None + self.name = jd['EventName'].lower() if 'EventName' in jd else None + self.compat = jd.get('Compat') + self.desc = fixdesc(jd.get('BriefDescription')) + self.long_desc = fixdesc(jd.get('PublicDescription')) + precise = jd.get('PEBS') + msr = lookup_msr(jd.get('MSRIndex')) + msrval = jd.get('MSRValue') + extra_desc = '' + if 'Data_LA' in jd: + extra_desc += ' Supports address when precise' + if 'Errata' in jd: + extra_desc += '.' + if 'Errata' in jd: + extra_desc += ' Spec update: ' + jd['Errata'] + self.pmu = unit_to_pmu(jd.get('Unit')) + filter = jd.get('Filter') + self.unit = jd.get('ScaleUnit') + self.perpkg = jd.get('PerPkg') + self.aggr_mode = convert_aggr_mode(jd.get('AggregationMode')) + self.deprecated = jd.get('Deprecated') + self.metric_name = jd.get('MetricName') + self.metric_group = jd.get('MetricGroup') + self.metric_constraint = jd.get('MetricConstraint') + self.metric_expr = jd.get('MetricExpr') + if self.metric_expr: + self.metric_expr = self.metric_expr.replace('\\', '\\\\') + arch_std = jd.get('ArchStdEvent') + if precise and self.desc and not '(Precise Event)' in self.desc: + extra_desc += ' (Must be precise)' if precise == '2' else (' (Precise ' + 'event)') + event = f'config={llx(configcode)}' if configcode is not None else f'event={llx(eventcode)}' + event_fields = [ + ('AnyThread', 'any='), + ('PortMask', 'ch_mask='), + ('CounterMask', 'cmask='), + ('EdgeDetect', 'edge='), + ('FCMask', 'fc_mask='), + ('Invert', 'inv='), + ('SampleAfterValue', 'period='), + ('UMask', 'umask='), + ] + for key, value in event_fields: + if key in jd and jd[key] != '0': + event += ',' + value + jd[key] + if filter: + event += f',{filter}' + if msr: + event += f',{msr}{msrval}' + if self.desc and extra_desc: + self.desc += extra_desc + if self.long_desc and extra_desc: + self.long_desc += extra_desc + if self.pmu: + if self.desc and not self.desc.endswith('. '): + self.desc += '. ' + self.desc = (self.desc if self.desc else '') + ('Unit: ' + self.pmu + ' ') + if arch_std and arch_std.lower() in _arch_std_events: + event = _arch_std_events[arch_std.lower()].event + # Copy from the architecture standard event to self for undefined fields. + for attr, value in _arch_std_events[arch_std.lower()].__dict__.items(): + if hasattr(self, attr) and not getattr(self, attr): + setattr(self, attr, value) + + self.event = real_event(self.name, event) + + def __repr__(self) -> str: + """String representation primarily for debugging.""" + s = '{\n' + for attr, value in self.__dict__.items(): + if value: + s += f'\t{attr} = {value},\n' + return s + '}' + + def to_c_string(self, topic_local: str) -> str: + """Representation of the event as a C struct initializer.""" + + def attr_string(attr: str, value: str) -> str: + return '\t.%s = \"%s\",\n' % (attr, value) + + def str_if_present(self, attr: str) -> str: + if not getattr(self, attr): + return '' + return attr_string(attr, getattr(self, attr)) + + s = '{\n' + for attr in ['name', 'event']: + s += str_if_present(self, attr) + if self.desc is not None: + s += attr_string('desc', self.desc) + else: + s += attr_string('desc', '(null)') + s += str_if_present(self, 'compat') + s += f'\t.topic = "{topic_local}",\n' + for attr in [ + 'long_desc', 'pmu', 'unit', 'perpkg', 'aggr_mode', 'metric_expr', + 'metric_name', 'metric_group', 'deprecated', 'metric_constraint' + ]: + s += str_if_present(self, attr) + s += '},\n' + return s + + +def read_json_events(path: str) -> Sequence[JsonEvent]: + """Read json events from the specified file.""" + return json.load(open(path), object_hook=lambda d: JsonEvent(d)) + + +def preprocess_arch_std_files(archpath: str) -> None: + """Read in all architecture standard events.""" + global _arch_std_events + for item in os.scandir(archpath): + if item.is_file() and item.name.endswith('.json'): + for event in read_json_events(item.path): + if event.name: + _arch_std_events[event.name.lower()] = event + + +def print_events_table_prefix(tblname: str) -> None: + """Called when a new events table is started.""" + global _close_table + if _close_table: + raise IOError('Printing table prefix but last table has no suffix') + _args.output_file.write(f'static const struct pmu_event {tblname}[] = {{\n') + _close_table = True + + +def print_events_table_entries(item: os.DirEntry, topic: str) -> None: + """Create contents of an events table.""" + if not _close_table: + raise IOError('Table entries missing prefix') + for event in read_json_events(item.path): + _args.output_file.write(event.to_c_string(topic)) + + +def print_events_table_suffix() -> None: + """Optionally close events table.""" + global _close_table + if _close_table: + _args.output_file.write("""{ +\t.name = 0, +\t.event = 0, +\t.desc = 0, +}, +}; +""") + _close_table = False + + +def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None: + """Process a JSON file during the main walk.""" + global _sys_event_tables + + def get_topic(topic: str) -> str: + return removesuffix(topic, '.json').replace('-', ' ') + + def is_leaf_dir(path: str) -> bool: + for item in os.scandir(path): + if item.is_dir(): + return False + return True + + # model directory, reset topic + if item.is_dir() and is_leaf_dir(item.path): + print_events_table_suffix() + + tblname = file_name_to_table_name(parents, item.name) + if item.name == 'sys': + _sys_event_tables.append(tblname) + print_events_table_prefix(tblname) + return + + # base dir or too deep + level = len(parents) + if level == 0 or level > 4: + return + + # Ignore other directories. If the file name does not have a .json + # extension, ignore it. It could be a readme.txt for instance. + if not item.is_file() or not item.name.endswith('.json'): + return + + print_events_table_entries(item, get_topic(item.name)) + + +def print_mapping_table() -> None: + """Read the mapfile and generate the struct from cpuid string to event table.""" + with open(f'{_args.starting_dir}/{_args.arch}/mapfile.csv') as csvfile: + table = csv.reader(csvfile) + _args.output_file.write( + 'const struct pmu_events_map pmu_events_map[] = {\n') + first = True + for row in table: + # Skip the first row or any row beginning with #. + if not first and len(row) > 0 and not row[0].startswith('#'): + tblname = file_name_to_table_name([], row[2].replace('/', '_')) + _args.output_file.write("""{ +\t.cpuid = \"%s\", +\t.version = \"%s\", +\t.type = \"%s\", +\t.table = %s +}, +""" % (row[0].replace('\\', '\\\\'), row[1], row[3], tblname)) + first = False + + _args.output_file.write("""{ +\t.cpuid = "testcpu", +\t.version = "v1", +\t.type = "core", +\t.table = pme_test_soc_cpu, +}, +{ +\t.cpuid = 0, +\t.version = 0, +\t.type = 0, +\t.table = 0, +}, +}; +""") + + +def print_system_mapping_table() -> None: + """C struct mapping table array for tables from /sys directories.""" + _args.output_file.write( + '\nconst struct pmu_sys_events pmu_sys_event_tables[] = {\n') + for tblname in _sys_event_tables: + _args.output_file.write(f"""\t{{ +\t\t.table = {tblname}, +\t\t.name = \"{tblname}\", +\t}}, +""") + _args.output_file.write("""\t{ +\t\t.table = 0 +\t}, +}; +""") + + +def main() -> None: + global _args + + def dir_path(path: str) -> str: + """Validate path is a directory for argparse.""" + if os.path.isdir(path): + return path + raise argparse.ArgumentTypeError(f'\'{path}\' is not a valid directory') + + def ftw(path: str, parents: Sequence[str], + action: Callable[[Sequence[str], os.DirEntry], None]) -> None: + """Replicate the directory/file walking behavior of C's file tree walk.""" + for item in os.scandir(path): + action(parents, item) + if item.is_dir(): + ftw(item.path, parents + [item.name], action) + + ap = argparse.ArgumentParser() + ap.add_argument('arch', help='Architecture name like x86') + ap.add_argument( + 'starting_dir', + type=dir_path, + help='Root of tree containing architecture directories containing json files' + ) + ap.add_argument( + 'output_file', type=argparse.FileType('w'), nargs='?', default=sys.stdout) + _args = ap.parse_args() + + _args.output_file.write("#include \"pmu-events/pmu-events.h\"\n") + for path in [_args.arch, 'test']: + arch_path = f'{_args.starting_dir}/{path}' + if not os.path.isdir(arch_path): + raise IOError(f'Missing architecture directory in \'{arch_path}\'') + preprocess_arch_std_files(arch_path) + ftw(arch_path, [], process_one_file) + print_events_table_suffix() + + print_mapping_table() + print_system_mapping_table() + + +if __name__ == '__main__': + main() -- cgit v1.2.3 From 00facc760903be6675870c2749e2cd72140e396e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 29 Jun 2022 11:25:04 -0700 Subject: perf jevents: Switch build to use jevents.py Generate pmu-events.c using jevents.py rather than the binary built from jevents.c. Add a new config variable NO_JEVENTS that is set when there is no architecture json or an appropriate python interpreter isn't present. When NO_JEVENTS is defined the file pmu-events/empty-pmu-events.c is copied and used as the pmu-events.c file. Signed-off-by: Ian Rogers Tested-by: John Garry Cc: Alexander Shishkin Cc: Ananth Narayan Cc: Andi Kleen Cc: Andrew Kilroy Cc: Caleb Biggers Cc: Felix Fietkau Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kajol Jain Cc: Kan Liang Cc: Kshipra Bopardikar Cc: Like Xu Cc: Mark Rutland Cc: Mathieu Poirier Cc: Namhyung Kim Cc: Nick Forrington Cc: Paul Clarke Cc: Perry Taylor Cc: Peter Zijlstra Cc: Qi Liu Cc: Ravi Bangoria Cc: Sandipan Das Cc: Santosh Shukla Cc: Stephane Eranian Cc: Will Deacon Cc: Xing Zhengjun Link: https://lore.kernel.org/r/20220629182505.406269-4-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 19 ++++ tools/perf/Makefile.perf | 1 + tools/perf/pmu-events/Build | 13 ++- tools/perf/pmu-events/empty-pmu-events.c | 158 +++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 tools/perf/pmu-events/empty-pmu-events.c (limited to 'tools') diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 16c1a87444b8..153c18909ff5 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -889,6 +889,25 @@ else endif endif +ifneq ($(NO_JEVENTS),1) + ifeq ($(wildcard pmu-events/arch/$(SRCARCH)/mapfile.csv),) + NO_JEVENTS := 1 + endif +endif +ifneq ($(NO_JEVENTS),1) + NO_JEVENTS := 0 + ifndef PYTHON + $(warning No python interpreter disabling jevent generation) + NO_JEVENTS := 1 + else + # jevents.py uses f-strings present in Python 3.6 released in Dec. 2016. + JEVENTS_PYTHON_GOOD := $(shell $(PYTHON) -c 'import sys;print("1" if(sys.version_info.major >= 3 and sys.version_info.minor >= 6) else "0")' 2> /dev/null) + ifneq ($(JEVENTS_PYTHON_GOOD), 1) + $(warning Python interpreter too old (older than 3.6) disabling jevent generation) + NO_JEVENTS := 1 + endif + endif +endif ifndef NO_LIBBFD ifeq ($(feature-libbfd), 1) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 1e29c8936f71..dc6b177ac1de 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -655,6 +655,7 @@ JEVENTS := $(OUTPUT)pmu-events/jevents JEVENTS_IN := $(OUTPUT)pmu-events/jevents-in.o PMU_EVENTS_IN := $(OUTPUT)pmu-events/pmu-events-in.o +export NO_JEVENTS export JEVENTS diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index a055dee6a46a..5ec5ce8c31ba 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -9,10 +9,19 @@ JSON = $(shell [ -d $(JDIR) ] && \ JDIR_TEST = pmu-events/arch/test JSON_TEST = $(shell [ -d $(JDIR_TEST) ] && \ find $(JDIR_TEST) -name '*.json') +JEVENTS_PY = pmu-events/jevents.py # # Locate/process JSON files in pmu-events/arch/ # directory and create tables in pmu-events.c. # -$(OUTPUT)pmu-events/pmu-events.c: $(JSON) $(JSON_TEST) $(JEVENTS) - $(Q)$(call echo-cmd,gen)$(JEVENTS) $(SRCARCH) pmu-events/arch $(OUTPUT)pmu-events/pmu-events.c $(V) + +ifeq ($(NO_JEVENTS),1) +$(OUTPUT)pmu-events/pmu-events.c: pmu-events/empty-pmu-events.c + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)cp $< $@ +else +$(OUTPUT)pmu-events/pmu-events.c: $(JSON) $(JSON_TEST) $(JEVENTS_PY) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(SRCARCH) pmu-events/arch $@ +endif diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c new file mode 100644 index 000000000000..77e655c6f116 --- /dev/null +++ b/tools/perf/pmu-events/empty-pmu-events.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * An empty pmu-events.c file used when there is no architecture json files in + * arch or when the jevents.py script cannot be run. + * + * The test cpu/soc is provided for testing. + */ +#include "pmu-events/pmu-events.h" + +static const struct pmu_event pme_test_soc_cpu[] = { + { + .name = "l3_cache_rd", + .event = "event=0x40", + .desc = "L3 cache access, read", + .topic = "cache", + .long_desc = "Attributable Level 3 cache access, read", + }, + { + .name = "segment_reg_loads.any", + .event = "event=0x6,period=200000,umask=0x80", + .desc = "Number of segment register loads", + .topic = "other", + }, + { + .name = "dispatch_blocked.any", + .event = "event=0x9,period=200000,umask=0x20", + .desc = "Memory cluster signals to block micro-op dispatch for any reason", + .topic = "other", + }, + { + .name = "eist_trans", + .event = "event=0x3a,period=200000,umask=0x0", + .desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions", + .topic = "other", + }, + { + .name = "uncore_hisi_ddrc.flux_wcmd", + .event = "event=0x2", + .desc = "DDRC write commands. Unit: hisi_sccl,ddrc ", + .topic = "uncore", + .long_desc = "DDRC write commands", + .pmu = "hisi_sccl,ddrc", + }, + { + .name = "unc_cbo_xsnp_response.miss_eviction", + .event = "event=0x22,umask=0x81", + .desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core. Unit: uncore_cbox ", + .topic = "uncore", + .long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core", + .pmu = "uncore_cbox", + }, + { + .name = "event-hyphen", + .event = "event=0xe0,umask=0x00", + .desc = "UNC_CBO_HYPHEN. Unit: uncore_cbox ", + .topic = "uncore", + .long_desc = "UNC_CBO_HYPHEN", + .pmu = "uncore_cbox", + }, + { + .name = "event-two-hyph", + .event = "event=0xc0,umask=0x00", + .desc = "UNC_CBO_TWO_HYPH. Unit: uncore_cbox ", + .topic = "uncore", + .long_desc = "UNC_CBO_TWO_HYPH", + .pmu = "uncore_cbox", + }, + { + .name = "uncore_hisi_l3c.rd_hit_cpipe", + .event = "event=0x7", + .desc = "Total read hits. Unit: hisi_sccl,l3c ", + .topic = "uncore", + .long_desc = "Total read hits", + .pmu = "hisi_sccl,l3c", + }, + { + .name = "uncore_imc_free_running.cache_miss", + .event = "event=0x12", + .desc = "Total cache misses. Unit: uncore_imc_free_running ", + .topic = "uncore", + .long_desc = "Total cache misses", + .pmu = "uncore_imc_free_running", + }, + { + .name = "uncore_imc.cache_hits", + .event = "event=0x34", + .desc = "Total cache hits. Unit: uncore_imc ", + .topic = "uncore", + .long_desc = "Total cache hits", + .pmu = "uncore_imc", + }, + { + .name = "bp_l1_btb_correct", + .event = "event=0x8a", + .desc = "L1 BTB Correction", + .topic = "branch", + }, + { + .name = "bp_l2_btb_correct", + .event = "event=0x8b", + .desc = "L2 BTB Correction", + .topic = "branch", + }, + { + .name = 0, + .event = 0, + .desc = 0, + }, +}; + +const struct pmu_events_map pmu_events_map[] = { + { + .cpuid = "testcpu", + .version = "v1", + .type = "core", + .table = pme_test_soc_cpu, + }, + { + .cpuid = 0, + .version = 0, + .type = 0, + .table = 0, + }, +}; + +static const struct pmu_event pme_test_soc_sys[] = { + { + .name = "sys_ddr_pmu.write_cycles", + .event = "event=0x2b", + .desc = "ddr write-cycles event. Unit: uncore_sys_ddr_pmu ", + .compat = "v8", + .topic = "uncore", + .pmu = "uncore_sys_ddr_pmu", + }, + { + .name = "sys_ccn_pmu.read_cycles", + .event = "config=0x2c", + .desc = "ccn read-cycles event. Unit: uncore_sys_ccn_pmu ", + .compat = "0x01", + .topic = "uncore", + .pmu = "uncore_sys_ccn_pmu", + }, + { + .name = 0, + .event = 0, + .desc = 0, + }, +}; + +const struct pmu_sys_events pmu_sys_event_tables[] = { + { + .table = pme_test_soc_sys, + .name = "pme_test_soc_sys", + }, + { + .table = 0 + }, +}; -- cgit v1.2.3 From 5a059790afe8a6e11dbe45475eb45401ec9937ea Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 29 Jun 2022 11:25:05 -0700 Subject: perf jevents: Remove jevents.c Remove files and build rules. Remove test for comparing with jevents.py as there is no longer a binary to compare with. Signed-off-by: Ian Rogers Tested-by: John Garry Cc: Alexander Shishkin Cc: Ananth Narayan Cc: Andi Kleen Cc: Andrew Kilroy Cc: Caleb Biggers Cc: Felix Fietkau Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kajol Jain Cc: Kan Liang Cc: Kshipra Bopardikar Cc: Like Xu Cc: Mark Rutland Cc: Mathieu Poirier Cc: Namhyung Kim Cc: Nick Forrington Cc: Paul Clarke Cc: Perry Taylor Cc: Peter Zijlstra Cc: Qi Liu Cc: Ravi Bangoria Cc: Sandipan Das Cc: Santosh Shukla Cc: Stephane Eranian Cc: Will Deacon Cc: Xing Zhengjun Link: https://lore.kernel.org/r/20220629182505.406269-5-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 21 +- tools/perf/pmu-events/Build | 4 - tools/perf/pmu-events/jevents-test.sh | 33 - tools/perf/pmu-events/jevents.c | 1342 --------------------------------- tools/perf/pmu-events/jsmn.c | 352 --------- tools/perf/pmu-events/jsmn.h | 68 -- tools/perf/pmu-events/json.c | 162 ---- tools/perf/pmu-events/json.h | 39 - 8 files changed, 2 insertions(+), 2019 deletions(-) delete mode 100755 tools/perf/pmu-events/jevents-test.sh delete mode 100644 tools/perf/pmu-events/jevents.c delete mode 100644 tools/perf/pmu-events/jsmn.c delete mode 100644 tools/perf/pmu-events/jsmn.h delete mode 100644 tools/perf/pmu-events/json.c delete mode 100644 tools/perf/pmu-events/json.h (limited to 'tools') diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index dc6b177ac1de..8f0b1fb39984 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -651,32 +651,15 @@ strip: $(PROGRAMS) $(OUTPUT)perf PERF_IN := $(OUTPUT)perf-in.o -JEVENTS := $(OUTPUT)pmu-events/jevents -JEVENTS_IN := $(OUTPUT)pmu-events/jevents-in.o - PMU_EVENTS_IN := $(OUTPUT)pmu-events/pmu-events-in.o export NO_JEVENTS -export JEVENTS - build := -f $(srctree)/tools/build/Makefile.build dir=. obj $(PERF_IN): prepare FORCE $(Q)$(MAKE) $(build)=perf -$(JEVENTS_IN): FORCE - $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=jevents - -$(JEVENTS): $(JEVENTS_IN) - $(QUIET_LINK)$(HOSTCC) $(JEVENTS_IN) -o $@ - -JEVENTS_PY := pmu-events/jevents.py -JEVENTS_PY_TEST := pmu-events/jevents-test.sh -.PHONY: jevents-py-test -jevents-py-test: $(JEVENTS) - $(Q)$(call echo-cmd,gen)$(JEVENTS_PY_TEST) $(JEVENTS) $(JEVENTS_PY) pmu-events/arch - -$(PMU_EVENTS_IN): $(JEVENTS) FORCE +$(PMU_EVENTS_IN): FORCE $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=pmu-events $(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(PMU_EVENTS_IN) $(LIBTRACEEVENT_DYNAMIC_LIST) @@ -1096,7 +1079,7 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea $(call QUIET_CLEAN, core-objs) $(RM) $(LIBPERF_A) $(OUTPUT)perf-archive $(OUTPUT)perf-iostat $(LANG_BINDINGS) $(Q)find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete $(Q)$(RM) $(OUTPUT).config-detected - $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(OUTPUT)pmu-events/jevents $(OUTPUT)$(LIBJVMTI).so + $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(OUTPUT)$(LIBJVMTI).so $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ $(OUTPUT)util/intel-pt-decoder/inat-tables.c \ $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \ diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index 5ec5ce8c31ba..28a9d01b08af 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -1,7 +1,3 @@ -hostprogs := jevents - -jevents-y += json.o jsmn.o jevents.o -HOSTCFLAGS_jevents.o = -I$(srctree)/tools/include pmu-events-y += pmu-events.o JDIR = pmu-events/arch/$(SRCARCH) JSON = $(shell [ -d $(JDIR) ] && \ diff --git a/tools/perf/pmu-events/jevents-test.sh b/tools/perf/pmu-events/jevents-test.sh deleted file mode 100755 index 9ae852292576..000000000000 --- a/tools/perf/pmu-events/jevents-test.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -# Validate that the legacy jevents and jevents.py produce identical output. -set -e - -JEVENTS="$1" -JEVENTS_PY="$2" -ARCH_PATH="$3" -JEVENTS_C_GENERATED=$(mktemp /tmp/jevents_c.XXXXX.c) -JEVENTS_PY_GENERATED=$(mktemp /tmp/jevents_py.XXXXX.c) - -cleanup() { - rm "$JEVENTS_C_GENERATED" "$JEVENTS_PY_GENERATED" - trap - exit term int -} -trap cleanup exit term int - -for path in "$ARCH_PATH"/* -do - arch=$(basename $path) - if [ "$arch" = "test" ] - then - continue - fi - echo "Checking architecture: $arch" - echo "Generating using jevents.c" - "$JEVENTS" "$arch" "$ARCH_PATH" "$JEVENTS_C_GENERATED" - echo "Generating using jevents.py" - "$JEVENTS_PY" "$arch" "$ARCH_PATH" "$JEVENTS_PY_GENERATED" - echo "Diffing" - diff -u "$JEVENTS_C_GENERATED" "$JEVENTS_PY_GENERATED" -done -cleanup diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c deleted file mode 100644 index e597e4bac90f..000000000000 --- a/tools/perf/pmu-events/jevents.c +++ /dev/null @@ -1,1342 +0,0 @@ -#define _XOPEN_SOURCE 500 /* needed for nftw() */ -#define _GNU_SOURCE /* needed for asprintf() */ - -/* Parse event JSON files */ - -/* - * Copyright (c) 2014, Intel Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* getrlimit */ -#include /* getrlimit */ -#include -#include -#include -#include -#include "jsmn.h" -#include "json.h" -#include "pmu-events.h" - -int verbose; -char *prog; - -struct json_event { - char *name; - char *compat; - char *event; - char *desc; - char *long_desc; - char *pmu; - char *unit; - char *perpkg; - char *aggr_mode; - char *metric_expr; - char *metric_name; - char *metric_group; - char *deprecated; - char *metric_constraint; -}; - -static enum aggr_mode_class convert(const char *aggr_mode) -{ - if (!strcmp(aggr_mode, "PerCore")) - return PerCore; - else if (!strcmp(aggr_mode, "PerChip")) - return PerChip; - - pr_err("%s: Wrong AggregationMode value '%s'\n", prog, aggr_mode); - return -1; -} - -static LIST_HEAD(sys_event_tables); - -struct sys_event_table { - struct list_head list; - char *soc_id; -}; - -static void free_sys_event_tables(void) -{ - struct sys_event_table *et, *next; - - list_for_each_entry_safe(et, next, &sys_event_tables, list) { - free(et->soc_id); - free(et); - } -} - -int eprintf(int level, int var, const char *fmt, ...) -{ - - int ret; - va_list args; - - if (var < level) - return 0; - - va_start(args, fmt); - - ret = vfprintf(stderr, fmt, args); - - va_end(args); - - return ret; -} - -static void addfield(char *map, char **dst, const char *sep, - const char *a, jsmntok_t *bt) -{ - unsigned int len = strlen(a) + 1 + strlen(sep); - int olen = *dst ? strlen(*dst) : 0; - int blen = bt ? json_len(bt) : 0; - char *out; - - out = realloc(*dst, len + olen + blen); - if (!out) { - /* Don't add field in this case */ - return; - } - *dst = out; - - if (!olen) - *(*dst) = 0; - else - strcat(*dst, sep); - strcat(*dst, a); - if (bt) - strncat(*dst, map + bt->start, blen); -} - -static void fixname(char *s) -{ - for (; *s; s++) - *s = tolower(*s); -} - -static void fixdesc(char *s) -{ - char *e = s + strlen(s); - - /* Remove trailing dots that look ugly in perf list */ - --e; - while (e >= s && isspace(*e)) - --e; - if (*e == '.') - *e = 0; -} - -/* Add escapes for '\' so they are proper C strings. */ -static char *fixregex(char *s) -{ - int len = 0; - int esc_count = 0; - char *fixed = NULL; - char *p, *q; - - /* Count the number of '\' in string */ - for (p = s; *p; p++) { - ++len; - if (*p == '\\') - ++esc_count; - } - - if (esc_count == 0) - return s; - - /* allocate space for a new string */ - fixed = (char *) malloc(len + esc_count + 1); - if (!fixed) - return NULL; - - /* copy over the characters */ - q = fixed; - for (p = s; *p; p++) { - if (*p == '\\') { - *q = '\\'; - ++q; - } - *q = *p; - ++q; - } - *q = '\0'; - return fixed; -} - -static struct msrmap { - const char *num; - const char *pname; -} msrmap[] = { - { "0x3F6", "ldlat=" }, - { "0x1A6", "offcore_rsp=" }, - { "0x1A7", "offcore_rsp=" }, - { "0x3F7", "frontend=" }, - { NULL, NULL } -}; - -static void cut_comma(char *map, jsmntok_t *newval) -{ - int i; - - /* Cut off everything after comma */ - for (i = newval->start; i < newval->end; i++) { - if (map[i] == ',') - newval->end = i; - } -} - -static struct msrmap *lookup_msr(char *map, jsmntok_t *val) -{ - jsmntok_t newval = *val; - static bool warned; - int i; - - cut_comma(map, &newval); - for (i = 0; msrmap[i].num; i++) - if (json_streq(map, &newval, msrmap[i].num)) - return &msrmap[i]; - if (!warned) { - warned = true; - pr_err("%s: Unknown MSR in event file %.*s\n", prog, - json_len(val), map + val->start); - } - return NULL; -} - -static struct map { - const char *json; - const char *perf; -} unit_to_pmu[] = { - { "CBO", "uncore_cbox" }, - { "QPI LL", "uncore_qpi" }, - { "SBO", "uncore_sbox" }, - { "iMPH-U", "uncore_arb" }, - { "CPU-M-CF", "cpum_cf" }, - { "CPU-M-SF", "cpum_sf" }, - { "UPI LL", "uncore_upi" }, - { "hisi_sicl,cpa", "hisi_sicl,cpa"}, - { "hisi_sccl,ddrc", "hisi_sccl,ddrc" }, - { "hisi_sccl,hha", "hisi_sccl,hha" }, - { "hisi_sccl,l3c", "hisi_sccl,l3c" }, - /* it's not realistic to keep adding these, we need something more scalable ... */ - { "imx8_ddr", "imx8_ddr" }, - { "L3PMC", "amd_l3" }, - { "DFPMC", "amd_df" }, - { "cpu_core", "cpu_core" }, - { "cpu_atom", "cpu_atom" }, - {} -}; - -static const char *field_to_perf(struct map *table, char *map, jsmntok_t *val) -{ - int i; - - for (i = 0; table[i].json; i++) { - if (json_streq(map, val, table[i].json)) - return table[i].perf; - } - return NULL; -} - -#define EXPECT(e, t, m) do { if (!(e)) { \ - jsmntok_t *loc = (t); \ - if (!(t)->start && (t) > tokens) \ - loc = (t) - 1; \ - pr_err("%s:%d: " m ", got %s\n", fn, \ - json_line(map, loc), \ - json_name(t)); \ - err = -EIO; \ - goto out_free; \ -} } while (0) - -static char *topic; - -static char *get_topic(void) -{ - char *tp; - int i; - - /* tp is free'd in process_one_file() */ - i = asprintf(&tp, "%s", topic); - if (i < 0) { - pr_info("%s: asprintf() error %s\n", prog); - return NULL; - } - - for (i = 0; i < (int) strlen(tp); i++) { - char c = tp[i]; - - if (c == '-') - tp[i] = ' '; - else if (c == '.') { - tp[i] = '\0'; - break; - } - } - - return tp; -} - -static int add_topic(char *bname) -{ - free(topic); - topic = strdup(bname); - if (!topic) { - pr_info("%s: strdup() error %s for file %s\n", prog, - strerror(errno), bname); - return -ENOMEM; - } - return 0; -} - -struct perf_entry_data { - FILE *outfp; - char *topic; -}; - -static int close_table; - -static void print_events_table_prefix(FILE *fp, const char *tblname) -{ - fprintf(fp, "static const struct pmu_event %s[] = {\n", tblname); - close_table = 1; -} - -static int print_events_table_entry(void *data, struct json_event *je) -{ - struct perf_entry_data *pd = data; - FILE *outfp = pd->outfp; - char *topic_local = pd->topic; - - /* - * TODO: Remove formatting chars after debugging to reduce - * string lengths. - */ - fprintf(outfp, "{\n"); - - if (je->name) - fprintf(outfp, "\t.name = \"%s\",\n", je->name); - if (je->event) - fprintf(outfp, "\t.event = \"%s\",\n", je->event); - fprintf(outfp, "\t.desc = \"%s\",\n", je->desc); - if (je->compat) - fprintf(outfp, "\t.compat = \"%s\",\n", je->compat); - fprintf(outfp, "\t.topic = \"%s\",\n", topic_local); - if (je->long_desc && je->long_desc[0]) - fprintf(outfp, "\t.long_desc = \"%s\",\n", je->long_desc); - if (je->pmu) - fprintf(outfp, "\t.pmu = \"%s\",\n", je->pmu); - if (je->unit) - fprintf(outfp, "\t.unit = \"%s\",\n", je->unit); - if (je->perpkg) - fprintf(outfp, "\t.perpkg = \"%s\",\n", je->perpkg); - if (je->aggr_mode) - fprintf(outfp, "\t.aggr_mode = \"%d\",\n", convert(je->aggr_mode)); - if (je->metric_expr) - fprintf(outfp, "\t.metric_expr = \"%s\",\n", je->metric_expr); - if (je->metric_name) - fprintf(outfp, "\t.metric_name = \"%s\",\n", je->metric_name); - if (je->metric_group) - fprintf(outfp, "\t.metric_group = \"%s\",\n", je->metric_group); - if (je->deprecated) - fprintf(outfp, "\t.deprecated = \"%s\",\n", je->deprecated); - if (je->metric_constraint) - fprintf(outfp, "\t.metric_constraint = \"%s\",\n", je->metric_constraint); - fprintf(outfp, "},\n"); - - return 0; -} - -struct event_struct { - struct list_head list; - char *name; - char *event; - char *compat; - char *desc; - char *long_desc; - char *pmu; - char *unit; - char *perpkg; - char *aggr_mode; - char *metric_expr; - char *metric_name; - char *metric_group; - char *deprecated; - char *metric_constraint; -}; - -#define ADD_EVENT_FIELD(field) do { if (je->field) { \ - es->field = strdup(je->field); \ - if (!es->field) \ - goto out_free; \ -} } while (0) - -#define FREE_EVENT_FIELD(field) free(es->field) - -#define TRY_FIXUP_FIELD(field) do { if (es->field && !je->field) {\ - je->field = strdup(es->field); \ - if (!je->field) \ - return -ENOMEM; \ -} } while (0) - -#define FOR_ALL_EVENT_STRUCT_FIELDS(op) do { \ - op(name); \ - op(event); \ - op(desc); \ - op(long_desc); \ - op(pmu); \ - op(unit); \ - op(perpkg); \ - op(aggr_mode); \ - op(metric_expr); \ - op(metric_name); \ - op(metric_group); \ - op(deprecated); \ -} while (0) - -static LIST_HEAD(arch_std_events); - -static void free_arch_std_events(void) -{ - struct event_struct *es, *next; - - list_for_each_entry_safe(es, next, &arch_std_events, list) { - FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD); - list_del_init(&es->list); - free(es); - } -} - -static int save_arch_std_events(void *data __maybe_unused, struct json_event *je) -{ - struct event_struct *es; - - es = malloc(sizeof(*es)); - if (!es) - return -ENOMEM; - memset(es, 0, sizeof(*es)); - FOR_ALL_EVENT_STRUCT_FIELDS(ADD_EVENT_FIELD); - list_add_tail(&es->list, &arch_std_events); - return 0; -out_free: - FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD); - free(es); - return -ENOMEM; -} - -static void print_events_table_suffix(FILE *outfp) -{ - fprintf(outfp, "{\n"); - - fprintf(outfp, "\t.name = 0,\n"); - fprintf(outfp, "\t.event = 0,\n"); - fprintf(outfp, "\t.desc = 0,\n"); - - fprintf(outfp, "},\n"); - fprintf(outfp, "};\n"); - close_table = 0; -} - -static struct fixed { - const char *name; - const char *event; -} fixed[] = { - { "inst_retired.any", "event=0xc0,period=2000003" }, - { "inst_retired.any_p", "event=0xc0,period=2000003" }, - { "cpu_clk_unhalted.ref", "event=0x0,umask=0x03,period=2000003" }, - { "cpu_clk_unhalted.thread", "event=0x3c,period=2000003" }, - { "cpu_clk_unhalted.core", "event=0x3c,period=2000003" }, - { "cpu_clk_unhalted.thread_any", "event=0x3c,any=1,period=2000003" }, - { NULL, NULL}, -}; - -/* - * Handle different fixed counter encodings between JSON and perf. - */ -static char *real_event(const char *name, char *event) -{ - int i; - - if (!name) - return NULL; - - for (i = 0; fixed[i].name; i++) - if (!strcasecmp(name, fixed[i].name)) - return (char *)fixed[i].event; - return event; -} - -static int -try_fixup(const char *fn, char *arch_std, struct json_event *je, char **event) -{ - /* try to find matching event from arch standard values */ - struct event_struct *es; - - list_for_each_entry(es, &arch_std_events, list) { - if (!strcmp(arch_std, es->name)) { - FOR_ALL_EVENT_STRUCT_FIELDS(TRY_FIXUP_FIELD); - *event = je->event; - return 0; - } - } - - pr_err("%s: could not find matching %s for %s\n", - prog, arch_std, fn); - return -1; -} - -/* Call func with each event in the json file */ -static int json_events(const char *fn, - int (*func)(void *data, struct json_event *je), - void *data) -{ - int err; - size_t size; - jsmntok_t *tokens, *tok; - int i, j, len; - char *map; - char buf[128]; - - if (!fn) - return -ENOENT; - - tokens = parse_json(fn, &map, &size, &len); - if (!tokens) - return -EIO; - EXPECT(tokens->type == JSMN_ARRAY, tokens, "expected top level array"); - tok = tokens + 1; - for (i = 0; i < tokens->size; i++) { - char *event = NULL; - char *extra_desc = NULL; - char *filter = NULL; - struct json_event je = {}; - char *arch_std = NULL; - unsigned long long eventcode = 0; - unsigned long long configcode = 0; - struct msrmap *msr = NULL; - jsmntok_t *msrval = NULL; - jsmntok_t *precise = NULL; - jsmntok_t *obj = tok++; - bool configcode_present = false; - char *umask = NULL; - char *cmask = NULL; - char *inv = NULL; - char *any = NULL; - char *edge = NULL; - char *period = NULL; - char *fc_mask = NULL; - char *ch_mask = NULL; - - EXPECT(obj->type == JSMN_OBJECT, obj, "expected object"); - for (j = 0; j < obj->size; j += 2) { - jsmntok_t *field, *val; - int nz; - char *s; - - field = tok + j; - EXPECT(field->type == JSMN_STRING, tok + j, - "Expected field name"); - val = tok + j + 1; - EXPECT(val->type == JSMN_STRING, tok + j + 1, - "Expected string value"); - - nz = !json_streq(map, val, "0"); - /* match_field */ - if (json_streq(map, field, "UMask") && nz) { - addfield(map, &umask, "", "umask=", val); - } else if (json_streq(map, field, "CounterMask") && nz) { - addfield(map, &cmask, "", "cmask=", val); - } else if (json_streq(map, field, "Invert") && nz) { - addfield(map, &inv, "", "inv=", val); - } else if (json_streq(map, field, "AnyThread") && nz) { - addfield(map, &any, "", "any=", val); - } else if (json_streq(map, field, "EdgeDetect") && nz) { - addfield(map, &edge, "", "edge=", val); - } else if (json_streq(map, field, "SampleAfterValue") && nz) { - addfield(map, &period, "", "period=", val); - } else if (json_streq(map, field, "FCMask") && nz) { - addfield(map, &fc_mask, "", "fc_mask=", val); - } else if (json_streq(map, field, "PortMask") && nz) { - addfield(map, &ch_mask, "", "ch_mask=", val); - } else if (json_streq(map, field, "EventCode")) { - char *code = NULL; - addfield(map, &code, "", "", val); - eventcode |= strtoul(code, NULL, 0); - free(code); - } else if (json_streq(map, field, "ConfigCode")) { - char *code = NULL; - addfield(map, &code, "", "", val); - configcode |= strtoul(code, NULL, 0); - free(code); - configcode_present = true; - } else if (json_streq(map, field, "ExtSel")) { - char *code = NULL; - addfield(map, &code, "", "", val); - eventcode |= strtoul(code, NULL, 0) << 8; - free(code); - } else if (json_streq(map, field, "EventName")) { - addfield(map, &je.name, "", "", val); - } else if (json_streq(map, field, "Compat")) { - addfield(map, &je.compat, "", "", val); - } else if (json_streq(map, field, "BriefDescription")) { - addfield(map, &je.desc, "", "", val); - fixdesc(je.desc); - } else if (json_streq(map, field, - "PublicDescription")) { - addfield(map, &je.long_desc, "", "", val); - fixdesc(je.long_desc); - } else if (json_streq(map, field, "PEBS") && nz) { - precise = val; - } else if (json_streq(map, field, "MSRIndex") && nz) { - msr = lookup_msr(map, val); - } else if (json_streq(map, field, "MSRValue")) { - msrval = val; - } else if (json_streq(map, field, "Errata") && - !json_streq(map, val, "null")) { - addfield(map, &extra_desc, ". ", - " Spec update: ", val); - } else if (json_streq(map, field, "Data_LA") && nz) { - addfield(map, &extra_desc, ". ", - " Supports address when precise", - NULL); - } else if (json_streq(map, field, "Unit")) { - const char *ppmu; - - ppmu = field_to_perf(unit_to_pmu, map, val); - if (ppmu) { - je.pmu = strdup(ppmu); - } else { - if (!je.pmu) - je.pmu = strdup("uncore_"); - addfield(map, &je.pmu, "", "", val); - for (s = je.pmu; *s; s++) - *s = tolower(*s); - } - } else if (json_streq(map, field, "Filter")) { - addfield(map, &filter, "", "", val); - } else if (json_streq(map, field, "ScaleUnit")) { - addfield(map, &je.unit, "", "", val); - } else if (json_streq(map, field, "PerPkg")) { - addfield(map, &je.perpkg, "", "", val); - } else if (json_streq(map, field, "AggregationMode")) { - addfield(map, &je.aggr_mode, "", "", val); - } else if (json_streq(map, field, "Deprecated")) { - addfield(map, &je.deprecated, "", "", val); - } else if (json_streq(map, field, "MetricName")) { - addfield(map, &je.metric_name, "", "", val); - } else if (json_streq(map, field, "MetricGroup")) { - addfield(map, &je.metric_group, "", "", val); - } else if (json_streq(map, field, "MetricConstraint")) { - addfield(map, &je.metric_constraint, "", "", val); - } else if (json_streq(map, field, "MetricExpr")) { - addfield(map, &je.metric_expr, "", "", val); - } else if (json_streq(map, field, "ArchStdEvent")) { - addfield(map, &arch_std, "", "", val); - for (s = arch_std; *s; s++) - *s = tolower(*s); - } - /* ignore unknown fields */ - } - if (precise && je.desc && !strstr(je.desc, "(Precise Event)")) { - if (json_streq(map, precise, "2")) - addfield(map, &extra_desc, " ", - "(Must be precise)", NULL); - else - addfield(map, &extra_desc, " ", - "(Precise event)", NULL); - } - if (configcode_present) - snprintf(buf, sizeof buf, "config=%#llx", configcode); - else - snprintf(buf, sizeof buf, "event=%#llx", eventcode); - addfield(map, &event, ",", buf, NULL); - if (any) - addfield(map, &event, ",", any, NULL); - if (ch_mask) - addfield(map, &event, ",", ch_mask, NULL); - if (cmask) - addfield(map, &event, ",", cmask, NULL); - if (edge) - addfield(map, &event, ",", edge, NULL); - if (fc_mask) - addfield(map, &event, ",", fc_mask, NULL); - if (inv) - addfield(map, &event, ",", inv, NULL); - if (period) - addfield(map, &event, ",", period, NULL); - if (umask) - addfield(map, &event, ",", umask, NULL); - - if (je.desc && extra_desc) - addfield(map, &je.desc, " ", extra_desc, NULL); - if (je.long_desc && extra_desc) - addfield(map, &je.long_desc, " ", extra_desc, NULL); - if (je.pmu) { - addfield(map, &je.desc, ". ", "Unit: ", NULL); - addfield(map, &je.desc, "", je.pmu, NULL); - addfield(map, &je.desc, "", " ", NULL); - } - if (filter) - addfield(map, &event, ",", filter, NULL); - if (msr != NULL) - addfield(map, &event, ",", msr->pname, msrval); - if (je.name) - fixname(je.name); - - if (arch_std) { - /* - * An arch standard event is referenced, so try to - * fixup any unassigned values. - */ - err = try_fixup(fn, arch_std, &je, &event); - if (err) - goto free_strings; - } - je.event = real_event(je.name, event); - err = func(data, &je); -free_strings: - free(umask); - free(cmask); - free(inv); - free(any); - free(edge); - free(period); - free(fc_mask); - free(ch_mask); - free(event); - free(je.desc); - free(je.name); - free(je.compat); - free(je.long_desc); - free(extra_desc); - free(je.pmu); - free(filter); - free(je.perpkg); - free(je.aggr_mode); - free(je.deprecated); - free(je.unit); - free(je.metric_expr); - free(je.metric_name); - free(je.metric_group); - free(je.metric_constraint); - free(arch_std); - - if (err) - break; - tok += j; - } - EXPECT(tok - tokens == len, tok, "unexpected objects at end"); - err = 0; -out_free: - free_json(map, size, tokens); - return err; -} - -static char *file_name_to_table_name(char *fname) -{ - unsigned int i; - int n; - int c; - char *tblname; - - /* - * Ensure tablename starts with alphabetic character. - * Derive rest of table name from basename of the JSON file, - * replacing hyphens and stripping out .json suffix. - */ - n = asprintf(&tblname, "pme_%s", fname); - if (n < 0) { - pr_info("%s: asprintf() error %s for file %s\n", prog, - strerror(errno), fname); - return NULL; - } - - for (i = 0; i < strlen(tblname); i++) { - c = tblname[i]; - - if (c == '-' || c == '/') - tblname[i] = '_'; - else if (c == '.') { - tblname[i] = '\0'; - break; - } else if (!isalnum(c) && c != '_') { - pr_err("%s: Invalid character '%c' in file name %s\n", - prog, c, basename(fname)); - free(tblname); - tblname = NULL; - break; - } - } - - return tblname; -} - -static bool is_sys_dir(char *fname) -{ - size_t len = strlen(fname), len2 = strlen("/sys"); - - if (len2 > len) - return false; - return !strcmp(fname+len-len2, "/sys"); -} - -static void print_mapping_table_prefix(FILE *outfp) -{ - fprintf(outfp, "const struct pmu_events_map pmu_events_map[] = {\n"); -} - -static void print_mapping_table_suffix(FILE *outfp) -{ - /* - * Print the terminating, NULL entry. - */ - fprintf(outfp, "{\n"); - fprintf(outfp, "\t.cpuid = 0,\n"); - fprintf(outfp, "\t.version = 0,\n"); - fprintf(outfp, "\t.type = 0,\n"); - fprintf(outfp, "\t.table = 0,\n"); - fprintf(outfp, "},\n"); - - /* and finally, the closing curly bracket for the struct */ - fprintf(outfp, "};\n"); -} - -static void print_mapping_test_table(FILE *outfp) -{ - /* - * Print the terminating, NULL entry. - */ - fprintf(outfp, "{\n"); - fprintf(outfp, "\t.cpuid = \"testcpu\",\n"); - fprintf(outfp, "\t.version = \"v1\",\n"); - fprintf(outfp, "\t.type = \"core\",\n"); - fprintf(outfp, "\t.table = pme_test_soc_cpu,\n"); - fprintf(outfp, "},\n"); -} - -static void print_system_event_mapping_table_prefix(FILE *outfp) -{ - fprintf(outfp, "\nconst struct pmu_sys_events pmu_sys_event_tables[] = {"); -} - -static void print_system_event_mapping_table_suffix(FILE *outfp) -{ - fprintf(outfp, "\n\t{\n\t\t.table = 0\n\t},"); - fprintf(outfp, "\n};\n"); -} - -static int process_system_event_tables(FILE *outfp) -{ - struct sys_event_table *sys_event_table; - - print_system_event_mapping_table_prefix(outfp); - - list_for_each_entry(sys_event_table, &sys_event_tables, list) { - fprintf(outfp, "\n\t{\n\t\t.table = %s,\n\t\t.name = \"%s\",\n\t},", - sys_event_table->soc_id, - sys_event_table->soc_id); - } - - print_system_event_mapping_table_suffix(outfp); - - return 0; -} - -static int process_mapfile(FILE *outfp, char *fpath) -{ - int n = 16384; - FILE *mapfp; - char *save = NULL; - char *line, *p; - int line_num; - char *tblname; - int ret = 0; - - pr_info("%s: Processing mapfile %s\n", prog, fpath); - - line = malloc(n); - if (!line) - return -1; - - mapfp = fopen(fpath, "r"); - if (!mapfp) { - pr_info("%s: Error %s opening %s\n", prog, strerror(errno), - fpath); - free(line); - return -1; - } - - print_mapping_table_prefix(outfp); - - /* Skip first line (header) */ - p = fgets(line, n, mapfp); - if (!p) - goto out; - - line_num = 1; - while (1) { - char *cpuid, *version, *type, *fname; - - line_num++; - p = fgets(line, n, mapfp); - if (!p) - break; - - if (line[0] == '#' || line[0] == '\n') - continue; - - if (line[strlen(line)-1] != '\n') { - /* TODO Deal with lines longer than 16K */ - pr_info("%s: Mapfile %s: line %d too long, aborting\n", - prog, fpath, line_num); - ret = -1; - goto out; - } - line[strlen(line)-1] = '\0'; - - cpuid = fixregex(strtok_r(p, ",", &save)); - version = strtok_r(NULL, ",", &save); - fname = strtok_r(NULL, ",", &save); - type = strtok_r(NULL, ",", &save); - - tblname = file_name_to_table_name(fname); - fprintf(outfp, "{\n"); - fprintf(outfp, "\t.cpuid = \"%s\",\n", cpuid); - fprintf(outfp, "\t.version = \"%s\",\n", version); - fprintf(outfp, "\t.type = \"%s\",\n", type); - - /* - * CHECK: We can't use the type (eg "core") field in the - * table name. For us to do that, we need to somehow tweak - * the other caller of file_name_to_table(), process_json() - * to determine the type. process_json() file has no way - * of knowing these are "core" events unless file name has - * core in it. If filename has core in it, we can safely - * ignore the type field here also. - */ - fprintf(outfp, "\t.table = %s\n", tblname); - fprintf(outfp, "},\n"); - } - -out: - print_mapping_test_table(outfp); - print_mapping_table_suffix(outfp); - fclose(mapfp); - free(line); - return ret; -} - -/* - * If we fail to locate/process JSON and map files, create a NULL mapping - * table. This would at least allow perf to build even if we can't find/use - * the aliases. - */ -static void create_empty_mapping(const char *output_file) -{ - FILE *outfp; - - pr_info("%s: Creating empty pmu_events_map[] table\n", prog); - - /* Truncate file to clear any partial writes to it */ - outfp = fopen(output_file, "w"); - if (!outfp) { - perror("fopen()"); - _Exit(1); - } - - fprintf(outfp, "#include \"pmu-events/pmu-events.h\"\n"); - print_mapping_table_prefix(outfp); - print_mapping_table_suffix(outfp); - print_system_event_mapping_table_prefix(outfp); - print_system_event_mapping_table_suffix(outfp); - fclose(outfp); -} - -static int get_maxfds(void) -{ - struct rlimit rlim; - - if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) - return min(rlim.rlim_max / 2, (rlim_t)512); - - return 512; -} - -/* - * nftw() doesn't let us pass an argument to the processing function, - * so use a global variables. - */ -static FILE *eventsfp; -static char *mapfile; - -static int is_leaf_dir(const char *fpath) -{ - DIR *d; - struct dirent *dir; - int res = 1; - - d = opendir(fpath); - if (!d) - return 0; - - while ((dir = readdir(d)) != NULL) { - if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) - continue; - - if (dir->d_type == DT_DIR) { - res = 0; - break; - } else if (dir->d_type == DT_UNKNOWN) { - char path[PATH_MAX]; - struct stat st; - - sprintf(path, "%s/%s", fpath, dir->d_name); - if (stat(path, &st)) - break; - - if (S_ISDIR(st.st_mode)) { - res = 0; - break; - } - } - } - - closedir(d); - - return res; -} - -static int is_json_file(const char *name) -{ - const char *suffix; - - if (strlen(name) < 5) - return 0; - - suffix = name + strlen(name) - 5; - - if (strncmp(suffix, ".json", 5) == 0) - return 1; - return 0; -} - -static int preprocess_arch_std_files(const char *fpath, const struct stat *sb, - int typeflag, struct FTW *ftwbuf) -{ - int level = ftwbuf->level; - int is_file = typeflag == FTW_F; - - if (level == 1 && is_file && is_json_file(fpath)) - return json_events(fpath, save_arch_std_events, (void *)sb); - - return 0; -} - -static int process_one_file(const char *fpath, const struct stat *sb, - int typeflag, struct FTW *ftwbuf) -{ - char *tblname, *bname; - int is_dir = typeflag == FTW_D; - int is_file = typeflag == FTW_F; - int level = ftwbuf->level; - int err = 0; - - if (level >= 2 && is_dir) { - int count = 0; - /* - * For level 2 directory, bname will include parent name, - * like vendor/platform. So search back from platform dir - * to find this. - * Something similar for level 3 directory, but we're a PMU - * category folder, like vendor/platform/cpu. - */ - bname = (char *) fpath + ftwbuf->base - 2; - for (;;) { - if (*bname == '/') - count++; - if (count == level - 1) - break; - bname--; - } - bname++; - } else - bname = (char *) fpath + ftwbuf->base; - - pr_debug("%s %d %7jd %-20s %s\n", - is_file ? "f" : is_dir ? "d" : "x", - level, sb->st_size, bname, fpath); - - /* base dir or too deep */ - if (level == 0 || level > 4) - return 0; - - - /* model directory, reset topic */ - if ((level == 1 && is_dir && is_leaf_dir(fpath)) || - (level >= 2 && is_dir && is_leaf_dir(fpath))) { - if (close_table) - print_events_table_suffix(eventsfp); - - /* - * Drop file name suffix. Replace hyphens with underscores. - * Fail if file name contains any alphanum characters besides - * underscores. - */ - tblname = file_name_to_table_name(bname); - if (!tblname) { - pr_info("%s: Error determining table name for %s\n", prog, - bname); - return -1; - } - - if (is_sys_dir(bname)) { - struct sys_event_table *sys_event_table; - - sys_event_table = malloc(sizeof(*sys_event_table)); - if (!sys_event_table) - return -1; - - sys_event_table->soc_id = strdup(tblname); - if (!sys_event_table->soc_id) { - free(sys_event_table); - return -1; - } - list_add_tail(&sys_event_table->list, - &sys_event_tables); - } - - print_events_table_prefix(eventsfp, tblname); - return 0; - } - - /* - * Save the mapfile name for now. We will process mapfile - * after processing all JSON files (so we can write out the - * mapping table after all PMU events tables). - * - */ - if (level == 1 && is_file) { - if (!strcmp(bname, "mapfile.csv")) { - mapfile = strdup(fpath); - return 0; - } - if (is_json_file(bname)) - pr_debug("%s: ArchStd json is preprocessed %s\n", prog, fpath); - else - pr_info("%s: Ignoring file %s\n", prog, fpath); - return 0; - } - - /* - * If the file name does not have a .json extension, - * ignore it. It could be a readme.txt for instance. - */ - if (is_file) { - if (!is_json_file(bname)) { - pr_info("%s: Ignoring file without .json suffix %s\n", prog, - fpath); - return 0; - } - } - - if (level > 1 && add_topic(bname)) - return -ENOMEM; - - /* - * Assume all other files are JSON files. - * - * If mapfile refers to 'power7_core.json', we create a table - * named 'power7_core'. Any inconsistencies between the mapfile - * and directory tree could result in build failure due to table - * names not being found. - * - * At least for now, be strict with processing JSON file names. - * i.e. if JSON file name cannot be mapped to C-style table name, - * fail. - */ - if (is_file) { - struct perf_entry_data data = { - .topic = get_topic(), - .outfp = eventsfp, - }; - - err = json_events(fpath, print_events_table_entry, &data); - - free(data.topic); - } - - return err; -} - -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - -/* - * Starting in directory 'start_dirname', find the "mapfile.csv" and - * the set of JSON files for the architecture 'arch'. - * - * From each JSON file, create a C-style "PMU events table" from the - * JSON file (see struct pmu_event). - * - * From the mapfile, create a mapping between the CPU revisions and - * PMU event tables (see struct pmu_events_map). - * - * Write out the PMU events tables and the mapping table to pmu-event.c. - */ -int main(int argc, char *argv[]) -{ - int rc, ret = 0, empty_map = 0; - int maxfds; - char ldirname[PATH_MAX]; - const char *arch; - const char *output_file; - const char *start_dirname; - const char *err_string_ext = ""; - struct stat stbuf; - - prog = basename(argv[0]); - if (argc < 4) { - pr_err("Usage: %s \n", prog); - return 1; - } - - arch = argv[1]; - start_dirname = argv[2]; - output_file = argv[3]; - - if (argc > 4) - verbose = atoi(argv[4]); - - eventsfp = fopen(output_file, "w"); - if (!eventsfp) { - pr_err("%s Unable to create required file %s (%s)\n", - prog, output_file, strerror(errno)); - return 2; - } - - sprintf(ldirname, "%s/%s", start_dirname, arch); - - /* If architecture does not have any event lists, bail out */ - if (stat(ldirname, &stbuf) < 0) { - pr_info("%s: Arch %s has no PMU event lists\n", prog, arch); - empty_map = 1; - goto err_close_eventsfp; - } - - /* Include pmu-events.h first */ - fprintf(eventsfp, "#include \"pmu-events/pmu-events.h\"\n"); - - /* - * The mapfile allows multiple CPUids to point to the same JSON file, - * so, not sure if there is a need for symlinks within the pmu-events - * directory. - * - * For now, treat symlinks of JSON files as regular files and create - * separate tables for each symlink (presumably, each symlink refers - * to specific version of the CPU). - */ - - maxfds = get_maxfds(); - rc = nftw(ldirname, preprocess_arch_std_files, maxfds, 0); - if (rc) - goto err_processing_std_arch_event_dir; - - rc = nftw(ldirname, process_one_file, maxfds, 0); - if (rc) - goto err_processing_dir; - - sprintf(ldirname, "%s/test", start_dirname); - - rc = nftw(ldirname, preprocess_arch_std_files, maxfds, 0); - if (rc) - goto err_processing_std_arch_event_dir; - - rc = nftw(ldirname, process_one_file, maxfds, 0); - if (rc) - goto err_processing_dir; - - if (close_table) - print_events_table_suffix(eventsfp); - - if (!mapfile) { - pr_info("%s: No CPU->JSON mapping?\n", prog); - empty_map = 1; - goto err_close_eventsfp; - } - - rc = process_mapfile(eventsfp, mapfile); - if (rc) { - pr_info("%s: Error processing mapfile %s\n", prog, mapfile); - /* Make build fail */ - ret = 1; - goto err_close_eventsfp; - } - - rc = process_system_event_tables(eventsfp); - fclose(eventsfp); - if (rc) { - ret = 1; - goto err_out; - } - - free_arch_std_events(); - free_sys_event_tables(); - free(mapfile); - return 0; - -err_processing_std_arch_event_dir: - err_string_ext = " for std arch event"; -err_processing_dir: - if (verbose) { - pr_info("%s: Error walking file tree %s%s\n", prog, ldirname, - err_string_ext); - empty_map = 1; - } else if (rc < 0) { - ret = 1; - } else { - empty_map = 1; - } -err_close_eventsfp: - fclose(eventsfp); - if (empty_map) - create_empty_mapping(output_file); -err_out: - free_arch_std_events(); - free_sys_event_tables(); - free(mapfile); - return ret; -} diff --git a/tools/perf/pmu-events/jsmn.c b/tools/perf/pmu-events/jsmn.c deleted file mode 100644 index 831dc44c4558..000000000000 --- a/tools/perf/pmu-events/jsmn.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (c) 2010 Serge A. Zaitsev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * Slightly modified by AK to not assume 0 terminated input. - */ - -#include -#include "jsmn.h" -#define JSMN_STRICT - -/* - * Allocates a fresh unused token from the token pool. - */ -static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, - jsmntok_t *tokens, size_t num_tokens) -{ - jsmntok_t *tok; - - if ((unsigned)parser->toknext >= num_tokens) - return NULL; - tok = &tokens[parser->toknext++]; - tok->start = tok->end = -1; - tok->size = 0; - return tok; -} - -/* - * Fills token type and boundaries. - */ -static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, - int start, int end) -{ - token->type = type; - token->start = start; - token->end = end; - token->size = 0; -} - -/* - * Fills next available token with JSON primitive. - */ -static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, - size_t len, - jsmntok_t *tokens, size_t num_tokens) -{ - jsmntok_t *token; - int start; - - start = parser->pos; - - for (; parser->pos < len; parser->pos++) { - switch (js[parser->pos]) { -#ifndef JSMN_STRICT - /* - * In strict mode primitive must be followed by "," - * or "}" or "]" - */ - case ':': -#endif - case '\t': - case '\r': - case '\n': - case ' ': - case ',': - case ']': - case '}': - goto found; - default: - break; - } - if (js[parser->pos] < 32 || js[parser->pos] >= 127) { - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } -#ifdef JSMN_STRICT - /* - * In strict mode primitive must be followed by a - * comma/object/array. - */ - parser->pos = start; - return JSMN_ERROR_PART; -#endif - -found: - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); - parser->pos--; /* parent sees closing brackets */ - return JSMN_SUCCESS; -} - -/* - * Fills next token with JSON string. - */ -static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, - size_t len, - jsmntok_t *tokens, size_t num_tokens) -{ - jsmntok_t *token; - int start = parser->pos; - - /* Skip starting quote */ - parser->pos++; - - for (; parser->pos < len; parser->pos++) { - char c = js[parser->pos]; - - /* Quote: end of string */ - if (c == '\"') { - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_STRING, start+1, - parser->pos); - return JSMN_SUCCESS; - } - - /* Backslash: Quoted symbol expected */ - if (c == '\\') { - parser->pos++; - switch (js[parser->pos]) { - /* Allowed escaped symbols */ - case '\"': - case '/': - case '\\': - case 'b': - case 'f': - case 'r': - case 'n': - case 't': - break; - /* Allows escaped symbol \uXXXX */ - case 'u': - /* TODO */ - break; - /* Unexpected symbol */ - default: - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } - } - parser->pos = start; - return JSMN_ERROR_PART; -} - -/* - * Parse JSON string and fill tokens. - */ -jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, - jsmntok_t *tokens, unsigned int num_tokens) -{ - jsmnerr_t r; - int i; - jsmntok_t *token; -#ifdef JSMN_STRICT - /* - * Keeps track of whether a new object/list/primitive is expected. New items are only - * allowed after an opening brace, comma or colon. A closing brace after a comma is not - * valid JSON. - */ - int expecting_item = 1; -#endif - - for (; parser->pos < len; parser->pos++) { - char c; - jsmntype_t type; - - c = js[parser->pos]; - switch (c) { - case '{': - case '[': -#ifdef JSMN_STRICT - if (!expecting_item) - return JSMN_ERROR_INVAL; -#endif - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) - return JSMN_ERROR_NOMEM; - if (parser->toksuper != -1) - tokens[parser->toksuper].size++; - token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); - token->start = parser->pos; - parser->toksuper = parser->toknext - 1; - break; - case '}': - case ']': -#ifdef JSMN_STRICT - if (expecting_item) - return JSMN_ERROR_INVAL; -#endif - type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); - for (i = parser->toknext - 1; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - if (token->type != type) - return JSMN_ERROR_INVAL; - parser->toksuper = -1; - token->end = parser->pos + 1; - break; - } - } - /* Error if unmatched closing bracket */ - if (i == -1) - return JSMN_ERROR_INVAL; - for (; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - parser->toksuper = i; - break; - } - } - break; - case '\"': -#ifdef JSMN_STRICT - if (!expecting_item) - return JSMN_ERROR_INVAL; - expecting_item = 0; -#endif - r = jsmn_parse_string(parser, js, len, tokens, - num_tokens); - if (r < 0) - return r; - if (parser->toksuper != -1) - tokens[parser->toksuper].size++; - break; - case '\t': - case '\r': - case '\n': - case ' ': - break; -#ifdef JSMN_STRICT - case ':': - case ',': - if (expecting_item) - return JSMN_ERROR_INVAL; - expecting_item = 1; - break; - /* - * In strict mode primitives are: - * numbers and booleans. - */ - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 't': - case 'f': - case 'n': -#else - case ':': - case ',': - break; - /* - * In non-strict mode every unquoted value - * is a primitive. - */ - /*FALL THROUGH */ - default: -#endif - -#ifdef JSMN_STRICT - if (!expecting_item) - return JSMN_ERROR_INVAL; - expecting_item = 0; -#endif - r = jsmn_parse_primitive(parser, js, len, tokens, - num_tokens); - if (r < 0) - return r; - if (parser->toksuper != -1) - tokens[parser->toksuper].size++; - break; - -#ifdef JSMN_STRICT - /* Unexpected char in strict mode */ - default: - return JSMN_ERROR_INVAL; -#endif - } - } - - for (i = parser->toknext - 1; i >= 0; i--) { - /* Unmatched opened object or array */ - if (tokens[i].start != -1 && tokens[i].end == -1) - return JSMN_ERROR_PART; - } - -#ifdef JSMN_STRICT - return expecting_item ? JSMN_ERROR_INVAL : JSMN_SUCCESS; -#else - return JSMN_SUCCESS; -#endif -} - -/* - * Creates a new parser based over a given buffer with an array of tokens - * available. - */ -void jsmn_init(jsmn_parser *parser) -{ - parser->pos = 0; - parser->toknext = 0; - parser->toksuper = -1; -} - -const char *jsmn_strerror(jsmnerr_t err) -{ - switch (err) { - case JSMN_ERROR_NOMEM: - return "No enough tokens"; - case JSMN_ERROR_INVAL: - return "Invalid character inside JSON string"; - case JSMN_ERROR_PART: - return "The string is not a full JSON packet, more bytes expected"; - case JSMN_SUCCESS: - return "Success"; - default: - return "Unknown json error"; - } -} diff --git a/tools/perf/pmu-events/jsmn.h b/tools/perf/pmu-events/jsmn.h deleted file mode 100644 index 1bdfd55fff30..000000000000 --- a/tools/perf/pmu-events/jsmn.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -#ifndef __JSMN_H_ -#define __JSMN_H_ - -/* - * JSON type identifier. Basic types are: - * o Object - * o Array - * o String - * o Other primitive: number, boolean (true/false) or null - */ -typedef enum { - JSMN_PRIMITIVE = 0, - JSMN_OBJECT = 1, - JSMN_ARRAY = 2, - JSMN_STRING = 3 -} jsmntype_t; - -typedef enum { - /* Not enough tokens were provided */ - JSMN_ERROR_NOMEM = -1, - /* Invalid character inside JSON string */ - JSMN_ERROR_INVAL = -2, - /* The string is not a full JSON packet, more bytes expected */ - JSMN_ERROR_PART = -3, - /* Everything was fine */ - JSMN_SUCCESS = 0 -} jsmnerr_t; - -/* - * JSON token description. - * @param type type (object, array, string etc.) - * @param start start position in JSON data string - * @param end end position in JSON data string - */ -typedef struct { - jsmntype_t type; - int start; - int end; - int size; -} jsmntok_t; - -/* - * JSON parser. Contains an array of token blocks available. Also stores - * the string being parsed now and current position in that string - */ -typedef struct { - unsigned int pos; /* offset in the JSON string */ - int toknext; /* next token to allocate */ - int toksuper; /* superior token node, e.g parent object or array */ -} jsmn_parser; - -/* - * Create JSON parser over an array of tokens - */ -void jsmn_init(jsmn_parser *parser); - -/* - * Run JSON parser. It parses a JSON data string into and array of tokens, - * each describing a single JSON object. - */ -jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, - size_t len, - jsmntok_t *tokens, unsigned int num_tokens); - -const char *jsmn_strerror(jsmnerr_t err); - -#endif /* __JSMN_H_ */ diff --git a/tools/perf/pmu-events/json.c b/tools/perf/pmu-events/json.c deleted file mode 100644 index 0544398d6e2d..000000000000 --- a/tools/perf/pmu-events/json.c +++ /dev/null @@ -1,162 +0,0 @@ -/* Parse JSON files using the JSMN parser. */ - -/* - * Copyright (c) 2014, Intel Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "jsmn.h" -#include "json.h" -#include - - -static char *mapfile(const char *fn, size_t *size) -{ - unsigned ps = sysconf(_SC_PAGESIZE); - struct stat st; - char *map = NULL; - int err; - int fd = open(fn, O_RDONLY); - - if (fd < 0 && verbose > 0 && fn) { - pr_err("Error opening events file '%s': %s\n", fn, - strerror(errno)); - } - - if (fd < 0) - return NULL; - err = fstat(fd, &st); - if (err < 0) - goto out; - *size = st.st_size; - map = mmap(NULL, - (st.st_size + ps - 1) & ~(ps - 1), - PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); - if (map == MAP_FAILED) - map = NULL; -out: - close(fd); - return map; -} - -static void unmapfile(char *map, size_t size) -{ - unsigned ps = sysconf(_SC_PAGESIZE); - munmap(map, roundup(size, ps)); -} - -/* - * Parse json file using jsmn. Return array of tokens, - * and mapped file. Caller needs to free array. - */ -jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len) -{ - jsmn_parser parser; - jsmntok_t *tokens; - jsmnerr_t res; - unsigned sz; - - *map = mapfile(fn, size); - if (!*map) - return NULL; - /* Heuristic */ - sz = *size * 16; - tokens = malloc(sz); - if (!tokens) - goto error; - jsmn_init(&parser); - res = jsmn_parse(&parser, *map, *size, tokens, - sz / sizeof(jsmntok_t)); - if (res != JSMN_SUCCESS) { - pr_err("%s: json error %s\n", fn, jsmn_strerror(res)); - goto error_free; - } - if (len) - *len = parser.toknext; - return tokens; -error_free: - free(tokens); -error: - unmapfile(*map, *size); - return NULL; -} - -void free_json(char *map, size_t size, jsmntok_t *tokens) -{ - free(tokens); - unmapfile(map, size); -} - -static int countchar(char *map, char c, int end) -{ - int i; - int count = 0; - for (i = 0; i < end; i++) - if (map[i] == c) - count++; - return count; -} - -/* Return line number of a jsmn token */ -int json_line(char *map, jsmntok_t *t) -{ - return countchar(map, '\n', t->start) + 1; -} - -static const char * const jsmn_types[] = { - [JSMN_PRIMITIVE] = "primitive", - [JSMN_ARRAY] = "array", - [JSMN_OBJECT] = "object", - [JSMN_STRING] = "string" -}; - -#define LOOKUP(a, i) ((i) < (sizeof(a)/sizeof(*(a))) ? ((a)[i]) : "?") - -/* Return type name of a jsmn token */ -const char *json_name(jsmntok_t *t) -{ - return LOOKUP(jsmn_types, t->type); -} - -int json_len(jsmntok_t *t) -{ - return t->end - t->start; -} - -/* Is string t equal to s? */ -int json_streq(char *map, jsmntok_t *t, const char *s) -{ - unsigned len = json_len(t); - return len == strlen(s) && !strncasecmp(map + t->start, s, len); -} diff --git a/tools/perf/pmu-events/json.h b/tools/perf/pmu-events/json.h deleted file mode 100644 index fbcd5a0590ad..000000000000 --- a/tools/perf/pmu-events/json.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef JSON_H -#define JSON_H 1 - -#include "jsmn.h" - -jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len); -void free_json(char *map, size_t size, jsmntok_t *tokens); -int json_line(char *map, jsmntok_t *t); -const char *json_name(jsmntok_t *t); -int json_streq(char *map, jsmntok_t *t, const char *s); -int json_len(jsmntok_t *t); - -extern int verbose; - -#include - -extern int eprintf(int level, int var, const char *fmt, ...); -#define pr_fmt(fmt) fmt - -#define pr_err(fmt, ...) \ - eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__) - -#define pr_info(fmt, ...) \ - eprintf(1, verbose, pr_fmt(fmt), ##__VA_ARGS__) - -#define pr_debug(fmt, ...) \ - eprintf(2, verbose, pr_fmt(fmt), ##__VA_ARGS__) - -#ifndef roundup -#define roundup(x, y) ( \ -{ \ - const typeof(y) __y = y; \ - (((x) + (__y - 1)) / __y) * __y; \ -} \ -) -#endif - -#endif -- cgit v1.2.3 From c7a774d78111d55f409d9ee0a5db2c4eb39d6657 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 7 Jul 2022 08:34:48 -0700 Subject: perf test: Add debug line to diagnose broken metrics Printing out the metric name and architecture makes finding the source of a failure easier. Signed-off-by: Ian Rogers Cc: Alexander Shishkin Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20220707153449.202409-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/pmu-events.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools') diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index f13368569d8b..478b33825790 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -1115,6 +1115,7 @@ static int test__parsing_fake(struct test_suite *test __maybe_unused, break; if (!pe->metric_expr) continue; + pr_debug("Found metric '%s' for '%s'\n", pe->metric_name, map->cpuid); err = metric_parse_fake(pe->metric_expr); if (err) return err; -- cgit v1.2.3 From 29d97deed6424d8dfac6797910d6920fb8aad33c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 7 Jul 2022 08:34:49 -0700 Subject: perf test: Make all metrics test more tolerant Metric names are truncated so don't try to match all of one. Allow AMX metrics to skip as floating point ones do. Metrics for optane memory can also skip rather than fail. Add a system wide check for uncore metrics. Restructure code to avoid extensive nesting. Some impetus for this in: https://lore.kernel.org/lkml/d32376b5-5538-ff00-6620-e74ad4b4abf2@huawei.com/ Suggested-by: John Garry Signed-off-by: Ian Rogers Cc: Alexander Shishkin Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20220707153449.202409-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/stat_all_metrics.sh | 47 ++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 16 deletions(-) (limited to 'tools') diff --git a/tools/perf/tests/shell/stat_all_metrics.sh b/tools/perf/tests/shell/stat_all_metrics.sh index e7c59e5a7a98..6e79349e42be 100755 --- a/tools/perf/tests/shell/stat_all_metrics.sh +++ b/tools/perf/tests/shell/stat_all_metrics.sh @@ -1,26 +1,41 @@ -#!/bin/sh +#!/bin/bash # perf all metrics test # SPDX-License-Identifier: GPL-2.0 -set -e - err=0 for m in $(perf list --raw-dump metrics); do echo "Testing $m" result=$(perf stat -M "$m" true 2>&1) - if [[ ! "$result" =~ "$m" ]] && [[ ! "$result" =~ "" ]]; then - # We failed to see the metric and the events are support. Possibly the - # workload was too small so retry with something longer. - result=$(perf stat -M "$m" perf bench internals synthesize 2>&1) - if [[ ! "$result" =~ "$m" ]]; then - echo "Metric '$m' not printed in:" - echo "$result" - if [[ "$result" =~ "FP_ARITH" && "$err" != "1" ]]; then - echo "Skip, not fail, for FP issues" - err=2 - else - err=1 - fi + if [[ "$result" =~ "${m:0:50}" ]] || [[ "$result" =~ "" ]] + then + continue + fi + # Failed so try system wide. + result=$(perf stat -M "$m" -a true 2>&1) + if [[ "$result" =~ "${m:0:50}" ]] + then + continue + fi + # Failed again, possibly the workload was too small so retry with something + # longer. + result=$(perf stat -M "$m" perf bench internals synthesize 2>&1) + if [[ "$result" =~ "${m:0:50}" ]] + then + continue + fi + echo "Metric '$m' not printed in:" + echo "$result" + if [[ "$err" != "1" ]] + then + err=2 + if [[ "$result" =~ "FP_ARITH" || "$result" =~ "AMX" ]] + then + echo "Skip, not fail, for FP issues" + elif [[ "$result" =~ "PMM" ]] + then + echo "Skip, not fail, for Optane memory issues" + else + err=1 fi fi done -- cgit v1.2.3 From b55878c90ab92a244b7b044a9e64aef5b75783e7 Mon Sep 17 00:00:00 2001 From: German Gomez Date: Tue, 5 Jul 2022 16:05:11 +0100 Subject: perf test: Add test for branch stack sampling Add a self test for branch stack sampling, to check that we get the expected branch types, and filters behave as expected. Suggested-by: Anshuman Khandual Signed-off-by: German Gomez Acked-by: Namhyung Kim Cc: Alexander Shishkin Cc: Ian Rogers Cc: Mark Rutland Link: https://lore.kernel.org/r/20220705150511.473919-2-german.gomez@arm.com Tested-by: Jiri Olsa Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_brstack.sh | 114 +++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100755 tools/perf/tests/shell/test_brstack.sh (limited to 'tools') diff --git a/tools/perf/tests/shell/test_brstack.sh b/tools/perf/tests/shell/test_brstack.sh new file mode 100755 index 000000000000..113ccd17bf03 --- /dev/null +++ b/tools/perf/tests/shell/test_brstack.sh @@ -0,0 +1,114 @@ +#!/bin/sh +# Check branch stack sampling + +# SPDX-License-Identifier: GPL-2.0 +# German Gomez , 2022 + +# we need a C compiler to build the test programs +# so bail if none is found +if ! [ -x "$(command -v cc)" ]; then + echo "failed: no compiler, install gcc" + exit 2 +fi + +# skip the test if the hardware doesn't support branch stack sampling +perf record -b -o- -e dummy -B true > /dev/null 2>&1 || exit 2 + +TMPDIR=$(mktemp -d /tmp/__perf_test.program.XXXXX) + +cleanup() { + rm -rf $TMPDIR +} + +trap cleanup exit term int + +gen_test_program() { + # generate test program + cat << EOF > $1 +#define BENCH_RUNS 999999 +int cnt; +void bar(void) { +} /* return */ +void foo(void) { + bar(); /* call */ +} /* return */ +void bench(void) { + void (*foo_ind)(void) = foo; + if ((cnt++) % 3) /* branch (cond) */ + foo(); /* call */ + bar(); /* call */ + foo_ind(); /* call (ind) */ +} +int main(void) +{ + int cnt = 0; + while (1) { + if ((cnt++) > BENCH_RUNS) + break; + bench(); /* call */ + } /* branch (uncond) */ + return 0; +} +EOF +} + +test_user_branches() { + echo "Testing user branch stack sampling" + + gen_test_program "$TEMPDIR/program.c" + cc -fno-inline -g "$TEMPDIR/program.c" -o $TMPDIR/a.out + + perf record -o $TMPDIR/perf.data --branch-filter any,save_type,u -- $TMPDIR/a.out > /dev/null 2>&1 + perf script -i $TMPDIR/perf.data --fields brstacksym | xargs -n1 > $TMPDIR/perf.script + + # example of branch entries: + # foo+0x14/bar+0x40/P/-/-/0/CALL + + set -x + egrep -m1 "^bench\+[^ ]*/foo\+[^ ]*/IND_CALL$" $TMPDIR/perf.script + egrep -m1 "^foo\+[^ ]*/bar\+[^ ]*/CALL$" $TMPDIR/perf.script + egrep -m1 "^bench\+[^ ]*/foo\+[^ ]*/CALL$" $TMPDIR/perf.script + egrep -m1 "^bench\+[^ ]*/bar\+[^ ]*/CALL$" $TMPDIR/perf.script + egrep -m1 "^bar\+[^ ]*/foo\+[^ ]*/RET$" $TMPDIR/perf.script + egrep -m1 "^foo\+[^ ]*/bench\+[^ ]*/RET$" $TMPDIR/perf.script + egrep -m1 "^bench\+[^ ]*/bench\+[^ ]*/COND$" $TMPDIR/perf.script + egrep -m1 "^main\+[^ ]*/main\+[^ ]*/UNCOND$" $TMPDIR/perf.script + set +x + + # some branch types are still not being tested: + # IND COND_CALL COND_RET SYSCALL SYSRET IRQ SERROR NO_TX +} + +# first argument is the argument passed to "--branch-stack ,save_type,u" +# second argument are the expected branch types for the given filter +test_filter() { + local filter=$1 + local expect=$2 + + echo "Testing branch stack filtering permutation ($filter,$expect)" + + gen_test_program "$TEMPDIR/program.c" + cc -fno-inline -g "$TEMPDIR/program.c" -o $TMPDIR/a.out + + perf record -o $TMPDIR/perf.data --branch-filter $filter,save_type,u -- $TMPDIR/a.out > /dev/null 2>&1 + perf script -i $TMPDIR/perf.data --fields brstack | xargs -n1 > $TMPDIR/perf.script + + # fail if we find any branch type that doesn't match any of the expected ones + # also consider UNKNOWN branch types (-) + if egrep -vm1 "^[^ ]*/($expect|-|( *))$" $TMPDIR/perf.script; then + return 1 + fi +} + +set -e + +test_user_branches + +test_filter "any_call" "CALL|IND_CALL|COND_CALL|SYSCALL|IRQ" +test_filter "call" "CALL|SYSCALL" +test_filter "cond" "COND" +test_filter "any_ret" "RET|COND_RET|SYSRET|ERET" + +test_filter "call,cond" "CALL|SYSCALL|COND" +test_filter "any_call,cond" "CALL|IND_CALL|COND_CALL|IRQ|SYSCALL|COND" +test_filter "cond,any_call,any_ret" "COND|CALL|IND_CALL|COND_CALL|SYSCALL|IRQ|RET|COND_RET|SYSRET|ERET" -- cgit v1.2.3 From ab0101768f639801b726f8be50e2ff7366ade056 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 15 Jun 2022 09:32:16 -0700 Subject: perf lock: Print wait times with unit Currently it only prints the time in nsec but it's a bit hard to read and takes longer in the screen. We can change it to use different units and keep the number small to save the space. Before: $ perf lock report Name acquired contended avg wait (ns) total wait (ns) max wait (ns) min wait (ns) jiffies_lock 433 32 2778 88908 13570 692 &lruvec->lru_lock 747 5 11254 56272 18317 1412 slock-AF_INET6 7 1 23543 23543 23543 23543 &newf->file_lock 706 15 1025 15388 2279 618 slock-AF_INET6 8 1 10379 10379 10379 10379 &rq->__lock 2143 5 2037 10185 3462 939 After: Name acquired contended avg wait total wait max wait min wait jiffies_lock 433 32 2.78 us 88.91 us 13.57 us 692 ns &lruvec->lru_lock 747 5 11.25 us 56.27 us 18.32 us 1.41 us slock-AF_INET6 7 1 23.54 us 23.54 us 23.54 us 23.54 us &newf->file_lock 706 15 1.02 us 15.39 us 2.28 us 618 ns slock-AF_INET6 8 1 10.38 us 10.38 us 10.38 us 10.38 us &rq->__lock 2143 5 2.04 us 10.19 us 3.46 us 939 ns Signed-off-by: Namhyung Kim Acked-by: Ian Rogers Cc: Boqun Feng Cc: Davidlohr Bueso Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Waiman Long Cc: Will Deacon Link: https://lore.kernel.org/r/20220615163222.1275500-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-lock.c | 48 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 23a33ac15e68..57e396323d05 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -251,6 +251,31 @@ struct lock_key { struct list_head list; }; +static void lock_stat_key_print_time(unsigned long long nsec, int len) +{ + static const struct { + float base; + const char *unit; + } table[] = { + { 1e9 * 3600, "h " }, + { 1e9 * 60, "m " }, + { 1e9, "s " }, + { 1e6, "ms" }, + { 1e3, "us" }, + { 0, NULL }, + }; + + for (int i = 0; table[i].unit; i++) { + if (nsec < table[i].base) + continue; + + pr_info("%*.2f %s", len - 3, nsec / table[i].base, table[i].unit); + return; + } + + pr_info("%*llu %s", len - 3, nsec, "ns"); +} + #define PRINT_KEY(member) \ static void lock_stat_key_print_ ## member(struct lock_key *key, \ struct lock_stat *ls) \ @@ -258,11 +283,18 @@ static void lock_stat_key_print_ ## member(struct lock_key *key, \ pr_info("%*llu", key->len, (unsigned long long)ls->member); \ } +#define PRINT_TIME(member) \ +static void lock_stat_key_print_ ## member(struct lock_key *key, \ + struct lock_stat *ls) \ +{ \ + lock_stat_key_print_time((unsigned long long)ls->member, key->len); \ +} + PRINT_KEY(nr_acquired) PRINT_KEY(nr_contended) -PRINT_KEY(avg_wait_time) -PRINT_KEY(wait_time_total) -PRINT_KEY(wait_time_max) +PRINT_TIME(avg_wait_time) +PRINT_TIME(wait_time_total) +PRINT_TIME(wait_time_max) static void lock_stat_key_print_wait_time_min(struct lock_key *key, struct lock_stat *ls) @@ -272,7 +304,7 @@ static void lock_stat_key_print_wait_time_min(struct lock_key *key, if (wait_time == ULLONG_MAX) wait_time = 0; - pr_info("%*"PRIu64, key->len, wait_time); + lock_stat_key_print_time(wait_time, key->len); } @@ -291,10 +323,10 @@ static const char *output_fields; struct lock_key keys[] = { DEF_KEY_LOCK(acquired, "acquired", nr_acquired, 10), DEF_KEY_LOCK(contended, "contended", nr_contended, 10), - DEF_KEY_LOCK(avg_wait, "avg wait (ns)", avg_wait_time, 15), - DEF_KEY_LOCK(wait_total, "total wait (ns)", wait_time_total, 15), - DEF_KEY_LOCK(wait_max, "max wait (ns)", wait_time_max, 15), - DEF_KEY_LOCK(wait_min, "min wait (ns)", wait_time_min, 15), + DEF_KEY_LOCK(avg_wait, "avg wait", avg_wait_time, 12), + DEF_KEY_LOCK(wait_total, "total wait", wait_time_total, 12), + DEF_KEY_LOCK(wait_max, "max wait", wait_time_max, 12), + DEF_KEY_LOCK(wait_min, "min wait", wait_time_min, 12), /* extra comparisons much complicated should be here */ { } -- cgit v1.2.3 From 309e133dfe2651eedd572b62af2215b94532b712 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 15 Jun 2022 09:32:17 -0700 Subject: perf lock: Allow to use different kernel symbols Add --vmlinux and --kallsyms options to support data file from different kernels. Signed-off-by: Namhyung Kim Cc: Boqun Feng Cc: Davidlohr Bueso Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Waiman Long Cc: Will Deacon Link: https://lore.kernel.org/r/20220615163222.1275500-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-lock.txt | 7 +++++++ tools/perf/builtin-lock.c | 4 ++++ 2 files changed, 11 insertions(+) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt index 656b537b2fba..4b8568f0c53b 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt @@ -46,6 +46,13 @@ COMMON OPTIONS --force:: Don't complain, do it. +--vmlinux=:: + vmlinux pathname + +--kallsyms=:: + kallsyms pathname + + REPORT OPTIONS -------------- diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 57e396323d05..118a036a81fb 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -1162,6 +1162,10 @@ int cmd_lock(int argc, const char **argv) OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), + OPT_STRING(0, "vmlinux", &symbol_conf.vmlinux_name, + "file", "vmlinux pathname"), + OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, + "file", "kallsyms pathname"), OPT_END() }; -- cgit v1.2.3 From 9565c9186d17ac55f5dc8a556bece847e49dee35 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 15 Jun 2022 09:32:18 -0700 Subject: perf lock: Skip print_bad_events() if nothing bad The debug output is meaningful when there are bad lock sequences. Skip it unless there's one or -v option is given. Signed-off-by: Namhyung Kim Cc: Boqun Feng Cc: Davidlohr Bueso Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Waiman Long Cc: Will Deacon Link: https://lore.kernel.org/r/20220615163222.1275500-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-lock.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'tools') diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 118a036a81fb..2337b09dd2cd 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -858,9 +858,16 @@ static void print_bad_events(int bad, int total) { /* Output for debug, this have to be removed */ int i; + int broken = 0; const char *name[4] = { "acquire", "acquired", "contended", "release" }; + for (i = 0; i < BROKEN_MAX; i++) + broken += bad_hist[i]; + + if (broken == 0 && !verbose) + return; + pr_info("\n=== output for debug===\n\n"); pr_info("bad: %d, total: %d\n", bad, total); pr_info("bad rate: %.2f %%\n", (double)bad / (double)total * 100); -- cgit v1.2.3 From 166a9764a38e37089088eb21f31cfcb6d5a0dde2 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 15 Jun 2022 09:32:19 -0700 Subject: perf lock: Add lock contention tracepoints record support When LOCKDEP and LOCK_STAT events are not available, it falls back to record the new lock contention tracepoints. Signed-off-by: Namhyung Kim Cc: Boqun Feng Cc: Davidlohr Bueso Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Waiman Long Cc: Will Deacon Link: https://lore.kernel.org/r/20220615163222.1275500-5-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-lock.c | 76 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 7 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 2337b09dd2cd..9e3b90cac505 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -516,17 +516,29 @@ alloc_failed: } struct trace_lock_handler { + /* it's used on CONFIG_LOCKDEP */ int (*acquire_event)(struct evsel *evsel, struct perf_sample *sample); + /* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */ int (*acquired_event)(struct evsel *evsel, struct perf_sample *sample); + /* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */ int (*contended_event)(struct evsel *evsel, struct perf_sample *sample); + /* it's used on CONFIG_LOCKDEP */ int (*release_event)(struct evsel *evsel, struct perf_sample *sample); + + /* it's used when CONFIG_LOCKDEP is off */ + int (*contention_begin_event)(struct evsel *evsel, + struct perf_sample *sample); + + /* it's used when CONFIG_LOCKDEP is off */ + int (*contention_end_event)(struct evsel *evsel, + struct perf_sample *sample); }; static struct lock_seq_stat *get_seq(struct thread_stat *ts, u64 addr) @@ -854,6 +866,20 @@ static int evsel__process_lock_release(struct evsel *evsel, struct perf_sample * return 0; } +static int evsel__process_contention_begin(struct evsel *evsel, struct perf_sample *sample) +{ + if (trace_handler->contention_begin_event) + return trace_handler->contention_begin_event(evsel, sample); + return 0; +} + +static int evsel__process_contention_end(struct evsel *evsel, struct perf_sample *sample) +{ + if (trace_handler->contention_end_event) + return trace_handler->contention_end_event(evsel, sample); + return 0; +} + static void print_bad_events(int bad, int total) { /* Output for debug, this have to be removed */ @@ -1062,6 +1088,11 @@ static const struct evsel_str_handler lock_tracepoints[] = { { "lock:lock_release", evsel__process_lock_release, }, /* CONFIG_LOCKDEP */ }; +static const struct evsel_str_handler contention_tracepoints[] = { + { "lock:contention_begin", evsel__process_contention_begin, }, + { "lock:contention_end", evsel__process_contention_end, }, +}; + static bool force; static int __cmd_report(bool display_info) @@ -1125,20 +1156,41 @@ static int __cmd_record(int argc, const char **argv) "record", "-R", "-m", "1024", "-c", "1", "--synth", "task", }; unsigned int rec_argc, i, j, ret; + unsigned int nr_tracepoints; const char **rec_argv; + bool has_lock_stat = true; for (i = 0; i < ARRAY_SIZE(lock_tracepoints); i++) { if (!is_valid_tracepoint(lock_tracepoints[i].name)) { - pr_err("tracepoint %s is not enabled. " - "Are CONFIG_LOCKDEP and CONFIG_LOCK_STAT enabled?\n", - lock_tracepoints[i].name); - return 1; + pr_debug("tracepoint %s is not enabled. " + "Are CONFIG_LOCKDEP and CONFIG_LOCK_STAT enabled?\n", + lock_tracepoints[i].name); + has_lock_stat = false; + break; + } + } + + if (has_lock_stat) + goto setup_args; + + for (i = 0; i < ARRAY_SIZE(contention_tracepoints); i++) { + if (!is_valid_tracepoint(contention_tracepoints[i].name)) { + pr_err("tracepoint %s is not enabled.\n", + contention_tracepoints[i].name); + return 1; } } +setup_args: rec_argc = ARRAY_SIZE(record_args) + argc - 1; + + if (has_lock_stat) + nr_tracepoints = ARRAY_SIZE(lock_tracepoints); + else + nr_tracepoints = ARRAY_SIZE(contention_tracepoints); + /* factor of 2 is for -e in front of each tracepoint */ - rec_argc += 2 * ARRAY_SIZE(lock_tracepoints); + rec_argc += 2 * nr_tracepoints; rec_argv = calloc(rec_argc + 1, sizeof(char *)); if (!rec_argv) @@ -1147,9 +1199,19 @@ static int __cmd_record(int argc, const char **argv) for (i = 0; i < ARRAY_SIZE(record_args); i++) rec_argv[i] = strdup(record_args[i]); - for (j = 0; j < ARRAY_SIZE(lock_tracepoints); j++) { + for (j = 0; j < nr_tracepoints; j++) { + const char *ev_name; + + if (has_lock_stat) + ev_name = strdup(lock_tracepoints[j].name); + else + ev_name = strdup(contention_tracepoints[j].name); + + if (!ev_name) + return -ENOMEM; + rec_argv[i++] = "-e"; - rec_argv[i++] = strdup(lock_tracepoints[j].name); + rec_argv[i++] = ev_name; } for (j = 1; j < (unsigned int)argc; j++, i++) -- cgit v1.2.3 From 3ae03f2650b8d87242906f6c160732bc9d991ca1 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 15 Jun 2022 09:32:20 -0700 Subject: perf lock: Handle lock contention tracepoints When the lock contention events are used, there's no tracking of acquire and release. So the state machine is simplified to use UNINITIALIZED -> CONTENDED -> ACQUIRED only. Note that CONTENDED state is re-entrant since mutex locks can hit two or more consecutive contention_begin events for optimistic spinning and sleep. Signed-off-by: Namhyung Kim Acked-by: Ian Rogers Cc: Boqun Feng Cc: Davidlohr Bueso Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Waiman Long Cc: Will Deacon Link: https://lore.kernel.org/r/20220615163222.1275500-6-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-lock.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) (limited to 'tools') diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 9e3b90cac505..546dad1963c8 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -483,6 +483,18 @@ static struct lock_stat *pop_from_result(void) return container_of(node, struct lock_stat, rb); } +static struct lock_stat *lock_stat_find(u64 addr) +{ + struct hlist_head *entry = lockhashentry(addr); + struct lock_stat *ret; + + hlist_for_each_entry(ret, entry, hash_entry) { + if (ret->addr == addr) + return ret; + } + return NULL; +} + static struct lock_stat *lock_stat_findnew(u64 addr, const char *name) { struct hlist_head *entry = lockhashentry(addr); @@ -827,6 +839,124 @@ end: return 0; } +static int report_lock_contention_begin_event(struct evsel *evsel, + struct perf_sample *sample) +{ + struct lock_stat *ls; + struct thread_stat *ts; + struct lock_seq_stat *seq; + u64 addr = evsel__intval(evsel, sample, "lock_addr"); + + if (show_thread_stats) + addr = sample->tid; + + ls = lock_stat_findnew(addr, "No name"); + if (!ls) + return -ENOMEM; + + ts = thread_stat_findnew(sample->tid); + if (!ts) + return -ENOMEM; + + seq = get_seq(ts, addr); + if (!seq) + return -ENOMEM; + + switch (seq->state) { + case SEQ_STATE_UNINITIALIZED: + case SEQ_STATE_ACQUIRED: + break; + case SEQ_STATE_CONTENDED: + /* + * It can have nested contention begin with mutex spinning, + * then we would use the original contention begin event and + * ignore the second one. + */ + goto end; + case SEQ_STATE_ACQUIRING: + case SEQ_STATE_READ_ACQUIRED: + case SEQ_STATE_RELEASED: + /* broken lock sequence */ + if (!ls->broken) { + ls->broken = 1; + bad_hist[BROKEN_CONTENDED]++; + } + list_del_init(&seq->list); + free(seq); + goto end; + default: + BUG_ON("Unknown state of lock sequence found!\n"); + break; + } + + if (seq->state != SEQ_STATE_CONTENDED) { + seq->state = SEQ_STATE_CONTENDED; + seq->prev_event_time = sample->time; + ls->nr_contended++; + } +end: + return 0; +} + +static int report_lock_contention_end_event(struct evsel *evsel, + struct perf_sample *sample) +{ + struct lock_stat *ls; + struct thread_stat *ts; + struct lock_seq_stat *seq; + u64 contended_term; + u64 addr = evsel__intval(evsel, sample, "lock_addr"); + + if (show_thread_stats) + addr = sample->tid; + + ls = lock_stat_find(addr); + if (!ls) + return 0; + + ts = thread_stat_find(sample->tid); + if (!ts) + return 0; + + seq = get_seq(ts, addr); + if (!seq) + return -ENOMEM; + + switch (seq->state) { + case SEQ_STATE_UNINITIALIZED: + goto end; + case SEQ_STATE_CONTENDED: + contended_term = sample->time - seq->prev_event_time; + ls->wait_time_total += contended_term; + if (contended_term < ls->wait_time_min) + ls->wait_time_min = contended_term; + if (ls->wait_time_max < contended_term) + ls->wait_time_max = contended_term; + break; + case SEQ_STATE_ACQUIRING: + case SEQ_STATE_ACQUIRED: + case SEQ_STATE_READ_ACQUIRED: + case SEQ_STATE_RELEASED: + /* broken lock sequence */ + if (!ls->broken) { + ls->broken = 1; + bad_hist[BROKEN_ACQUIRED]++; + } + list_del_init(&seq->list); + free(seq); + goto end; + default: + BUG_ON("Unknown state of lock sequence found!\n"); + break; + } + + seq->state = SEQ_STATE_ACQUIRED; + ls->nr_acquired++; + ls->avg_wait_time = ls->wait_time_total/ls->nr_acquired; +end: + return 0; +} + /* lock oriented handlers */ /* TODO: handlers for CPU oriented, thread oriented */ static struct trace_lock_handler report_lock_ops = { @@ -834,6 +964,8 @@ static struct trace_lock_handler report_lock_ops = { .acquired_event = report_lock_acquired_event, .contended_event = report_lock_contended_event, .release_event = report_lock_release_event, + .contention_begin_event = report_lock_contention_begin_event, + .contention_end_event = report_lock_contention_end_event, }; static struct trace_lock_handler *trace_handler; @@ -1126,6 +1258,11 @@ static int __cmd_report(bool display_info) goto out_delete; } + if (perf_session__set_tracepoints_handlers(session, contention_tracepoints)) { + pr_err("Initializing perf session tracepoint handlers failed\n"); + goto out_delete; + } + if (setup_output_field(output_fields)) goto out_delete; -- cgit v1.2.3 From 7cb2a53f7f413734734bac214c4855e9863b9853 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 15 Jun 2022 09:32:21 -0700 Subject: perf record: Allow to specify max stack depth of fp callchain Currently it has no interface to specify the max stack depth for perf record. Extend the command line parameter to accept a number after 'fp' to specify the depth like '--call-graph fp,32'. Signed-off-by: Namhyung Kim Cc: Boqun Feng Cc: Davidlohr Bueso Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Waiman Long Cc: Will Deacon Link: https://lore.kernel.org/r/20220615163222.1275500-7-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 5 +++++ tools/perf/util/callchain.c | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 6bd6d07021ba..099817ef5150 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -275,6 +275,11 @@ OPTIONS User can change the size by passing the size after comma like "--call-graph dwarf,4096". + When "fp" recording is used, perf tries to save stack enties + up to the number specified in sysctl.kernel.perf_event_max_stack + by default. User can change the number by passing it after comma + like "--call-graph fp,32". + -q:: --quiet:: Don't print any message, useful for scripting. diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 5c27a4b2e7a7..7e663673f79f 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -31,6 +31,7 @@ #include "callchain.h" #include "branch.h" #include "symbol.h" +#include "util.h" #include "../perf.h" #define CALLCHAIN_PARAM_DEFAULT \ @@ -266,12 +267,17 @@ int parse_callchain_record(const char *arg, struct callchain_param *param) do { /* Framepointer style */ if (!strncmp(name, "fp", sizeof("fp"))) { - if (!strtok_r(NULL, ",", &saveptr)) { - param->record_mode = CALLCHAIN_FP; - ret = 0; - } else - pr_err("callchain: No more arguments " - "needed for --call-graph fp\n"); + ret = 0; + param->record_mode = CALLCHAIN_FP; + + tok = strtok_r(NULL, ",", &saveptr); + if (tok) { + unsigned long size; + + size = strtoul(tok, &name, 0); + if (size < (unsigned) sysctl__max_stack()) + param->max_stack = size; + } break; /* Dwarf style */ -- cgit v1.2.3 From 0d2997f750d1de394231bc22768dab94a5b5db2f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 15 Jun 2022 09:32:22 -0700 Subject: perf lock: Look up callchain for the contended locks The lock contention tracepoints don't provide lock names. All we can do is to get stack traces and show the caller instead. To minimize the overhead it's limited to up to 8 stack traces and display the first non-lock function symbol name as a caller. $ perf lock report -F acquired,contended,avg_wait,wait_total Name acquired contended avg wait total wait update_blocked_a... 40 40 3.61 us 144.45 us kernfs_fop_open+... 5 5 3.64 us 18.18 us _nohz_idle_balance 3 3 2.65 us 7.95 us tick_do_update_j... 1 1 6.04 us 6.04 us ep_scan_ready_list 1 1 3.93 us 3.93 us ... Signed-off-by: Namhyung Kim Acked-by: Ian Rogers Cc: Boqun Feng Cc: Davidlohr Bueso Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Waiman Long Cc: Will Deacon Link: https://lore.kernel.org/r/20220615163222.1275500-8-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-lock.c | 160 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 156 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 546dad1963c8..c5ca34741561 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -9,6 +9,7 @@ #include "util/symbol.h" #include "util/thread.h" #include "util/header.h" +#include "util/callchain.h" #include #include @@ -19,6 +20,7 @@ #include "util/tool.h" #include "util/data.h" #include "util/string2.h" +#include "util/map.h" #include #include @@ -32,6 +34,7 @@ #include #include #include +#include static struct perf_session *session; @@ -120,6 +123,24 @@ static struct rb_root thread_stats; static bool combine_locks; static bool show_thread_stats; +/* + * CONTENTION_STACK_DEPTH + * Number of stack trace entries to find callers + */ +#define CONTENTION_STACK_DEPTH 8 + +/* + * CONTENTION_STACK_SKIP + * Number of stack trace entries to skip when finding callers. + * The first few entries belong to the locking implementation itself. + */ +#define CONTENTION_STACK_SKIP 3 + +static u64 sched_text_start; +static u64 sched_text_end; +static u64 lock_text_start; +static u64 lock_text_end; + static struct thread_stat *thread_stat_find(u32 tid) { struct rb_node *node; @@ -839,6 +860,116 @@ end: return 0; } +static bool is_lock_function(u64 addr) +{ + if (!sched_text_start) { + struct machine *machine = &session->machines.host; + struct map *kmap; + struct symbol *sym; + + sym = machine__find_kernel_symbol_by_name(machine, + "__sched_text_start", + &kmap); + if (!sym) { + /* to avoid retry */ + sched_text_start = 1; + return false; + } + + sched_text_start = kmap->unmap_ip(kmap, sym->start); + + /* should not fail from here */ + sym = machine__find_kernel_symbol_by_name(machine, + "__sched_text_end", + &kmap); + sched_text_end = kmap->unmap_ip(kmap, sym->start); + + sym = machine__find_kernel_symbol_by_name(machine, + "__lock_text_start", + &kmap); + lock_text_start = kmap->unmap_ip(kmap, sym->start); + + sym = machine__find_kernel_symbol_by_name(machine, + "__lock_text_end", + &kmap); + lock_text_start = kmap->unmap_ip(kmap, sym->start); + } + + /* failed to get kernel symbols */ + if (sched_text_start == 1) + return false; + + /* mutex and rwsem functions are in sched text section */ + if (sched_text_start <= addr && addr < sched_text_end) + return true; + + /* spinlock functions are in lock text section */ + if (lock_text_start <= addr && addr < lock_text_end) + return true; + + return false; +} + +static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sample, + char *buf, int size) +{ + struct thread *thread; + struct callchain_cursor *cursor = &callchain_cursor; + struct symbol *sym; + int skip = 0; + int ret; + + /* lock names will be replaced to task name later */ + if (show_thread_stats) + return -1; + + thread = machine__findnew_thread(&session->machines.host, + -1, sample->pid); + if (thread == NULL) + return -1; + + /* use caller function name from the callchain */ + ret = thread__resolve_callchain(thread, cursor, evsel, sample, + NULL, NULL, CONTENTION_STACK_DEPTH); + if (ret != 0) { + thread__put(thread); + return -1; + } + + callchain_cursor_commit(cursor); + thread__put(thread); + + while (true) { + struct callchain_cursor_node *node; + + node = callchain_cursor_current(cursor); + if (node == NULL) + break; + + /* skip first few entries - for lock functions */ + if (++skip <= CONTENTION_STACK_SKIP) + goto next; + + sym = node->ms.sym; + if (sym && !is_lock_function(node->ip)) { + struct map *map = node->ms.map; + u64 offset; + + offset = map->map_ip(map, node->ip) - sym->start; + + if (offset) + scnprintf(buf, size, "%s+%#lx", sym->name, offset); + else + strlcpy(buf, sym->name, size); + return 0; + } + +next: + callchain_cursor_advance(cursor); + } + return -1; +} + static int report_lock_contention_begin_event(struct evsel *evsel, struct perf_sample *sample) { @@ -850,9 +981,18 @@ static int report_lock_contention_begin_event(struct evsel *evsel, if (show_thread_stats) addr = sample->tid; - ls = lock_stat_findnew(addr, "No name"); - if (!ls) - return -ENOMEM; + ls = lock_stat_find(addr); + if (!ls) { + char buf[128]; + const char *caller = buf; + + if (lock_contention_caller(evsel, sample, buf, sizeof(buf)) < 0) + caller = "Unknown"; + + ls = lock_stat_findnew(addr, caller); + if (!ls) + return -ENOMEM; + } ts = thread_stat_findnew(sample->tid); if (!ts) @@ -1233,6 +1373,7 @@ static int __cmd_report(bool display_info) struct perf_tool eops = { .sample = process_sample_event, .comm = perf_event__process_comm, + .mmap = perf_event__process_mmap, .namespaces = perf_event__process_namespaces, .ordered_events = true, }; @@ -1248,6 +1389,8 @@ static int __cmd_report(bool display_info) return PTR_ERR(session); } + /* for lock function check */ + symbol_conf.sort_by_name = true; symbol__init(&session->header.env); if (!perf_session__has_traces(session, "lock record")) @@ -1292,8 +1435,12 @@ static int __cmd_record(int argc, const char **argv) const char *record_args[] = { "record", "-R", "-m", "1024", "-c", "1", "--synth", "task", }; + const char *callgraph_args[] = { + "--call-graph", "fp," __stringify(CONTENTION_STACK_DEPTH), + }; unsigned int rec_argc, i, j, ret; unsigned int nr_tracepoints; + unsigned int nr_callgraph_args = 0; const char **rec_argv; bool has_lock_stat = true; @@ -1318,8 +1465,10 @@ static int __cmd_record(int argc, const char **argv) } } + nr_callgraph_args = ARRAY_SIZE(callgraph_args); + setup_args: - rec_argc = ARRAY_SIZE(record_args) + argc - 1; + rec_argc = ARRAY_SIZE(record_args) + nr_callgraph_args + argc - 1; if (has_lock_stat) nr_tracepoints = ARRAY_SIZE(lock_tracepoints); @@ -1351,6 +1500,9 @@ setup_args: rec_argv[i++] = ev_name; } + for (j = 0; j < nr_callgraph_args; j++, i++) + rec_argv[i] = callgraph_args[j]; + for (j = 1; j < (unsigned int)argc; j++, i++) rec_argv[i] = argv[j]; -- cgit v1.2.3 From a6bd98c45d1aeec59493617b02a86de39d384535 Mon Sep 17 00:00:00 2001 From: Blake Jones Date: Wed, 29 Jun 2022 14:36:32 -0700 Subject: perf buildid-list: Add a "-m" option to show kernel and modules build-ids This new option displays all of the information needed to do external BuildID-based symbolization of kernel stack traces, such as those collected by bpf_get_stackid(). For each kernel module plus the main kernel, it displays the BuildID, the start and end virtual addresses of that module's text range (rounded out to page boundaries), and the pathname of the module. When run as a non-privileged user, the actual addresses of the modules' text ranges are not available, so the tools displays "0, " for kernel modules and "0, 0xffffffffffffffff" for the kernel itself. Sample output: root# perf buildid-list -m cf6df852fd4da122d616153353cc8f560fd12fe0 ffffffffa5400000 ffffffffa6001e27 [kernel.kallsyms] 1aa7209aa2acb067d66ed6cf7676d65066384d61 ffffffffc0087000 ffffffffc008b000 /lib/modules/5.15.15-1rodete2-amd64/kernel/crypto/sha512_generic.ko 3857815b5bf0183697b68f8fe0ea06121644041e ffffffffc008c000 ffffffffc0098000 /lib/modules/5.15.15-1rodete2-amd64/kernel/arch/x86/crypto/sha512-ssse3.ko 4081fde0bca2bc097cb3e9d1efcb836047d485f1 ffffffffc0099000 ffffffffc009f000 /lib/modules/5.15.15-1rodete2-amd64/kernel/drivers/acpi/button.ko 1ef81ba4890552ea6b0314f9635fc43fc8cef568 ffffffffc00a4000 ffffffffc00aa000 /lib/modules/5.15.15-1rodete2-amd64/kernel/crypto/cryptd.ko cc5c985506cb240d7d082b55ed260cbb851f983e ffffffffc00af000 ffffffffc00b6000 /lib/modules/5.15.15-1rodete2-amd64/kernel/drivers/i2c/busses/i2c-piix4.ko [...] Committer notes: u64 formatter should be PRIx64 for printing as hex numbers, fix this: 28 5.28 debian:experimental-x-mips : FAIL gcc version 11.2.0 (Debian 11.2.0-18) builtin-buildid-list.c: In function 'buildid__map_cb': builtin-buildid-list.c:32:24: error: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'u64' {aka 'long long unsigned int'} [-Werror=format=] 32 | printf("%s %16lx %16lx", bid_buf, map->start, map->end); | ~~~~^ ~~~~~~~~~~ | | | | long unsigned int u64 {aka long long unsigned int} | %16llx builtin-buildid-list.c:32:30: error: format '%lx' expects argument of type 'long unsigned int', but argument 4 has type 'u64' {aka 'long long unsigned int'} [-Werror=format=] 32 | printf("%s %16lx %16lx", bid_buf, map->start, map->end); | ~~~~^ ~~~~~~~~ | | | | long unsigned int u64 {aka long long unsigned int} | %16llx cc1: all warnings being treated as errors Signed-off-by: Blake Jones Acked-by: Jiri Olsa Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20220629213632.3899212-1-blakejones@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-buildid-list.txt | 4 +++ tools/perf/builtin-buildid-list.c | 39 +++++++++++++++++++++++++- tools/perf/util/machine.c | 15 ++++++++++ tools/perf/util/machine.h | 5 ++++ 4 files changed, 62 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-buildid-list.txt b/tools/perf/Documentation/perf-buildid-list.txt index 25c52efcc7f0..e1e8fdbe06b9 100644 --- a/tools/perf/Documentation/perf-buildid-list.txt +++ b/tools/perf/Documentation/perf-buildid-list.txt @@ -33,6 +33,10 @@ OPTIONS -k:: --kernel:: Show running kernel build id. +-m:: +--kernel-maps:: + Show buildid, start/end text address, and path of running kernel and + its modules. -v:: --verbose:: Be more verbose. diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index cebadd632234..00bfe89f0b5d 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -12,14 +12,44 @@ #include "util/build-id.h" #include "util/debug.h" #include "util/dso.h" +#include "util/map.h" #include #include #include "util/session.h" #include "util/symbol.h" #include "util/data.h" #include +#include #include +static int buildid__map_cb(struct map *map, void *arg __maybe_unused) +{ + const struct dso *dso = map->dso; + char bid_buf[SBUILD_ID_SIZE]; + + memset(bid_buf, 0, sizeof(bid_buf)); + if (dso->has_build_id) + build_id__sprintf(&dso->bid, bid_buf); + printf("%s %16" PRIx64 " %16" PRIx64, bid_buf, map->start, map->end); + if (dso->long_name != NULL) { + printf(" %s", dso->long_name); + } else if (dso->short_name != NULL) { + printf(" %s", dso->short_name); + } + printf("\n"); + + return 0; +} + +static void buildid__show_kernel_maps(void) +{ + struct machine *machine; + + machine = machine__new_host(); + machine__for_each_kernel_map(machine, buildid__map_cb, NULL); + machine__delete(machine); +} + static int sysfs__fprintf_build_id(FILE *fp) { char sbuild_id[SBUILD_ID_SIZE]; @@ -99,6 +129,7 @@ out: int cmd_buildid_list(int argc, const char **argv) { bool show_kernel = false; + bool show_kernel_maps = false; bool with_hits = false; bool force = false; const struct option options[] = { @@ -106,6 +137,8 @@ int cmd_buildid_list(int argc, const char **argv) OPT_STRING('i', "input", &input_name, "file", "input file name"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), OPT_BOOLEAN('k', "kernel", &show_kernel, "Show current kernel build id"), + OPT_BOOLEAN('m', "kernel-maps", &show_kernel_maps, + "Show build id of current kernel + modules"), OPT_INCR('v', "verbose", &verbose, "be more verbose"), OPT_END() }; @@ -117,8 +150,12 @@ int cmd_buildid_list(int argc, const char **argv) argc = parse_options(argc, argv, options, buildid_list_usage, 0); setup_pager(); - if (show_kernel) + if (show_kernel) { return !(sysfs__fprintf_build_id(stdout) > 0); + } else if (show_kernel_maps) { + buildid__show_kernel_maps(); + return 0; + } return perf_session__list_build_ids(force, with_hits); } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 009061852808..16d225149b93 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -3327,3 +3327,18 @@ int machine__for_each_dso(struct machine *machine, machine__dso_t fn, void *priv } return err; } + +int machine__for_each_kernel_map(struct machine *machine, machine__map_t fn, void *priv) +{ + struct maps *maps = machine__kernel_maps(machine); + struct map *map; + int err = 0; + + for (map = maps__first(maps); map != NULL; map = map__next(map)) { + err = fn(map, priv); + if (err != 0) { + break; + } + } + return err; +} diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 5d7daf7cb7bc..e1476343cbb2 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -262,6 +262,11 @@ typedef int (*machine__dso_t)(struct dso *dso, struct machine *machine, void *pr int machine__for_each_dso(struct machine *machine, machine__dso_t fn, void *priv); + +typedef int (*machine__map_t)(struct map *map, void *priv); +int machine__for_each_kernel_map(struct machine *machine, machine__map_t fn, + void *priv); + int machine__for_each_thread(struct machine *machine, int (*fn)(struct thread *thread, void *p), void *priv); -- cgit v1.2.3 From 68566a7cf56bf3148797c218ed45a9de078ef47c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:44 +0300 Subject: perf tools: Fix dso_id inode generation comparison Synthesized MMAP events have zero ino_generation, so do not compare them to DSOs with a real ino_generation otherwise we end up with a DSO without a build id. Fixes: 0e3149f86b99ddab ("perf dso: Move dso_id from 'struct map' to 'struct dso'") Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Ian Rogers Cc: Jiri Olsa Cc: kvm@vger.kernel.org Cc: Namhyung Kim Link: https://lore.kernel.org/r/20220711093218.10967-2-adrian.hunter@intel.com [ Added clarification to the comment from Ian + more detailed explanation from Adrian ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dsos.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c index b97366f77bbf..2bd23e4cf19e 100644 --- a/tools/perf/util/dsos.c +++ b/tools/perf/util/dsos.c @@ -23,8 +23,19 @@ static int __dso_id__cmp(struct dso_id *a, struct dso_id *b) if (a->ino > b->ino) return -1; if (a->ino < b->ino) return 1; - if (a->ino_generation > b->ino_generation) return -1; - if (a->ino_generation < b->ino_generation) return 1; + /* + * Synthesized MMAP events have zero ino_generation, avoid comparing + * them with MMAP events with actual ino_generation. + * + * I found it harmful because the mismatch resulted in a new + * dso that did not have a build ID whereas the original dso did have a + * build ID. The build ID was essential because the object was not found + * otherwise. - Adrian + */ + if (a->ino_generation && b->ino_generation) { + if (a->ino_generation > b->ino_generation) return -1; + if (a->ino_generation < b->ino_generation) return 1; + } return 0; } -- cgit v1.2.3 From 163dac34d7a22b4fd980e4d00459a07090f8b9af Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:45 +0300 Subject: perf tools: Export dsos__for_each_with_build_id() Export dsos__for_each_with_build_id() so it can be used elsewhere. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-3-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 6 ------ tools/perf/util/dso.h | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 328668f38c69..4c9093b64d1f 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -300,12 +300,6 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size, return __dso__build_id_filename(dso, bf, size, is_debug, is_kallsyms); } -#define dsos__for_each_with_build_id(pos, head) \ - list_for_each_entry(pos, head, node) \ - if (!pos->has_build_id) \ - continue; \ - else - static int write_buildid(const char *name, size_t name_len, struct build_id *bid, pid_t pid, u16 misc, struct feat_fd *fd) { diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 97047a11282b..66981c7a9a18 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -227,6 +227,12 @@ struct dso { #define dso__for_each_symbol(dso, pos, n) \ symbols__for_each_entry(&(dso)->symbols, pos, n) +#define dsos__for_each_with_build_id(pos, head) \ + list_for_each_entry(pos, head, node) \ + if (!pos->has_build_id) \ + continue; \ + else + static inline void dso__set_loaded(struct dso *dso) { dso->loaded = true; -- cgit v1.2.3 From f8bcf1e223ad08a4f0d45369a749c58c9bcd5f3c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:46 +0300 Subject: perf ordered_events: Add ordered_events__last_flush_time() Allow callers to get the ordered_events last flush timestamp. This is needed in perf inject to obey finished-round ordering when injecting additional events (e.g. from a guest perf.data file) with timestamps. Any additional events that have timestamps before the last flush time must be injected before the corresponding FINISHED_ROUND event. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-4-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ordered-events.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/ordered-events.h b/tools/perf/util/ordered-events.h index 0b05c3c0aeaa..8febbd7c98ca 100644 --- a/tools/perf/util/ordered-events.h +++ b/tools/perf/util/ordered-events.h @@ -75,4 +75,10 @@ void ordered_events__set_copy_on_queue(struct ordered_events *oe, bool copy) { oe->copy_on_queue = copy; } + +static inline u64 ordered_events__last_flush_time(struct ordered_events *oe) +{ + return oe->last_flush; +} + #endif /* __ORDERED_EVENTS_H */ -- cgit v1.2.3 From eddc6e3f6684597924f368cb564d7432b2f7d23e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:47 +0300 Subject: perf tools: Export perf_event__process_finished_round() Export perf_event__process_finished_round() so it can be used elsewhere. This is needed in perf inject to obey finished-round ordering. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-5-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 12 ++++-------- tools/perf/util/session.h | 4 ++++ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 37f833c3c81b..4c9513bc6d89 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -374,10 +374,6 @@ static int process_finished_round_stub(struct perf_tool *tool __maybe_unused, return 0; } -static int process_finished_round(struct perf_tool *tool, - union perf_event *event, - struct ordered_events *oe); - static int skipn(int fd, off_t n) { char buf[4096]; @@ -534,7 +530,7 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->build_id = process_event_op2_stub; if (tool->finished_round == NULL) { if (tool->ordered_events) - tool->finished_round = process_finished_round; + tool->finished_round = perf_event__process_finished_round; else tool->finished_round = process_finished_round_stub; } @@ -1069,9 +1065,9 @@ static perf_event__swap_op perf_event__swap_ops[] = { * Flush every events below timestamp 7 * etc... */ -static int process_finished_round(struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct ordered_events *oe) +int perf_event__process_finished_round(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct ordered_events *oe) { if (dump_trace) fprintf(stdout, "\n"); diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 34500a3da735..be5871ea558f 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -155,4 +155,8 @@ int perf_session__deliver_synth_event(struct perf_session *session, int perf_event__process_id_index(struct perf_session *session, union perf_event *event); +int perf_event__process_finished_round(struct perf_tool *tool, + union perf_event *event, + struct ordered_events *oe); + #endif /* __PERF_SESSION_H */ -- cgit v1.2.3 From 0a64de04c94ad4285120bed0dfb382ea98d6d499 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:48 +0300 Subject: perf tools: Factor out evsel__id_hdr_size() Factor out evsel__id_hdr_size() so it can be reused. This is needed by perf inject. When injecting events from a guest perf.data file, there is a possibility that the sample ID numbers conflict. To re-write an ID sample, the old one needs to be removed first, which means determining how big it is with evsel__id_hdr_size() and then subtracting that from the event size. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-6-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 28 +--------------------------- tools/perf/util/evsel.c | 26 ++++++++++++++++++++++++++ tools/perf/util/evsel.h | 2 ++ 3 files changed, 29 insertions(+), 27 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 48af7d379d82..03fbe151b0c4 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1244,34 +1244,8 @@ bool evlist__valid_read_format(struct evlist *evlist) u16 evlist__id_hdr_size(struct evlist *evlist) { struct evsel *first = evlist__first(evlist); - struct perf_sample *data; - u64 sample_type; - u16 size = 0; - if (!first->core.attr.sample_id_all) - goto out; - - sample_type = first->core.attr.sample_type; - - if (sample_type & PERF_SAMPLE_TID) - size += sizeof(data->tid) * 2; - - if (sample_type & PERF_SAMPLE_TIME) - size += sizeof(data->time); - - if (sample_type & PERF_SAMPLE_ID) - size += sizeof(data->id); - - if (sample_type & PERF_SAMPLE_STREAM_ID) - size += sizeof(data->stream_id); - - if (sample_type & PERF_SAMPLE_CPU) - size += sizeof(data->cpu) * 2; - - if (sample_type & PERF_SAMPLE_IDENTIFIER) - size += sizeof(data->id); -out: - return size; + return first->core.attr.sample_id_all ? evsel__id_hdr_size(first) : 0; } bool evlist__valid_sample_id_all(struct evlist *evlist) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index a67cc3f2fa74..9a30ccb7b104 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -2724,6 +2724,32 @@ int evsel__parse_sample_timestamp(struct evsel *evsel, union perf_event *event, return 0; } +u16 evsel__id_hdr_size(struct evsel *evsel) +{ + u64 sample_type = evsel->core.attr.sample_type; + u16 size = 0; + + if (sample_type & PERF_SAMPLE_TID) + size += sizeof(u64); + + if (sample_type & PERF_SAMPLE_TIME) + size += sizeof(u64); + + if (sample_type & PERF_SAMPLE_ID) + size += sizeof(u64); + + if (sample_type & PERF_SAMPLE_STREAM_ID) + size += sizeof(u64); + + if (sample_type & PERF_SAMPLE_CPU) + size += sizeof(u64); + + if (sample_type & PERF_SAMPLE_IDENTIFIER) + size += sizeof(u64); + + return size; +} + struct tep_format_field *evsel__field(struct evsel *evsel, const char *name) { return tep_find_field(evsel->tp_format, name); diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 92bed8e2f7d8..699448f2bc2b 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -381,6 +381,8 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, int evsel__parse_sample_timestamp(struct evsel *evsel, union perf_event *event, u64 *timestamp); +u16 evsel__id_hdr_size(struct evsel *evsel); + static inline struct evsel *evsel__next(struct evsel *evsel) { return list_entry(evsel->core.node.next, struct evsel, core.node); -- cgit v1.2.3 From 1ee94463e9ac4721cfef27ffcd6f97ab026e3aac Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:49 +0300 Subject: perf tools: Add perf_event__synthesize_id_sample() Add perf_event__synthesize_id_sample() to enable the synthesis of ID samples. This is needed by perf inject. When injecting events from a guest perf.data file, there is a possibility that the sample ID numbers conflict. In that case, perf_event__synthesize_id_sample() can be used to re-write the ID sample. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-7-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/synthetic-events.c | 47 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/synthetic-events.h | 1 + 2 files changed, 48 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index fe5db4bf0042..ed9623702f34 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -1712,6 +1712,53 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo return 0; } +int perf_event__synthesize_id_sample(__u64 *array, u64 type, const struct perf_sample *sample) +{ + __u64 *start = array; + + /* + * used for cross-endian analysis. See git commit 65014ab3 + * for why this goofiness is needed. + */ + union u64_swap u; + + if (type & PERF_SAMPLE_TID) { + u.val32[0] = sample->pid; + u.val32[1] = sample->tid; + *array = u.val64; + array++; + } + + if (type & PERF_SAMPLE_TIME) { + *array = sample->time; + array++; + } + + if (type & PERF_SAMPLE_ID) { + *array = sample->id; + array++; + } + + if (type & PERF_SAMPLE_STREAM_ID) { + *array = sample->stream_id; + array++; + } + + if (type & PERF_SAMPLE_CPU) { + u.val32[0] = sample->cpu; + u.val32[1] = 0; + *array = u.val64; + array++; + } + + if (type & PERF_SAMPLE_IDENTIFIER) { + *array = sample->id; + array++; + } + + return (void *)array - (void *)start; +} + int perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process, struct evlist *evlist, struct machine *machine) { diff --git a/tools/perf/util/synthetic-events.h b/tools/perf/util/synthetic-events.h index 78a0450db164..b136ec3ec95d 100644 --- a/tools/perf/util/synthetic-events.h +++ b/tools/perf/util/synthetic-events.h @@ -55,6 +55,7 @@ int perf_event__synthesize_extra_attr(struct perf_tool *tool, struct evlist *evs int perf_event__synthesize_extra_kmaps(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_features(struct perf_tool *tool, struct perf_session *session, struct evlist *evlist, perf_event__handler_t process); int perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process, struct evlist *evlist, struct machine *machine); +int perf_event__synthesize_id_sample(__u64 *array, u64 type, const struct perf_sample *sample); int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_mmap_events(struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, struct machine *machine, bool mmap_data); int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); -- cgit v1.2.3 From 57190e38b00d25abba9631b8db53740fce4baced Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:50 +0300 Subject: perf script: Add --dump-unsorted-raw-trace option When reviewing the results of perf inject, it is useful to be able to see the events in the order they appear in the file. So add --dump-unsorted-raw-trace option to do an unsorted dump. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-8-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-script.txt | 3 +++ tools/perf/builtin-script.c | 8 ++++++++ 2 files changed, 11 insertions(+) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 1a557ff8f210..e250ff5566cf 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -79,6 +79,9 @@ OPTIONS --dump-raw-trace=:: Display verbose dump of the trace data. +--dump-unsorted-raw-trace=:: + Same as --dump-raw-trace but not sorted in time order. + -L:: --Latency=:: Show latency attributes (irqs/preemption disabled, etc). diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 7cf21ab16f4f..4b00a50faf00 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -3746,6 +3746,7 @@ int cmd_script(int argc, const char **argv) bool header = false; bool header_only = false; bool script_started = false; + bool unsorted_dump = false; char *rec_script_path = NULL; char *rep_script_path = NULL; struct perf_session *session; @@ -3794,6 +3795,8 @@ int cmd_script(int argc, const char **argv) const struct option options[] = { OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), + OPT_BOOLEAN(0, "dump-unsorted-raw-trace", &unsorted_dump, + "dump unsorted raw trace in ASCII"), OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('L', "Latency", &latency_format, @@ -3956,6 +3959,11 @@ int cmd_script(int argc, const char **argv) data.path = input_name; data.force = symbol_conf.force; + if (unsorted_dump) { + dump_trace = true; + script.tool.ordered_events = false; + } + if (symbol__validate_sym_arguments()) return -1; -- cgit v1.2.3 From 15fe03621d9df90c23de7a2099b692e2da344cde Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:51 +0300 Subject: perf buildid-cache: Add guestmount'd files to the build ID cache When the guestmount option is used, a guest machine's file system mount point is recorded in machine->root_dir. perf already iterates guest machines when adding files to the build ID cache, but does not take machine->root_dir into account. Use machine->root_dir to find files for guest build IDs, and add them to the build ID cache using the "proper" name i.e. relative to the guest root directory not the host root directory. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-9-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 67 +++++++++++++++++++++++++++++++++++----------- tools/perf/util/build-id.h | 16 ++++++++--- 2 files changed, 63 insertions(+), 20 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 4c9093b64d1f..7c9f441936ee 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -625,9 +625,12 @@ static int build_id_cache__add_sdt_cache(const char *sbuild_id, #endif static char *build_id_cache__find_debug(const char *sbuild_id, - struct nsinfo *nsi) + struct nsinfo *nsi, + const char *root_dir) { + const char *dirname = "/usr/lib/debug/.build-id/"; char *realname = NULL; + char dirbuf[PATH_MAX]; char *debugfile; struct nscookie nsc; size_t len = 0; @@ -636,8 +639,12 @@ static char *build_id_cache__find_debug(const char *sbuild_id, if (!debugfile) goto out; - len = __symbol__join_symfs(debugfile, PATH_MAX, - "/usr/lib/debug/.build-id/"); + if (root_dir) { + path__join(dirbuf, PATH_MAX, root_dir, dirname); + dirname = dirbuf; + } + + len = __symbol__join_symfs(debugfile, PATH_MAX, dirname); snprintf(debugfile + len, PATH_MAX - len, "%.2s/%s.debug", sbuild_id, sbuild_id + 2); @@ -668,14 +675,18 @@ out: int build_id_cache__add(const char *sbuild_id, const char *name, const char *realname, - struct nsinfo *nsi, bool is_kallsyms, bool is_vdso) + struct nsinfo *nsi, bool is_kallsyms, bool is_vdso, + const char *proper_name, const char *root_dir) { const size_t size = PATH_MAX; char *filename = NULL, *dir_name = NULL, *linkname = zalloc(size), *tmp; char *debugfile = NULL; int err = -1; - dir_name = build_id_cache__cachedir(sbuild_id, name, nsi, is_kallsyms, + if (!proper_name) + proper_name = name; + + dir_name = build_id_cache__cachedir(sbuild_id, proper_name, nsi, is_kallsyms, is_vdso); if (!dir_name) goto out_free; @@ -715,7 +726,7 @@ build_id_cache__add(const char *sbuild_id, const char *name, const char *realnam */ if (!is_kallsyms && !is_vdso && strncmp(".ko", name + strlen(name) - 3, 3)) { - debugfile = build_id_cache__find_debug(sbuild_id, nsi); + debugfile = build_id_cache__find_debug(sbuild_id, nsi, root_dir); if (debugfile) { zfree(&filename); if (asprintf(&filename, "%s/%s", dir_name, @@ -781,8 +792,9 @@ out_free: return err; } -int build_id_cache__add_s(const char *sbuild_id, const char *name, - struct nsinfo *nsi, bool is_kallsyms, bool is_vdso) +int __build_id_cache__add_s(const char *sbuild_id, const char *name, + struct nsinfo *nsi, bool is_kallsyms, bool is_vdso, + const char *proper_name, const char *root_dir) { char *realname = NULL; int err = -1; @@ -796,8 +808,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, goto out_free; } - err = build_id_cache__add(sbuild_id, name, realname, nsi, is_kallsyms, is_vdso); - + err = build_id_cache__add(sbuild_id, name, realname, nsi, + is_kallsyms, is_vdso, proper_name, root_dir); out_free: if (!is_kallsyms) free(realname); @@ -806,14 +818,16 @@ out_free: static int build_id_cache__add_b(const struct build_id *bid, const char *name, struct nsinfo *nsi, - bool is_kallsyms, bool is_vdso) + bool is_kallsyms, bool is_vdso, + const char *proper_name, + const char *root_dir) { char sbuild_id[SBUILD_ID_SIZE]; build_id__sprintf(bid, sbuild_id); - return build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms, - is_vdso); + return __build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms, + is_vdso, proper_name, root_dir); } bool build_id_cache__cached(const char *sbuild_id) @@ -896,6 +910,10 @@ static int dso__cache_build_id(struct dso *dso, struct machine *machine, bool is_kallsyms = dso__is_kallsyms(dso); bool is_vdso = dso__is_vdso(dso); const char *name = dso->long_name; + const char *proper_name = NULL; + const char *root_dir = NULL; + char *allocated_name = NULL; + int ret = 0; if (!dso->has_build_id) return 0; @@ -905,11 +923,28 @@ static int dso__cache_build_id(struct dso *dso, struct machine *machine, name = machine->mmap_name; } + if (!machine__is_host(machine)) { + if (*machine->root_dir) { + root_dir = machine->root_dir; + ret = asprintf(&allocated_name, "%s/%s", root_dir, name); + if (ret < 0) + return ret; + proper_name = name; + name = allocated_name; + } else if (is_kallsyms) { + /* Cannot get guest kallsyms */ + return 0; + } + } + if (!is_kallsyms && dso__build_id_mismatch(dso, name)) - return 0; + goto out_free; - return build_id_cache__add_b(&dso->bid, name, dso->nsinfo, - is_kallsyms, is_vdso); + ret = build_id_cache__add_b(&dso->bid, name, dso->nsinfo, + is_kallsyms, is_vdso, proper_name, root_dir); +out_free: + free(allocated_name); + return ret; } static int diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index c19617151670..4e3a1169379b 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -66,10 +66,18 @@ int build_id_cache__list_build_ids(const char *pathname, struct nsinfo *nsi, struct strlist **result); bool build_id_cache__cached(const char *sbuild_id); int build_id_cache__add(const char *sbuild_id, const char *name, const char *realname, - struct nsinfo *nsi, bool is_kallsyms, bool is_vdso); -int build_id_cache__add_s(const char *sbuild_id, - const char *name, struct nsinfo *nsi, - bool is_kallsyms, bool is_vdso); + struct nsinfo *nsi, bool is_kallsyms, bool is_vdso, + const char *proper_name, const char *root_dir); +int __build_id_cache__add_s(const char *sbuild_id, + const char *name, struct nsinfo *nsi, + bool is_kallsyms, bool is_vdso, + const char *proper_name, const char *root_dir); +static inline int build_id_cache__add_s(const char *sbuild_id, + const char *name, struct nsinfo *nsi, + bool is_kallsyms, bool is_vdso) +{ + return __build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms, is_vdso, NULL, NULL); +} int build_id_cache__remove_s(const char *sbuild_id); extern char buildid_dir[]; -- cgit v1.2.3 From c1fd5b7d8aed8104d5189c3d55545d67f9149bb6 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:52 +0300 Subject: perf buildid-cache: Do not require purge files to also be in the file system realname() returns NULL if the file is not in the file system, but we can still remove it from the build ID cache in that case, so continue and attempt the purge with the name provided. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-10-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 7c9f441936ee..9e176146eb10 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -561,14 +561,11 @@ char *build_id_cache__cachedir(const char *sbuild_id, const char *name, char *realname = (char *)name, *filename; bool slash = is_kallsyms || is_vdso; - if (!slash) { + if (!slash) realname = nsinfo__realpath(name, nsi); - if (!realname) - return NULL; - } if (asprintf(&filename, "%s%s%s%s%s", buildid_dir, slash ? "/" : "", - is_vdso ? DSO__NAME_VDSO : realname, + is_vdso ? DSO__NAME_VDSO : (realname ? realname : name), sbuild_id ? "/" : "", sbuild_id ?: "") < 0) filename = NULL; -- cgit v1.2.3 From b47bb18661eaed30790d70b7563a5220b3c59594 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:53 +0300 Subject: perf tools: Add machine_pid and vcpu to id_index When injecting events from a guest perf.data file, the events will have separate sample ID numbers. These ID numbers can then be used to determine which machine an event belongs to. To facilitate that, add machine_pid and vcpu to id_index records. For backward compatibility, these are added at the end of the record, and the length of the record is used to determine if they are present or not. Note, this is needed because the events from a guest perf.data file contain the pid/tid of the process running at that time inside the VM not the pid/tid of the (QEMU) hypervisor thread. So a way is needed to relate guest events back to the guest machine and VCPU, and using sample ID numbers for that is relatively simple and convenient. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-11-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/include/internal/evsel.h | 4 +++ tools/lib/perf/include/perf/event.h | 5 ++++ tools/perf/util/session.c | 40 ++++++++++++++++++++++---- tools/perf/util/synthetic-events.c | 51 +++++++++++++++++++++++++-------- tools/perf/util/synthetic-events.h | 1 + 5 files changed, 84 insertions(+), 17 deletions(-) (limited to 'tools') diff --git a/tools/lib/perf/include/internal/evsel.h b/tools/lib/perf/include/internal/evsel.h index 2a912a1f1989..a99a75d9e78f 100644 --- a/tools/lib/perf/include/internal/evsel.h +++ b/tools/lib/perf/include/internal/evsel.h @@ -30,6 +30,10 @@ struct perf_sample_id { struct perf_cpu cpu; pid_t tid; + /* Guest machine pid and VCPU, valid only if machine_pid is non-zero */ + pid_t machine_pid; + struct perf_cpu vcpu; + /* Holds total ID period value for PERF_SAMPLE_READ processing. */ u64 period; }; diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h index 9f7ca070da87..c2dbd3e88885 100644 --- a/tools/lib/perf/include/perf/event.h +++ b/tools/lib/perf/include/perf/event.h @@ -237,6 +237,11 @@ struct id_index_entry { __u64 tid; }; +struct id_index_entry_2 { + __u64 machine_pid; + __u64 vcpu; +}; + struct perf_record_id_index { struct perf_event_header header; __u64 nr; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 4c9513bc6d89..5141fe164e97 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2756,18 +2756,35 @@ int perf_event__process_id_index(struct perf_session *session, { struct evlist *evlist = session->evlist; struct perf_record_id_index *ie = &event->id_index; + size_t sz = ie->header.size - sizeof(*ie); size_t i, nr, max_nr; + size_t e1_sz = sizeof(struct id_index_entry); + size_t e2_sz = sizeof(struct id_index_entry_2); + size_t etot_sz = e1_sz + e2_sz; + struct id_index_entry_2 *e2; - max_nr = (ie->header.size - sizeof(struct perf_record_id_index)) / - sizeof(struct id_index_entry); + max_nr = sz / e1_sz; nr = ie->nr; - if (nr > max_nr) + if (nr > max_nr) { + printf("Too big: nr %zu max_nr %zu\n", nr, max_nr); return -EINVAL; + } + + if (sz >= nr * etot_sz) { + max_nr = sz / etot_sz; + if (nr > max_nr) { + printf("Too big2: nr %zu max_nr %zu\n", nr, max_nr); + return -EINVAL; + } + e2 = (void *)ie + sizeof(*ie) + nr * e1_sz; + } else { + e2 = NULL; + } if (dump_trace) fprintf(stdout, " nr: %zu\n", nr); - for (i = 0; i < nr; i++) { + for (i = 0; i < nr; i++, (e2 ? e2++ : 0)) { struct id_index_entry *e = &ie->entries[i]; struct perf_sample_id *sid; @@ -2775,15 +2792,28 @@ int perf_event__process_id_index(struct perf_session *session, fprintf(stdout, " ... id: %"PRI_lu64, e->id); fprintf(stdout, " idx: %"PRI_lu64, e->idx); fprintf(stdout, " cpu: %"PRI_ld64, e->cpu); - fprintf(stdout, " tid: %"PRI_ld64"\n", e->tid); + fprintf(stdout, " tid: %"PRI_ld64, e->tid); + if (e2) { + fprintf(stdout, " machine_pid: %"PRI_ld64, e2->machine_pid); + fprintf(stdout, " vcpu: %"PRI_lu64"\n", e2->vcpu); + } else { + fprintf(stdout, "\n"); + } } sid = evlist__id2sid(evlist, e->id); if (!sid) return -ENOENT; + sid->idx = e->idx; sid->cpu.cpu = e->cpu; sid->tid = e->tid; + + if (!e2) + continue; + + sid->machine_pid = e2->machine_pid; + sid->vcpu.cpu = e2->vcpu; } return 0; } diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index ed9623702f34..2ae59c03ae77 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -1759,19 +1759,26 @@ int perf_event__synthesize_id_sample(__u64 *array, u64 type, const struct perf_s return (void *)array - (void *)start; } -int perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process, - struct evlist *evlist, struct machine *machine) +int __perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process, + struct evlist *evlist, struct machine *machine, size_t from) { union perf_event *ev; struct evsel *evsel; - size_t nr = 0, i = 0, sz, max_nr, n; + size_t nr = 0, i = 0, sz, max_nr, n, pos; + size_t e1_sz = sizeof(struct id_index_entry); + size_t e2_sz = sizeof(struct id_index_entry_2); + size_t etot_sz = e1_sz + e2_sz; + bool e2_needed = false; int err; - max_nr = (UINT16_MAX - sizeof(struct perf_record_id_index)) / - sizeof(struct id_index_entry); + max_nr = (UINT16_MAX - sizeof(struct perf_record_id_index)) / etot_sz; - evlist__for_each_entry(evlist, evsel) + pos = 0; + evlist__for_each_entry(evlist, evsel) { + if (pos++ < from) + continue; nr += evsel->core.ids; + } if (!nr) return 0; @@ -1779,31 +1786,38 @@ int perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_ pr_debug2("Synthesizing id index\n"); n = nr > max_nr ? max_nr : nr; - sz = sizeof(struct perf_record_id_index) + n * sizeof(struct id_index_entry); + sz = sizeof(struct perf_record_id_index) + n * etot_sz; ev = zalloc(sz); if (!ev) return -ENOMEM; + sz = sizeof(struct perf_record_id_index) + n * e1_sz; + ev->id_index.header.type = PERF_RECORD_ID_INDEX; - ev->id_index.header.size = sz; ev->id_index.nr = n; + pos = 0; evlist__for_each_entry(evlist, evsel) { u32 j; - for (j = 0; j < evsel->core.ids; j++) { + if (pos++ < from) + continue; + for (j = 0; j < evsel->core.ids; j++, i++) { struct id_index_entry *e; + struct id_index_entry_2 *e2; struct perf_sample_id *sid; if (i >= n) { + ev->id_index.header.size = sz + (e2_needed ? n * e2_sz : 0); err = process(tool, ev, NULL, machine); if (err) goto out_err; nr -= n; i = 0; + e2_needed = false; } - e = &ev->id_index.entries[i++]; + e = &ev->id_index.entries[i]; e->id = evsel->core.id[j]; @@ -1816,11 +1830,18 @@ int perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_ e->idx = sid->idx; e->cpu = sid->cpu.cpu; e->tid = sid->tid; + + if (sid->machine_pid) + e2_needed = true; + + e2 = (void *)ev + sz; + e2[i].machine_pid = sid->machine_pid; + e2[i].vcpu = sid->vcpu.cpu; } } - sz = sizeof(struct perf_record_id_index) + nr * sizeof(struct id_index_entry); - ev->id_index.header.size = sz; + sz = sizeof(struct perf_record_id_index) + nr * e1_sz; + ev->id_index.header.size = sz + (e2_needed ? nr * e2_sz : 0); ev->id_index.nr = nr; err = process(tool, ev, NULL, machine); @@ -1830,6 +1851,12 @@ out_err: return err; } +int perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process, + struct evlist *evlist, struct machine *machine) +{ + return __perf_event__synthesize_id_index(tool, process, evlist, machine, 0); +} + int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, struct target *target, struct perf_thread_map *threads, perf_event__handler_t process, bool needs_mmap, diff --git a/tools/perf/util/synthetic-events.h b/tools/perf/util/synthetic-events.h index b136ec3ec95d..81cb3d6af0b9 100644 --- a/tools/perf/util/synthetic-events.h +++ b/tools/perf/util/synthetic-events.h @@ -55,6 +55,7 @@ int perf_event__synthesize_extra_attr(struct perf_tool *tool, struct evlist *evs int perf_event__synthesize_extra_kmaps(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_features(struct perf_tool *tool, struct perf_session *session, struct evlist *evlist, perf_event__handler_t process); int perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process, struct evlist *evlist, struct machine *machine); +int __perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process, struct evlist *evlist, struct machine *machine, size_t from); int perf_event__synthesize_id_sample(__u64 *array, u64 type, const struct perf_sample *sample); int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_mmap_events(struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, struct machine *machine, bool mmap_data); -- cgit v1.2.3 From ff7a78c210ed90bed915df55cc56876d4151c5ac Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:54 +0300 Subject: perf session: Create guest machines from id_index Now that id_index has machine_pid, use it to create guest machines. Create the guest machines with an idle thread because guest events for "swapper" will be possible. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-12-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5141fe164e97..1af981d5ad3c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2751,6 +2751,24 @@ void perf_session__fprintf_info(struct perf_session *session, FILE *fp, fprintf(fp, "# ========\n#\n"); } +static int perf_session__register_guest(struct perf_session *session, pid_t machine_pid) +{ + struct machine *machine = machines__findnew(&session->machines, machine_pid); + struct thread *thread; + + if (!machine) + return -ENOMEM; + + machine->single_address_space = session->machines.host.single_address_space; + + thread = machine__idle_thread(machine); + if (!thread) + return -ENOMEM; + thread__put(thread); + + return 0; +} + int perf_event__process_id_index(struct perf_session *session, union perf_event *event) { @@ -2762,6 +2780,7 @@ int perf_event__process_id_index(struct perf_session *session, size_t e2_sz = sizeof(struct id_index_entry_2); size_t etot_sz = e1_sz + e2_sz; struct id_index_entry_2 *e2; + pid_t last_pid = 0; max_nr = sz / e1_sz; nr = ie->nr; @@ -2787,6 +2806,7 @@ int perf_event__process_id_index(struct perf_session *session, for (i = 0; i < nr; i++, (e2 ? e2++ : 0)) { struct id_index_entry *e = &ie->entries[i]; struct perf_sample_id *sid; + int ret; if (dump_trace) { fprintf(stdout, " ... id: %"PRI_lu64, e->id); @@ -2814,6 +2834,17 @@ int perf_event__process_id_index(struct perf_session *session, sid->machine_pid = e2->machine_pid; sid->vcpu.cpu = e2->vcpu; + + if (!sid->machine_pid) + continue; + + if (sid->machine_pid != last_pid) { + ret = perf_session__register_guest(session, sid->machine_pid); + if (ret) + return ret; + last_pid = sid->machine_pid; + perf_guest = true; + } } return 0; } -- cgit v1.2.3 From 797efbc523b37de29dc533a8561d34b97deb42e4 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:55 +0300 Subject: perf tools: Add guest_cpu to hypervisor threads It is possible to know which guest machine was running at a point in time based on the PID of the currently running host thread. That is, perf identifies guest machines by the PID of the hypervisor. To determine the guest CPU, put it on the hypervisor (QEMU) thread for that VCPU. This is done when processing the id_index which provides the necessary information. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-13-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 18 ++++++++++++++++++ tools/perf/util/thread.c | 1 + tools/perf/util/thread.h | 1 + 3 files changed, 20 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 1af981d5ad3c..91a091c35945 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2769,6 +2769,20 @@ static int perf_session__register_guest(struct perf_session *session, pid_t mach return 0; } +static int perf_session__set_guest_cpu(struct perf_session *session, pid_t pid, + pid_t tid, int guest_cpu) +{ + struct machine *machine = &session->machines.host; + struct thread *thread = machine__findnew_thread(machine, pid, tid); + + if (!thread) + return -ENOMEM; + thread->guest_cpu = guest_cpu; + thread__put(thread); + + return 0; +} + int perf_event__process_id_index(struct perf_session *session, union perf_event *event) { @@ -2845,6 +2859,10 @@ int perf_event__process_id_index(struct perf_session *session, last_pid = sid->machine_pid; perf_guest = true; } + + ret = perf_session__set_guest_cpu(session, sid->machine_pid, e->tid, e2->vcpu); + if (ret) + return ret; } return 0; } diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 665e5c0618ed..e3e5427e1c3c 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -47,6 +47,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) thread->tid = tid; thread->ppid = -1; thread->cpu = -1; + thread->guest_cpu = -1; thread->lbr_stitch_enable = false; INIT_LIST_HEAD(&thread->namespaces_list); INIT_LIST_HEAD(&thread->comm_list); diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index b066fb30d203..241f300d7d6e 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -39,6 +39,7 @@ struct thread { pid_t tid; pid_t ppid; int cpu; + int guest_cpu; /* For QEMU thread */ refcount_t refcnt; bool comm_set; int comm_len; -- cgit v1.2.3 From 3461b65da7d48c57080d40e6b545f1360f0b195b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:56 +0300 Subject: perf tools: Add machine_pid and vcpu to perf_sample When parsing a sample with a sample ID, copy machine_pid and vcpu from perf_sample_id to perf_sample. Note, machine_pid will be zero when unused, so only a non-zero value represents a guest machine. vcpu should be ignored if machine_pid is zero. Note also, machine_pid is used with events that have come from injecting a guest perf.data file, however guest events recorded on the host (i.e. using perf kvm) have the (QEMU) hypervisor process pid to identify them - refer machines__find_for_cpumode(). Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-14-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.h | 2 ++ tools/perf/util/evlist.c | 14 +++++++++++++- tools/perf/util/evsel.c | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index cdd72e05fd28..a660f304f83c 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -148,6 +148,8 @@ struct perf_sample { u64 code_page_size; u64 cgroup; u32 flags; + u32 machine_pid; + u32 vcpu; u16 insn_len; u8 cpumode; u16 misc; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 03fbe151b0c4..64f5a8074c0c 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1507,10 +1507,22 @@ int evlist__start_workload(struct evlist *evlist) int evlist__parse_sample(struct evlist *evlist, union perf_event *event, struct perf_sample *sample) { struct evsel *evsel = evlist__event2evsel(evlist, event); + int ret; if (!evsel) return -EFAULT; - return evsel__parse_sample(evsel, event, sample); + ret = evsel__parse_sample(evsel, event, sample); + if (ret) + return ret; + if (perf_guest && sample->id) { + struct perf_sample_id *sid = evlist__id2sid(evlist, sample->id); + + if (sid) { + sample->machine_pid = sid->machine_pid; + sample->vcpu = sid->vcpu.cpu; + } + } + return 0; } int evlist__parse_sample_timestamp(struct evlist *evlist, union perf_event *event, u64 *timestamp) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 9a30ccb7b104..14396ea5a968 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -2365,6 +2365,7 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, data->misc = event->header.misc; data->id = -1ULL; data->data_src = PERF_MEM_DATA_SRC_NONE; + data->vcpu = -1; if (event->header.type != PERF_RECORD_SAMPLE) { if (!evsel->core.attr.sample_id_all) -- cgit v1.2.3 From 635049099582e45be4720722919912ca113c823c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:57 +0300 Subject: perf session: Use sample->machine_pid to find guest machine If machine_pid is set, use it to find the guest machine. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-15-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 91a091c35945..f3e9fa557bc9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1418,7 +1418,9 @@ static struct machine *machines__find_for_cpumode(struct machines *machines, (sample->cpumode == PERF_RECORD_MISC_GUEST_USER))) { u32 pid; - if (event->header.type == PERF_RECORD_MMAP + if (sample->machine_pid) + pid = sample->machine_pid; + else if (event->header.type == PERF_RECORD_MMAP || event->header.type == PERF_RECORD_MMAP2) pid = event->mmap.pid; else -- cgit v1.2.3 From e28fb159f1163e76811b9b9024564c33027d9a44 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:58 +0300 Subject: perf script: Add machine_pid and vcpu Add fields machine_pid and vcpu. These are displayed only if machine_pid is non-zero. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-16-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-script.txt | 7 ++++++- tools/perf/builtin-script.c | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index e250ff5566cf..c09cc44e50ee 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -133,7 +133,8 @@ OPTIONS comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline, period, iregs, uregs, brstack, brstacksym, flags, bpf-output, brstackinsn, brstackinsnlen, brstackoff, callindent, insn, insnlen, synth, - phys_addr, metric, misc, srccode, ipc, data_page_size, code_page_size, ins_lat. + phys_addr, metric, misc, srccode, ipc, data_page_size, code_page_size, ins_lat, + machine_pid, vcpu. Field list can be prepended with the type, trace, sw or hw, to indicate to which event type the field list applies. e.g., -F sw:comm,tid,time,ip,sym and -F trace:time,cpu,trace @@ -226,6 +227,10 @@ OPTIONS The ipc (instructions per cycle) field is synthesized and may have a value when Instruction Trace decoding. + The machine_pid and vcpu fields are derived from data resulting from using + perf insert to insert a perf.data file recorded inside a virtual machine into + a perf.data file recorded on the host at the same time. + Finally, a user may not set fields to none for all event types. i.e., -F "" is not allowed. diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 4b00a50faf00..ac19fee62d8e 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -125,6 +125,8 @@ enum perf_output_field { PERF_OUTPUT_CODE_PAGE_SIZE = 1ULL << 34, PERF_OUTPUT_INS_LAT = 1ULL << 35, PERF_OUTPUT_BRSTACKINSNLEN = 1ULL << 36, + PERF_OUTPUT_MACHINE_PID = 1ULL << 37, + PERF_OUTPUT_VCPU = 1ULL << 38, }; struct perf_script { @@ -193,6 +195,8 @@ struct output_option { {.str = "code_page_size", .field = PERF_OUTPUT_CODE_PAGE_SIZE}, {.str = "ins_lat", .field = PERF_OUTPUT_INS_LAT}, {.str = "brstackinsnlen", .field = PERF_OUTPUT_BRSTACKINSNLEN}, + {.str = "machine_pid", .field = PERF_OUTPUT_MACHINE_PID}, + {.str = "vcpu", .field = PERF_OUTPUT_VCPU}, }; enum { @@ -746,6 +750,13 @@ static int perf_sample__fprintf_start(struct perf_script *script, int printed = 0; char tstr[128]; + if (PRINT_FIELD(MACHINE_PID) && sample->machine_pid) + printed += fprintf(fp, "VM:%5d ", sample->machine_pid); + + /* Print VCPU only for guest events i.e. with machine_pid */ + if (PRINT_FIELD(VCPU) && sample->machine_pid) + printed += fprintf(fp, "VCPU:%03d ", sample->vcpu); + if (PRINT_FIELD(COMM)) { const char *comm = thread ? thread__comm_str(thread) : ":-1"; -- cgit v1.2.3 From 2273e46b98377f563628d694a1ad49b5d11afb43 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:31:59 +0300 Subject: perf dlfilter: Add machine_pid and vcpu Add machine_pid and vcpu to struct perf_dlfilter_sample. The 'size' can be used to determine if the values are present, however machine_pid is zero if unused in any case. vcpu should be ignored if machine_pid is zero. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-17-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-dlfilter.txt | 22 ++++++++++++++++++++++ tools/perf/include/perf/perf_dlfilter.h | 8 ++++++++ tools/perf/util/dlfilter.c | 2 ++ 3 files changed, 32 insertions(+) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-dlfilter.txt b/tools/perf/Documentation/perf-dlfilter.txt index 594f5a5a0c9e..fb22e3b31dc5 100644 --- a/tools/perf/Documentation/perf-dlfilter.txt +++ b/tools/perf/Documentation/perf-dlfilter.txt @@ -107,9 +107,31 @@ struct perf_dlfilter_sample { __u64 raw_callchain_nr; /* Number of raw_callchain entries */ const __u64 *raw_callchain; /* Refer */ const char *event; + __s32 machine_pid; + __s32 vcpu; }; ---- +Note: 'machine_pid' and 'vcpu' are not original members, but were added together later. +'size' can be used to determine their presence at run time. +PERF_DLFILTER_HAS_MACHINE_PID will be defined if they are present at compile time. +For example: +[source,c] +---- +#include +#include +#include + +static inline bool have_machine_pid(const struct perf_dlfilter_sample *sample) +{ +#ifdef PERF_DLFILTER_HAS_MACHINE_PID + return sample->size >= offsetof(struct perf_dlfilter_sample, vcpu) + sizeof(sample->vcpu); +#else + return false; +#endif +} +---- + The perf_dlfilter_fns structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tools/perf/include/perf/perf_dlfilter.h b/tools/perf/include/perf/perf_dlfilter.h index 3eef03d661b4..a26e2f129f83 100644 --- a/tools/perf/include/perf/perf_dlfilter.h +++ b/tools/perf/include/perf/perf_dlfilter.h @@ -9,6 +9,12 @@ #include #include +/* + * The following macro can be used to determine if this header defines + * perf_dlfilter_sample machine_pid and vcpu. + */ +#define PERF_DLFILTER_HAS_MACHINE_PID + /* Definitions for perf_dlfilter_sample flags */ enum { PERF_DLFILTER_FLAG_BRANCH = 1ULL << 0, @@ -62,6 +68,8 @@ struct perf_dlfilter_sample { __u64 raw_callchain_nr; /* Number of raw_callchain entries */ const __u64 *raw_callchain; /* Refer */ const char *event; + __s32 machine_pid; + __s32 vcpu; }; /* diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c index db964d5a52af..54e4d4495e00 100644 --- a/tools/perf/util/dlfilter.c +++ b/tools/perf/util/dlfilter.c @@ -495,6 +495,8 @@ int dlfilter__do_filter_event(struct dlfilter *d, ASSIGN(misc); ASSIGN(raw_size); ASSIGN(raw_data); + ASSIGN(machine_pid); + ASSIGN(vcpu); if (sample->branch_stack) { d_sample.brstack_nr = sample->branch_stack->nr; -- cgit v1.2.3 From 7151c1d17820c0cfcda0c890f55a868cb4336afc Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:32:00 +0300 Subject: perf auxtrace: Add machine_pid and vcpu to auxtrace_error Add machine_pid and vcpu to struct perf_record_auxtrace_error. The existing fmt member is used to identify the new format. The new members make it possible to easily differentiate errors from guest machines. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-18-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/include/perf/event.h | 2 ++ tools/perf/util/auxtrace.c | 30 +++++++++++++++++----- tools/perf/util/auxtrace.h | 4 +++ .../util/scripting-engines/trace-event-python.c | 4 ++- tools/perf/util/session.c | 4 +++ 5 files changed, 37 insertions(+), 7 deletions(-) (limited to 'tools') diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h index c2dbd3e88885..556bb06798f2 100644 --- a/tools/lib/perf/include/perf/event.h +++ b/tools/lib/perf/include/perf/event.h @@ -279,6 +279,8 @@ struct perf_record_auxtrace_error { __u64 ip; __u64 time; char msg[MAX_AUXTRACE_ERROR_MSG]; + __u32 machine_pid; + __u32 vcpu; }; struct perf_record_aux { diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 511dd3caa1bc..6edab8a16de6 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -1189,9 +1189,10 @@ void auxtrace_buffer__free(struct auxtrace_buffer *buffer) free(buffer); } -void auxtrace_synth_error(struct perf_record_auxtrace_error *auxtrace_error, int type, - int code, int cpu, pid_t pid, pid_t tid, u64 ip, - const char *msg, u64 timestamp) +void auxtrace_synth_guest_error(struct perf_record_auxtrace_error *auxtrace_error, int type, + int code, int cpu, pid_t pid, pid_t tid, u64 ip, + const char *msg, u64 timestamp, + pid_t machine_pid, int vcpu) { size_t size; @@ -1207,12 +1208,26 @@ void auxtrace_synth_error(struct perf_record_auxtrace_error *auxtrace_error, int auxtrace_error->ip = ip; auxtrace_error->time = timestamp; strlcpy(auxtrace_error->msg, msg, MAX_AUXTRACE_ERROR_MSG); - - size = (void *)auxtrace_error->msg - (void *)auxtrace_error + - strlen(auxtrace_error->msg) + 1; + if (machine_pid) { + auxtrace_error->fmt = 2; + auxtrace_error->machine_pid = machine_pid; + auxtrace_error->vcpu = vcpu; + size = sizeof(*auxtrace_error); + } else { + size = (void *)auxtrace_error->msg - (void *)auxtrace_error + + strlen(auxtrace_error->msg) + 1; + } auxtrace_error->header.size = PERF_ALIGN(size, sizeof(u64)); } +void auxtrace_synth_error(struct perf_record_auxtrace_error *auxtrace_error, int type, + int code, int cpu, pid_t pid, pid_t tid, u64 ip, + const char *msg, u64 timestamp) +{ + auxtrace_synth_guest_error(auxtrace_error, type, code, cpu, pid, tid, + ip, msg, timestamp, 0, -1); +} + int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, struct perf_tool *tool, struct perf_session *session, @@ -1662,6 +1677,9 @@ size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp) if (!e->fmt) msg = (const char *)&e->time; + if (e->fmt >= 2 && e->machine_pid) + ret += fprintf(fp, " machine_pid %d vcpu %d", e->machine_pid, e->vcpu); + ret += fprintf(fp, " cpu %d pid %d tid %d ip %#"PRI_lx64" code %u: %s\n", e->cpu, e->pid, e->tid, e->ip, e->code, msg); return ret; diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index cd0d25c2751c..6a4fbfd34c6b 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -595,6 +595,10 @@ int auxtrace_index__process(int fd, u64 size, struct perf_session *session, bool needs_swap); void auxtrace_index__free(struct list_head *head); +void auxtrace_synth_guest_error(struct perf_record_auxtrace_error *auxtrace_error, int type, + int code, int cpu, pid_t pid, pid_t tid, u64 ip, + const char *msg, u64 timestamp, + pid_t machine_pid, int vcpu); void auxtrace_synth_error(struct perf_record_auxtrace_error *auxtrace_error, int type, int code, int cpu, pid_t pid, pid_t tid, u64 ip, const char *msg, u64 timestamp); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index adba01b7d9dd..3367c5479199 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -1559,7 +1559,7 @@ static void python_process_auxtrace_error(struct perf_session *session __maybe_u msg = (const char *)&e->time; } - t = tuple_new(9); + t = tuple_new(11); tuple_set_u32(t, 0, e->type); tuple_set_u32(t, 1, e->code); @@ -1570,6 +1570,8 @@ static void python_process_auxtrace_error(struct perf_session *session __maybe_u tuple_set_u64(t, 6, tm); tuple_set_string(t, 7, msg); tuple_set_u32(t, 8, cpumode); + tuple_set_s32(t, 9, e->machine_pid); + tuple_set_s32(t, 10, e->vcpu); call_object(handler, t, handler_name); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f3e9fa557bc9..7ea0b91013ea 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -895,6 +895,10 @@ static void perf_event__auxtrace_error_swap(union perf_event *event, event->auxtrace_error.ip = bswap_64(event->auxtrace_error.ip); if (event->auxtrace_error.fmt) event->auxtrace_error.time = bswap_64(event->auxtrace_error.time); + if (event->auxtrace_error.fmt >= 2) { + event->auxtrace_error.machine_pid = bswap_32(event->auxtrace_error.machine_pid); + event->auxtrace_error.vcpu = bswap_32(event->auxtrace_error.vcpu); + } } static void perf_event__thread_map_swap(union perf_event *event, -- cgit v1.2.3 From 6de306b7a5304e873fd6fc014121bd8f19ec418f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:32:01 +0300 Subject: perf script python: Add machine_pid and vcpu Add machine_pid and vcpu to python sample events and context switch events. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-19-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/scripting-engines/trace-event-python.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 3367c5479199..5bbc1b16f368 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -861,6 +861,13 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample, brstacksym = python_process_brstacksym(sample, al->thread); pydict_set_item_string_decref(dict, "brstacksym", brstacksym); + if (sample->machine_pid) { + pydict_set_item_string_decref(dict_sample, "machine_pid", + _PyLong_FromLong(sample->machine_pid)); + pydict_set_item_string_decref(dict_sample, "vcpu", + _PyLong_FromLong(sample->vcpu)); + } + pydict_set_item_string_decref(dict_sample, "cpumode", _PyLong_FromLong((unsigned long)sample->cpumode)); @@ -1509,7 +1516,7 @@ static void python_do_process_switch(union perf_event *event, np_tid = event->context_switch.next_prev_tid; } - t = tuple_new(9); + t = tuple_new(11); if (!t) return; @@ -1522,6 +1529,8 @@ static void python_do_process_switch(union perf_event *event, tuple_set_s32(t, 6, machine->pid); tuple_set_bool(t, 7, out); tuple_set_bool(t, 8, out_preempt); + tuple_set_s32(t, 9, sample->machine_pid); + tuple_set_s32(t, 10, sample->vcpu); call_object(handler, t, handler_name); -- cgit v1.2.3 From 13a133b2550e062bb2710e9dd3d9a6b794e91053 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:32:02 +0300 Subject: perf script python: intel-pt-events: Add machine_pid and vcpu Add machine_pid and vcpu to the intel-pt-events.py script. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-20-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/intel-pt-events.py | 32 +++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/perf/scripts/python/intel-pt-events.py b/tools/perf/scripts/python/intel-pt-events.py index 9b7746b89381..6be7fd8fd615 100644 --- a/tools/perf/scripts/python/intel-pt-events.py +++ b/tools/perf/scripts/python/intel-pt-events.py @@ -197,7 +197,12 @@ def common_start_str(comm, sample): cpu = sample["cpu"] pid = sample["pid"] tid = sample["tid"] - return "%16s %5u/%-5u [%03u] %9u.%09u " % (comm, pid, tid, cpu, ts / 1000000000, ts %1000000000) + if "machine_pid" in sample: + machine_pid = sample["machine_pid"] + vcpu = sample["vcpu"] + return "VM:%5d VCPU:%03d %16s %5u/%-5u [%03u] %9u.%09u " % (machine_pid, vcpu, comm, pid, tid, cpu, ts / 1000000000, ts %1000000000) + else: + return "%16s %5u/%-5u [%03u] %9u.%09u " % (comm, pid, tid, cpu, ts / 1000000000, ts %1000000000) def print_common_start(comm, sample, name): flags_disp = get_optional_null(sample, "flags_disp") @@ -379,9 +384,19 @@ def process_event(param_dict): sys.exit(1) def auxtrace_error(typ, code, cpu, pid, tid, ip, ts, msg, cpumode, *x): + if len(x) >= 2 and x[0]: + machine_pid = x[0] + vcpu = x[1] + else: + machine_pid = 0 + vcpu = -1 try: - print("%16s %5u/%-5u [%03u] %9u.%09u error type %u code %u: %s ip 0x%16x" % - ("Trace error", pid, tid, cpu, ts / 1000000000, ts %1000000000, typ, code, msg, ip)) + if machine_pid: + print("VM:%5d VCPU:%03d %16s %5u/%-5u [%03u] %9u.%09u error type %u code %u: %s ip 0x%16x" % + (machine_pid, vcpu, "Trace error", pid, tid, cpu, ts / 1000000000, ts %1000000000, typ, code, msg, ip)) + else: + print("%16s %5u/%-5u [%03u] %9u.%09u error type %u code %u: %s ip 0x%16x" % + ("Trace error", pid, tid, cpu, ts / 1000000000, ts %1000000000, typ, code, msg, ip)) except broken_pipe_exception: # Stop python printing broken pipe errors and traceback sys.stdout = open(os.devnull, 'w') @@ -396,14 +411,21 @@ def context_switch(ts, cpu, pid, tid, np_pid, np_tid, machine_pid, out, out_pree preempt_str = "preempt" else: preempt_str = "" + if len(x) >= 2 and x[0]: + machine_pid = x[0] + vcpu = x[1] + else: + vcpu = None; if machine_pid == -1: machine_str = "" - else: + elif vcpu is None: machine_str = "machine PID %d" % machine_pid + else: + machine_str = "machine PID %d VCPU %d" % (machine_pid, vcpu) switch_str = "%16s %5d/%-5d [%03u] %9u.%09u %5d/%-5d %s %s" % \ (out_str, pid, tid, cpu, ts / 1000000000, ts %1000000000, np_pid, np_tid, machine_str, preempt_str) if glb_args.all_switch_events: - print(switch_str); + print(switch_str) else: global glb_switch_str glb_switch_str[cpu] = switch_str -- cgit v1.2.3 From 386e0d83d351a4461525b06b845ffcd58235381e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:32:03 +0300 Subject: perf tools: Remove also guest kcore_dir with host kcore_dir Copies of /proc/kallsyms, /proc/modules and an extract of /proc/kcore can be stored in the perf.data output directory under the subdirectory named kcore_dir. Guest machines will have their files also under subdirectories beginning kcore_dir__ followed by the machine pid. Remove these also when removing kcore_dir. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-21-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/util.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index eeb83c80f458..9b02edf9311d 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -200,7 +200,7 @@ static int rm_rf_depth_pat(const char *path, int depth, const char **pat) return rmdir(path); } -static int rm_rf_kcore_dir(const char *path) +static int rm_rf_a_kcore_dir(const char *path, const char *name) { char kcore_dir_path[PATH_MAX]; const char *pat[] = { @@ -210,11 +210,44 @@ static int rm_rf_kcore_dir(const char *path) NULL, }; - snprintf(kcore_dir_path, sizeof(kcore_dir_path), "%s/kcore_dir", path); + snprintf(kcore_dir_path, sizeof(kcore_dir_path), "%s/%s", path, name); return rm_rf_depth_pat(kcore_dir_path, 0, pat); } +static bool kcore_dir_filter(const char *name __maybe_unused, struct dirent *d) +{ + const char *pat[] = { + "kcore_dir", + "kcore_dir__[1-9]*", + NULL, + }; + + return match_pat(d->d_name, pat); +} + +static int rm_rf_kcore_dir(const char *path) +{ + struct strlist *kcore_dirs; + struct str_node *nd; + int ret; + + kcore_dirs = lsdir(path, kcore_dir_filter); + + if (!kcore_dirs) + return 0; + + strlist__for_each_entry(nd, kcore_dirs) { + ret = rm_rf_a_kcore_dir(path, nd->s); + if (ret) + return ret; + } + + strlist__delete(kcore_dirs); + + return 0; +} + int rm_rf_perf_data(const char *path) { const char *pat[] = { -- cgit v1.2.3 From 65691e9ff0c90109dd7d9d495d9528ad4c1b82d4 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:32:04 +0300 Subject: perf tools: Make has_kcore_dir() work also for guest kcore_dir Copies of /proc/kallsyms, /proc/modules and an extract of /proc/kcore can be stored in the perf.data output directory under the subdirectory named kcore_dir. Guest machines will have their files also under subdirectories beginning kcore_dir__ followed by the machine pid. Make has_kcore_dir() return true also if there is a guest machine kcore_dir. Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Andi Kleen Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-22-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index caabeac24c69..9782ccbe595d 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -481,16 +482,21 @@ int perf_data__make_kcore_dir(struct perf_data *data, char *buf, size_t buf_sz) bool has_kcore_dir(const char *path) { - char *kcore_dir; - int ret; - - if (asprintf(&kcore_dir, "%s/kcore_dir", path) < 0) - return false; - - ret = access(kcore_dir, F_OK); + struct dirent *d = ERR_PTR(-EINVAL); + const char *name = "kcore_dir"; + DIR *dir = opendir(path); + size_t n = strlen(name); + bool result = false; + + if (dir) { + while (d && !result) { + d = readdir(dir); + result = d ? strncmp(d->d_name, name, n) : false; + } + closedir(dir); + } - free(kcore_dir); - return !ret; + return result; } char *perf_data__kallsyms_name(struct perf_data *data) -- cgit v1.2.3 From a5367ecb5353fbf28bfd3979fc4f61ddebec80b1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:32:05 +0300 Subject: perf tools: Automatically use guest kcore_dir if present When registering a guest machine using machine_pid from the id index, check perf.data for a matching kcore_dir subdirectory and set the kallsyms file name accordingly. If set, use it to find the machine's kernel symbols and object code (from kcore). Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-23-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data.c | 19 +++++++++++++++++++ tools/perf/util/data.h | 1 + tools/perf/util/machine.h | 1 + tools/perf/util/session.c | 2 ++ tools/perf/util/symbol.c | 6 ++++-- 5 files changed, 27 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 9782ccbe595d..a7f68c309545 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c @@ -518,6 +518,25 @@ char *perf_data__kallsyms_name(struct perf_data *data) return kallsyms_name; } +char *perf_data__guest_kallsyms_name(struct perf_data *data, pid_t machine_pid) +{ + char *kallsyms_name; + struct stat st; + + if (!data->is_dir) + return NULL; + + if (asprintf(&kallsyms_name, "%s/kcore_dir__%d/kallsyms", data->path, machine_pid) < 0) + return NULL; + + if (stat(kallsyms_name, &st)) { + free(kallsyms_name); + return NULL; + } + + return kallsyms_name; +} + bool is_perf_data(const char *path) { bool ret = false; diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index 7de53d6e2d7f..173132d502f5 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h @@ -101,5 +101,6 @@ unsigned long perf_data__size(struct perf_data *data); int perf_data__make_kcore_dir(struct perf_data *data, char *buf, size_t buf_sz); bool has_kcore_dir(const char *path); char *perf_data__kallsyms_name(struct perf_data *data); +char *perf_data__guest_kallsyms_name(struct perf_data *data, pid_t machine_pid); bool is_perf_data(const char *path); #endif /* __PERF_DATA_H */ diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index e1476343cbb2..199807ac3c54 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -48,6 +48,7 @@ struct machine { bool single_address_space; char *root_dir; char *mmap_name; + char *kallsyms_filename; struct threads threads[THREADS__TABLE_SIZE]; struct vdso_info *vdso_info; struct perf_env *env; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 7ea0b91013ea..98e16659a149 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2772,6 +2772,8 @@ static int perf_session__register_guest(struct perf_session *session, pid_t mach return -ENOMEM; thread__put(thread); + machine->kallsyms_filename = perf_data__guest_kallsyms_name(session->data, machine_pid); + return 0; } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index f72baf636724..a4b22caa7c24 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2300,11 +2300,13 @@ do_kallsyms: static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map) { int err; - const char *kallsyms_filename = NULL; + const char *kallsyms_filename; struct machine *machine = map__kmaps(map)->machine; char path[PATH_MAX]; - if (machine__is_default_guest(machine)) { + if (machine->kallsyms_filename) { + kallsyms_filename = machine->kallsyms_filename; + } else if (machine__is_default_guest(machine)) { /* * if the user specified a vmlinux filename, use it and only * it, reporting errors to the user if it cannot be used. -- cgit v1.2.3 From 10d34700223b14ed94a6399cf026142956f87965 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:32:06 +0300 Subject: perf tools: Add reallocarray_as_needed() Add helper reallocarray_as_needed() to reallocate an array to a larger size and initialize the extra entries to an arbitrary value. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-24-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/util.c | 33 +++++++++++++++++++++++++++++++++ tools/perf/util/util.h | 15 +++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 9b02edf9311d..391c1e928bd7 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "cap.h" #include "strlist.h" @@ -500,3 +501,35 @@ char *filename_with_chroot(int pid, const char *filename) return new_name; } + +/* + * Reallocate an array *arr of size *arr_sz so that it is big enough to contain + * x elements of size msz, initializing new entries to *init_val or zero if + * init_val is NULL + */ +int do_realloc_array_as_needed(void **arr, size_t *arr_sz, size_t x, size_t msz, const void *init_val) +{ + size_t new_sz = *arr_sz; + void *new_arr; + size_t i; + + if (!new_sz) + new_sz = msz >= 64 ? 1 : roundup(64, msz); /* Start with at least 64 bytes */ + while (x >= new_sz) { + if (check_mul_overflow(new_sz, (size_t)2, &new_sz)) + return -ENOMEM; + } + if (new_sz == *arr_sz) + return 0; + new_arr = calloc(new_sz, msz); + if (!new_arr) + return -ENOMEM; + memcpy(new_arr, *arr, *arr_sz * msz); + if (init_val) { + for (i = *arr_sz; i < new_sz; i++) + memcpy(new_arr + (i * msz), init_val, msz); + } + *arr = new_arr; + *arr_sz = new_sz; + return 0; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 0f78f1e7782d..c1f2d423a9ec 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -79,4 +79,19 @@ struct perf_debuginfod { void perf_debuginfod_setup(struct perf_debuginfod *di); char *filename_with_chroot(int pid, const char *filename); + +int do_realloc_array_as_needed(void **arr, size_t *arr_sz, size_t x, + size_t msz, const void *init_val); + +#define realloc_array_as_needed(a, n, x, v) ({ \ + typeof(x) __x = (x); \ + __x >= (n) ? \ + do_realloc_array_as_needed((void **)&(a), \ + &(n), \ + __x, \ + sizeof(*(a)), \ + (const void *)(v)) : \ + 0; \ + }) + #endif /* GIT_COMPAT_UTIL_H */ -- cgit v1.2.3 From 97406a7e4fa6e5cad3be80cd2188d8fb50a6ec75 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Jul 2022 12:32:07 +0300 Subject: perf inject: Add support for injecting guest sideband events Inject events from a perf.data file recorded in a virtual machine into a perf.data file recorded on the host at the same time. Only side band events (e.g. mmap, comm, fork, exit etc) and build IDs are injected. Additionally, the guest kcore_dir is copied as kcore_dir__ appended to the machine PID. This is non-trivial because: o It is not possible to process 2 sessions simultaneously so instead events are first written to a temporary file. o To avoid conflict, guest sample IDs are replaced with new unused sample IDs. o Guest event's CPU is changed to be the host CPU because it is more useful for reporting and analysis. o Sample ID is mapped to machine PID which is recorded with VCPU in the id index. This is important to allow guest events to be related to the guest machine and VCPU. o Timestamps must be converted. o Events are inserted to obey finished-round ordering. The anticipated use-case is: - start recording sideband events in a guest machine - start recording an AUX area trace on the host which can trace also the guest (e.g. Intel PT) - run test case on the guest - stop recording on the host - stop recording on the guest - copy the guest perf.data file to the host - inject the guest perf.data file sideband events into the host perf.data file using perf inject - the resulting perf.data file can now be used Subsequent patches provide Intel PT support for this. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: kvm@vger.kernel.org Link: https://lore.kernel.org/r/20220711093218.10967-25-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-inject.txt | 17 + tools/perf/builtin-inject.c | 1043 +++++++++++++++++++++++++++++- 2 files changed, 1059 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt index 0570a1ccd344..646aa31586ed 100644 --- a/tools/perf/Documentation/perf-inject.txt +++ b/tools/perf/Documentation/perf-inject.txt @@ -85,6 +85,23 @@ include::itrace.txt[] without updating it. Currently this option is supported only by Intel PT, refer linkperf:perf-intel-pt[1] +--guest-data=,[,