#include "symbol.h"
 #include "debug.h"
 #include "cpumap.h"
+#include "pmu.h"
 
 static bool no_buildid_cache = false;
 
        return ret;
 }
 
+/*
+ * File format:
+ *
+ * struct pmu_mappings {
+ *     u32     pmu_num;
+ *     struct pmu_map {
+ *             u32     type;
+ *             char    name[];
+ *     }[pmu_num];
+ * };
+ */
+
+static int write_pmu_mappings(int fd, struct perf_header *h __used,
+                             struct perf_evlist *evlist __used)
+{
+       struct perf_pmu *pmu = NULL;
+       off_t offset = lseek(fd, 0, SEEK_CUR);
+       __u32 pmu_num = 0;
+
+       /* write real pmu_num later */
+       do_write(fd, &pmu_num, sizeof(pmu_num));
+
+       while ((pmu = perf_pmu__scan(pmu))) {
+               if (!pmu->name)
+                       continue;
+               pmu_num++;
+               do_write(fd, &pmu->type, sizeof(pmu->type));
+               do_write_string(fd, pmu->name);
+       }
+
+       if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) {
+               /* discard all */
+               lseek(fd, offset, SEEK_SET);
+               return -1;
+       }
+
+       return 0;
+}
+
 /*
  * default get_cpuid(): nothing gets recorded
  * actual implementation must be in arch/$(ARCH)/util/header.c
        fprintf(fp, "# contains samples with branch stack\n");
 }
 
+static void print_pmu_mappings(struct perf_header *ph, int fd, FILE *fp)
+{
+       const char *delimiter = "# pmu mappings: ";
+       char *name;
+       int ret;
+       u32 pmu_num;
+       u32 type;
+
+       ret = read(fd, &pmu_num, sizeof(pmu_num));
+       if (ret != sizeof(pmu_num))
+               goto error;
+
+       if (!pmu_num) {
+               fprintf(fp, "# pmu mappings: not available\n");
+               return;
+       }
+
+       while (pmu_num) {
+               if (read(fd, &type, sizeof(type)) != sizeof(type))
+                       break;
+               name = do_read_string(fd, ph);
+               if (!name)
+                       break;
+               pmu_num--;
+               fprintf(fp, "%s%s = %" PRIu32, delimiter, name, type);
+               free(name);
+               delimiter = ", ";
+       }
+
+       fprintf(fp, "\n");
+
+       if (!pmu_num)
+               return;
+error:
+       fprintf(fp, "# pmu mappings: unable to read\n");
+}
+
 static int __event_process_build_id(struct build_id_event *bev,
                                    char *filename,
                                    struct perf_session *session)
        FEAT_OPF(HEADER_CPU_TOPOLOGY,   cpu_topology),
        FEAT_OPF(HEADER_NUMA_TOPOLOGY,  numa_topology),
        FEAT_OPA(HEADER_BRANCH_STACK,   branch_stack),
+       FEAT_OPA(HEADER_PMU_MAPPINGS,   pmu_mappings),
 };
 
 struct header_print_data {
 
 #include "pmu.h"
 #include "parse-events.h"
 
+#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
+
 int perf_pmu_parse(struct list_head *list, char *name);
 extern FILE *perf_pmu_in;
 
                return -1;
 
        snprintf(path, PATH_MAX,
-                "%s/bus/event_source/devices/%s/format", sysfs, name);
+                "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name);
 
        if (stat(path, &st) < 0)
                return 0;       /* no error if format does not exist */
                return -1;
 
        snprintf(path, PATH_MAX,
-                "%s/bus/event_source/devices/%s/type", sysfs, name);
+                "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name);
 
        if (stat(path, &st) < 0)
                return -1;
        return ret;
 }
 
+/* Add all pmus in sysfs to pmu list: */
+static void pmu_read_sysfs(void)
+{
+       char path[PATH_MAX];
+       const char *sysfs;
+       DIR *dir;
+       struct dirent *dent;
+
+       sysfs = sysfs_find_mountpoint();
+       if (!sysfs)
+               return;
+
+       snprintf(path, PATH_MAX,
+                "%s" EVENT_SOURCE_DEVICE_PATH, sysfs);
+
+       dir = opendir(path);
+       if (!dir)
+               return;
+
+       while ((dent = readdir(dir))) {
+               if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
+                       continue;
+               /* add to static LIST_HEAD(pmus): */
+               perf_pmu__find(dent->d_name);
+       }
+
+       closedir(dir);
+}
+
 static struct perf_pmu *pmu_lookup(char *name)
 {
        struct perf_pmu *pmu;
        return NULL;
 }
 
+struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)
+{
+       /*
+        * pmu iterator: If pmu is NULL, we start at the begin,
+        * otherwise return the next pmu. Returns NULL on end.
+        */
+       if (!pmu) {
+               pmu_read_sysfs();
+               pmu = list_prepare_entry(pmu, &pmus, list);
+       }
+       list_for_each_entry_continue(pmu, &pmus, list)
+               return pmu;
+       return NULL;
+}
+
 struct perf_pmu *perf_pmu__find(char *name)
 {
        struct perf_pmu *pmu;