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 <davi...@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

Reply via email to