From 5f3339d2e83ca587c2e13c3e37e1b5fb7c68ebe5 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:19 +0100 Subject: [PATCH 01/43] perf thread_map: Add thread_map user level event Adding the thread_map event to pass/store thread maps as data in the pipe/perf.data. Storing the thread ID along with the standard comm[16] thread name string. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-4-git-send-email-jolsa@kernel.org [ Renamed thread_map_data_event to thread_map_event_entry ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 13 +++++++++++++ tools/perf/util/session.c | 26 ++++++++++++++++++++++++++ tools/perf/util/tool.h | 3 ++- 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 8b10621b415c..771545a27b9b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -37,6 +37,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_AUXTRACE_INFO] = "AUXTRACE_INFO", [PERF_RECORD_AUXTRACE] = "AUXTRACE", [PERF_RECORD_AUXTRACE_ERROR] = "AUXTRACE_ERROR", + [PERF_RECORD_THREAD_MAP] = "THREAD_MAP", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index a0dbcbd4f6d8..66f303e69c4d 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -226,6 +226,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_AUXTRACE_INFO = 70, PERF_RECORD_AUXTRACE = 71, PERF_RECORD_AUXTRACE_ERROR = 72, + PERF_RECORD_THREAD_MAP = 73, PERF_RECORD_HEADER_MAX }; @@ -356,6 +357,17 @@ struct context_switch_event { u32 next_prev_tid; }; +struct thread_map_event_entry { + u64 pid; + char comm[16]; +}; + +struct thread_map_event { + struct perf_event_header header; + u64 nr; + struct thread_map_event_entry entries[]; +}; + union perf_event { struct perf_event_header header; struct mmap_event mmap; @@ -378,6 +390,7 @@ union perf_event { struct aux_event aux; struct itrace_start_event itrace_start; struct context_switch_event context_switch; + struct thread_map_event thread_map; }; void perf_event__print_totals(void); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 9774686525b4..36b07b22392d 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -296,6 +296,16 @@ int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused, return 0; } + +static +int process_event_thread_map_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *session __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + void perf_tool__fill_defaults(struct perf_tool *tool) { if (tool->sample == NULL) @@ -346,6 +356,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->auxtrace = process_event_auxtrace_stub; if (tool->auxtrace_error == NULL) tool->auxtrace_error = process_event_auxtrace_error_stub; + if (tool->thread_map == NULL) + tool->thread_map = process_event_thread_map_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -616,6 +628,17 @@ static void perf_event__auxtrace_error_swap(union perf_event *event, event->auxtrace_error.ip = bswap_64(event->auxtrace_error.ip); } +static void perf_event__thread_map_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + unsigned i; + + event->thread_map.nr = bswap_64(event->thread_map.nr); + + for (i = 0; i < event->thread_map.nr; i++) + event->thread_map.entries[i].pid = bswap_64(event->thread_map.entries[i].pid); +} + typedef void (*perf_event__swap_op)(union perf_event *event, bool sample_id_all); @@ -643,6 +666,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_AUXTRACE_INFO] = perf_event__auxtrace_info_swap, [PERF_RECORD_AUXTRACE] = perf_event__auxtrace_swap, [PERF_RECORD_AUXTRACE_ERROR] = perf_event__auxtrace_error_swap, + [PERF_RECORD_THREAD_MAP] = perf_event__thread_map_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1179,6 +1203,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, case PERF_RECORD_AUXTRACE_ERROR: perf_session__auxtrace_error_inc(session, event); return tool->auxtrace_error(tool, event, session); + case PERF_RECORD_THREAD_MAP: + return tool->thread_map(tool, event, session); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index cab8cc24831b..1af4774960c3 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -55,7 +55,8 @@ struct perf_tool { event_op2 build_id, id_index, auxtrace_info, - auxtrace_error; + auxtrace_error, + thread_map; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; From 99471c967a00c875bb5d61f377d4267904545499 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:20 +0100 Subject: [PATCH 02/43] perf thread_map: Add thread_map event sythesize function Introduce the perf_event__synthesize_thread_map2 function to synthesize struct thread_map. The perf_event__synthesize_thread_map name is already taken for synthesizing the complete threads data (comm/mmap/fork). Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-5-git-send-email-jolsa@kernel.org [ Rename thread_map_data_event to thread_map_event_entry ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 4 ++++ tools/perf/tests/tests.h | 1 + tools/perf/tests/thread-map.c | 29 ++++++++++++++++++++++++++ tools/perf/util/event.c | 36 +++++++++++++++++++++++++++++++++ tools/perf/util/event.h | 4 ++++ 5 files changed, 74 insertions(+) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 0372d5945910..745bdb02d22b 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -179,6 +179,10 @@ static struct test generic_tests[] = { .get_desc = test__bpf_subtest_get_desc, }, }, + { + .desc = "Test thread map synthesize", + .func = test__thread_map_synthesize, + }, { .func = NULL, }, diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index a0733aaad081..3fe52ccc4d05 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -79,6 +79,7 @@ int test__bpf(int subtest); const char *test__bpf_subtest_get_desc(int subtest); int test__bpf_subtest_get_nr(void); int test_session_topology(int subtest); +int test__thread_map_synthesize(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c index 2be02d303e82..ac5be2578367 100644 --- a/tools/perf/tests/thread-map.c +++ b/tools/perf/tests/thread-map.c @@ -40,3 +40,32 @@ int test__thread_map(int subtest __maybe_unused) thread_map__put(map); return 0; } + +static int process_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct thread_map_event *map = &event->thread_map; + + TEST_ASSERT_VAL("wrong nr", map->nr == 1); + TEST_ASSERT_VAL("wrong pid", map->entries[0].pid == (u64) getpid()); + TEST_ASSERT_VAL("wrong comm", !strcmp(map->entries[0].comm, "perf")); + return 0; +} + +int test__thread_map_synthesize(int subtest __maybe_unused) +{ + struct thread_map *threads; + + /* test map on current pid */ + threads = thread_map__new_by_pid(getpid()); + TEST_ASSERT_VAL("failed to alloc map", threads); + + thread_map__read_comms(threads); + + TEST_ASSERT_VAL("failed to synthesize map", + !perf_event__synthesize_thread_map2(NULL, threads, process_event, NULL)); + + return 0; +} diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 771545a27b9b..b13373a60337 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -700,6 +700,42 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, return err; } +int perf_event__synthesize_thread_map2(struct perf_tool *tool, + struct thread_map *threads, + perf_event__handler_t process, + struct machine *machine) +{ + union perf_event *event; + int i, err, size; + + size = sizeof(event->thread_map); + size += threads->nr * sizeof(event->thread_map.entries[0]); + + event = zalloc(size); + if (!event) + return -ENOMEM; + + event->header.type = PERF_RECORD_THREAD_MAP; + event->header.size = size; + event->thread_map.nr = threads->nr; + + for (i = 0; i < threads->nr; i++) { + struct thread_map_event_entry *entry = &event->thread_map.entries[i]; + char *comm = thread_map__comm(threads, i); + + if (!comm) + comm = (char *) ""; + + entry->pid = thread_map__pid(threads, i); + strncpy((char *) &entry->comm, comm, sizeof(entry->comm)); + } + + err = process(tool, event, NULL, machine); + + free(event); + return err; +} + size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) { const char *s; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 66f303e69c4d..952dd4d83f81 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -408,6 +408,10 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine, bool mmap_data, unsigned int proc_map_timeout); +int perf_event__synthesize_thread_map2(struct perf_tool *tool, + struct thread_map *threads, + perf_event__handler_t process, + struct machine *machine); int perf_event__synthesize_threads(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine, bool mmap_data, From 59660942397b57b37eccba014544623cf4beb12b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:21 +0100 Subject: [PATCH 03/43] perf thread_map: Add thread_map__new_event function Introducing the thread_map__new_event function to create a struct thread_map object from a thread_map event. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-6-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/thread-map.c | 14 ++++++++++++++ tools/perf/util/thread_map.c | 27 +++++++++++++++++++++++++++ tools/perf/util/thread_map.h | 3 +++ 3 files changed, 44 insertions(+) diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c index ac5be2578367..fccde848fe9c 100644 --- a/tools/perf/tests/thread-map.c +++ b/tools/perf/tests/thread-map.c @@ -47,10 +47,24 @@ static int process_event(struct perf_tool *tool __maybe_unused, struct machine *machine __maybe_unused) { struct thread_map_event *map = &event->thread_map; + struct thread_map *threads; TEST_ASSERT_VAL("wrong nr", map->nr == 1); TEST_ASSERT_VAL("wrong pid", map->entries[0].pid == (u64) getpid()); TEST_ASSERT_VAL("wrong comm", !strcmp(map->entries[0].comm, "perf")); + + threads = thread_map__new_event(&event->thread_map); + TEST_ASSERT_VAL("failed to alloc map", threads); + + TEST_ASSERT_VAL("wrong nr", threads->nr == 1); + TEST_ASSERT_VAL("wrong pid", + thread_map__pid(threads, 0) == getpid()); + TEST_ASSERT_VAL("wrong comm", + thread_map__comm(threads, 0) && + !strcmp(thread_map__comm(threads, 0), "perf")); + TEST_ASSERT_VAL("wrong refcnt", + atomic_read(&threads->refcnt) == 1); + thread_map__put(threads); return 0; } diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 371fb28fe5b1..08afc6909953 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -13,6 +13,7 @@ #include "thread_map.h" #include "util.h" #include "debug.h" +#include "event.h" /* Skip "." and ".." directories */ static int filter(const struct dirent *dir) @@ -409,3 +410,29 @@ void thread_map__read_comms(struct thread_map *threads) for (i = 0; i < threads->nr; ++i) comm_init(threads, i); } + +static void thread_map__copy_event(struct thread_map *threads, + struct thread_map_event *event) +{ + unsigned i; + + threads->nr = (int) event->nr; + + for (i = 0; i < event->nr; i++) { + thread_map__set_pid(threads, i, (pid_t) event->entries[i].pid); + threads->map[i].comm = strndup(event->entries[i].comm, 16); + } + + atomic_set(&threads->refcnt, 1); +} + +struct thread_map *thread_map__new_event(struct thread_map_event *event) +{ + struct thread_map *threads; + + threads = thread_map__alloc(event->nr); + if (threads) + thread_map__copy_event(threads, event); + + return threads; +} diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index af679d8a50f8..85e4c7c4fbde 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -16,11 +16,14 @@ struct thread_map { struct thread_map_data map[]; }; +struct thread_map_event; + struct thread_map *thread_map__new_dummy(void); struct thread_map *thread_map__new_by_pid(pid_t pid); struct thread_map *thread_map__new_by_tid(pid_t tid); struct thread_map *thread_map__new_by_uid(uid_t uid); struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid); +struct thread_map *thread_map__new_event(struct thread_map_event *event); struct thread_map *thread_map__get(struct thread_map *map); void thread_map__put(struct thread_map *map); From ec7fa596f514b76a5f1003ffe9e6dfb50cb9e811 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:22 +0100 Subject: [PATCH 04/43] perf thread_map: Add perf_event__fprintf_thread_map function To display a thread_map event for a raw dump. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-7-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 16 ++++++++++++++++ tools/perf/util/event.h | 1 + 2 files changed, 17 insertions(+) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index b13373a60337..938f006c758e 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -820,6 +820,22 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) event->mmap2.filename); } +size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp) +{ + struct thread_map *threads = thread_map__new_event(&event->thread_map); + size_t ret; + + ret = fprintf(fp, " nr: "); + + if (threads) + ret += thread_map__fprintf(threads, fp); + else + ret += fprintf(fp, "failed to get threads from event\n"); + + thread_map__put(threads); + return ret; +} + int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 952dd4d83f81..b7ad896d1317 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -516,6 +516,7 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp); size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp); size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp); size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp); +size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp); size_t perf_event__fprintf(union perf_event *event, FILE *fp); u64 kallsyms__get_function_start(const char *kallsyms_filename, From 6640b6c227fc85fd8bdcc4a31239a04450487f6a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:23 +0100 Subject: [PATCH 05/43] perf cpu_map: Add cpu_map user level event Adding the cpu_map event to pass/store cpu maps as data in a pipe/perf.data. We store maps in 2 formats: - list of cpus - mask of cpus The format that takes less space is selected transparently in the following patch. The interface is made generic, so we could add the cpumap event data into another event in the following patches. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-8-git-send-email-jolsa@kernel.org [ cpu_map_data_cpus -> cpu_map_entries, cpu_map_data_mask -> cpu_map_mask ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 28 ++++++++++++++++++++++ tools/perf/util/session.c | 50 +++++++++++++++++++++++++++++++++++++++ tools/perf/util/tool.h | 3 ++- 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 938f006c758e..719c0781a82a 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -38,6 +38,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_AUXTRACE] = "AUXTRACE", [PERF_RECORD_AUXTRACE_ERROR] = "AUXTRACE_ERROR", [PERF_RECORD_THREAD_MAP] = "THREAD_MAP", + [PERF_RECORD_CPU_MAP] = "CPU_MAP", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index b7ad896d1317..1c82a0ebda73 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -227,6 +227,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_AUXTRACE = 71, PERF_RECORD_AUXTRACE_ERROR = 72, PERF_RECORD_THREAD_MAP = 73, + PERF_RECORD_CPU_MAP = 74, PERF_RECORD_HEADER_MAX }; @@ -271,6 +272,32 @@ struct events_stats { u32 nr_proc_map_timeout; }; +enum { + PERF_CPU_MAP__CPUS = 0, + PERF_CPU_MAP__MASK = 1, +}; + +struct cpu_map_entries { + u16 nr; + u16 cpu[]; +}; + +struct cpu_map_mask { + u16 nr; + u16 long_size; + unsigned long mask[]; +}; + +struct cpu_map_data { + u16 type; + char data[]; +}; + +struct cpu_map_event { + struct perf_event_header header; + struct cpu_map_data data; +}; + struct attr_event { struct perf_event_header header; struct perf_event_attr attr; @@ -391,6 +418,7 @@ union perf_event { struct itrace_start_event itrace_start; struct context_switch_event context_switch; struct thread_map_event thread_map; + struct cpu_map_event cpu_map; }; void perf_event__print_totals(void); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 36b07b22392d..4350f5e85bf5 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -306,6 +306,15 @@ int process_event_thread_map_stub(struct perf_tool *tool __maybe_unused, return 0; } +static +int process_event_cpu_map_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *session __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + void perf_tool__fill_defaults(struct perf_tool *tool) { if (tool->sample == NULL) @@ -358,6 +367,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->auxtrace_error = process_event_auxtrace_error_stub; if (tool->thread_map == NULL) tool->thread_map = process_event_thread_map_stub; + if (tool->cpu_map == NULL) + tool->cpu_map = process_event_cpu_map_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -639,6 +650,42 @@ static void perf_event__thread_map_swap(union perf_event *event, event->thread_map.entries[i].pid = bswap_64(event->thread_map.entries[i].pid); } +static void perf_event__cpu_map_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + struct cpu_map_data *data = &event->cpu_map.data; + struct cpu_map_entries *cpus; + struct cpu_map_mask *mask; + unsigned i; + + data->type = bswap_64(data->type); + + switch (data->type) { + case PERF_CPU_MAP__CPUS: + cpus = (struct cpu_map_entries *)data->data; + + cpus->nr = bswap_16(cpus->nr); + + for (i = 0; i < cpus->nr; i++) + cpus->cpu[i] = bswap_16(cpus->cpu[i]); + break; + case PERF_CPU_MAP__MASK: + mask = (struct cpu_map_mask *) data->data; + + mask->nr = bswap_16(mask->nr); + mask->long_size = bswap_16(mask->long_size); + + switch (mask->long_size) { + case 4: mem_bswap_32(&mask->mask, mask->nr); break; + case 8: mem_bswap_64(&mask->mask, mask->nr); break; + default: + pr_err("cpu_map swap: unsupported long size\n"); + } + default: + break; + } +} + typedef void (*perf_event__swap_op)(union perf_event *event, bool sample_id_all); @@ -667,6 +714,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_AUXTRACE] = perf_event__auxtrace_swap, [PERF_RECORD_AUXTRACE_ERROR] = perf_event__auxtrace_error_swap, [PERF_RECORD_THREAD_MAP] = perf_event__thread_map_swap, + [PERF_RECORD_CPU_MAP] = perf_event__cpu_map_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1205,6 +1253,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, return tool->auxtrace_error(tool, event, session); case PERF_RECORD_THREAD_MAP: return tool->thread_map(tool, event, session); + case PERF_RECORD_CPU_MAP: + return tool->cpu_map(tool, event, session); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 1af4774960c3..9e5925c78519 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -56,7 +56,8 @@ struct perf_tool { id_index, auxtrace_info, auxtrace_error, - thread_map; + thread_map, + cpu_map; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; From 6c872901af07c41745f1abf5ceac9b3b4d9cdbb6 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:24 +0100 Subject: [PATCH 06/43] perf cpu_map: Add cpu_map event synthesize function Introduce the perf_event__synthesize_cpu_map function to synthesize a struct cpu_map. Added generic interface: cpu_map_data__alloc cpu_map_data__synthesize to make the cpu_map synthesizing usable for other events. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-9-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 + tools/perf/tests/cpumap.c | 71 +++++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/event.c | 131 ++++++++++++++++++++++++++++++++ tools/perf/util/event.h | 8 ++ 6 files changed, 216 insertions(+) create mode 100644 tools/perf/tests/cpumap.c diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index f23fb7ed4400..7abad28fe17e 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -34,6 +34,7 @@ perf-y += thread-map.o perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o perf-y += bpf.o perf-y += topology.o +perf-y += cpumap.o $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 745bdb02d22b..0c3fe2846de8 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -183,6 +183,10 @@ static struct test generic_tests[] = { .desc = "Test thread map synthesize", .func = test__thread_map_synthesize, }, + { + .desc = "Test cpu map synthesize", + .func = test__cpu_map_synthesize, + }, { .func = NULL, }, diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c new file mode 100644 index 000000000000..715480558088 --- /dev/null +++ b/tools/perf/tests/cpumap.c @@ -0,0 +1,71 @@ +#include "tests.h" +#include "cpumap.h" + +static int process_event_mask(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct cpu_map_event *map = &event->cpu_map; + struct cpu_map_mask *mask; + struct cpu_map_data *data; + int i; + + data = &map->data; + + TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__MASK); + + mask = (struct cpu_map_mask *)data->data; + + TEST_ASSERT_VAL("wrong nr", mask->nr == 1); + + for (i = 0; i < 20; i++) { + TEST_ASSERT_VAL("wrong cpu", test_bit(i, mask->mask)); + } + + return 0; +} + +static int process_event_cpus(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct cpu_map_event *map = &event->cpu_map; + struct cpu_map_entries *cpus; + struct cpu_map_data *data; + + data = &map->data; + + TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__CPUS); + + cpus = (struct cpu_map_entries *)data->data; + + TEST_ASSERT_VAL("wrong nr", cpus->nr == 2); + TEST_ASSERT_VAL("wrong cpu", cpus->cpu[0] == 1); + TEST_ASSERT_VAL("wrong cpu", cpus->cpu[1] == 256); + return 0; +} + + +int test__cpu_map_synthesize(int subtest __maybe_unused) +{ + struct cpu_map *cpus; + + /* This one is better stores in mask. */ + cpus = cpu_map__new("0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19"); + + TEST_ASSERT_VAL("failed to synthesize map", + !perf_event__synthesize_cpu_map(NULL, cpus, process_event_mask, NULL)); + + cpu_map__put(cpus); + + /* This one is better stores in cpu values. */ + cpus = cpu_map__new("1,256"); + + TEST_ASSERT_VAL("failed to synthesize map", + !perf_event__synthesize_cpu_map(NULL, cpus, process_event_cpus, NULL)); + + cpu_map__put(cpus); + return 0; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 3fe52ccc4d05..f85160f6ebb8 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -80,6 +80,7 @@ const char *test__bpf_subtest_get_desc(int subtest); int test__bpf_subtest_get_nr(void); int test_session_topology(int subtest); int test__thread_map_synthesize(int subtest); +int test__cpu_map_synthesize(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 719c0781a82a..15d6466a4b8f 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -737,6 +737,137 @@ int perf_event__synthesize_thread_map2(struct perf_tool *tool, return err; } +static void synthesize_cpus(struct cpu_map_entries *cpus, + struct cpu_map *map) +{ + int i; + + cpus->nr = map->nr; + + for (i = 0; i < map->nr; i++) + cpus->cpu[i] = map->map[i]; +} + +static void synthesize_mask(struct cpu_map_mask *mask, + struct cpu_map *map, int max) +{ + int i; + + mask->nr = BITS_TO_LONGS(max); + mask->long_size = sizeof(long); + + for (i = 0; i < map->nr; i++) + set_bit(map->map[i], mask->mask); +} + +static size_t cpus_size(struct cpu_map *map) +{ + return sizeof(struct cpu_map_entries) + map->nr * sizeof(u16); +} + +static size_t mask_size(struct cpu_map *map, int *max) +{ + int i; + + *max = 0; + + for (i = 0; i < map->nr; i++) { + /* bit possition of the cpu is + 1 */ + int bit = map->map[i] + 1; + + if (bit > *max) + *max = bit; + } + + return sizeof(struct cpu_map_mask) + BITS_TO_LONGS(*max) * sizeof(long); +} + +void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max) +{ + size_t size_cpus, size_mask; + bool is_dummy = cpu_map__empty(map); + + /* + * Both array and mask data have variable size based + * on the number of cpus and their actual values. + * The size of the 'struct cpu_map_data' is: + * + * array = size of 'struct cpu_map_entries' + + * number of cpus * sizeof(u64) + * + * mask = size of 'struct cpu_map_mask' + + * maximum cpu bit converted to size of longs + * + * and finaly + the size of 'struct cpu_map_data'. + */ + size_cpus = cpus_size(map); + size_mask = mask_size(map, max); + + if (is_dummy || (size_cpus < size_mask)) { + *size += size_cpus; + *type = PERF_CPU_MAP__CPUS; + } else { + *size += size_mask; + *type = PERF_CPU_MAP__MASK; + } + + *size += sizeof(struct cpu_map_data); + return zalloc(*size); +} + +void cpu_map_data__synthesize(struct cpu_map_data *data, struct cpu_map *map, + u16 type, int max) +{ + data->type = type; + + switch (type) { + case PERF_CPU_MAP__CPUS: + synthesize_cpus((struct cpu_map_entries *) data->data, map); + break; + case PERF_CPU_MAP__MASK: + synthesize_mask((struct cpu_map_mask *) data->data, map, max); + default: + break; + }; +} + +static struct cpu_map_event* cpu_map_event__new(struct cpu_map *map) +{ + size_t size = sizeof(struct cpu_map_event); + struct cpu_map_event *event; + int max; + u16 type; + + event = cpu_map_data__alloc(map, &size, &type, &max); + if (!event) + return NULL; + + event->header.type = PERF_RECORD_CPU_MAP; + event->header.size = size; + event->data.type = type; + + cpu_map_data__synthesize(&event->data, map, type, max); + return event; +} + +int perf_event__synthesize_cpu_map(struct perf_tool *tool, + struct cpu_map *map, + perf_event__handler_t process, + struct machine *machine) +{ + struct cpu_map_event *event; + int err; + + event = cpu_map_event__new(map); + if (!event) + return -ENOMEM; + + err = process(tool, (union perf_event *) event, NULL, machine); + + free(event); + return err; +} + size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) { const char *s; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 1c82a0ebda73..de18ee0e9c96 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -425,6 +425,7 @@ void perf_event__print_totals(void); struct perf_tool; struct thread_map; +struct cpu_map; typedef int (*perf_event__handler_t)(struct perf_tool *tool, union perf_event *event, @@ -440,6 +441,10 @@ int perf_event__synthesize_thread_map2(struct perf_tool *tool, struct thread_map *threads, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_cpu_map(struct perf_tool *tool, + struct cpu_map *cpus, + perf_event__handler_t process, + struct machine *machine); int perf_event__synthesize_threads(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine, bool mmap_data, @@ -550,4 +555,7 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp); u64 kallsyms__get_function_start(const char *kallsyms_filename, const char *symbol_name); +void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max); +void cpu_map_data__synthesize(struct cpu_map_data *data, struct cpu_map *map, + u16 type, int max); #endif /* __PERF_RECORD_H */ From f77b57ad4fc42a074eae564bbb6660f0a3ff5503 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:25 +0100 Subject: [PATCH 07/43] perf cpu_map: Add cpu_map__new_event function Introducing the cpu_map__new_event function to create a struct cpu_map object from a cpu_map event. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-10-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/cpumap.c | 25 +++++++++++++++++++---- tools/perf/util/cpumap.c | 42 +++++++++++++++++++++++++++++++++++++++ tools/perf/util/cpumap.h | 1 + 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c index 715480558088..4cb6418a8ffc 100644 --- a/tools/perf/tests/cpumap.c +++ b/tools/perf/tests/cpumap.c @@ -6,12 +6,13 @@ static int process_event_mask(struct perf_tool *tool __maybe_unused, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { - struct cpu_map_event *map = &event->cpu_map; + struct cpu_map_event *map_event = &event->cpu_map; struct cpu_map_mask *mask; struct cpu_map_data *data; + struct cpu_map *map; int i; - data = &map->data; + data = &map_event->data; TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__MASK); @@ -23,6 +24,14 @@ static int process_event_mask(struct perf_tool *tool __maybe_unused, TEST_ASSERT_VAL("wrong cpu", test_bit(i, mask->mask)); } + map = cpu_map__new_data(data); + TEST_ASSERT_VAL("wrong nr", map->nr == 20); + + for (i = 0; i < 20; i++) { + TEST_ASSERT_VAL("wrong cpu", map->map[i] == i); + } + + cpu_map__put(map); return 0; } @@ -31,11 +40,12 @@ static int process_event_cpus(struct perf_tool *tool __maybe_unused, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { - struct cpu_map_event *map = &event->cpu_map; + struct cpu_map_event *map_event = &event->cpu_map; struct cpu_map_entries *cpus; struct cpu_map_data *data; + struct cpu_map *map; - data = &map->data; + data = &map_event->data; TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__CPUS); @@ -44,6 +54,13 @@ static int process_event_cpus(struct perf_tool *tool __maybe_unused, TEST_ASSERT_VAL("wrong nr", cpus->nr == 2); TEST_ASSERT_VAL("wrong cpu", cpus->cpu[0] == 1); TEST_ASSERT_VAL("wrong cpu", cpus->cpu[1] == 256); + + map = cpu_map__new_data(data); + TEST_ASSERT_VAL("wrong nr", map->nr == 2); + TEST_ASSERT_VAL("wrong cpu", map->map[0] == 1); + TEST_ASSERT_VAL("wrong cpu", map->map[1] == 256); + TEST_ASSERT_VAL("wrong refcnt", atomic_read(&map->refcnt) == 1); + cpu_map__put(map); return 0; } diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 10af1e7524fb..a0717b93d8f5 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "asm/bug.h" static struct cpu_map *cpu_map__default_new(void) @@ -179,6 +180,47 @@ out: return cpus; } +static struct cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus) +{ + struct cpu_map *map; + + map = cpu_map__empty_new(cpus->nr); + if (map) { + unsigned i; + + for (i = 0; i < cpus->nr; i++) + map->map[i] = (int)cpus->cpu[i]; + } + + return map; +} + +static struct cpu_map *cpu_map__from_mask(struct cpu_map_mask *mask) +{ + struct cpu_map *map; + int nr, nbits = mask->nr * mask->long_size * BITS_PER_BYTE; + + nr = bitmap_weight(mask->mask, nbits); + + map = cpu_map__empty_new(nr); + if (map) { + int cpu, i = 0; + + for_each_set_bit(cpu, mask->mask, nbits) + map->map[i++] = cpu; + } + return map; + +} + +struct cpu_map *cpu_map__new_data(struct cpu_map_data *data) +{ + if (data->type == PERF_CPU_MAP__CPUS) + return cpu_map__from_entries((struct cpu_map_entries *)data->data); + else + return cpu_map__from_mask((struct cpu_map_mask *)data->data); +} + size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp) { int i; diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 85f7772457fa..71c41b9efabb 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -17,6 +17,7 @@ struct cpu_map { struct cpu_map *cpu_map__new(const char *cpu_list); struct cpu_map *cpu_map__empty_new(int nr); struct cpu_map *cpu_map__dummy_new(void); +struct cpu_map *cpu_map__new_data(struct cpu_map_data *data); struct cpu_map *cpu_map__read(FILE *file); size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); int cpu_map__get_socket_id(int cpu); From eb12a1afdc02e59fc09934743490549c77327b1a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:26 +0100 Subject: [PATCH 08/43] perf cpu_map: Add perf_event__fprintf_cpu_map function To display a cpu_map event for raw dump. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-11-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 16 ++++++++++++++++ tools/perf/util/event.h | 1 + 2 files changed, 17 insertions(+) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 15d6466a4b8f..f31ab3b8f918 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -968,6 +968,22 @@ size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp) return ret; } +size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp) +{ + struct cpu_map *cpus = cpu_map__new_data(&event->cpu_map.data); + size_t ret; + + ret = fprintf(fp, " nr: "); + + if (cpus) + ret += cpu_map__fprintf(cpus, fp); + else + ret += fprintf(fp, "failed to get cpumap from event\n"); + + cpu_map__put(cpus); + return ret; +} + int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index de18ee0e9c96..74a434116b2c 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -550,6 +550,7 @@ size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp); size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp); size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp); size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp); +size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp); size_t perf_event__fprintf(union perf_event *event, FILE *fp); u64 kallsyms__get_function_start(const char *kallsyms_filename, From 374fb9e362f64e730388abc1de9bb93829670a54 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:27 +0100 Subject: [PATCH 09/43] perf tools: Add stat config user level event Adding the stat config event to pass/store stat config data, so report tools (report/script) know how to interpret stat data. The config data is stored in a 'tag|value' way to allow for easy extension and backwards compatibility. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-12-git-send-email-jolsa@kernel.org [ stat_config_term_event -> stat_config_event_entry ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 20 ++++++++++++++++++++ tools/perf/util/session.c | 24 ++++++++++++++++++++++++ tools/perf/util/tool.h | 3 ++- 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index f31ab3b8f918..43e2dfc2c73b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -39,6 +39,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_AUXTRACE_ERROR] = "AUXTRACE_ERROR", [PERF_RECORD_THREAD_MAP] = "THREAD_MAP", [PERF_RECORD_CPU_MAP] = "CPU_MAP", + [PERF_RECORD_STAT_CONFIG] = "STAT_CONFIG", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 74a434116b2c..16cee44de56b 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -228,6 +228,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_AUXTRACE_ERROR = 72, PERF_RECORD_THREAD_MAP = 73, PERF_RECORD_CPU_MAP = 74, + PERF_RECORD_STAT_CONFIG = 75, PERF_RECORD_HEADER_MAX }; @@ -395,6 +396,24 @@ struct thread_map_event { struct thread_map_event_entry entries[]; }; +enum { + PERF_STAT_CONFIG_TERM__AGGR_MODE = 0, + PERF_STAT_CONFIG_TERM__INTERVAL = 1, + PERF_STAT_CONFIG_TERM__SCALE = 2, + PERF_STAT_CONFIG_TERM__MAX = 3, +}; + +struct stat_config_event_entry { + u64 tag; + u64 val; +}; + +struct stat_config_event { + struct perf_event_header header; + u64 nr; + struct stat_config_event_entry data[]; +}; + union perf_event { struct perf_event_header header; struct mmap_event mmap; @@ -419,6 +438,7 @@ union perf_event { struct context_switch_event context_switch; struct thread_map_event thread_map; struct cpu_map_event cpu_map; + struct stat_config_event stat_config; }; void perf_event__print_totals(void); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 4350f5e85bf5..fbc52ab3eb75 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -315,6 +315,15 @@ int process_event_cpu_map_stub(struct perf_tool *tool __maybe_unused, return 0; } +static +int process_event_stat_config_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *session __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + void perf_tool__fill_defaults(struct perf_tool *tool) { if (tool->sample == NULL) @@ -369,6 +378,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->thread_map = process_event_thread_map_stub; if (tool->cpu_map == NULL) tool->cpu_map = process_event_cpu_map_stub; + if (tool->stat_config == NULL) + tool->stat_config = process_event_stat_config_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -686,6 +697,16 @@ static void perf_event__cpu_map_swap(union perf_event *event, } } +static void perf_event__stat_config_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + u64 size; + + size = event->stat_config.nr * sizeof(event->stat_config.data[0]); + size += 1; /* nr item itself */ + mem_bswap_64(&event->stat_config.nr, size); +} + typedef void (*perf_event__swap_op)(union perf_event *event, bool sample_id_all); @@ -715,6 +736,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_AUXTRACE_ERROR] = perf_event__auxtrace_error_swap, [PERF_RECORD_THREAD_MAP] = perf_event__thread_map_swap, [PERF_RECORD_CPU_MAP] = perf_event__cpu_map_swap, + [PERF_RECORD_STAT_CONFIG] = perf_event__stat_config_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1255,6 +1277,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, return tool->thread_map(tool, event, session); case PERF_RECORD_CPU_MAP: return tool->cpu_map(tool, event, session); + case PERF_RECORD_STAT_CONFIG: + return tool->stat_config(tool, event, session); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 9e5925c78519..aa7ae73d76b4 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -57,7 +57,8 @@ struct perf_tool { auxtrace_info, auxtrace_error, thread_map, - cpu_map; + cpu_map, + stat_config; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; From 6742434261158ad9678bf15b165304e0200cc324 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:28 +0100 Subject: [PATCH 10/43] perf tools: Add stat config event synthesize function Introduce the perf_event__synthesize_stat_config to synthesize a 'struct perf_stat_config'. Storing the stat config in the form of tag-value pairs will, I believe, sort out future version extensibility issues. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-13-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 +++ tools/perf/tests/stat.c | 53 +++++++++++++++++++++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/event.c | 40 +++++++++++++++++++++++++ tools/perf/util/event.h | 5 ++++ 6 files changed, 104 insertions(+) create mode 100644 tools/perf/tests/stat.c diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 7abad28fe17e..fc0293150f93 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -35,6 +35,7 @@ perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o perf-y += bpf.o perf-y += topology.o perf-y += cpumap.o +perf-y += stat.o $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 0c3fe2846de8..ed8402f339fa 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -187,6 +187,10 @@ static struct test generic_tests[] = { .desc = "Test cpu map synthesize", .func = test__cpu_map_synthesize, }, + { + .desc = "Test stat config synthesize", + .func = test__synthesize_stat_config, + }, { .func = NULL, }, diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c new file mode 100644 index 000000000000..c7a2bdb97708 --- /dev/null +++ b/tools/perf/tests/stat.c @@ -0,0 +1,53 @@ +#include +#include "event.h" +#include "tests.h" +#include "stat.h" +#include "debug.h" + +static bool has_term(struct stat_config_event *config, + u64 tag, u64 val) +{ + unsigned i; + + for (i = 0; i < config->nr; i++) { + if ((config->data[i].tag == tag) && + (config->data[i].val == val)) + return true; + } + + return false; +} + +static int process_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct stat_config_event *config = &event->stat_config; + +#define HAS(term, val) \ + has_term(config, PERF_STAT_CONFIG_TERM__##term, val) + + TEST_ASSERT_VAL("wrong nr", config->nr == PERF_STAT_CONFIG_TERM__MAX); + TEST_ASSERT_VAL("wrong aggr_mode", HAS(AGGR_MODE, AGGR_CORE)); + TEST_ASSERT_VAL("wrong scale", HAS(SCALE, 1)); + TEST_ASSERT_VAL("wrong interval", HAS(INTERVAL, 1)); + +#undef HAS + + return 0; +} + +int test__synthesize_stat_config(int subtest __maybe_unused) +{ + struct perf_stat_config stat_config = { + .aggr_mode = AGGR_CORE, + .scale = 1, + .interval = 1, + }; + + TEST_ASSERT_VAL("failed to synthesize stat_config", + !perf_event__synthesize_stat_config(NULL, &stat_config, process_event, NULL)); + + return 0; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index f85160f6ebb8..319757a3ca69 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -81,6 +81,7 @@ int test__bpf_subtest_get_nr(void); int test_session_topology(int subtest); int test__thread_map_synthesize(int subtest); int test__cpu_map_synthesize(int subtest); +int test__synthesize_stat_config(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 43e2dfc2c73b..1ea693c2a14b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -10,6 +10,8 @@ #include "thread.h" #include "thread_map.h" #include "symbol/kallsyms.h" +#include "asm/bug.h" +#include "stat.h" static const char *perf_event__names[] = { [0] = "TOTAL", @@ -869,6 +871,44 @@ int perf_event__synthesize_cpu_map(struct perf_tool *tool, return err; } +int perf_event__synthesize_stat_config(struct perf_tool *tool, + struct perf_stat_config *config, + perf_event__handler_t process, + struct machine *machine) +{ + struct stat_config_event *event; + int size, i = 0, err; + + size = sizeof(*event); + size += (PERF_STAT_CONFIG_TERM__MAX * sizeof(event->data[0])); + + event = zalloc(size); + if (!event) + return -ENOMEM; + + event->header.type = PERF_RECORD_STAT_CONFIG; + event->header.size = size; + event->nr = PERF_STAT_CONFIG_TERM__MAX; + +#define ADD(__term, __val) \ + event->data[i].tag = PERF_STAT_CONFIG_TERM__##__term; \ + event->data[i].val = __val; \ + i++; + + ADD(AGGR_MODE, config->aggr_mode) + ADD(INTERVAL, config->interval) + ADD(SCALE, config->scale) + + WARN_ONCE(i != PERF_STAT_CONFIG_TERM__MAX, + "stat config terms unbalanced\n"); +#undef ADD + + err = process(tool, (union perf_event *) event, NULL, machine); + + free(event); + return err; +} + size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) { const char *s; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 16cee44de56b..39014c7c5a5b 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -446,6 +446,7 @@ void perf_event__print_totals(void); struct perf_tool; struct thread_map; struct cpu_map; +struct perf_stat_config; typedef int (*perf_event__handler_t)(struct perf_tool *tool, union perf_event *event, @@ -472,6 +473,10 @@ int perf_event__synthesize_threads(struct perf_tool *tool, int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_stat_config(struct perf_tool *tool, + struct perf_stat_config *config, + perf_event__handler_t process, + struct machine *machine); int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, From 8e381596b67af53564a69f16440d3e5d5a73d034 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:29 +0100 Subject: [PATCH 11/43] perf tools: Add stat config event read function Introducing the perf_event__read_stat_config function to read a struct perf_stat_config object data from a stat config event. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-14-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/stat.c | 6 ++++++ tools/perf/util/event.c | 24 ++++++++++++++++++++++++ tools/perf/util/event.h | 2 ++ 3 files changed, 32 insertions(+) diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c index c7a2bdb97708..aa35d28294a0 100644 --- a/tools/perf/tests/stat.c +++ b/tools/perf/tests/stat.c @@ -24,6 +24,7 @@ static int process_event(struct perf_tool *tool __maybe_unused, struct machine *machine __maybe_unused) { struct stat_config_event *config = &event->stat_config; + struct perf_stat_config stat_config; #define HAS(term, val) \ has_term(config, PERF_STAT_CONFIG_TERM__##term, val) @@ -35,6 +36,11 @@ static int process_event(struct perf_tool *tool __maybe_unused, #undef HAS + perf_event__read_stat_config(&stat_config, config); + + TEST_ASSERT_VAL("wrong aggr_mode", stat_config.aggr_mode == AGGR_CORE); + TEST_ASSERT_VAL("wrong scale", stat_config.scale == 1); + TEST_ASSERT_VAL("wrong interval", stat_config.interval == 1); return 0; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1ea693c2a14b..223deaf2fba7 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -909,6 +909,30 @@ int perf_event__synthesize_stat_config(struct perf_tool *tool, return err; } +void perf_event__read_stat_config(struct perf_stat_config *config, + struct stat_config_event *event) +{ + unsigned i; + + for (i = 0; i < event->nr; i++) { + + switch (event->data[i].tag) { +#define CASE(__term, __val) \ + case PERF_STAT_CONFIG_TERM__##__term: \ + config->__val = event->data[i].val; \ + break; + + CASE(AGGR_MODE, aggr_mode) + CASE(SCALE, scale) + CASE(INTERVAL, interval) +#undef CASE + default: + pr_warning("unknown stat config term %" PRIu64 "\n", + event->data[i].tag); + } + } +} + size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) { const char *s; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 39014c7c5a5b..4e87be2e1afa 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -477,6 +477,8 @@ int perf_event__synthesize_stat_config(struct perf_tool *tool, struct perf_stat_config *config, perf_event__handler_t process, struct machine *machine); +void perf_event__read_stat_config(struct perf_stat_config *config, + struct stat_config_event *event); int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, From d80518c90bb2b4af9755d79af5dfe9d44e04cdb9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:30 +0100 Subject: [PATCH 12/43] perf tools: Add stat user level event Adding a stat event to store a 'struct perf_counter_values' for a given event/cpu/thread. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-15-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 19 +++++++++++++++++++ tools/perf/util/session.c | 25 +++++++++++++++++++++++++ tools/perf/util/tool.h | 3 ++- 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 223deaf2fba7..670123fee60a 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -42,6 +42,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_THREAD_MAP] = "THREAD_MAP", [PERF_RECORD_CPU_MAP] = "CPU_MAP", [PERF_RECORD_STAT_CONFIG] = "STAT_CONFIG", + [PERF_RECORD_STAT] = "STAT", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 4e87be2e1afa..f23f464c680a 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -229,6 +229,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_THREAD_MAP = 73, PERF_RECORD_CPU_MAP = 74, PERF_RECORD_STAT_CONFIG = 75, + PERF_RECORD_STAT = 76, PERF_RECORD_HEADER_MAX }; @@ -414,6 +415,23 @@ struct stat_config_event { struct stat_config_event_entry data[]; }; +struct stat_event { + struct perf_event_header header; + + u64 id; + u32 cpu; + u32 thread; + + union { + struct { + u64 val; + u64 ena; + u64 run; + }; + u64 values[3]; + }; +}; + union perf_event { struct perf_event_header header; struct mmap_event mmap; @@ -439,6 +457,7 @@ union perf_event { struct thread_map_event thread_map; struct cpu_map_event cpu_map; struct stat_config_event stat_config; + struct stat_event stat; }; void perf_event__print_totals(void); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index fbc52ab3eb75..663a2fdab42c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -324,6 +324,15 @@ int process_event_stat_config_stub(struct perf_tool *tool __maybe_unused, return 0; } +static int process_stat_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *perf_session + __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + void perf_tool__fill_defaults(struct perf_tool *tool) { if (tool->sample == NULL) @@ -380,6 +389,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->cpu_map = process_event_cpu_map_stub; if (tool->stat_config == NULL) tool->stat_config = process_event_stat_config_stub; + if (tool->stat == NULL) + tool->stat = process_stat_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -707,6 +718,17 @@ static void perf_event__stat_config_swap(union perf_event *event, mem_bswap_64(&event->stat_config.nr, size); } +static void perf_event__stat_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + event->stat.id = bswap_64(event->stat.id); + event->stat.thread = bswap_32(event->stat.thread); + event->stat.cpu = bswap_32(event->stat.cpu); + event->stat.val = bswap_64(event->stat.val); + event->stat.ena = bswap_64(event->stat.ena); + event->stat.run = bswap_64(event->stat.run); +} + typedef void (*perf_event__swap_op)(union perf_event *event, bool sample_id_all); @@ -737,6 +759,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_THREAD_MAP] = perf_event__thread_map_swap, [PERF_RECORD_CPU_MAP] = perf_event__cpu_map_swap, [PERF_RECORD_STAT_CONFIG] = perf_event__stat_config_swap, + [PERF_RECORD_STAT] = perf_event__stat_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1279,6 +1302,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, return tool->cpu_map(tool, event, session); case PERF_RECORD_STAT_CONFIG: return tool->stat_config(tool, event, session); + case PERF_RECORD_STAT: + return tool->stat(tool, event, session); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index aa7ae73d76b4..f0b9da0c166a 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -58,7 +58,8 @@ struct perf_tool { auxtrace_error, thread_map, cpu_map, - stat_config; + stat_config, + stat; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; From 5796f8f073fe50171376f058376dde93ec5f3785 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:31 +0100 Subject: [PATCH 13/43] perf tools: Add stat event synthesize function Introduce the perf_event__synthesize_stat function to synthesize a 'struct stat_event'. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-16-git-send-email-jolsa@kernel.org [ Renamed 'stat' parameter to 'st' to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 4 ++++ tools/perf/tests/stat.c | 41 +++++++++++++++++++++++++++++---- tools/perf/tests/tests.h | 1 + tools/perf/util/event.c | 22 ++++++++++++++++++ tools/perf/util/event.h | 7 +++++- 5 files changed, 69 insertions(+), 6 deletions(-) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index ed8402f339fa..4a7d9989e1c3 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -191,6 +191,10 @@ static struct test generic_tests[] = { .desc = "Test stat config synthesize", .func = test__synthesize_stat_config, }, + { + .desc = "Test stat synthesize", + .func = test__synthesize_stat, + }, { .func = NULL, }, diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c index aa35d28294a0..d319875a5e7c 100644 --- a/tools/perf/tests/stat.c +++ b/tools/perf/tests/stat.c @@ -2,6 +2,7 @@ #include "event.h" #include "tests.h" #include "stat.h" +#include "counts.h" #include "debug.h" static bool has_term(struct stat_config_event *config, @@ -18,10 +19,10 @@ static bool has_term(struct stat_config_event *config, return false; } -static int process_event(struct perf_tool *tool __maybe_unused, - union perf_event *event, - struct perf_sample *sample __maybe_unused, - struct machine *machine __maybe_unused) +static int process_stat_config_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) { struct stat_config_event *config = &event->stat_config; struct perf_stat_config stat_config; @@ -53,7 +54,37 @@ int test__synthesize_stat_config(int subtest __maybe_unused) }; TEST_ASSERT_VAL("failed to synthesize stat_config", - !perf_event__synthesize_stat_config(NULL, &stat_config, process_event, NULL)); + !perf_event__synthesize_stat_config(NULL, &stat_config, process_stat_config_event, NULL)); + + return 0; +} + +static int process_stat_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct stat_event *st = &event->stat; + + TEST_ASSERT_VAL("wrong cpu", st->cpu == 1); + TEST_ASSERT_VAL("wrong thread", st->thread == 2); + TEST_ASSERT_VAL("wrong id", st->id == 3); + TEST_ASSERT_VAL("wrong val", st->val == 100); + TEST_ASSERT_VAL("wrong run", st->ena == 200); + TEST_ASSERT_VAL("wrong ena", st->run == 300); + return 0; +} + +int test__synthesize_stat(int subtest __maybe_unused) +{ + struct perf_counts_values count; + + count.val = 100; + count.ena = 200; + count.run = 300; + + TEST_ASSERT_VAL("failed to synthesize stat_config", + !perf_event__synthesize_stat(NULL, 1, 2, 3, &count, process_stat_event, NULL)); return 0; } diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 319757a3ca69..d36eda17a5f5 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -82,6 +82,7 @@ int test_session_topology(int subtest); int test__thread_map_synthesize(int subtest); int test__cpu_map_synthesize(int subtest); int test__synthesize_stat_config(int subtest); +int test__synthesize_stat(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 670123fee60a..eb8243ab6ab1 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -910,6 +910,28 @@ int perf_event__synthesize_stat_config(struct perf_tool *tool, return err; } +int perf_event__synthesize_stat(struct perf_tool *tool, + u32 cpu, u32 thread, u64 id, + struct perf_counts_values *count, + perf_event__handler_t process, + struct machine *machine) +{ + struct stat_event event; + + event.header.type = PERF_RECORD_STAT; + event.header.size = sizeof(event); + event.header.misc = 0; + + event.id = id; + event.cpu = cpu; + event.thread = thread; + event.val = count->val; + event.ena = count->ena; + event.run = count->run; + + return process(tool, (union perf_event *) &event, NULL, machine); +} + void perf_event__read_stat_config(struct perf_stat_config *config, struct stat_config_event *event) { diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index f23f464c680a..336eb44babf8 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -466,6 +466,7 @@ struct perf_tool; struct thread_map; struct cpu_map; struct perf_stat_config; +struct perf_counts_values; typedef int (*perf_event__handler_t)(struct perf_tool *tool, union perf_event *event, @@ -498,7 +499,11 @@ int perf_event__synthesize_stat_config(struct perf_tool *tool, struct machine *machine); void perf_event__read_stat_config(struct perf_stat_config *config, struct stat_config_event *event); - +int perf_event__synthesize_stat(struct perf_tool *tool, + u32 cpu, u32 thread, u64 id, + struct perf_counts_values *count, + perf_event__handler_t process, + struct machine *machine); int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); From 0ea0e3558607626196eb09ace796aac585e61f5c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:32 +0100 Subject: [PATCH 14/43] perf tools: Add stat event read function Introducing the perf_event__process_stat_event function to process a 'struct perf_stat' data from a stat event. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-17-git-send-email-jolsa@kernel.org [ Renamed 'stat' parameter to 'st' to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/stat.c | 23 +++++++++++++++++++++++ tools/perf/util/stat.h | 6 ++++++ 2 files changed, 29 insertions(+) diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 2d9d8306dbd3..0ad59cea318c 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -341,3 +341,26 @@ int perf_stat_process_counter(struct perf_stat_config *config, return 0; } + +int perf_event__process_stat_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session) +{ + struct perf_counts_values count; + struct stat_event *st = &event->stat; + struct perf_evsel *counter; + + count.val = st->val; + count.ena = st->ena; + count.run = st->run; + + counter = perf_evlist__id2evsel(session->evlist, st->id); + if (!counter) { + pr_err("Failed to resolve counter for stat event.\n"); + return -EINVAL; + } + + *perf_counts(counter->counts, st->cpu, st->thread) = count; + counter->supported = true; + return 0; +} diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index da1d11c4f8c1..afe6844e5219 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -90,4 +90,10 @@ void perf_evlist__reset_stats(struct perf_evlist *evlist); int perf_stat_process_counter(struct perf_stat_config *config, struct perf_evsel *counter); +struct perf_tool; +union perf_event; +struct perf_session; +int perf_event__process_stat_event(struct perf_tool *tool, + union perf_event *event, + struct perf_session *session); #endif From 2d8f0f18a5c37cf0322cb385b99adb1167b7cf78 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:33 +0100 Subject: [PATCH 15/43] perf tools: Add stat round user level event Adding the stat round event to be stored after each stat interval round, so that report tools (report/script) gets notified and process interval data. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-18-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 13 +++++++++++++ tools/perf/util/session.c | 21 +++++++++++++++++++++ tools/perf/util/tool.h | 3 ++- 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index eb8243ab6ab1..725db548c7d5 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -43,6 +43,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_CPU_MAP] = "CPU_MAP", [PERF_RECORD_STAT_CONFIG] = "STAT_CONFIG", [PERF_RECORD_STAT] = "STAT", + [PERF_RECORD_STAT_ROUND] = "STAT_ROUND", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 336eb44babf8..5eb4f55a141d 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -230,6 +230,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_CPU_MAP = 74, PERF_RECORD_STAT_CONFIG = 75, PERF_RECORD_STAT = 76, + PERF_RECORD_STAT_ROUND = 77, PERF_RECORD_HEADER_MAX }; @@ -432,6 +433,17 @@ struct stat_event { }; }; +enum { + PERF_STAT_ROUND_TYPE__INTERVAL = 0, + PERF_STAT_ROUND_TYPE__FINAL = 1, +}; + +struct stat_round_event { + struct perf_event_header header; + u64 type; + u64 time; +}; + union perf_event { struct perf_event_header header; struct mmap_event mmap; @@ -458,6 +470,7 @@ union perf_event { struct cpu_map_event cpu_map; struct stat_config_event stat_config; struct stat_event stat; + struct stat_round_event stat_round; }; void perf_event__print_totals(void); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 663a2fdab42c..5b3a81a6b795 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -333,6 +333,15 @@ static int process_stat_stub(struct perf_tool *tool __maybe_unused, return 0; } +static int process_stat_round_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *perf_session + __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + void perf_tool__fill_defaults(struct perf_tool *tool) { if (tool->sample == NULL) @@ -391,6 +400,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->stat_config = process_event_stat_config_stub; if (tool->stat == NULL) tool->stat = process_stat_stub; + if (tool->stat_round == NULL) + tool->stat_round = process_stat_round_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -729,6 +740,13 @@ static void perf_event__stat_swap(union perf_event *event, event->stat.run = bswap_64(event->stat.run); } +static void perf_event__stat_round_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + event->stat_round.type = bswap_64(event->stat_round.type); + event->stat_round.time = bswap_64(event->stat_round.time); +} + typedef void (*perf_event__swap_op)(union perf_event *event, bool sample_id_all); @@ -760,6 +778,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_CPU_MAP] = perf_event__cpu_map_swap, [PERF_RECORD_STAT_CONFIG] = perf_event__stat_config_swap, [PERF_RECORD_STAT] = perf_event__stat_swap, + [PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1304,6 +1323,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, return tool->stat_config(tool, event, session); case PERF_RECORD_STAT: return tool->stat(tool, event, session); + case PERF_RECORD_STAT_ROUND: + return tool->stat_round(tool, event, session); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index f0b9da0c166a..d04d9e5f444a 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -59,7 +59,8 @@ struct perf_tool { thread_map, cpu_map, stat_config, - stat; + stat, + stat_round; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; From d4c2259195f538505d2570e78555532372fb4ad2 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:34 +0100 Subject: [PATCH 16/43] perf tools: Add stat round event synthesize function Introduce the perf_event__synthesize_stat_round function to synthesize a 'struct stat_round_event'. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-19-git-send-email-jolsa@kernel.org [ Renamed 'time' parameter to 'evtime' to fix build on older systems ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 4 ++++ tools/perf/tests/stat.c | 21 +++++++++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/event.c | 17 +++++++++++++++++ tools/perf/util/event.h | 4 ++++ 5 files changed, 47 insertions(+) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 4a7d9989e1c3..6a3519814492 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -195,6 +195,10 @@ static struct test generic_tests[] = { .desc = "Test stat synthesize", .func = test__synthesize_stat, }, + { + .desc = "Test stat round synthesize", + .func = test__synthesize_stat_round, + }, { .func = NULL, }, diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c index d319875a5e7c..6a20ff2326bb 100644 --- a/tools/perf/tests/stat.c +++ b/tools/perf/tests/stat.c @@ -88,3 +88,24 @@ int test__synthesize_stat(int subtest __maybe_unused) return 0; } + +static int process_stat_round_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct stat_round_event *stat_round = &event->stat_round; + + TEST_ASSERT_VAL("wrong time", stat_round->time == 0xdeadbeef); + TEST_ASSERT_VAL("wrong type", stat_round->type == PERF_STAT_ROUND_TYPE__INTERVAL); + return 0; +} + +int test__synthesize_stat_round(int subtest __maybe_unused) +{ + TEST_ASSERT_VAL("failed to synthesize stat_config", + !perf_event__synthesize_stat_round(NULL, 0xdeadbeef, PERF_STAT_ROUND_TYPE__INTERVAL, + process_stat_round_event, NULL)); + + return 0; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index d36eda17a5f5..a82ab9c4c0ca 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -83,6 +83,7 @@ int test__thread_map_synthesize(int subtest); int test__cpu_map_synthesize(int subtest); int test__synthesize_stat_config(int subtest); int test__synthesize_stat(int subtest); +int test__synthesize_stat_round(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 725db548c7d5..e4c68ba79974 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -933,6 +933,23 @@ int perf_event__synthesize_stat(struct perf_tool *tool, return process(tool, (union perf_event *) &event, NULL, machine); } +int perf_event__synthesize_stat_round(struct perf_tool *tool, + u64 evtime, u64 type, + perf_event__handler_t process, + struct machine *machine) +{ + struct stat_round_event event; + + event.header.type = PERF_RECORD_STAT_ROUND; + event.header.size = sizeof(event); + event.header.misc = 0; + + event.time = evtime; + event.type = type; + + return process(tool, (union perf_event *) &event, NULL, machine); +} + void perf_event__read_stat_config(struct perf_stat_config *config, struct stat_config_event *event) { diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 5eb4f55a141d..1afaa21eeebe 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -517,6 +517,10 @@ int perf_event__synthesize_stat(struct perf_tool *tool, struct perf_counts_values *count, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_stat_round(struct perf_tool *tool, + u64 time, u64 type, + perf_event__handler_t process, + struct machine *machine); int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); From e08a4564e23f8f89a055d717887674f54a9da515 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:35 +0100 Subject: [PATCH 17/43] perf tools: Add stat events fprintf functions Introducing the following functions to display the stat events for raw dump. perf_event__fprintf_stat perf_event__fprintf_stat_round perf_event__fprintf_stat_config Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-20-git-send-email-jolsa@kernel.org [ s/stat/st/g and s/round/rd/g parameters to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/stat.c | 39 +++++++++++++++++++++++++++++++++++++++ tools/perf/util/stat.h | 4 ++++ 2 files changed, 43 insertions(+) diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 0ad59cea318c..2f901d15e063 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -364,3 +364,42 @@ int perf_event__process_stat_event(struct perf_tool *tool __maybe_unused, counter->supported = true; return 0; } + +size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp) +{ + struct stat_event *st = (struct stat_event *) event; + size_t ret; + + ret = fprintf(fp, "\n... id %" PRIu64 ", cpu %d, thread %d\n", + st->id, st->cpu, st->thread); + ret += fprintf(fp, "... value %" PRIu64 ", enabled %" PRIu64 ", running %" PRIu64 "\n", + st->val, st->ena, st->run); + + return ret; +} + +size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp) +{ + struct stat_round_event *rd = (struct stat_round_event *)event; + size_t ret; + + ret = fprintf(fp, "\n... time %" PRIu64 ", type %s\n", rd->time, + rd->type == PERF_STAT_ROUND_TYPE__FINAL ? "FINAL" : "INTERVAL"); + + return ret; +} + +size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp) +{ + struct perf_stat_config sc; + size_t ret; + + perf_event__read_stat_config(&sc, &event->stat_config); + + ret = fprintf(fp, "\n"); + ret += fprintf(fp, "... aggr_mode %d\n", sc.aggr_mode); + ret += fprintf(fp, "... scale %d\n", sc.scale); + ret += fprintf(fp, "... interval %u\n", sc.interval); + + return ret; +} diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index afe6844e5219..086f4e128d63 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -96,4 +96,8 @@ struct perf_session; int perf_event__process_stat_event(struct perf_tool *tool, union perf_event *event, struct perf_session *session); + +size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp); +size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp); +size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp); #endif From ffe777254cce24fb5fde3f0aa91fc755cfb1b812 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:36 +0100 Subject: [PATCH 18/43] perf tools: Add event_update user level event It'll serve as a base event for additional event attributes details, that are not part of the attr event. At the moment this event is just a dummy one without any specific functionality. The type value will distinguish the update event details. It'll come in the following patches. The idea for this event is to be extensible for any update that the event might need in the future. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-21-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 10 ++++++++++ tools/perf/util/header.c | 20 ++++++++++++++++++++ tools/perf/util/header.h | 3 +++ tools/perf/util/session.c | 21 +++++++++++++++++++++ tools/perf/util/tool.h | 1 + 6 files changed, 56 insertions(+) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index e4c68ba79974..cd61bb1f3917 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -44,6 +44,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_STAT_CONFIG] = "STAT_CONFIG", [PERF_RECORD_STAT] = "STAT", [PERF_RECORD_STAT_ROUND] = "STAT_ROUND", + [PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 1afaa21eeebe..6966a4b7c0f0 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -231,6 +231,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_STAT_CONFIG = 75, PERF_RECORD_STAT = 76, PERF_RECORD_STAT_ROUND = 77, + PERF_RECORD_EVENT_UPDATE = 78, PERF_RECORD_HEADER_MAX }; @@ -307,6 +308,14 @@ struct attr_event { u64 id[]; }; +struct event_update_event { + struct perf_event_header header; + u64 type; + u64 id; + + char data[]; +}; + #define MAX_EVENT_NAME 64 struct perf_trace_event_type { @@ -456,6 +465,7 @@ union perf_event { struct throttle_event throttle; struct sample_event sample; struct attr_event attr; + struct event_update_event event_update; struct event_type_event event_type; struct tracing_data_event tracing_data; struct build_id_event build_id; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 5ac7bdb0dff7..6b4e00220a5a 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2745,6 +2745,26 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused, return 0; } +int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_evlist **pevlist) +{ + struct event_update_event *ev = &event->event_update; + struct perf_evlist *evlist; + struct perf_evsel *evsel; + + if (!pevlist || *pevlist == NULL) + return -EINVAL; + + evlist = *pevlist; + + evsel = perf_evlist__id2evsel(evlist, ev->id); + if (evsel == NULL) + return -EINVAL; + + return 0; +} + int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, struct perf_evlist *evlist, perf_event__handler_t process) diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 05f27cb6b7e3..1e843c67a4ff 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -107,6 +107,9 @@ int perf_event__synthesize_attrs(struct perf_tool *tool, perf_event__handler_t process); int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist); +int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_evlist **pevlist); int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, struct perf_evlist *evlist, diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5b3a81a6b795..49e5cdc4cc5a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -205,6 +205,15 @@ static int process_event_synth_attr_stub(struct perf_tool *tool __maybe_unused, return 0; } +static int process_event_synth_event_update_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_evlist **pevlist + __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + static int process_event_sample_stub(struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, @@ -374,6 +383,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->unthrottle = process_event_stub; if (tool->attr == NULL) tool->attr = process_event_synth_attr_stub; + if (tool->event_update == NULL) + tool->event_update = process_event_synth_event_update_stub; if (tool->tracing_data == NULL) tool->tracing_data = process_event_synth_tracing_data_stub; if (tool->build_id == NULL) @@ -625,6 +636,13 @@ static void perf_event__hdr_attr_swap(union perf_event *event, mem_bswap_64(event->attr.id, size); } +static void perf_event__event_update_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + event->event_update.type = bswap_64(event->event_update.type); + event->event_update.id = bswap_64(event->event_update.id); +} + static void perf_event__event_type_swap(union perf_event *event, bool sample_id_all __maybe_unused) { @@ -779,6 +797,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_STAT_CONFIG] = perf_event__stat_config_swap, [PERF_RECORD_STAT] = perf_event__stat_swap, [PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap, + [PERF_RECORD_EVENT_UPDATE] = perf_event__event_update_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1290,6 +1309,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, perf_session__set_comm_exec(session); } return err; + case PERF_RECORD_EVENT_UPDATE: + return tool->event_update(tool, event, &session->evlist); case PERF_RECORD_HEADER_EVENT_TYPE: /* * Depreceated, but we need to handle it for sake diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index d04d9e5f444a..55de4cffcd4e 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -50,6 +50,7 @@ struct perf_tool { throttle, unthrottle; event_attr_op attr; + event_attr_op event_update; event_op2 tracing_data; event_oe finished_round; event_op2 build_id, From a6e5281780d1da65c15ce529707f43eb4a6df856 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:37 +0100 Subject: [PATCH 19/43] perf tools: Add event_update event unit type Adding unit type 'event update' event, that stores/transfer events unit name. The unit name is part of the perf stat output data. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-22-git-send-email-jolsa@kernel.org [ Rename __alloc() to __new() for consistency ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 +++ tools/perf/tests/event_update.c | 42 +++++++++++++++++++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/event.h | 4 +++ tools/perf/util/header.c | 44 +++++++++++++++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 7 files changed, 99 insertions(+) create mode 100644 tools/perf/tests/event_update.c diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index fc0293150f93..614899b88b37 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -36,6 +36,7 @@ perf-y += bpf.o perf-y += topology.o perf-y += cpumap.o perf-y += stat.o +perf-y += event_update.o $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 6a3519814492..f2b1dcac45d3 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -199,6 +199,10 @@ static struct test generic_tests[] = { .desc = "Test stat round synthesize", .func = test__synthesize_stat_round, }, + { + .desc = "Test attr update synthesize", + .func = test__event_update, + }, { .func = NULL, }, diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c new file mode 100644 index 000000000000..9cdf4c934977 --- /dev/null +++ b/tools/perf/tests/event_update.c @@ -0,0 +1,42 @@ +#include +#include "evlist.h" +#include "evsel.h" +#include "machine.h" +#include "tests.h" +#include "debug.h" + +static int process_event_unit(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct event_update_event *ev = (struct event_update_event *) event; + + TEST_ASSERT_VAL("wrong id", ev->id == 123); + TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__UNIT); + TEST_ASSERT_VAL("wrong unit", !strcmp(ev->data, "KRAVA")); + return 0; +} + +int test__event_update(int subtest __maybe_unused) +{ + struct perf_evlist *evlist; + struct perf_evsel *evsel; + + evlist = perf_evlist__new_default(); + TEST_ASSERT_VAL("failed to get evlist", evlist); + + evsel = perf_evlist__first(evlist); + + TEST_ASSERT_VAL("failed to allos ids", + !perf_evsel__alloc_id(evsel, 1, 1)); + + perf_evlist__id_add(evlist, evsel, 0, 0, 123); + + evsel->unit = strdup("KRAVA"); + + TEST_ASSERT_VAL("failed to synthesize attr update unit", + !perf_event__synthesize_event_update_unit(NULL, evsel, process_event_unit)); + + return 0; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index a82ab9c4c0ca..82b2b5e6ba7c 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -84,6 +84,7 @@ int test__cpu_map_synthesize(int subtest); int test__synthesize_stat_config(int subtest); int test__synthesize_stat(int subtest); int test__synthesize_stat_round(int subtest); +int test__event_update(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 6966a4b7c0f0..64c4cdf5aada 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -308,6 +308,10 @@ struct attr_event { u64 id[]; }; +enum { + PERF_EVENT_UPDATE__UNIT = 0, +}; + struct event_update_event { struct perf_event_header header; u64 type; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 6b4e00220a5a..5759ebfde666 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2686,6 +2686,43 @@ int perf_event__synthesize_attr(struct perf_tool *tool, return err; } +static struct event_update_event * +event_update_event__new(size_t size, u64 type, u64 id) +{ + struct event_update_event *ev; + + size += sizeof(*ev); + size = PERF_ALIGN(size, sizeof(u64)); + + ev = zalloc(size); + if (ev) { + ev->header.type = PERF_RECORD_EVENT_UPDATE; + ev->header.size = (u16)size; + ev->type = type; + ev->id = id; + } + return ev; +} + +int +perf_event__synthesize_event_update_unit(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process) +{ + struct event_update_event *ev; + size_t size = strlen(evsel->unit); + int err; + + ev = event_update_event__new(size + 1, PERF_EVENT_UPDATE__UNIT, evsel->id[0]); + if (ev == NULL) + return -ENOMEM; + + strncpy(ev->data, evsel->unit, size); + err = process(tool, (union perf_event *)ev, NULL, NULL); + free(ev); + return err; +} + int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, perf_event__handler_t process) @@ -2762,6 +2799,13 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, if (evsel == NULL) return -EINVAL; + switch (ev->type) { + case PERF_EVENT_UPDATE__UNIT: + evsel->unit = strdup(ev->data); + default: + break; + } + return 0; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 1e843c67a4ff..6aa2b9242fc1 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -105,6 +105,9 @@ int perf_event__synthesize_attr(struct perf_tool *tool, int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, perf_event__handler_t process); +int perf_event__synthesize_event_update_unit(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process); int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist); int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, From daeecbc0c431f15f492fb8d704080a02de6e2918 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:38 +0100 Subject: [PATCH 20/43] perf tools: Add event_update event scale type A__allocdding scale type 'event update' event, that stores/transfer events scale value. The PMU events can define the scale value which is used to multiply events data. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-23-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/event_update.c | 21 +++++++++++++++++++++ tools/perf/util/event.h | 5 +++++ tools/perf/util/header.c | 26 ++++++++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 4 files changed, 55 insertions(+) diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index 9cdf4c934977..a91fcefc9f67 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -18,6 +18,22 @@ static int process_event_unit(struct perf_tool *tool __maybe_unused, return 0; } +static int process_event_scale(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct event_update_event *ev = (struct event_update_event *) event; + struct event_update_event_scale *ev_data; + + ev_data = (struct event_update_event_scale *) ev->data; + + TEST_ASSERT_VAL("wrong id", ev->id == 123); + TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__SCALE); + TEST_ASSERT_VAL("wrong scale", ev_data->scale = 0.123); + return 0; +} + int test__event_update(int subtest __maybe_unused) { struct perf_evlist *evlist; @@ -38,5 +54,10 @@ int test__event_update(int subtest __maybe_unused) TEST_ASSERT_VAL("failed to synthesize attr update unit", !perf_event__synthesize_event_update_unit(NULL, evsel, process_event_unit)); + evsel->scale = 0.123; + + TEST_ASSERT_VAL("failed to synthesize attr update scale", + !perf_event__synthesize_event_update_scale(NULL, evsel, process_event_scale)); + return 0; } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 64c4cdf5aada..44198e8550e4 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -310,6 +310,11 @@ struct attr_event { enum { PERF_EVENT_UPDATE__UNIT = 0, + PERF_EVENT_UPDATE__SCALE = 1, +}; + +struct event_update_event_scale { + double scale; }; struct event_update_event { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 5759ebfde666..30edb4ba258e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2723,6 +2723,27 @@ perf_event__synthesize_event_update_unit(struct perf_tool *tool, return err; } +int +perf_event__synthesize_event_update_scale(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process) +{ + struct event_update_event *ev; + struct event_update_event_scale *ev_data; + int err; + + ev = event_update_event__new(sizeof(*ev_data), PERF_EVENT_UPDATE__SCALE, evsel->id[0]); + if (ev == NULL) + return -ENOMEM; + + ev_data = (struct event_update_event_scale *) ev->data; + ev_data->scale = evsel->scale; + err = process(tool, (union perf_event*) ev, NULL, NULL); + free(ev); + return err; +} + + int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, perf_event__handler_t process) @@ -2787,6 +2808,7 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, struct perf_evlist **pevlist) { struct event_update_event *ev = &event->event_update; + struct event_update_event_scale *ev_scale; struct perf_evlist *evlist; struct perf_evsel *evsel; @@ -2802,6 +2824,10 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, switch (ev->type) { case PERF_EVENT_UPDATE__UNIT: evsel->unit = strdup(ev->data); + break; + case PERF_EVENT_UPDATE__SCALE: + ev_scale = (struct event_update_event_scale *) ev->data; + evsel->scale = ev_scale->scale; default: break; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 6aa2b9242fc1..fad04cbab666 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -108,6 +108,9 @@ int perf_event__synthesize_attrs(struct perf_tool *tool, int perf_event__synthesize_event_update_unit(struct perf_tool *tool, struct perf_evsel *evsel, perf_event__handler_t process); +int perf_event__synthesize_event_update_scale(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process); int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist); int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, From 802c9048b824eaa3c75d875e2d107460ad586439 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:39 +0100 Subject: [PATCH 21/43] perf tools: Add event_update event name type Adding name type 'event update' event, that stores/transfer events name. Event's name is stored within perf.data's EVENT_DESC feature, but we don't have it if we get the report data from pipe. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-24-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/event_update.c | 25 +++++++++++++++++++++++++ tools/perf/util/event.h | 1 + tools/perf/util/header.c | 21 +++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 4 files changed, 50 insertions(+) diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index a91fcefc9f67..482b89650bbe 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -34,10 +34,30 @@ static int process_event_scale(struct perf_tool *tool __maybe_unused, return 0; } +struct event_name { + struct perf_tool tool; + const char *name; +}; + +static int process_event_name(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct event_name *tmp = container_of(tool, struct event_name, tool); + struct event_update_event *ev = (struct event_update_event*) event; + + TEST_ASSERT_VAL("wrong id", ev->id == 123); + TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__NAME); + TEST_ASSERT_VAL("wrong name", !strcmp(ev->data, tmp->name)); + return 0; +} + int test__event_update(int subtest __maybe_unused) { struct perf_evlist *evlist; struct perf_evsel *evsel; + struct event_name tmp; evlist = perf_evlist__new_default(); TEST_ASSERT_VAL("failed to get evlist", evlist); @@ -59,5 +79,10 @@ int test__event_update(int subtest __maybe_unused) TEST_ASSERT_VAL("failed to synthesize attr update scale", !perf_event__synthesize_event_update_scale(NULL, evsel, process_event_scale)); + tmp.name = perf_evsel__name(evsel); + + TEST_ASSERT_VAL("failed to synthesize attr update name", + !perf_event__synthesize_event_update_name(&tmp.tool, evsel, process_event_name)); + return 0; } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 44198e8550e4..235196b575d6 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -311,6 +311,7 @@ struct attr_event { enum { PERF_EVENT_UPDATE__UNIT = 0, PERF_EVENT_UPDATE__SCALE = 1, + PERF_EVENT_UPDATE__NAME = 2, }; struct event_update_event_scale { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 30edb4ba258e..cd3d005a34af 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2743,6 +2743,24 @@ perf_event__synthesize_event_update_scale(struct perf_tool *tool, return err; } +int +perf_event__synthesize_event_update_name(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process) +{ + struct event_update_event *ev; + size_t len = strlen(evsel->name); + int err; + + ev = event_update_event__new(len + 1, PERF_EVENT_UPDATE__NAME, evsel->id[0]); + if (ev == NULL) + return -ENOMEM; + + strncpy(ev->data, evsel->name, len); + err = process(tool, (union perf_event*) ev, NULL, NULL); + free(ev); + return err; +} int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, @@ -2825,6 +2843,9 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, case PERF_EVENT_UPDATE__UNIT: evsel->unit = strdup(ev->data); break; + case PERF_EVENT_UPDATE__NAME: + evsel->name = strdup(ev->data); + break; case PERF_EVENT_UPDATE__SCALE: ev_scale = (struct event_update_event_scale *) ev->data; evsel->scale = ev_scale->scale; diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index fad04cbab666..51cf566a0835 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -111,6 +111,9 @@ int perf_event__synthesize_event_update_unit(struct perf_tool *tool, int perf_event__synthesize_event_update_scale(struct perf_tool *tool, struct perf_evsel *evsel, perf_event__handler_t process); +int perf_event__synthesize_event_update_name(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process); int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist); int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, From 86ebb09f96fe6886e1e5d53b648df5537ba859ca Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:40 +0100 Subject: [PATCH 22/43] perf tools: Add event_update event cpus type Adding the cpumask 'event update' event, that stores/transfer the cpumask for a event. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-25-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/event_update.c | 29 +++++++++++++++++++++++ tools/perf/util/event.h | 5 ++++ tools/perf/util/header.c | 42 +++++++++++++++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 4 files changed, 79 insertions(+) diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index 482b89650bbe..012eab5d1df1 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -53,6 +53,29 @@ static int process_event_name(struct perf_tool *tool, return 0; } +static int process_event_cpus(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct event_update_event *ev = (struct event_update_event*) event; + struct event_update_event_cpus *ev_data; + struct cpu_map *map; + + ev_data = (struct event_update_event_cpus*) ev->data; + + map = cpu_map__new_data(&ev_data->cpus); + + TEST_ASSERT_VAL("wrong id", ev->id == 123); + TEST_ASSERT_VAL("wrong type", ev->type == PERF_EVENT_UPDATE__CPUS); + TEST_ASSERT_VAL("wrong cpus", map->nr == 3); + TEST_ASSERT_VAL("wrong cpus", map->map[0] == 1); + TEST_ASSERT_VAL("wrong cpus", map->map[1] == 2); + TEST_ASSERT_VAL("wrong cpus", map->map[2] == 3); + cpu_map__put(map); + return 0; +} + int test__event_update(int subtest __maybe_unused) { struct perf_evlist *evlist; @@ -84,5 +107,11 @@ int test__event_update(int subtest __maybe_unused) TEST_ASSERT_VAL("failed to synthesize attr update name", !perf_event__synthesize_event_update_name(&tmp.tool, evsel, process_event_name)); + evsel->own_cpus = cpu_map__new("1,2,3"); + + TEST_ASSERT_VAL("failed to synthesize attr update cpus", + !perf_event__synthesize_event_update_cpus(&tmp.tool, evsel, process_event_cpus)); + + cpu_map__put(evsel->own_cpus); return 0; } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 235196b575d6..b7ffb7ee9971 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -312,6 +312,11 @@ enum { PERF_EVENT_UPDATE__UNIT = 0, PERF_EVENT_UPDATE__SCALE = 1, PERF_EVENT_UPDATE__NAME = 2, + PERF_EVENT_UPDATE__CPUS = 3, +}; + +struct event_update_event_cpus { + struct cpu_map_data cpus; }; struct event_update_event_scale { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index cd3d005a34af..79d3eb984db7 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2762,6 +2762,38 @@ perf_event__synthesize_event_update_name(struct perf_tool *tool, return err; } +int +perf_event__synthesize_event_update_cpus(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process) +{ + size_t size = sizeof(struct event_update_event); + struct event_update_event *ev; + int max, err; + u16 type; + + if (!evsel->own_cpus) + return 0; + + ev = cpu_map_data__alloc(evsel->own_cpus, &size, &type, &max); + if (!ev) + return -ENOMEM; + + ev->header.type = PERF_RECORD_EVENT_UPDATE; + ev->header.size = (u16)size; + ev->type = PERF_EVENT_UPDATE__CPUS; + ev->id = evsel->id[0]; + + cpu_map_data__synthesize((struct cpu_map_data *) ev->data, + evsel->own_cpus, + type, max); + + err = process(tool, (union perf_event*) ev, NULL, NULL); + free(ev); + return err; +} + + int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, perf_event__handler_t process) @@ -2827,8 +2859,10 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, { struct event_update_event *ev = &event->event_update; struct event_update_event_scale *ev_scale; + struct event_update_event_cpus *ev_cpus; struct perf_evlist *evlist; struct perf_evsel *evsel; + struct cpu_map *map; if (!pevlist || *pevlist == NULL) return -EINVAL; @@ -2849,6 +2883,14 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, case PERF_EVENT_UPDATE__SCALE: ev_scale = (struct event_update_event_scale *) ev->data; evsel->scale = ev_scale->scale; + case PERF_EVENT_UPDATE__CPUS: + ev_cpus = (struct event_update_event_cpus *) ev->data; + + map = cpu_map__new_data(&ev_cpus->cpus); + if (map) + evsel->own_cpus = map; + else + pr_err("failed to get event_update cpus\n"); default: break; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 51cf566a0835..a1bc0c5706a3 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -114,6 +114,9 @@ int perf_event__synthesize_event_update_scale(struct perf_tool *tool, int perf_event__synthesize_event_update_name(struct perf_tool *tool, struct perf_evsel *evsel, perf_event__handler_t process); +int perf_event__synthesize_event_update_cpus(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process); int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist); int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, From c853f9394b7bc189632673cac802bdbf6537463b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:41 +0100 Subject: [PATCH 23/43] perf tools: Add perf_event__fprintf_event_update function To display a 'event update' event for raw dump. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-26-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 38 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/header.h | 1 + 2 files changed, 39 insertions(+) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 79d3eb984db7..49676c14c8ae 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2793,6 +2793,44 @@ perf_event__synthesize_event_update_cpus(struct perf_tool *tool, return err; } +size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp) +{ + struct event_update_event *ev = &event->event_update; + struct event_update_event_scale *ev_scale; + struct event_update_event_cpus *ev_cpus; + struct cpu_map *map; + size_t ret; + + ret = fprintf(fp, "\n... id: %" PRIu64 "\n", ev->id); + + switch (ev->type) { + case PERF_EVENT_UPDATE__SCALE: + ev_scale = (struct event_update_event_scale *) ev->data; + ret += fprintf(fp, "... scale: %f\n", ev_scale->scale); + break; + case PERF_EVENT_UPDATE__UNIT: + ret += fprintf(fp, "... unit: %s\n", ev->data); + break; + case PERF_EVENT_UPDATE__NAME: + ret += fprintf(fp, "... name: %s\n", ev->data); + break; + case PERF_EVENT_UPDATE__CPUS: + ev_cpus = (struct event_update_event_cpus *) ev->data; + ret += fprintf(fp, "... "); + + map = cpu_map__new_data(&ev_cpus->cpus); + if (map) + ret += cpu_map__fprintf(map, fp); + else + ret += fprintf(fp, "failed to get cpus\n"); + break; + default: + ret += fprintf(fp, "... unknown type\n"); + break; + } + + return ret; +} int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index a1bc0c5706a3..710deecf8f80 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -122,6 +122,7 @@ int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_evlist **pevlist); +size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp); int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, struct perf_evlist *evlist, From 2d2aea6ae736503d3896c4997b494760ed8febc1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:42 +0100 Subject: [PATCH 24/43] perf report: Display newly added events in raw dump The 'perf report -D' command will now display detailed output for these newly added events: event_update thread_map cpu_map stat stat_config stat_round Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-27-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 49e5cdc4cc5a..a90c74b67e43 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -17,6 +17,7 @@ #include "asm/bug.h" #include "auxtrace.h" #include "thread-stack.h" +#include "stat.h" static int perf_session__deliver_event(struct perf_session *session, union perf_event *event, @@ -210,6 +211,9 @@ static int process_event_synth_event_update_stub(struct perf_tool *tool __maybe_ struct perf_evlist **pevlist __maybe_unused) { + if (dump_trace) + perf_event__fprintf_event_update(event, stdout); + dump_printf(": unhandled!\n"); return 0; } @@ -311,6 +315,9 @@ int process_event_thread_map_stub(struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_session *session __maybe_unused) { + if (dump_trace) + perf_event__fprintf_thread_map(event, stdout); + dump_printf(": unhandled!\n"); return 0; } @@ -320,6 +327,9 @@ int process_event_cpu_map_stub(struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_session *session __maybe_unused) { + if (dump_trace) + perf_event__fprintf_cpu_map(event, stdout); + dump_printf(": unhandled!\n"); return 0; } @@ -329,6 +339,9 @@ int process_event_stat_config_stub(struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_session *session __maybe_unused) { + if (dump_trace) + perf_event__fprintf_stat_config(event, stdout); + dump_printf(": unhandled!\n"); return 0; } @@ -338,6 +351,9 @@ static int process_stat_stub(struct perf_tool *tool __maybe_unused, struct perf_session *perf_session __maybe_unused) { + if (dump_trace) + perf_event__fprintf_stat(event, stdout); + dump_printf(": unhandled!\n"); return 0; } @@ -347,6 +363,9 @@ static int process_stat_round_stub(struct perf_tool *tool __maybe_unused, struct perf_session *perf_session __maybe_unused) { + if (dump_trace) + perf_event__fprintf_stat_round(event, stdout); + dump_printf(": unhandled!\n"); return 0; } From ffa517adf625fa6a6c168285534e1ff7344fa2f1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:43 +0100 Subject: [PATCH 25/43] perf tools: Introduce stat perf.data header feature Introducing the 'stat' feature to mark a perf.data as created by the 'perf stat record' command. It contains no data. It's needed so that the report tools (report/script) can differentiate sampling data from counting data, because they need to be treated in a different way. In the future it might be used to store the version of the stat storage system used. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-28-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 2 ++ tools/perf/util/header.c | 14 ++++++++++++++ tools/perf/util/header.h | 1 + 3 files changed, 17 insertions(+) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 1435ef6265b6..9c5cdc2c4471 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -452,6 +452,8 @@ static void record__init_features(struct record *rec) if (!rec->opts.full_auxtrace) perf_header__clear_feat(&session->header, HEADER_AUXTRACE); + + perf_header__clear_feat(&session->header, HEADER_STAT); } static volatile int workload_exec_errno; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 49676c14c8ae..f50b7235ecb6 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -868,6 +868,13 @@ static int write_auxtrace(int fd, struct perf_header *h, return err; } +static int write_stat(int fd __maybe_unused, + struct perf_header *h __maybe_unused, + struct perf_evlist *evlist __maybe_unused) +{ + return 0; +} + static void print_hostname(struct perf_header *ph, int fd __maybe_unused, FILE *fp) { @@ -1159,6 +1166,12 @@ static void print_auxtrace(struct perf_header *ph __maybe_unused, fprintf(fp, "# contains AUX area data (e.g. instruction trace)\n"); } +static void print_stat(struct perf_header *ph __maybe_unused, + int fd __maybe_unused, FILE *fp) +{ + fprintf(fp, "# contains stat data\n"); +} + static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, FILE *fp) { @@ -1948,6 +1961,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), FEAT_OPP(HEADER_GROUP_DESC, group_desc), FEAT_OPP(HEADER_AUXTRACE, auxtrace), + FEAT_OPA(HEADER_STAT, stat), }; struct header_print_data { diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 710deecf8f80..cff9892452ee 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -31,6 +31,7 @@ enum { HEADER_PMU_MAPPINGS, HEADER_GROUP_DESC, HEADER_AUXTRACE, + HEADER_STAT, HEADER_LAST_FEATURE, HEADER_FEAT_BITS = 256, }; From 4979d0c7d0c73a3e799d4dcfbacd3cd11cc55638 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:46 +0100 Subject: [PATCH 26/43] perf stat record: Add record command Add 'perf stat record' command support. It creates simple (header only) perf.data file ATM. The record command could be specified anywhere among stat options. All stat command options are valid for stat record command with '-o' option exception. If specified for record command it denotes the perf data file name. Committer note: Set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless while avoiding that older tools show confusing messages, for instance, with sample_type = 0, we get: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.630237 task-clock (msec) # 0.528 CPUs utilized 1 context-switches # 0.002 M/sec 0 cpu-migrations # 0.000 K/sec 52 page-faults # 0.083 M/sec 978,312 cycles # 1.552 GHz 671,931 stalled-cycles-frontend # 68.68% frontend cycles idle stalled-cycles-backend 646,379 instructions # 0.66 insns per cycle # 1.04 stalled cycles per insn 131,046 branches # 207.931 M/sec 7,073 branch-misses # 5.40% of all branches 0.001193240 seconds time elapsed $ oldperf evlist WARNING: The perf.data file's data size field is 0 which is unexpected. Was the 'perf record' command properly terminated? non matching sample_type $ While with sample_type set to PERF_SAMPLE_IDENTIFIER, after we re-run 'perf stat record usleep' we get: $ oldperf evlist WARNING: The perf.data file's data size field is 0 which is unexpected. Was the 'perf record' command properly terminated? task-clock context-switches cpu-migrations page-faults cycles stalled-cycles-frontend stalled-cycles-backend instructions branches branch-misses $ Which at least shows the names of the events in the perf.data file. Additionally, such files, when passed to 'perf report' will produce: $ oldperf report --stdio WARNING: The perf.data file's data size field is 0 which is unexpected. Was the 'perf record' command properly terminated? Warning: Kernel address maps (/proc/{kallsyms,modules}) were restricted. Check /proc/sys/kernel/kptr_restrict before running 'perf record'. As no suitable kallsyms nor vmlinux was found, kernel samples can't be resolved. Samples in kernel modules can't be resolved as well. Error: The perf.data file has no samples! # To display the perf.data header info, please use --header/--header-only options. # $ Which is confusing and can be solved by just adding the kernel mmap record, which will also remove that warning about the data size field being equal to zero, after generating the mmap record: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.600796 task-clock (msec) # 0.478 CPUs utilized 1 context-switches # 0.002 M/sec 0 cpu-migrations # 0.000 K/sec 54 page-faults # 0.090 M/sec 886,844 cycles # 1.476 GHz 582,169 stalled-cycles-frontend # 65.65% frontend cycles idle stalled-cycles-backend 638,344 instructions # 0.72 insns per cycle # 0.91 stalled cycles per insn 130,204 branches # 216.719 M/sec 7,500 branch-misses # 5.76% of all branches 0.001255897 seconds time elapsed $ oldperf evlist task-clock context-switches cpu-migrations page-faults cycles stalled-cycles-frontend stalled-cycles-backend instructions branches branch-misses $ oldperf report --stdio Error: The perf.data file has no samples! # To display the perf.data header info, please use --header/--header-only options. # [acme@zoo linux]$ No warnings, sensible output about what are the events in the perf.data file and also a "file has no samples" message, which indeed it doesn't. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: htp://lkml.kernel.org/r/1446734469-11352-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 12 +++ tools/perf/builtin-stat.c | 120 ++++++++++++++++++++++++- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 4e074a660826..70eee1c2c444 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -10,6 +10,7 @@ SYNOPSIS [verse] 'perf stat' [-e | --event=EVENT] [-a] 'perf stat' [-e | --event=EVENT] [-a] -- [] +'perf stat' [-e | --event=EVENT] [-a] record [-o file] -- [] DESCRIPTION ----------- @@ -22,6 +23,8 @@ OPTIONS ...:: Any command you can specify in a shell. +record:: + See STAT RECORD. -e:: --event=:: @@ -159,6 +162,15 @@ filter out the startup phase of the program, which is often very different. Print statistics of transactional execution if supported. +STAT RECORD +----------- +Stores stat data into perf data file. + +-o file:: +--output file:: +Output file name. + + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index bbf42eefd5e5..af2a3bf659f7 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -59,6 +59,7 @@ #include "util/thread.h" #include "util/thread_map.h" #include "util/counts.h" +#include "util/session.h" #include #include @@ -126,6 +127,16 @@ static bool append_file; static const char *output_name; static int output_fd; +struct perf_stat { + bool record; + struct perf_data_file file; + struct perf_session *session; + u64 bytes_written; +}; + +static struct perf_stat perf_stat; +#define STAT_RECORD perf_stat.record + static volatile int done = 0; static struct perf_stat_config stat_config = { @@ -166,7 +177,11 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) * like tracepoints. Clear it up for counting. */ attr->sample_period = 0; - attr->sample_type = 0; + /* + * But set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless + * while avoiding that older tools show confusing messages. + */ + attr->sample_type = PERF_SAMPLE_IDENTIFIER; /* * Disabling all counters initially, they will be enabled @@ -202,6 +217,26 @@ static inline int nsec_counter(struct perf_evsel *evsel) return 0; } +static int perf_stat__write(struct perf_stat *stat, void *bf, size_t size) +{ + if (perf_data_file__write(stat->session->file, bf, size) < 0) { + pr_err("failed to write perf data, error: %m\n"); + return -1; + } + + stat->bytes_written += size; + return 0; +} + +static int process_synthesized_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct perf_stat *stat = (void *)tool; + return perf_stat__write(stat, event, event->header.size); +} + /* * Read out the results of a single counter: * do not aggregate counts across CPUs in system-wide mode @@ -361,6 +396,15 @@ static int __run_perf_stat(int argc, const char **argv) return -1; } + if (STAT_RECORD) { + int err, fd = perf_data_file__fd(&perf_stat.file); + + err = perf_session__write_header(perf_stat.session, evsel_list, + fd, false); + if (err < 0) + return err; + } + /* * Enable counters and exec the command: */ @@ -1261,6 +1305,38 @@ static int add_default_attributes(void) return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs); } +static const char * const recort_usage[] = { + "perf stat record []", + NULL, +}; + +static int __cmd_record(int argc, const char **argv) +{ + struct perf_session *session; + struct perf_data_file *file = &perf_stat.file; + + argc = parse_options(argc, argv, stat_options, record_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (output_name) + file->path = output_name; + + session = perf_session__new(file, false, NULL); + if (session == NULL) { + pr_err("Perf session creation failed.\n"); + return -1; + } + + /* No pipe support ATM */ + if (perf_stat.file.is_pipe) + return -EINVAL; + + session->evlist = evsel_list; + perf_stat.session = session; + perf_stat.record = true; + return argc; +} + int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) { const char * const stat_usage[] = { @@ -1271,6 +1347,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) const char *mode; FILE *output = stderr; unsigned int interval; + const char * const stat_subcommands[] = { "record" }; setlocale(LC_ALL, ""); @@ -1278,12 +1355,22 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (evsel_list == NULL) return -ENOMEM; - argc = parse_options(argc, argv, stat_options, stat_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, + (const char **) stat_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (argc && !strncmp(argv[0], "rec", 3)) { + argc = __cmd_record(argc, argv); + if (argc < 0) + return -1; + } interval = stat_config.interval; - if (output_name && strcmp(output_name, "-")) + /* + * For record command the -o is already taken care of. + */ + if (!STAT_RECORD && output_name && strcmp(output_name, "-")) output = NULL; if (output_name && output_fd) { @@ -1450,6 +1537,31 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (!forever && status != -1 && !interval) print_counters(NULL, argc, argv); + if (STAT_RECORD) { + /* + * We synthesize the kernel mmap record just so that older tools + * don't emit warnings about not being able to resolve symbols + * due to /proc/sys/kernel/kptr_restrict settings and instear provide + * a saner message about no samples being in the perf.data file. + * + * This also serves to suppress a warning about f_header.data.size == 0 + * in header.c. -acme + */ + int fd = perf_data_file__fd(&perf_stat.file); + int err = perf_event__synthesize_kernel_mmap((void *)&perf_stat, + process_synthesized_event, + &perf_stat.session->machines.host); + if (err) { + pr_warning("Couldn't synthesize the kernel mmap record, harmless, " + "older tools may produce warnings about this file\n."); + } + + perf_stat.session->header.data_size += perf_stat.bytes_written; + perf_session__write_header(perf_stat.session, evsel_list, fd, true); + + perf_session__delete(perf_stat.session); + } + perf_stat__exit_aggr_mode(); perf_evlist__free_stats(evsel_list); out: From 3ba78bd00e508bf46a6aa2b8e296dc8287ea4c29 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:47 +0100 Subject: [PATCH 27/43] perf stat record: Initialize record features Disabling all non stat related features. Also as we now enable STAT feature in the data file, adding code to instruct session open to skip sample type checking for stat data files. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-4-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 15 +++++++++++++++ tools/perf/util/session.c | 3 +++ 2 files changed, 18 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index af2a3bf659f7..c9c896a94fee 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1310,6 +1310,19 @@ static const char * const recort_usage[] = { NULL, }; +static void init_features(struct perf_session *session) +{ + int feat; + + for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) + perf_header__set_feat(&session->header, feat); + + perf_header__clear_feat(&session->header, HEADER_BUILD_ID); + perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); + perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); + perf_header__clear_feat(&session->header, HEADER_AUXTRACE); +} + static int __cmd_record(int argc, const char **argv) { struct perf_session *session; @@ -1331,6 +1344,8 @@ static int __cmd_record(int argc, const char **argv) if (perf_stat.file.is_pipe) return -EINVAL; + init_features(session); + session->evlist = evsel_list; perf_stat.session = session; perf_stat.record = true; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index a90c74b67e43..d5636ba94b20 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -37,6 +37,9 @@ static int perf_session__open(struct perf_session *session) if (perf_data_file__is_pipe(file)) return 0; + if (perf_header__has_feat(&session->header, HEADER_STAT)) + return 0; + if (!perf_evlist__valid_sample_type(session->evlist)) { pr_err("non matching sample_type\n"); return -1; From 8b99b1a4e0b082ea6a277766982dac84483d4d3c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:48 +0100 Subject: [PATCH 28/43] perf stat record: Synthesize stat record data Synthesizing needed stat record data for report/script: - cpu/thread maps - stat config Committer note: New records generated on a perf.data file with this patch: $ perf report -D | grep PERF_RECORD_ 0x568 [0x28]: PERF_RECORD_THREAD_MAP nr: 1 thread: 29097 0x590 [0x12]: PERF_RECORD_CPU_MAP nr: 1 cpu: 65535 0x5a2 [0x40]: PERF_RECORD_STAT_CONFIG $ Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-5-git-send-email-jolsa@kernel.org [ Adjusted wrt kernel PERF_RECORD_MMAP added when introducing 'perf stat record' ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 60 +++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c9c896a94fee..45bf4d2caebe 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -217,24 +217,18 @@ static inline int nsec_counter(struct perf_evsel *evsel) return 0; } -static int perf_stat__write(struct perf_stat *stat, void *bf, size_t size) -{ - if (perf_data_file__write(stat->session->file, bf, size) < 0) { - pr_err("failed to write perf data, error: %m\n"); - return -1; - } - - stat->bytes_written += size; - return 0; -} - -static int process_synthesized_event(struct perf_tool *tool, +static int process_synthesized_event(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { - struct perf_stat *stat = (void *)tool; - return perf_stat__write(stat, event, event->header.size); + if (perf_data_file__write(&perf_stat.file, event, event->header.size) < 0) { + pr_err("failed to write perf data, error: %m\n"); + return -1; + } + + perf_stat.bytes_written += event->header.size; + return 0; } /* @@ -323,6 +317,35 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf workload_exec_errno = info->si_value.sival_int; } +static int perf_stat_synthesize_config(void) +{ + int err; + + err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, + process_synthesized_event, + NULL); + if (err < 0) { + pr_err("Couldn't synthesize thread map.\n"); + return err; + } + + err = perf_event__synthesize_cpu_map(NULL, evsel_list->cpus, + process_synthesized_event, NULL); + if (err < 0) { + pr_err("Couldn't synthesize thread map.\n"); + return err; + } + + err = perf_event__synthesize_stat_config(NULL, &stat_config, + process_synthesized_event, NULL); + if (err < 0) { + pr_err("Couldn't synthesize config.\n"); + return err; + } + + return 0; +} + static int __run_perf_stat(int argc, const char **argv) { int interval = stat_config.interval; @@ -403,6 +426,10 @@ static int __run_perf_stat(int argc, const char **argv) fd, false); if (err < 0) return err; + + err = perf_stat_synthesize_config(); + if (err < 0) + return err; } /* @@ -1560,7 +1587,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) * a saner message about no samples being in the perf.data file. * * This also serves to suppress a warning about f_header.data.size == 0 - * in header.c. -acme + * in header.c at the moment 'perf stat record' gets introduced, which + * is not really needed once we start adding the stat specific PERF_RECORD_ + * records, but the need to suppress the kptr_restrict messages in older + * tools remain -acme */ int fd = perf_data_file__fd(&perf_stat.file); int err = perf_event__synthesize_kernel_mmap((void *)&perf_stat, From 1c59612de0264790698e32eb0368daf3fcba4c65 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:49 +0100 Subject: [PATCH 29/43] perf evlist: Export id_add_fd() Will be used to storing the event IDs in evlist object so it get stored into perf.data file. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-6-git-send-email-jolsa@kernel.org [ Split from the patch storing the ids in the perf.data file ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 6 +++--- tools/perf/util/evlist.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 8c44aadb9810..b9eac0daa0b9 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -534,9 +534,9 @@ void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, evsel->id[evsel->ids++] = id; } -static int perf_evlist__id_add_fd(struct perf_evlist *evlist, - struct perf_evsel *evsel, - int cpu, int thread, int fd) +int perf_evlist__id_add_fd(struct perf_evlist *evlist, + struct perf_evsel *evsel, + int cpu, int thread, int fd) { u64 read_data[4] = { 0, }; int id_idx = 1; /* The first entry is the counter value */ diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index a459fe71b452..139a50038097 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -97,6 +97,9 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, int cpu, int thread, u64 id); +int perf_evlist__id_add_fd(struct perf_evlist *evlist, + struct perf_evsel *evsel, + int cpu, int thread, int fd); int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); From 2af4646d1041ee590b0032d2b0103fa81aa43174 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:49 +0100 Subject: [PATCH 30/43] perf stat record: Store events IDs in perf data file Store event IDs in evlist object so it get stored into perf.data file. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-6-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 45bf4d2caebe..39d0c30f0f59 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -346,6 +346,38 @@ static int perf_stat_synthesize_config(void) return 0; } +#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) + +static int __store_counter_ids(struct perf_evsel *counter, + struct cpu_map *cpus, + struct thread_map *threads) +{ + int cpu, thread; + + for (cpu = 0; cpu < cpus->nr; cpu++) { + for (thread = 0; thread < threads->nr; thread++) { + int fd = FD(counter, cpu, thread); + + if (perf_evlist__id_add_fd(evsel_list, counter, + cpu, thread, fd) < 0) + return -1; + } + } + + return 0; +} + +static int store_counter_ids(struct perf_evsel *counter) +{ + struct cpu_map *cpus = counter->cpus; + struct thread_map *threads = counter->threads; + + if (perf_evsel__alloc_id(counter, cpus->nr, threads->nr)) + return -ENOMEM; + + return __store_counter_ids(counter, cpus, threads); +} + static int __run_perf_stat(int argc, const char **argv) { int interval = stat_config.interval; @@ -410,6 +442,9 @@ static int __run_perf_stat(int argc, const char **argv) l = strlen(counter->unit); if (l > unit_width) unit_width = l; + + if (STAT_RECORD && store_counter_ids(counter)) + return -1; } if (perf_evlist__apply_filters(evsel_list, &counter)) { From 664c98d4e1c2ff60627d78d4c8ae81cd2df13783 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:50 +0100 Subject: [PATCH 31/43] perf stat record: Add pipe support for record command Allowing storing stat record data into pipe, so report tools (report/script) could read data directly from record. Committer note: Before this patch: $ perf stat record -o - usleep 1 | perf report -i - incompatible file format (rerun with -v to learn more) $ perf stat record -o - usleep 1 | perf script -i - incompatible file format (rerun with -v to learn more) $ ls -la perf.data ls: cannot access perf.data: No such file or directory $ After: $ perf stat record -o - usleep 1 | perf report -i - # To display the perf.data header info, please use # --header/--header-only options. # Error: The - file has no samples! $ perf stat record -o - usleep 1 | perf script -i - Display of symbols requested but neither sample IP nor sample address is selected. Hence, no addresses to convert to symbols. 0 [0x80]: failed to process type: 64 $ ls -la perf.data ls: cannot access perf.data: No such file or directory $ Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-7-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 39d0c30f0f59..8a2f9ce677e7 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -317,10 +317,19 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf workload_exec_errno = info->si_value.sival_int; } -static int perf_stat_synthesize_config(void) +static int perf_stat_synthesize_config(bool is_pipe) { int err; + if (is_pipe) { + err = perf_event__synthesize_attrs(NULL, perf_stat.session, + process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize attrs.\n"); + return err; + } + } + err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, process_synthesized_event, NULL); @@ -388,6 +397,7 @@ static int __run_perf_stat(int argc, const char **argv) size_t l; int status = 0; const bool forks = (argc > 0); + bool is_pipe = STAT_RECORD ? perf_stat.file.is_pipe : false; if (interval) { ts.tv_sec = interval / 1000; @@ -398,7 +408,7 @@ static int __run_perf_stat(int argc, const char **argv) } if (forks) { - if (perf_evlist__prepare_workload(evsel_list, &target, argv, false, + if (perf_evlist__prepare_workload(evsel_list, &target, argv, is_pipe, workload_exec_failed_signal) < 0) { perror("failed to prepare workload"); return -1; @@ -457,12 +467,17 @@ static int __run_perf_stat(int argc, const char **argv) if (STAT_RECORD) { int err, fd = perf_data_file__fd(&perf_stat.file); - err = perf_session__write_header(perf_stat.session, evsel_list, - fd, false); + if (is_pipe) { + err = perf_header__write_pipe(perf_data_file__fd(&perf_stat.file)); + } else { + err = perf_session__write_header(perf_stat.session, evsel_list, + fd, false); + } + if (err < 0) return err; - err = perf_stat_synthesize_config(); + err = perf_stat_synthesize_config(is_pipe); if (err < 0) return err; } @@ -970,6 +985,10 @@ static void print_counters(struct timespec *ts, int argc, const char **argv) struct perf_evsel *counter; char buf[64], *prefix = NULL; + /* Do not print anything if we record to the pipe. */ + if (STAT_RECORD && perf_stat.file.is_pipe) + return; + if (interval) print_interval(prefix = buf, ts); else @@ -1402,10 +1421,6 @@ static int __cmd_record(int argc, const char **argv) return -1; } - /* No pipe support ATM */ - if (perf_stat.file.is_pipe) - return -EINVAL; - init_features(session); session->evlist = evsel_list; @@ -1636,8 +1651,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) "older tools may produce warnings about this file\n."); } - perf_stat.session->header.data_size += perf_stat.bytes_written; - perf_session__write_header(perf_stat.session, evsel_list, fd, true); + if (!perf_stat.file.is_pipe) { + perf_stat.session->header.data_size += perf_stat.bytes_written; + perf_session__write_header(perf_stat.session, evsel_list, fd, true); + } perf_session__delete(perf_stat.session); } From 5a6ea81b8f9ce2736759d256ac4d63be65751199 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:51 +0100 Subject: [PATCH 32/43] perf stat record: Write stat events on record Writing stat events on 'perf stat record' at the time we read counter values from kernel. Committer note: After the patch: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.598006 task-clock (msec) # 0.484 CPUs utilized 1 context-switches # 0.002 M/sec 0 cpu-migrations # 0.000 K/sec 52 page-faults # 0.087 M/sec 882,744 cycles # 1.476 GHz 581,416 stalled-cycles-frontend # 65.86% frontend cycles idle stalled-cycles-backend 636,479 instructions # 0.72 insns per cycle # 0.91 stalled cycles per insn 129,334 branches # 216.275 M/sec 7,512 branch-misses # 5.81% of all branches 0.001235157 seconds time elapsed $ oldperf evlist task-clock context-switches cpu-migrations page-faults cycles stalled-cycles-frontend stalled-cycles-backend instructions branches branch-misses $ oldperf report --stdio Error: The perf.data file has no samples! # To display the perf.data header info, please use --header/--header-only options. # $ perf report -D | grep PERF_RECORD 0x5b0 [0x28]: PERF_RECORD_THREAD_MAP nr: 1 thread: 5504 0x5d8 [0x12]: PERF_RECORD_CPU_MAP nr: 1 cpu: 65535 0x5ea [0x40]: PERF_RECORD_STAT_CONFIG 0x62a [0x30]: PERF_RECORD_STAT 0x65a [0x30]: PERF_RECORD_STAT 0x68a [0x30]: PERF_RECORD_STAT 0x6ba [0x30]: PERF_RECORD_STAT 0x6ea [0x30]: PERF_RECORD_STAT 0x71a [0x30]: PERF_RECORD_STAT 0x74a [0x30]: PERF_RECORD_STAT 0x77a [0x30]: PERF_RECORD_STAT 0x7aa [0x30]: PERF_RECORD_STAT -1 -1 0x7da [0x40]: PERF_RECORD_MMAP -1/0: [0xffffffff81000000(0x1f000000) @ 0xffffffff81000000]: x [kernel.kallsyms]_text $ Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-8-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 8a2f9ce677e7..32aa2ea1c553 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -231,6 +231,18 @@ static int process_synthesized_event(struct perf_tool *tool __maybe_unused, return 0; } +#define SID(e, x, y) xyarray__entry(e->sample_id, x, y) + +static int +perf_evsel__write_stat_event(struct perf_evsel *counter, u32 cpu, u32 thread, + struct perf_counts_values *count) +{ + struct perf_sample_id *sid = SID(counter, cpu, thread); + + return perf_event__synthesize_stat(NULL, cpu, thread, sid->id, count, + process_synthesized_event, NULL); +} + /* * Read out the results of a single counter: * do not aggregate counts across CPUs in system-wide mode @@ -254,6 +266,13 @@ static int read_counter(struct perf_evsel *counter) count = perf_counts(counter->counts, cpu, thread); if (perf_evsel__read(counter, cpu, thread, count)) return -1; + + if (STAT_RECORD) { + if (perf_evsel__write_stat_event(counter, cpu, thread, count)) { + pr_err("failed to write stat event\n"); + return -1; + } + } } } From 7aad0c32bb6aaa39aab596264ddc49d44c8088f3 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:52 +0100 Subject: [PATCH 33/43] perf stat record: Write stat round events on record Writing stat round events on 'perf stat record' for each interval round. In non interval mode we store round event after the last stat event. Committer note: After the patch: $ perf report -D | grep PERF_RECORD | grep ROUND 0x852 [0x18]: PERF_RECORD_STAT_ROUND $ Reported-by: Kan Liang Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-9-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 32aa2ea1c553..fcece42c2611 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -231,6 +231,16 @@ static int process_synthesized_event(struct perf_tool *tool __maybe_unused, return 0; } +static int write_stat_round_event(u64 time, u64 type) +{ + return perf_event__synthesize_stat_round(NULL, time, type, + process_synthesized_event, + NULL); +} + +#define WRITE_STAT_ROUND_EVENT(time, interval) \ + write_stat_round_event(time, PERF_STAT_ROUND_TYPE__ ## interval) + #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) static int @@ -306,6 +316,11 @@ static void process_interval(void) clock_gettime(CLOCK_MONOTONIC, &ts); diff_timespec(&rs, &ts, &ref_time); + if (STAT_RECORD) { + if (WRITE_STAT_ROUND_EVENT(rs.tv_sec * NSECS_PER_SEC + rs.tv_nsec, INTERVAL)) + pr_err("failed to write stat round event\n"); + } + print_counters(&rs, 0, NULL); } @@ -1670,6 +1685,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) "older tools may produce warnings about this file\n."); } + if (!interval) { + if (WRITE_STAT_ROUND_EVENT(walltime_nsecs_stats.max, FINAL)) + pr_err("failed to write stat round event\n"); + } + if (!perf_stat.file.is_pipe) { perf_stat.session->header.data_size += perf_stat.bytes_written; perf_session__write_header(perf_stat.session, evsel_list, fd, true); From e9d6db8e8df42a38f79f264ab58c104e1678b12c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:53 +0100 Subject: [PATCH 34/43] perf stat record: Do not allow record with multiple runs mode We currently don't support storing multiple session in perf.data, so we can't allow -r option in stat record. $ perf stat -e cycles -r 2 record ls Cannot use -r option with perf stat record. Committer note: Before this patch we would a perf.data file such as: $ perf stat -e cycles -r 2 record ls Performance counter stats for 'ls' (2 runs): 3,935,236 cycles 0.002353261 seconds time elapsed ( +- 4.76% ) $ perf report -D | grep PERF_RECORD | grep ROUND 0xf0 [0]: failed to process type: 16 Error: failed to process sample $ Reported-by: Kan Liang Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-10-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index fcece42c2611..10f86a6d7b15 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1449,6 +1449,11 @@ static int __cmd_record(int argc, const char **argv) if (output_name) file->path = output_name; + if (run_count != 1 || forever) { + pr_err("Cannot use -r option with perf stat record.\n"); + return -1; + } + session = perf_session__new(file, false, NULL); if (session == NULL) { pr_err("Perf session creation failed.\n"); From 7b60a7e3a687481553d2b6ec7e6390a6e82f1849 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:54 +0100 Subject: [PATCH 35/43] perf stat record: Synthesize event update events Synthesize other events stuff not carried within attr event - unit, scale, name. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-11-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 10f86a6d7b15..575e2535ea03 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -351,8 +351,19 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf workload_exec_errno = info->si_value.sival_int; } +static bool has_unit(struct perf_evsel *counter) +{ + return counter->unit && *counter->unit; +} + +static bool has_scale(struct perf_evsel *counter) +{ + return counter->scale != 1; +} + static int perf_stat_synthesize_config(bool is_pipe) { + struct perf_evsel *counter; int err; if (is_pipe) { @@ -364,6 +375,54 @@ static int perf_stat_synthesize_config(bool is_pipe) } } + /* + * Synthesize other events stuff not carried within + * attr event - unit, scale, name + */ + evlist__for_each(evsel_list, counter) { + if (!counter->supported) + continue; + + /* + * Synthesize unit and scale only if it's defined. + */ + if (has_unit(counter)) { + err = perf_event__synthesize_event_update_unit(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel unit.\n"); + return err; + } + } + + if (has_scale(counter)) { + err = perf_event__synthesize_event_update_scale(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel scale.\n"); + return err; + } + } + + if (counter->own_cpus) { + err = perf_event__synthesize_event_update_cpus(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel scale.\n"); + return err; + } + } + + /* + * Name is needed only for pipe output, + * perf.data carries event names. + */ + if (is_pipe) { + err = perf_event__synthesize_event_update_name(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel name.\n"); + return err; + } + } + } + err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, process_synthesized_event, NULL); From ba6039b6c8fcc24de7d6ab7b0bada4becaf84a2c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:55 +0100 Subject: [PATCH 36/43] perf stat report: Add report command Adding 'perf stat report' command support. ATM it only processes attr events and display nothing. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-12-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 12 +++++ tools/perf/builtin-stat.c | 61 ++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 70eee1c2c444..95f492828657 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -11,6 +11,7 @@ SYNOPSIS 'perf stat' [-e | --event=EVENT] [-a] 'perf stat' [-e | --event=EVENT] [-a] -- [] 'perf stat' [-e | --event=EVENT] [-a] record [-o file] -- [] +'perf stat' report [-i file] DESCRIPTION ----------- @@ -26,6 +27,9 @@ OPTIONS record:: See STAT RECORD. +report:: + See STAT REPORT. + -e:: --event=:: Select the PMU event. Selection can be: @@ -170,6 +174,14 @@ Stores stat data into perf data file. --output file:: Output file name. +STAT REPORT +----------- +Reads and reports stat data from perf data file. + +-i file:: +--input file:: +Input file name. + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 575e2535ea03..abba49b847d2 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -60,6 +60,8 @@ #include "util/thread_map.h" #include "util/counts.h" #include "util/session.h" +#include "util/tool.h" +#include "asm/bug.h" #include #include @@ -132,6 +134,7 @@ struct perf_stat { struct perf_data_file file; struct perf_session *session; u64 bytes_written; + struct perf_tool tool; }; static struct perf_stat perf_stat; @@ -1041,8 +1044,8 @@ static void print_header(int argc, const char **argv) else if (target.cpu_list) fprintf(output, "\'CPU(s) %s", target.cpu_list); else if (!target__has_task(&target)) { - fprintf(output, "\'%s", argv[0]); - for (i = 1; i < argc; i++) + fprintf(output, "\'%s", argv ? argv[0] : "pipe"); + for (i = 1; argv && (i < argc); i++) fprintf(output, " %s", argv[i]); } else if (target.pid) fprintf(output, "process id \'%s", target.pid); @@ -1527,6 +1530,55 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static const char * const report_usage[] = { + "perf stat report []", + NULL, +}; + +static struct perf_stat perf_stat = { + .tool = { + .attr = perf_event__process_attr, + }, +}; + +static int __cmd_report(int argc, const char **argv) +{ + struct perf_session *session; + const struct option options[] = { + OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_END() + }; + struct stat st; + int ret; + + argc = parse_options(argc, argv, options, report_usage, 0); + + if (!input_name || !strlen(input_name)) { + if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) + input_name = "-"; + else + input_name = "perf.data"; + } + + perf_stat.file.path = input_name; + perf_stat.file.mode = PERF_DATA_MODE_READ; + + session = perf_session__new(&perf_stat.file, false, &perf_stat.tool); + if (session == NULL) + return -1; + + perf_stat.session = session; + stat_config.output = stderr; + evsel_list = session->evlist; + + ret = perf_session__process_events(session); + if (ret) + return ret; + + perf_session__delete(session); + return 0; +} + int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) { const char * const stat_usage[] = { @@ -1537,7 +1589,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) const char *mode; FILE *output = stderr; unsigned int interval; - const char * const stat_subcommands[] = { "record" }; + const char * const stat_subcommands[] = { "record", "report" }; setlocale(LC_ALL, ""); @@ -1553,7 +1605,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) argc = __cmd_record(argc, argv); if (argc < 0) return -1; - } + } else if (argc && !strncmp(argv[0], "rep", 3)) + return __cmd_report(argc, argv); interval = stat_config.interval; From 1975d36e14b3314d1d0c7a428946ec0c27fd6e95 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:56 +0100 Subject: [PATCH 37/43] perf stat report: Process cpu/threads maps Adding processing of cpu/threads maps. Configuring session's evlist with these maps. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-13-git-send-email-jolsa@kernel.org [ s/stat/st/g, s/time/tm/g parameters to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 66 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index abba49b847d2..0a1cfdd70df0 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -135,6 +135,9 @@ struct perf_stat { struct perf_session *session; u64 bytes_written; struct perf_tool tool; + bool maps_allocated; + struct cpu_map *cpus; + struct thread_map *threads; }; static struct perf_stat perf_stat; @@ -234,9 +237,9 @@ static int process_synthesized_event(struct perf_tool *tool __maybe_unused, return 0; } -static int write_stat_round_event(u64 time, u64 type) +static int write_stat_round_event(u64 tm, u64 type) { - return perf_event__synthesize_stat_round(NULL, time, type, + return perf_event__synthesize_stat_round(NULL, tm, type, process_synthesized_event, NULL); } @@ -1530,6 +1533,63 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static int set_maps(struct perf_stat *st) +{ + if (!st->cpus || !st->threads) + return 0; + + if (WARN_ONCE(st->maps_allocated, "stats double allocation\n")) + return -EINVAL; + + perf_evlist__set_maps(evsel_list, st->cpus, st->threads); + + if (perf_evlist__alloc_stats(evsel_list, true)) + return -ENOMEM; + + st->maps_allocated = true; + return 0; +} + +static +int process_thread_map_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + struct perf_stat *st = container_of(tool, struct perf_stat, tool); + + if (st->threads) { + pr_warning("Extra thread map event, ignoring.\n"); + return 0; + } + + st->threads = thread_map__new_event(&event->thread_map); + if (!st->threads) + return -ENOMEM; + + return set_maps(st); +} + +static +int process_cpu_map_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + struct perf_stat *st = container_of(tool, struct perf_stat, tool); + struct cpu_map *cpus; + + if (st->cpus) { + pr_warning("Extra cpu map event, ignoring.\n"); + return 0; + } + + cpus = cpu_map__new_data(&event->cpu_map.data); + if (!cpus) + return -ENOMEM; + + st->cpus = cpus; + return set_maps(st); +} + static const char * const report_usage[] = { "perf stat report []", NULL, @@ -1538,6 +1598,8 @@ static const char * const report_usage[] = { static struct perf_stat perf_stat = { .tool = { .attr = perf_event__process_attr, + .thread_map = process_thread_map_event, + .cpu_map = process_cpu_map_event, }, }; From 62ba18ba938a8740ab18e02342b282d7378986f7 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:57 +0100 Subject: [PATCH 38/43] perf stat report: Process stat config event Adding processing of stat config event and initialize stat_config object. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-14-git-send-email-jolsa@kernel.org [ Renamed 'stat' parameter to 'st' to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 0a1cfdd70df0..1e5db50dab9e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1533,6 +1533,15 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static +int process_stat_config_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + perf_event__read_stat_config(&stat_config, &event->stat_config); + return 0; +} + static int set_maps(struct perf_stat *st) { if (!st->cpus || !st->threads) @@ -1600,6 +1609,7 @@ static struct perf_stat perf_stat = { .attr = perf_event__process_attr, .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, + .stat_config = process_stat_config_event, }, }; From 68d702f7a1202dd39d9fa01b7bea92ba9e5785d9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:58 +0100 Subject: [PATCH 39/43] perf stat report: Add support to initialize aggr_map from file Using perf.data's perf_env data to initialize aggregate config. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-15-git-send-email-jolsa@kernel.org [ s/stat/st/g, s/socket/socket_id/g to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 103 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 1e5db50dab9e..c78052580023 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1326,6 +1326,101 @@ static void perf_stat__exit_aggr_mode(void) cpus_aggr_map = NULL; } +static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, int idx) +{ + int cpu; + + if (idx > map->nr) + return -1; + + cpu = map->map[idx]; + + if (cpu >= env->nr_cpus_online) + return -1; + + return cpu; +} + +static int perf_env__get_socket(struct cpu_map *map, int idx, void *data) +{ + struct perf_env *env = data; + int cpu = perf_env__get_cpu(env, map, idx); + + return cpu == -1 ? -1 : env->cpu[cpu].socket_id; +} + +static int perf_env__get_core(struct cpu_map *map, int idx, void *data) +{ + struct perf_env *env = data; + int core = -1, cpu = perf_env__get_cpu(env, map, idx); + + if (cpu != -1) { + int socket_id = env->cpu[cpu].socket_id; + + /* + * Encode socket in upper 16 bits + * core_id is relative to socket, and + * we need a global id. So we combine + * socket + core id. + */ + core = (socket_id << 16) | (env->cpu[cpu].core_id & 0xffff); + } + + return core; +} + +static int perf_env__build_socket_map(struct perf_env *env, struct cpu_map *cpus, + struct cpu_map **sockp) +{ + return cpu_map__build_map(cpus, sockp, perf_env__get_socket, env); +} + +static int perf_env__build_core_map(struct perf_env *env, struct cpu_map *cpus, + struct cpu_map **corep) +{ + return cpu_map__build_map(cpus, corep, perf_env__get_core, env); +} + +static int perf_stat__get_socket_file(struct cpu_map *map, int idx) +{ + return perf_env__get_socket(map, idx, &perf_stat.session->header.env); +} + +static int perf_stat__get_core_file(struct cpu_map *map, int idx) +{ + return perf_env__get_core(map, idx, &perf_stat.session->header.env); +} + +static int perf_stat_init_aggr_mode_file(struct perf_stat *st) +{ + struct perf_env *env = &st->session->header.env; + + switch (stat_config.aggr_mode) { + case AGGR_SOCKET: + if (perf_env__build_socket_map(env, evsel_list->cpus, &aggr_map)) { + perror("cannot build socket map"); + return -1; + } + aggr_get_id = perf_stat__get_socket_file; + break; + case AGGR_CORE: + if (perf_env__build_core_map(env, evsel_list->cpus, &aggr_map)) { + perror("cannot build core map"); + return -1; + } + aggr_get_id = perf_stat__get_core_file; + break; + case AGGR_NONE: + case AGGR_GLOBAL: + case AGGR_THREAD: + case AGGR_UNSET: + default: + break; + } + + return 0; +} + /* * Add default attributes, if there were no attributes specified or * if -d/--detailed, -d -d or -d -d -d is used: @@ -1538,7 +1633,15 @@ int process_stat_config_event(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_session *session __maybe_unused) { + struct perf_stat *st = container_of(tool, struct perf_stat, tool); + perf_event__read_stat_config(&stat_config, &event->stat_config); + + if (perf_stat.file.is_pipe) + perf_stat_init_aggr_mode(); + else + perf_stat_init_aggr_mode_file(st); + return 0; } From 6edb78a2178fd85d07b1a7fbb3629be56b860224 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:41:01 +0100 Subject: [PATCH 40/43] perf stat report: Move csv_sep initialization before report command So we have csv_sep properly initialized before report command leg. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-18-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c78052580023..f9d4e0963ac7 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1776,6 +1776,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) (const char **) stat_usage, PARSE_OPT_STOP_AT_NON_OPTION); + if (csv_sep) { + csv_output = true; + if (!strcmp(csv_sep, "\\t")) + csv_sep = "\t"; + } else + csv_sep = DEFAULT_SEPARATOR; + if (argc && !strncmp(argv[0], "rec", 3)) { argc = __cmd_record(argc, argv); if (argc < 0) @@ -1826,13 +1833,6 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) stat_config.output = output; - if (csv_sep) { - csv_output = true; - if (!strcmp(csv_sep, "\\t")) - csv_sep = "\t"; - } else - csv_sep = DEFAULT_SEPARATOR; - /* * let the spreadsheet do the pretty-printing */ From a56f9390aa9d9b1c782c3dbd5ca2c4245eb265fc Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:59 +0100 Subject: [PATCH 41/43] perf stat report: Process stat and stat round events Adding processing of stat and stat round events. The stat data com in stat events, using generic function process_stat_round_event to store data under perf_evsel object. The stat-round events comes each interval or as last event in non interval mode. The function process_stat_round_event process stored data for each perf_evsel object and print it out. Committer note: After this patch: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.498381 task-clock (msec) # 0.571 CPUs utilized 2 context-switches # 0.004 M/sec 0 cpu-migrations # 0.000 K/sec 149 page-faults # 0.299 M/sec 1,271,635 cycles # 2.552 GHz 928,712 stalled-cycles-frontend # 73.03% frontend cycles idle 663,286 stalled-cycles-backend # 52.16% backend cycles idle 792,614 instructions # 0.62 insns per cycle # 1.17 stalled cycles per insn 136,850 branches # 274.589 M/sec branch-misses (0.00%) 0.000873419 seconds time elapsed $ $ perf stat report Performance counter stats for '/home/acme/bin/perf stat record usleep 1': 0.498381 task-clock (msec) # 0.571 CPUs utilized 2 context-switches # 0.004 M/sec 0 cpu-migrations # 0.000 K/sec 149 page-faults # 0.299 M/sec 1,271,635 cycles # 2.552 GHz 928,712 stalled-cycles-frontend # 73.03% frontend cycles idle 663,286 stalled-cycles-backend # 52.16% backend cycles idle 792,614 instructions # 0.62 insns per cycle # 1.17 stalled cycles per insn 136,850 branches # 274.589 M/sec branch-misses (0.00%) 0.000873419 seconds time elapsed $ Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-16-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index f9d4e0963ac7..d27d1b921efa 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1628,6 +1628,32 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static int process_stat_round_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session) +{ + struct stat_round_event *round = &event->stat_round; + struct perf_evsel *counter; + struct timespec tsh, *ts = NULL; + const char **argv = session->header.env.cmdline_argv; + int argc = session->header.env.nr_cmdline; + + evlist__for_each(evsel_list, counter) + perf_stat_process_counter(&stat_config, counter); + + if (round->type == PERF_STAT_ROUND_TYPE__FINAL) + update_stats(&walltime_nsecs_stats, round->time); + + if (stat_config.interval && round->time) { + tsh.tv_sec = round->time / NSECS_PER_SEC; + tsh.tv_nsec = round->time % NSECS_PER_SEC; + ts = &tsh; + } + + print_counters(ts, argc, argv); + return 0; +} + static int process_stat_config_event(struct perf_tool *tool __maybe_unused, union perf_event *event, @@ -1713,6 +1739,8 @@ static struct perf_stat perf_stat = { .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, .stat_config = process_stat_config_event, + .stat = perf_event__process_stat_event, + .stat_round = process_stat_round_event, }, }; From fa6ea7817db3839b58d46649b7834320257e7702 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:41:00 +0100 Subject: [PATCH 42/43] perf stat report: Process event update events Adding processing of event update events, so perf stat report can store additional info for events - unit,scale,name. Committer note: Before: # perf stat record -e power/energy-cores/ -a ^C Performance counter stats for 'system wide': 77.41 Joules power/energy-cores/ 1.597176695 seconds time elapsed # perf stat report Performance counter stats for '/home/acme/bin/perf stat record -e power/energy-cores/ -a': 332,488,114,176 power/energy-cores/ 1.597176695 seconds time elapsed # After, using the same perf.data file generated in the "Before" case above: # perf stat report Performance counter stats for '/home/acme/bin/perf stat record -e power/energy-cores/ -a': 77.41 Joules power/energy-cores/ 1.597176695 seconds time elapsed # Reported-by: Kan Liang Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-17-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index d27d1b921efa..3ccf5a9dab33 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1736,6 +1736,7 @@ static const char * const report_usage[] = { static struct perf_stat perf_stat = { .tool = { .attr = perf_event__process_attr, + .event_update = perf_event__process_event_update, .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, .stat_config = process_stat_config_event, From 89af4e05c21d68f22e07fe66940ea675615a49ed Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:41:02 +0100 Subject: [PATCH 43/43] perf stat report: Allow to override aggr_mode Allowing to override record aggr_mode. It's possible to use perf stat like: $ perf stat report -A $ perf stat report --per-core $ perf stat report --per-socket To customize the recorded aggregate mode regardless what was used during the stat record command. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-19-git-send-email-jolsa@kernel.org [ Renamed 'stat' parameter to 'st' to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 10 ++++++++++ tools/perf/builtin-stat.c | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 95f492828657..52ef7a9d50aa 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -182,6 +182,16 @@ Reads and reports stat data from perf data file. --input file:: Input file name. +--per-socket:: +Aggregate counts per processor socket for system-wide mode measurements. + +--per-core:: +Aggregate counts per physical processor for system-wide mode measurements. + +-A:: +--no-aggr:: +Do not aggregate counts across all monitored CPUs. + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 3ccf5a9dab33..9805e03ab163 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -138,6 +138,7 @@ struct perf_stat { bool maps_allocated; struct cpu_map *cpus; struct thread_map *threads; + enum aggr_mode aggr_mode; }; static struct perf_stat perf_stat; @@ -1663,6 +1664,15 @@ int process_stat_config_event(struct perf_tool *tool __maybe_unused, perf_event__read_stat_config(&stat_config, &event->stat_config); + if (cpu_map__empty(st->cpus)) { + if (st->aggr_mode != AGGR_UNSET) + pr_warning("warning: processing task data, aggregation mode not set\n"); + return 0; + } + + if (st->aggr_mode != AGGR_UNSET) + stat_config.aggr_mode = st->aggr_mode; + if (perf_stat.file.is_pipe) perf_stat_init_aggr_mode(); else @@ -1743,6 +1753,7 @@ static struct perf_stat perf_stat = { .stat = perf_event__process_stat_event, .stat_round = process_stat_round_event, }, + .aggr_mode = AGGR_UNSET, }; static int __cmd_report(int argc, const char **argv) @@ -1750,6 +1761,12 @@ static int __cmd_report(int argc, const char **argv) struct perf_session *session; const struct option options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_SET_UINT(0, "per-socket", &perf_stat.aggr_mode, + "aggregate counts per processor socket", AGGR_SOCKET), + OPT_SET_UINT(0, "per-core", &perf_stat.aggr_mode, + "aggregate counts per physical processor core", AGGR_CORE), + OPT_SET_UINT('A', "no-aggr", &perf_stat.aggr_mode, + "disable CPU count aggregation", AGGR_NONE), OPT_END() }; struct stat st;