Messages in this thread Patch in this message | | | From | David Carrillo-Cisneros <> | Subject | [PATCH 7/7] perf tools: add feature header record to pipe-mode | Date | Wed, 17 May 2017 21:16:02 -0700 |
| |
Add header record types to pipe-mode, reusing the functions used in file-mode and leveraging the new struct feat_fd.
Add the perf_event__synthesize_feature event call back to process the new header records.
Before this patch:
$ perf record -o - -e cycles -c 100000 sleep 1 | perf report --stdio [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.000 MB - ] ...
After this patch: $ perf record -o - -e cycles -c 100000 sleep 1 | perf report --stdio [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.000 MB - ] # hostname : lphh7 # os release : 4.11.0-dbx-up_perf # perf version : 4.11.rc6.g6277c80 # arch : x86_64 # nrcpus online : 72 # nrcpus avail : 72 # cpudesc : Intel(R) Xeon(R) CPU E5-2696 v3 @ 2.30GHz # cpuid : GenuineIntel,6,63,2 # total memory : 263457192 kB # cmdline : /root/perf record -o - -e cycles -c 100000 sleep 1 # HEADER_CPU_TOPOLOGY info available, use -I to display # HEADER_NUMA_TOPOLOGY info available, use -I to display # pmu mappings: intel_bts = 6, uncore_imc_4 = 22, uncore_sbox_1 = 47, uncore_cbox_5 = 33, uncore_ha_0 = 16, uncore_cbox Percent | Source code & Disassembly of kcore for cycles (9 samples) ...
Signed-off-by: David Carrillo-Cisneros <davidcc@google.com> --- tools/perf/builtin-annotate.c | 1 + tools/perf/builtin-inject.c | 1 + tools/perf/builtin-record.c | 7 ++ tools/perf/builtin-report.c | 1 + tools/perf/util/event.c | 13 +++ tools/perf/util/event.h | 19 ++++ tools/perf/util/header.c | 224 ++++++++++++++++++++++++++++++++++-------- tools/perf/util/header.h | 9 ++ tools/perf/util/session.c | 12 +++ tools/perf/util/tool.h | 3 +- 10 files changed, 248 insertions(+), 42 deletions(-)
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ce44edc30c71..ffe28002dc4f 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -398,6 +398,7 @@ int cmd_annotate(int argc, const char **argv) .attr = perf_event__process_attr, .build_id = perf_event__process_build_id, .tracing_data = perf_event__process_tracing_data, + .feature = perf_event__process_feature, .ordered_events = true, .ordering_requires_timestamps = true, }, diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index ea8db38eedd1..2b8032908fb2 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -770,6 +770,7 @@ int cmd_inject(int argc, const char **argv) .finished_round = perf_event__repipe_oe_synth, .build_id = perf_event__repipe_op2_synth, .id_index = perf_event__repipe_op2_synth, + .feature = perf_event__repipe_op2_synth, }, .input_name = "-", .samples = LIST_HEAD_INIT(inject.samples), diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index ee7d0a82ccd0..a1bcb72b4195 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -799,6 +799,13 @@ static int record__synthesize(struct record *rec, bool tail) return 0; if (file->is_pipe) { + err = perf_event__synthesize_features( + tool, session, rec->evlist, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize features.\n"); + return err; + } + err = perf_event__synthesize_attrs(tool, session, process_synthesized_event); if (err < 0) { diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 08c90d65a252..5a6917410469 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -717,6 +717,7 @@ int cmd_report(int argc, const char **argv) .id_index = perf_event__process_id_index, .auxtrace_info = perf_event__process_auxtrace_info, .auxtrace = perf_event__process_auxtrace, + .feature = perf_event__process_feature, .ordered_events = true, .ordering_requires_timestamps = true, }, diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 142835c0ca0a..cef1322d2993 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -56,6 +56,19 @@ static const char *perf_event__names[] = { [PERF_RECORD_STAT_ROUND] = "STAT_ROUND", [PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE", [PERF_RECORD_TIME_CONV] = "TIME_CONV", + [PERF_RECORD_HEADER_HOSTNAME] = "HOSTNAME", + [PERF_RECORD_HEADER_OSRELEASE] = "OSRELEASE", + [PERF_RECORD_HEADER_VERSION] = "VERSION", + [PERF_RECORD_HEADER_ARCH] = "ARCH", + [PERF_RECORD_HEADER_NRCPUS] = "NRCPUS", + [PERF_RECORD_HEADER_CPUDESC] = "CPUDESC", + [PERF_RECORD_HEADER_CPUID] = "CPUID", + [PERF_RECORD_HEADER_TOTAL_MEM] = "TOTAL_MEM", + [PERF_RECORD_HEADER_CMDLINE] = "CMDLINE", + [PERF_RECORD_HEADER_EVENT_DESC] = "EVENT_DESC", + [PERF_RECORD_HEADER_CPU_TOPOLOGY] = "CPU_TOPOLOGY", + [PERF_RECORD_HEADER_NUMA_TOPOLOGY] = "NUMA_TOPOLOGY", + [PERF_RECORD_HEADER_PMU_MAPPINGS] = "PMU_MAPPINGS", }; static const char *perf_ns__names[] = { diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index db2de6413518..d404f50260f8 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -244,6 +244,19 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_STAT_ROUND = 77, PERF_RECORD_EVENT_UPDATE = 78, PERF_RECORD_TIME_CONV = 79, + PERF_RECORD_HEADER_HOSTNAME = 80, + PERF_RECORD_HEADER_OSRELEASE = 81, + PERF_RECORD_HEADER_VERSION = 82, + PERF_RECORD_HEADER_ARCH = 83, + PERF_RECORD_HEADER_NRCPUS = 84, + PERF_RECORD_HEADER_CPUDESC = 85, + PERF_RECORD_HEADER_CPUID = 86, + PERF_RECORD_HEADER_TOTAL_MEM = 87, + PERF_RECORD_HEADER_CMDLINE = 88, + PERF_RECORD_HEADER_EVENT_DESC = 89, + PERF_RECORD_HEADER_CPU_TOPOLOGY = 90, + PERF_RECORD_HEADER_NUMA_TOPOLOGY = 91, + PERF_RECORD_HEADER_PMU_MAPPINGS = 92, PERF_RECORD_HEADER_MAX }; @@ -488,6 +501,11 @@ struct time_conv_event { u64 time_zero; }; +struct feature_event { + struct perf_event_header header; + char data[]; /* size bytes of raw data specific to the feature */ +}; + union perf_event { struct perf_event_header header; struct mmap_event mmap; @@ -518,6 +536,7 @@ union perf_event { struct stat_event stat; struct stat_round_event stat_round; struct time_conv_event time_conv; + struct feature_event feat; }; void perf_event__print_totals(void); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 981f5e9685e2..f6ce7ca80c12 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -11,6 +11,7 @@ #include <linux/list.h> #include <linux/kernel.h> #include <linux/bitops.h> +#include <linux/stringify.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/utsname.h> @@ -32,6 +33,7 @@ #include "data.h" #include <api/fs/fs.h> #include "asm/bug.h" +#include "tool.h" #include "sane_ctype.h" @@ -2107,42 +2109,80 @@ struct feature_ops { int (*process)(struct feat_fd *fd, void *data); const char *name; bool full_only; + int record_type; }; -#define FEAT_OPA(n, func) \ - [n] = { .name = #n, .write = write_##func, .print = print_##func } -#define FEAT_OPP(n, func) \ - [n] = { .name = #n, .write = write_##func, .print = print_##func, \ - .process = process_##func } -#define FEAT_OPF(n, func) \ - [n] = { .name = #n, .write = write_##func, .print = print_##func, \ - .process = process_##func, .full_only = true } +#define FEAT_OPP(n, func, __full_only) \ + [HEADER_##n] = { \ + .name = __stringify(HEADER_##n), \ + .write = write_##func, \ + .print = print_##func, \ + .full_only = __full_only, \ + .process = process_##func, \ + .record_type = PERF_RECORD_HEADER_##n \ + } + +#define FEAT_OPN(n, func, __full_only) \ + [HEADER_##n] = { \ + .name = __stringify(HEADER_##n), \ + .write = write_##func, \ + .print = print_##func, \ + .full_only = __full_only, \ + .process = process_##func \ + } /* feature_ops not implemented: */ #define print_tracing_data NULL #define print_build_id NULL +#define process_branch_stack NULL +#define process_stat NULL + + static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { - FEAT_OPP(HEADER_TRACING_DATA, tracing_data), - FEAT_OPP(HEADER_BUILD_ID, build_id), - FEAT_OPP(HEADER_HOSTNAME, hostname), - FEAT_OPP(HEADER_OSRELEASE, osrelease), - FEAT_OPP(HEADER_VERSION, version), - FEAT_OPP(HEADER_ARCH, arch), - FEAT_OPP(HEADER_NRCPUS, nrcpus), - FEAT_OPP(HEADER_CPUDESC, cpudesc), - FEAT_OPP(HEADER_CPUID, cpuid), - FEAT_OPP(HEADER_TOTAL_MEM, total_mem), - FEAT_OPP(HEADER_EVENT_DESC, event_desc), - FEAT_OPP(HEADER_CMDLINE, cmdline), - FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), - FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), - FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), - FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), - FEAT_OPP(HEADER_GROUP_DESC, group_desc), - FEAT_OPP(HEADER_AUXTRACE, auxtrace), - FEAT_OPA(HEADER_STAT, stat), - FEAT_OPF(HEADER_CACHE, cache), + FEAT_OPP(TRACING_DATA, tracing_data, false), + FEAT_OPP(BUILD_ID, build_id, false), + FEAT_OPP(HOSTNAME, hostname, false), + FEAT_OPP(OSRELEASE, osrelease, false), + FEAT_OPP(VERSION, version, false), + FEAT_OPP(ARCH, arch, false), + FEAT_OPP(NRCPUS, nrcpus, false), + FEAT_OPP(CPUDESC, cpudesc, false), + FEAT_OPP(CPUID, cpuid, false), + FEAT_OPP(TOTAL_MEM, total_mem, false), + FEAT_OPP(EVENT_DESC, event_desc, false), + FEAT_OPP(CMDLINE, cmdline, false), + FEAT_OPP(CPU_TOPOLOGY, cpu_topology, true), + FEAT_OPP(NUMA_TOPOLOGY, numa_topology, true), + FEAT_OPN(BRANCH_STACK, branch_stack, false), + FEAT_OPP(PMU_MAPPINGS, pmu_mappings, false), + FEAT_OPN(GROUP_DESC, group_desc, false), + FEAT_OPN(AUXTRACE, auxtrace, false), + FEAT_OPN(STAT, stat, false), + FEAT_OPN(CACHE, cache, true), +}; + +/* + * we use a mapping table to go from record type to feature header + * because we have no guarantee that new record types may not be added + * after the feature header. + */ +#define REC2FEAT(a) [PERF_RECORD_HEADER_##a] = HEADER_##a + +static const int rec2feat[PERF_RECORD_HEADER_MAX] = { + REC2FEAT(HOSTNAME), + REC2FEAT(OSRELEASE), + REC2FEAT(VERSION), + REC2FEAT(ARCH), + REC2FEAT(NRCPUS), + REC2FEAT(CPUDESC), + REC2FEAT(CPUID), + REC2FEAT(TOTAL_MEM), + REC2FEAT(EVENT_DESC), + REC2FEAT(CMDLINE), + REC2FEAT(CPU_TOPOLOGY), + REC2FEAT(NUMA_TOPOLOGY), + REC2FEAT(PMU_MAPPINGS), }; struct header_print_data { @@ -2218,33 +2258,33 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) return 0; } -static int do_write_feat(struct feat_fd *fd, struct perf_header *h, int type, +static int do_write_feat(struct feat_fd *fdd, int type, struct perf_file_section **p, struct perf_evlist *evlist) { int err; int ret = 0; - if (perf_header__has_feat(h, type)) { + if (perf_header__has_feat(fdd->ph, type)) { if (!feat_ops[type].write) return -1; - if (fd->buf) { - pr_err("do_write_feat to memory buffer\n"); + if (fdd->buf) { + pr_debug("Called do_write_feat for memory buffer\n"); return -1; } - (*p)->offset = lseek(fd->fd, 0, SEEK_CUR); + (*p)->offset = lseek(fdd->fd, 0, SEEK_CUR); - err = feat_ops[type].write(fd, evlist); + err = feat_ops[type].write(fdd, evlist); if (err < 0) { pr_debug("failed to write feature %s\n", feat_ops[type].name); /* undo anything written */ - lseek(fd->fd, (*p)->offset, SEEK_SET); + lseek(fdd->fd, (*p)->offset, SEEK_SET); return -1; } - (*p)->size = lseek(fd->fd, 0, SEEK_CUR) - (*p)->offset; + (*p)->size = lseek(fdd->fd, 0, SEEK_CUR) - (*p)->offset; (*p)++; } return ret; @@ -2261,10 +2301,6 @@ static int perf_header__adds_write(struct perf_header *header, int feat; int err; - /* - * may write more than needed due to dropped feature, but - * this is okay, reader will skip the mising entries - */ fdd = (struct feat_fd){ .fd = fd, .ph = header, @@ -2284,7 +2320,7 @@ static int perf_header__adds_write(struct perf_header *header, lseek(fd, sec_start + sec_size, SEEK_SET); for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) { - if (do_write_feat(&fdd, header, feat, &p, evlist)) + if (do_write_feat(&fdd, feat, &p, evlist)) perf_header__clear_feat(header, feat); } @@ -2941,6 +2977,112 @@ int perf_event__synthesize_attr(struct perf_tool *tool, return err; } +int perf_event__synthesize_features(struct perf_tool *tool, + struct perf_session *session, + struct perf_evlist *evlist, + perf_event__handler_t process) +{ + struct perf_header *header = &session->header; + struct feat_fd fdd; + struct feature_event *fe; + size_t sz, sz_hdr; + int feat, ret; + + sz_hdr = sizeof(fe->header); + sz = sizeof(union perf_event); + /* get a nice alignment */ + sz = PERF_ALIGN(sz, getpagesize()); + + memset(&fdd, 0, sizeof(fdd)); + + fdd.buf = malloc(sz); + if (!fdd.buf) + return -ENOMEM; + + fdd.size = sz - sz_hdr; + + for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) { + /* + * those are already written as: + * - PERF_RECORD_HEADER_TRACING_DATA + * - PERF_RECORD_HEADER_BUILD_ID + */ + if (feat == HEADER_TRACING_DATA || feat == HEADER_BUILD_ID) + continue; + + if (!feat_ops[feat].record_type) { + pr_debug("No record type for header :%d\n", feat); + continue; + } + + fdd.offset = sizeof(*fe); + + ret = feat_ops[feat].write(&fdd, evlist); + if (ret || fdd.offset <= (ssize_t)sizeof(*fe)) { + pr_debug("Error writing feature\n"); + continue; + } + + /* fdd.buf may have changed due to realloc in do_write() */ + fe = fdd.buf; + memset(fe, 0, sizeof(*fe)); + + fe->header.type = feat_ops[feat].record_type; + fe->header.size = fdd.offset; + + process(tool, fdd.buf, NULL, NULL); + } + free(fdd.buf); + return 0; +} + +int perf_event__process_feature(struct perf_tool *tool, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + struct feat_fd fd = { .fd = 0 }; + int type = event->header.type; + int feat; + + if (type < 0 || type >= PERF_RECORD_HEADER_MAX) { + pr_warning("invalid record type %d\n", type); + return 0; + } + feat = rec2feat[type]; + if (feat == HEADER_RESERVED) + return -1; + + if (feat > HEADER_LAST_FEATURE) + return 0; + + if (!feat_ops[feat].process) + return 0; + + /* + * no print routine + */ + if (!feat_ops[feat].print) + return 0; + + fd.buf = (void *)event; + fd.buf += sizeof(event->header); + fd.size = event->header.size - sizeof(event->header); + fd.ph = &session->header; + + if (!feat_ops[feat].full_only || tool->show_full_info) { + if (feat_ops[feat].process) { + if (feat_ops[feat].process(&fd, NULL)) + return -1; + } + feat_ops[feat].print(&fd, stdout); + } else { + fprintf(stdout, "# %s info available, use -I to display\n", + feat_ops[feat].name); + } + + return 0; +} + static struct event_update_event * event_update_event__new(size_t size, u64 type, u64 id) { diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 86ab723cbee4..fbe1a4690627 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -102,6 +102,15 @@ int perf_header__process_sections(struct perf_header *header, int fd, int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); +int perf_event__synthesize_features(struct perf_tool *tool, + struct perf_session *session, + struct perf_evlist *evlist, + perf_event__handler_t process); + +int perf_event__process_feature(struct perf_tool *tool, + union perf_event *event, + struct perf_session *session); + int perf_event__synthesize_attr(struct perf_tool *tool, struct perf_event_attr *attr, u32 ids, u64 *id, perf_event__handler_t process); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 3041c6b98191..ef788b91239e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -256,6 +256,14 @@ static int process_event_stub(struct perf_tool *tool __maybe_unused, return 0; } +static int process_feature_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; +} + static int process_finished_round_stub(struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct ordered_events *oe __maybe_unused) @@ -427,6 +435,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->stat_round = process_stat_round_stub; if (tool->time_conv == NULL) tool->time_conv = process_event_op2_stub; + if (tool->feature == NULL) + tool->feature = process_feature_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -1370,6 +1380,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, case PERF_RECORD_TIME_CONV: session->time_conv = event->time_conv; return tool->time_conv(tool, event, session); + case PERF_RECORD_HEADER_HOSTNAME ... PERF_RECORD_HEADER_PMU_MAPPINGS: + return tool->feature(tool, event, session); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 795b2be7b51f..70cb36e6b098 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -63,7 +63,8 @@ struct perf_tool { cpu_map, stat_config, stat, - stat_round; + stat_round, + feature; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; -- 2.13.0.303.g4ebf302169-goog
| |