perf: Move to dev-utils/perf
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 16 Jun 2016 21:41:23 +0000 (17:41 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 17 Jun 2016 16:17:56 +0000 (12:17 -0400)
For lack of a better naming system, we'll use Gentoo's.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
28 files changed:
tools/dev-util/perf/.gitignore [new file with mode: 0644]
tools/dev-util/perf/Makefile [new file with mode: 0644]
tools/dev-util/perf/akaros.c [new file with mode: 0644]
tools/dev-util/perf/akaros.h [new file with mode: 0644]
tools/dev-util/perf/elf.h [new file with mode: 0644]
tools/dev-util/perf/perf.c [new file with mode: 0644]
tools/dev-util/perf/perf_core.c [new file with mode: 0644]
tools/dev-util/perf/perf_core.h [new file with mode: 0644]
tools/dev-util/perf/perf_format.h [new file with mode: 0644]
tools/dev-util/perf/perfconv.c [new file with mode: 0644]
tools/dev-util/perf/perfconv.h [new file with mode: 0644]
tools/dev-util/perf/symbol-elf.c [new file with mode: 0644]
tools/dev-util/perf/xlib.c [new file with mode: 0644]
tools/dev-util/perf/xlib.h [new file with mode: 0644]
tools/profile/perf/.gitignore [deleted file]
tools/profile/perf/Makefile [deleted file]
tools/profile/perf/akaros.c [deleted file]
tools/profile/perf/akaros.h [deleted file]
tools/profile/perf/elf.h [deleted file]
tools/profile/perf/perf.c [deleted file]
tools/profile/perf/perf_core.c [deleted file]
tools/profile/perf/perf_core.h [deleted file]
tools/profile/perf/perf_format.h [deleted file]
tools/profile/perf/perfconv.c [deleted file]
tools/profile/perf/perfconv.h [deleted file]
tools/profile/perf/symbol-elf.c [deleted file]
tools/profile/perf/xlib.c [deleted file]
tools/profile/perf/xlib.h [deleted file]

diff --git a/tools/dev-util/perf/.gitignore b/tools/dev-util/perf/.gitignore
new file mode 100644 (file)
index 0000000..bd14107
--- /dev/null
@@ -0,0 +1 @@
+perf
diff --git a/tools/dev-util/perf/Makefile b/tools/dev-util/perf/Makefile
new file mode 100644 (file)
index 0000000..6e506f6
--- /dev/null
@@ -0,0 +1,36 @@
+include ../../Makefrag
+
+SOURCES = perf.c perfconv.c xlib.c perf_core.c akaros.c symbol-elf.c
+
+XCC = $(CROSS_COMPILE)gcc
+
+LIBS=-lperfmon -lelf
+
+PHONY := all
+all: perf
+
+
+PHONY += perf
+perf: $(SOURCES)
+       @echo "  CC      perf"
+       $(Q)$(XCC) -O2 -Wall -Wno-format -Wno-unused -Werror \
+                  -std=gnu99 -o perf $(SOURCES) $(LIBS)
+
+
+PHONY += install
+install: perf
+       @echo "  IN      perf"
+       $(Q)cp perf $(KFS_ROOT)/bin/perf
+
+
+PHONY += clean
+clean:
+       @echo "  RM      perf"
+       $(Q)rm -f perf
+
+
+PHONY += mrproper
+mrproper: clean
+
+
+.PHONY: $(PHONY)
diff --git a/tools/dev-util/perf/akaros.c b/tools/dev-util/perf/akaros.c
new file mode 100644 (file)
index 0000000..3ae2a22
--- /dev/null
@@ -0,0 +1,141 @@
+/* Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ */
+
+#include <ros/arch/arch.h>
+#include <ros/common.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <parlib/parlib.h>
+#include "xlib.h"
+#include "akaros.h"
+
+static const unsigned int llcores[] = {
+       0
+};
+
+void ros_get_low_latency_core_set(struct core_set *cores)
+{
+       for (size_t i = 0; i < COUNT_OF(llcores); i++)
+               ros_set_bit(cores, llcores[i]);
+}
+
+size_t ros_get_low_latency_core_count(void)
+{
+       return COUNT_OF(llcores);
+}
+
+static int guess_nr_cores(void)
+{
+       return max_vcores() + ros_get_low_latency_core_count();
+}
+
+static int get_vars_nr_cores(void)
+{
+       int fd, ret;
+       char buf[10];
+
+       fd = xopen("#vars/num_cores!dw", O_READ, 0);
+       if (fd < 0)
+               return -1;
+       if (read(fd, buf, sizeof(buf)) <= 0) {
+               close(fd);
+               return -1;
+       }
+       ret = atoi(buf);
+       return ret;
+}
+
+static int nr_cores;
+
+static void set_nr_cores(void)
+{
+       nr_cores = get_vars_nr_cores();
+       if (nr_cores == -1)
+               nr_cores = guess_nr_cores();
+}
+
+
+size_t ros_total_cores(void)
+{
+       run_once(set_nr_cores());
+       return nr_cores;
+}
+
+void ros_parse_cores(const char *str, struct core_set *cores)
+{
+       unsigned int fcpu, ncpu;
+       char *dstr = xstrdup(str);
+       char *sptr = NULL;
+       char *tok, *sptr2;
+
+       ZERO_DATA(*cores);
+       for (tok = strtok_r(dstr, ",", &sptr); tok;
+                tok = strtok_r(NULL, ",", &sptr)) {
+
+               if (strchr(tok, '-')) {
+                       if (sscanf(tok, "%u-%u", &fcpu, &ncpu) != 2) {
+                               fprintf(stderr, "Invalid CPU range: %s\n", tok);
+                               exit(1);
+                       }
+                       if (fcpu >= ros_total_cores()) {
+                               fprintf(stderr, "CPU number out of bound: %u\n", fcpu);
+                               exit(1);
+                       }
+                       if (ncpu >= ros_total_cores()) {
+                               fprintf(stderr, "CPU number out of bound: %u\n", ncpu);
+                               exit(1);
+                       }
+                       if (fcpu > ncpu) {
+                               fprintf(stderr, "CPU range is backwards: %u-%u\n", fcpu, ncpu);
+                               exit(1);
+                       }
+                       for (; fcpu <= ncpu; fcpu++)
+                               ros_set_bit(cores->core_set, fcpu);
+               } else {
+                       fcpu = atoi(tok);
+                       if (fcpu >= ros_total_cores()) {
+                               fprintf(stderr, "CPU number out of bound: %u\n",
+                                               fcpu);
+                               exit(1);
+                       }
+                       ros_set_bit(cores->core_set, fcpu);
+               }
+       }
+       free(dstr);
+}
+
+void ros_get_all_cores_set(struct core_set *cores)
+{
+       size_t max_cores = ros_total_cores();
+
+       memset(cores->core_set, 0xff, DIV_ROUND_UP(max_cores, CHAR_BIT));
+}
+
+void ros_not_core_set(struct core_set *dcs)
+{
+       size_t max_cores = ros_total_cores();
+
+       for (size_t i = 0; (max_cores > 0) && (i < sizeof(dcs->core_set)); i++) {
+               size_t nb = (max_cores >= CHAR_BIT) ? CHAR_BIT : max_cores;
+
+               dcs->core_set[i] = (~dcs->core_set[i]) & ((1 << nb) - 1);
+               max_cores -= nb;
+       }
+}
+
+void ros_and_core_sets(struct core_set *dcs, const struct core_set *scs)
+{
+       for (size_t i = 0; i < sizeof(dcs->core_set); i++)
+               dcs->core_set[i] &= scs->core_set[i];
+}
+
+void ros_or_core_sets(struct core_set *dcs, const struct core_set *scs)
+{
+       for (size_t i = 0; i < sizeof(dcs->core_set); i++)
+               dcs->core_set[i] |= scs->core_set[i];
+}
diff --git a/tools/dev-util/perf/akaros.h b/tools/dev-util/perf/akaros.h
new file mode 100644 (file)
index 0000000..98c9779
--- /dev/null
@@ -0,0 +1,48 @@
+/* Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ */
+
+#pragma once
+
+#include <ros/arch/arch.h>
+#include <ros/common.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <limits.h>
+#include <parlib/parlib.h>
+
+#define CORE_SET_SIZE DIV_ROUND_UP(MAX_NUM_CORES, CHAR_BIT)
+
+/* Not using sched.h CPU set because that file has definitions for a large
+ * number of APIs Akaros does not support.
+ * Making Akaros core_set.h visible in userslace might be a cleaner approach.
+ */
+struct core_set {
+       uint8_t core_set[CORE_SET_SIZE];
+};
+
+void ros_get_low_latency_core_set(struct core_set *cores);
+size_t ros_get_low_latency_core_count(void);
+size_t ros_total_cores(void);
+void ros_parse_cores(const char *str, struct core_set *cores);
+void ros_get_all_cores_set(struct core_set *cores);
+void ros_not_core_set(struct core_set *dcs);
+void ros_and_core_sets(struct core_set *dcs, const struct core_set *scs);
+void ros_or_core_sets(struct core_set *dcs, const struct core_set *scs);
+
+static inline void ros_set_bit(void *addr, size_t nbit)
+{
+       ((uint8_t *) addr)[nbit % CHAR_BIT] |= 1 << (nbit % CHAR_BIT);
+}
+
+static inline void ros_clear_bit(void *addr, size_t nbit)
+{
+       ((uint8_t *) addr)[nbit % CHAR_BIT] &= ~(1 << (nbit % CHAR_BIT));
+}
+
+static inline bool ros_get_bit(const void *addr, size_t nbit)
+{
+       return ((const uint8_t *) addr)[nbit % CHAR_BIT] & (1 << (nbit % CHAR_BIT));
+}
diff --git a/tools/dev-util/perf/elf.h b/tools/dev-util/perf/elf.h
new file mode 100644 (file)
index 0000000..cc86df2
--- /dev/null
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <stddef.h>
+
+#define BUILD_ID_SIZE   20
+
+int filename__read_build_id(const char *filename, void *bf, size_t size);
+void symbol__elf_init(void);
diff --git a/tools/dev-util/perf/perf.c b/tools/dev-util/perf/perf.c
new file mode 100644 (file)
index 0000000..b6b3aa1
--- /dev/null
@@ -0,0 +1,620 @@
+/* Copyright (c) 2015-2016 Google Inc
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <argp.h>
+#include <time.h>
+#include <parlib/parlib.h>
+#include <parlib/timing.h>
+#include "xlib.h"
+#include "akaros.h"
+#include "perfconv.h"
+#include "perf_core.h"
+
+/* Helpers */
+static void run_process_and_wait(int argc, char *argv[],
+                                                                const struct core_set *cores);
+
+/* For communicating with perf_create_context() */
+static struct perf_context_config perf_cfg = {
+       .perf_file = "#arch/perf",
+       .kpctl_file = "#kprof/kpctl",
+       .kpdata_file = "#kprof/kpdata",
+};
+
+static struct perfconv_context *cctx;
+static struct perf_context *pctx;
+extern char **environ; /* POSIX envp */
+
+struct perf_opts {
+       FILE                                            *outfile;
+       const char                                      *events;
+       char                                            **cmd_argv;
+       int                                                     cmd_argc;
+       struct core_set                         cores;
+       bool                                            got_cores;
+       bool                                            verbose;
+       bool                                            sampling;
+       bool                                            stat_bignum;
+       bool                                            record_quiet;
+       unsigned long                           record_period;
+};
+static struct perf_opts opts;
+
+struct perf_cmd {
+       char                                            *name;
+       char                                            *desc;
+       char                                            *opts;
+       int (*func)(struct perf_cmd *, int, char **);
+};
+
+static int perf_help(struct perf_cmd *cmd, int argc, char *argv[]);
+static int perf_list(struct perf_cmd *cmd, int argc, char *argv[]);
+static int perf_record(struct perf_cmd *cmd, int argc, char *argv[]);
+static int perf_stat(struct perf_cmd *cmd, int argc, char *argv[]);
+static int perf_pmu_caps(struct perf_cmd *cmd, int argc, char *argv[]);
+
+static struct perf_cmd perf_cmds[] = {
+       { .name = "help",
+         .desc = "Detailed help for commands",
+         .opts = "COMMAND",
+         .func = perf_help,
+       },
+       { .name = "list",
+         .desc = "Lists all available events",
+         .opts = "[REGEX]",
+         .func = perf_list,
+       },
+       { .name = "record",
+         .desc = "Samples events during command execution",
+         .opts = 0,
+         .func = perf_record,
+       },
+       { .name = "stat",
+         .desc = "Counts events during command execution",
+         .opts = 0,
+         .func = perf_stat,
+       },
+       { .name = "pmu_caps",
+         .desc = "Shows PMU capabilities",
+         .opts = "",
+         .func = perf_pmu_caps,
+       },
+};
+
+/**************************** perf help ****************************/
+
+static int perf_help(struct perf_cmd *cmd, int argc, char *argv[])
+{
+       char *sub_argv[2];
+
+       if (argc < 2) {
+               fprintf(stderr, "perf %s %s\n", cmd->name, cmd->opts);
+               return -1;
+       }
+       for (int i = 0; i < COUNT_OF(perf_cmds); i++) {
+               if (!strcmp(perf_cmds[i].name, argv[1])) {
+                       if (perf_cmds[i].opts) {
+                               fprintf(stdout, "perf %s %s\n", perf_cmds[i].name,
+                                       perf_cmds[i].opts);
+                               fprintf(stdout, "\t%s\n", perf_cmds[i].desc);
+                       } else {
+                               /* For argp subcommands, call their help directly. */
+                               sub_argv[0] = xstrdup(perf_cmds[i].name);
+                               sub_argv[1] = xstrdup("--help");
+                               perf_cmds[i].func(&perf_cmds[i], 2, sub_argv);
+                               free(sub_argv[0]);
+                               free(sub_argv[1]);
+                       }
+                       return 0;
+               }
+       }
+       fprintf(stderr, "Unknown perf command %s\n", argv[1]);
+       return -1;
+}
+
+/**************************** perf list ****************************/
+
+static int perf_list(struct perf_cmd *cmd, int argc, char *argv[])
+{
+       char *show_regex = NULL;
+
+       if (argc > 1)
+               show_regex = argv[1];
+       perf_show_events(show_regex, stdout);
+       return 0;
+}
+
+/**************************** perf pmu_caps ************************/
+
+static int perf_pmu_caps(struct perf_cmd *cmd, int argc, char *argv[])
+{
+       const struct perf_arch_info *pai = perf_context_get_arch_info(pctx);
+
+       fprintf(stdout,
+                       "PERF.version             = %u\n"
+                       "PERF.proc_arch_events    = %u\n"
+                       "PERF.bits_x_counter      = %u\n"
+                       "PERF.counters_x_proc     = %u\n"
+                       "PERF.bits_x_fix_counter  = %u\n"
+                       "PERF.fix_counters_x_proc = %u\n",
+                       pai->perfmon_version, pai->proc_arch_events, pai->bits_x_counter,
+                       pai->counters_x_proc, pai->bits_x_fix_counter,
+                       pai->fix_counters_x_proc);
+       return 0;
+}
+
+/**************************** Common argp ************************/
+
+/* Collection argument parsing.  These options are common to any function that
+ * will collect perf events, e.g. perf record and perf stat. */
+
+static struct argp_option collect_opts[] = {
+       {"event", 'e', "EVENT", 0, "Event string, e.g. cycles:u:k"},
+       {"cores", 'C', "CORE_LIST", 0, "List of cores, e.g. 0.2.4:8-19"},
+       {"cpu", 'C', 0, OPTION_ALIAS},
+       {"all-cpus", 'a', 0, 0, "Collect events on all cores (on by default)"},
+       {"verbose", 'v', 0, 0, 0},
+       { 0 }
+};
+
+static const char *collect_args_doc = "COMMAND [ARGS]";
+
+static error_t parse_collect_opt(int key, char *arg, struct argp_state *state)
+{
+       struct perf_opts *p_opts = state->input;
+
+       /* argp doesn't pass input to the child parser(s) by default... */
+       state->child_inputs[0] = state->input;
+
+       switch (key) {
+       case 'a':
+               /* Our default operation is to track all cores; we don't follow
+                * processes yet. */
+               break;
+       case 'C':
+               ros_parse_cores(arg, &p_opts->cores);
+               p_opts->got_cores = TRUE;
+               break;
+       case 'e':
+               p_opts->events = arg;
+               break;
+       case 'v':
+               p_opts->verbose = TRUE;
+               break;
+       case ARGP_KEY_ARG:
+               p_opts->cmd_argc = state->argc - state->next + 1;
+               p_opts->cmd_argv = xmalloc(sizeof(char*) * (p_opts->cmd_argc + 1));
+               p_opts->cmd_argv[0] = arg;
+               memcpy(&p_opts->cmd_argv[1], &state->argv[state->next],
+                      sizeof(char*) * (p_opts->cmd_argc - 1));
+               p_opts->cmd_argv[p_opts->cmd_argc] = NULL;
+               state->next = state->argc;
+               break;
+       case ARGP_KEY_END:
+               if (!p_opts->cmd_argc)
+                       argp_usage(state);
+               /* By default, we set all cores (different than linux) */
+               if (!p_opts->got_cores)
+                       ros_get_all_cores_set(&p_opts->cores);
+               break;
+       default:
+               return ARGP_ERR_UNKNOWN;
+       }
+       return 0;
+}
+
+/* Helper, parses args using the collect_opts and the child parser for a given
+ * cmd. */
+static void collect_argp(struct perf_cmd *cmd, int argc, char *argv[],
+                         struct argp_child *children, struct perf_opts *opts)
+{
+       struct argp collect_opt = {collect_opts, parse_collect_opt,
+                                  collect_args_doc, cmd->desc, children};
+       char *cmd_name;
+       const char *fmt = "perf %s";
+       size_t cmd_sz = strlen(cmd->name) + strlen(fmt) + 1;
+
+       /* Rewrite the command name from foo to perf foo for the --help output */
+       cmd_name = xmalloc(cmd_sz);
+       snprintf(cmd_name, cmd_sz, fmt, cmd->name);
+       cmd_name[cmd_sz - 1] = '\0';
+       argv[0] = cmd_name;
+       argp_parse(&collect_opt, argc, argv, ARGP_IN_ORDER, 0, opts);
+       /* It's possible that someone could still be using cmd_name */
+}
+
+/* Helper, submits the events in opts to the kernel for monitoring. */
+static void submit_events(struct perf_opts *opts)
+{
+       struct perf_eventsel *sel;
+       char *dup_evts, *tok, *tok_save = 0;
+
+       dup_evts = xstrdup(opts->events);
+       for (tok = strtok_r(dup_evts, ",", &tok_save);
+            tok;
+                tok = strtok_r(NULL, ",", &tok_save)) {
+
+               sel = perf_parse_event(tok);
+               PMEV_SET_INTEN(sel->ev.event, opts->sampling);
+               sel->ev.trigger_count = opts->record_period;
+               perf_context_event_submit(pctx, &opts->cores, sel);
+       }
+       free(dup_evts);
+}
+
+/**************************** perf record ************************/
+
+static struct argp_option record_opts[] = {
+       {"count", 'c', "PERIOD", 0, "Sampling period"},
+       {"output", 'o', "FILE", 0, "Output file name (default perf.data)"},
+       {"freq", 'F', "FREQUENCY", 0, "Sampling frequency (assumes cycles)"},
+       {"call-graph", 'g', 0, 0, "Backtrace recording (always on!)"},
+       {"quiet", 'q', 0, 0, "No printing to stdio"},
+       { 0 }
+};
+
+/* In lieu of adaptively changing the period to maintain a set freq, we
+ * just assume they want cycles and that the TSC is close to that.
+ *
+ * (cycles/sec) / (samples/sec) = cycles / sample = period.
+ *
+ * TODO: this also assumes we're running the core at full speed. */
+static unsigned long freq_to_period(unsigned long freq)
+{
+       return get_tsc_freq() / freq;
+}
+
+static error_t parse_record_opt(int key, char *arg, struct argp_state *state)
+{
+       struct perf_opts *p_opts = state->input;
+
+       switch (key) {
+       case 'c':
+               if (p_opts->record_period)
+                       argp_error(state, "Period set.  Only use at most one of -c -F");
+               p_opts->record_period = atol(arg);
+               break;
+       case 'F':
+               if (p_opts->record_period)
+                       argp_error(state, "Period set.  Only use at most one of -c -F");
+               /* TODO: when we properly support freq, multiple events will have the
+                * same freq but different, dynamic, periods. */
+               p_opts->record_period = freq_to_period(atol(arg));
+               break;
+       case 'g':
+               /* Our default operation is to record backtraces. */
+               break;
+       case 'o':
+               p_opts->outfile = xfopen(arg, "wb");
+               break;
+       case 'q':
+               p_opts->record_quiet = TRUE;
+               break;
+       case ARGP_KEY_END:
+               if (!p_opts->events)
+                       p_opts->events = "cycles";
+               if (!p_opts->outfile)
+                       p_opts->outfile = xfopen("perf.data", "wb");
+               if (!p_opts->record_period)
+                       p_opts->record_period = freq_to_period(1000);
+               break;
+       default:
+               return ARGP_ERR_UNKNOWN;
+       }
+       return 0;
+}
+
+static int perf_record(struct perf_cmd *cmd, int argc, char *argv[])
+{
+       struct argp argp_record = {record_opts, parse_record_opt};
+       struct argp_child children[] = { {&argp_record, 0, 0, 0}, {0} };
+
+       collect_argp(cmd, argc, argv, children, &opts);
+       opts.sampling = TRUE;
+
+       /* Once a perf event is submitted, it'll start counting and firing the IRQ.
+        * However, we can control whether or not the samples are collected. */
+       submit_events(&opts);
+       perf_start_sampling(pctx);
+       run_process_and_wait(opts.cmd_argc, opts.cmd_argv, &opts.cores);
+       perf_stop_sampling(pctx);
+       if (opts.verbose)
+               perf_context_show_events(pctx, stdout);
+       /* The events are still counting and firing IRQs.  Let's be nice and turn
+        * them off to minimize our impact. */
+       perf_stop_events(pctx);
+       /* Generate the Linux perf file format with the traces which have been
+        * created during this operation. */
+       perf_convert_trace_data(cctx, perf_cfg.kpdata_file, opts.outfile);
+       fclose(opts.outfile);
+       return 0;
+}
+
+/**************************** perf stat  ************************/
+
+static struct argp_option stat_opts[] = {
+       {"big-num", 'B', 0, 0, "Formatting option"},
+       {"output", 'o', "FILE", 0, "Print output to file (default stdout)"},
+       { 0 }
+};
+
+static error_t parse_stat_opt(int key, char *arg, struct argp_state *state)
+{
+       struct perf_opts *p_opts = state->input;
+
+       switch (key) {
+       case 'B':
+               p_opts->stat_bignum = TRUE;
+               break;
+       case 'o':
+               p_opts->outfile = xfopen(arg, "w+");
+               break;
+       case ARGP_KEY_END:
+               if (!p_opts->events)
+                       p_opts->events = "cache-misses,cache-references,"
+                                        "branch-misses,branches,instructions,cycles";
+               if (!p_opts->outfile)
+                       p_opts->outfile = xfdopen(1, "w+");
+               break;
+       default:
+               return ARGP_ERR_UNKNOWN;
+       }
+       return 0;
+}
+
+struct stat_val {
+       char                                            *name;
+       uint64_t                                        count;
+};
+
+/* Helper, given a name, fetches its value as a float. */
+static float get_count_for(char *name, struct stat_val *all_vals,
+                           size_t nr_vals)
+{
+       for (int i = 0; i < nr_vals; i++) {
+               if (!strcmp(name, all_vals[i].name))
+                       return (float)all_vals[i].count;
+       }
+       return 0.0;
+}
+
+/* Helper, gets the seconds count as a float */
+static float get_seconds(struct stat_val *all_vals, size_t nr_vals)
+{
+       float sec = get_count_for("nsec", all_vals, nr_vals) / 1000000000;
+
+       /* We should never have a time of 0, but in case something went wrong, don't
+        * hand back 0 (divide by 0 errors). */
+       return sec != 0.0 ? sec : 1.0;
+}
+
+/* Prints "X per second", scaling for K, M, or G. */
+static void print_default_rate(FILE *out, struct stat_val *val,
+                               struct stat_val *all_vals, size_t nr_vals)
+{
+       float rate = val->count / get_seconds(all_vals, nr_vals);
+       char scale = ' ';
+
+       if (rate > 1000000000) {
+               rate /= 1000000000;
+               scale = 'G';
+       } else if (rate > 1000000) {
+               rate /= 1000000;
+               scale = 'M';
+       } else if (rate > 1000) {
+               rate /= 1000;
+               scale = 'K';
+       }
+       fprintf(out, "%9.3f %c/sec\n", rate, scale);
+}
+
+/* Prints a line for the given stat val.  We pass all the vals since some stats
+ * will adjust their output based on *other* known values.  e.g. IPC. */
+static void stat_print_val(FILE *out, struct stat_val *val,
+                           struct stat_val *all_vals, size_t nr_vals)
+{
+       /* Everyone gets the same front part of the printout */
+       fprintf(out, "%18llu      %-25s #", val->count, val->name);
+
+       /* Based on the particular event and what other events we know, we may print
+        * something different to the summary bit after the #. */
+       if (!strcmp(val->name, "instructions")) {
+               float cycles = get_count_for("cycles", all_vals, nr_vals);
+
+               if (cycles != 0.0)
+                       fprintf(out, "%9.3f insns per cycle\n", val->count / cycles);
+               else
+                       print_default_rate(out, val, all_vals, nr_vals);
+       } else if (!strcmp(val->name, "cache-misses")) {
+               float cache_ref = get_count_for("cache-references", all_vals, nr_vals);
+
+               if (cache_ref != 0.0)
+                       fprintf(out, "%8.2f%% of all refs\n", val->count * 100 / cache_ref);
+               else
+                       print_default_rate(out, val, all_vals, nr_vals);
+       } else if (!strcmp(val->name, "branch-misses")) {
+               float branches = get_count_for("branches", all_vals, nr_vals);
+
+               if (branches != 0.0)
+                       fprintf(out, "%8.2f%% of all branches\n",
+                               val->count * 100 / branches);
+               else
+                       print_default_rate(out, val, all_vals, nr_vals);
+       } else {
+               print_default_rate(out, val, all_vals, nr_vals);
+       }
+}
+
+static char *cmd_as_str(int argc, char *const argv[])
+{
+       size_t len = 0;
+       char *str;
+
+       for (int i = 0; i < argc; i++)
+               len += strlen(argv[i]) + 1;
+       str = xzmalloc(len);
+       for (int i = 0; i < argc; i++) {
+               strlcat(str, argv[i], len);
+               if (i != argc - 1)
+                       strlcat(str, " ", len);
+       }
+       return str;
+}
+
+static struct stat_val *collect_stats(struct perf_context *pctx,
+                                      struct timespec *diff)
+{
+       struct stat_val *stat_vals;
+
+       /* the last stat is time (nsec). */
+       stat_vals = xzmalloc(sizeof(struct stat_val) * (pctx->event_count + 1));
+       for (int i = 0; i < pctx->event_count; i++) {
+               stat_vals[i].count = perf_get_event_count(pctx, i);
+               stat_vals[i].name = pctx->events[i].sel.fq_str;
+       }
+       stat_vals[pctx->event_count].name = "nsec";
+       stat_vals[pctx->event_count].count = diff->tv_sec * 1000000000 +
+                                            diff->tv_nsec;
+       return stat_vals;
+}
+
+static int perf_stat(struct perf_cmd *cmd, int argc, char *argv[])
+{
+       struct argp argp_stat = {stat_opts, parse_stat_opt};
+       struct argp_child children[] = { {&argp_stat, 0, 0, 0}, {0} };
+       FILE *out;
+       struct timespec start, end, diff;
+       struct stat_val *stat_vals;
+       char *cmd_string;
+
+       collect_argp(cmd, argc, argv, children, &opts);
+       opts.sampling = FALSE;
+       out = opts.outfile;
+
+       /* As soon as we submit one event, that event is being tracked, meaning that
+        * the setup/teardown of perf events is also tracked.  Each event (including
+        * the clock measurement) will roughly account for either the start or stop
+        * of every other event. */
+       clock_gettime(CLOCK_REALTIME, &start);
+       submit_events(&opts);
+       run_process_and_wait(opts.cmd_argc, opts.cmd_argv, &opts.cores);
+       clock_gettime(CLOCK_REALTIME, &end);
+       subtract_timespecs(&diff, &end, &start);
+       stat_vals = collect_stats(pctx, &diff);
+       perf_stop_events(pctx);
+       cmd_string = cmd_as_str(opts.cmd_argc, opts.cmd_argv);
+       fprintf(out, "\nPerformance counter stats for '%s':\n\n", cmd_string);
+       free(cmd_string);
+       for (int i = 0; i < pctx->event_count; i++)
+               stat_print_val(out, &stat_vals[i], stat_vals, pctx->event_count + 1);
+       fprintf(out, "\n%8llu.%09llu seconds time elapsed\n\n", diff.tv_sec,
+               diff.tv_nsec);
+       fclose(out);
+       free(stat_vals);
+       return 0;
+}
+
+static void run_process_and_wait(int argc, char *argv[],
+                                                                const struct core_set *cores)
+{
+       int pid, status;
+       size_t max_cores = ros_total_cores();
+       struct core_set pvcores;
+
+       pid = create_child_with_stdfds(argv[0], argc, argv, environ);
+       if (pid < 0) {
+               perror("Unable to spawn child");
+               fflush(stderr);
+               exit(1);
+       }
+
+       ros_get_low_latency_core_set(&pvcores);
+       ros_not_core_set(&pvcores);
+       ros_and_core_sets(&pvcores, cores);
+       for (size_t i = 0; i < max_cores; i++) {
+               if (ros_get_bit(&pvcores, i)) {
+                       if (sys_provision(pid, RES_CORES, i)) {
+                               fprintf(stderr,
+                                               "Unable to provision CPU %lu to PID %d: cmd='%s'\n",
+                                               i, pid, argv[0]);
+                               sys_proc_destroy(pid, -1);
+                               exit(1);
+                       }
+               }
+       }
+
+       sys_proc_run(pid);
+       waitpid(pid, &status, 0);
+}
+
+static void save_cmdline(int argc, char *argv[])
+{
+       size_t len = 0;
+       char *p;
+
+       for (int i = 0; i < argc; i++)
+               len += strlen(argv[i]) + 1;
+       cmd_line_save = xmalloc(len);
+       p = cmd_line_save;
+       for (int i = 0; i < argc; i++) {
+               strcpy(p, argv[i]);
+               p += strlen(argv[i]);
+               if (!(i == argc - 1)) {
+                       *p = ' ';       /* overwrite \0 with ' ' */
+                       p++;
+               }
+       }
+}
+
+static void global_usage(void)
+{
+       fprintf(stderr, "  Usage: perf COMMAND [ARGS]\n");
+       fprintf(stderr, "\n  Available commands:\n\n");
+       for (int i = 0; i < COUNT_OF(perf_cmds); i++)
+               fprintf(stderr, "  \t%s: %s\n", perf_cmds[i].name, perf_cmds[i].desc);
+       exit(-1);
+}
+
+int main(int argc, char *argv[])
+{
+       int i, ret = -1;
+
+       save_cmdline(argc, argv);
+
+       /* Common inits.  Some functions don't need these, but it doesn't hurt. */
+       perf_initialize();
+       pctx = perf_create_context(&perf_cfg);
+       cctx = perfconv_create_context(pctx);
+
+       if (argc < 2)
+               global_usage();
+       for (i = 0; i < COUNT_OF(perf_cmds); i++) {
+               if (!strcmp(perf_cmds[i].name, argv[1])) {
+                       ret = perf_cmds[i].func(&perf_cmds[i], argc - 1, argv + 1);
+                       break;
+               }
+       }
+       if (i == COUNT_OF(perf_cmds))
+               global_usage();
+       /* This cleanup is optional - they'll all be dealt with when the program
+        * exits.  This means its safe for us to exit(-1) at any point in the
+        * program. */
+       perf_free_context(pctx);
+       perfconv_free_context(cctx);
+       perf_finalize();
+       return ret;
+}
diff --git a/tools/dev-util/perf/perf_core.c b/tools/dev-util/perf/perf_core.c
new file mode 100644 (file)
index 0000000..181d2f1
--- /dev/null
@@ -0,0 +1,775 @@
+/* Copyright (c) 2015-2016 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * Stephane Eranian <eranian@gmail.com> (perf_show_event_info() from libpfm4)
+ *
+ * See LICENSE for details. */
+
+#include <ros/arch/msr-index.h>
+#include <ros/arch/perfmon.h>
+#include <ros/common.h>
+#include <ros/memops.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+#include <regex.h>
+#include <parlib/parlib.h>
+#include <perfmon/err.h>
+#include <perfmon/pfmlib.h>
+#include "xlib.h"
+#include "perfconv.h"
+#include "akaros.h"
+#include "perf_core.h"
+#include "elf.h"
+
+struct perf_generic_event {
+       char                                            *name;
+       uint32_t                                        type;
+       uint32_t                                        config;
+};
+
+struct perf_generic_event generic_events[] = {
+       { .name = "cycles",
+         .type = PERF_TYPE_HARDWARE,
+         .config = PERF_COUNT_HW_CPU_CYCLES,
+       },
+       { .name = "cpu-cycles",
+         .type = PERF_TYPE_HARDWARE,
+         .config = PERF_COUNT_HW_CPU_CYCLES,
+       },
+       { .name = "instructions",
+         .type = PERF_TYPE_HARDWARE,
+         .config = PERF_COUNT_HW_INSTRUCTIONS,
+       },
+       { .name = "cache-references",
+         .type = PERF_TYPE_HARDWARE,
+         .config = PERF_COUNT_HW_CACHE_REFERENCES,
+       },
+       { .name = "cache-misses",
+         .type = PERF_TYPE_HARDWARE,
+         .config = PERF_COUNT_HW_CACHE_MISSES,
+       },
+       { .name = "branches",
+         .type = PERF_TYPE_HARDWARE,
+         .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
+       },
+       { .name = "branch-instructions",
+         .type = PERF_TYPE_HARDWARE,
+         .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
+       },
+       { .name = "branch-misses",
+         .type = PERF_TYPE_HARDWARE,
+         .config = PERF_COUNT_HW_BRANCH_MISSES,
+       },
+       { .name = "bus-cycles",
+         .type = PERF_TYPE_HARDWARE,
+         .config = PERF_COUNT_HW_BUS_CYCLES,
+       },
+};
+
+static const char *perf_get_event_mask_name(const pfm_event_info_t *einfo,
+                                                                                       uint32_t mask)
+{
+       int i;
+       pfm_event_attr_info_t ainfo;
+
+       ZERO_DATA(ainfo);
+       ainfo.size = sizeof(ainfo);
+       pfm_for_each_event_attr(i, einfo) {
+               pfm_err_t err = pfm_get_event_attr_info(einfo->idx, i, PFM_OS_NONE,
+                                                                                               &ainfo);
+
+               if (err != PFM_SUCCESS) {
+                       fprintf(stderr, "Failed to get attribute info: %s\n",
+                                       pfm_strerror(err));
+                       exit(1);
+               }
+               if (ainfo.type == PFM_ATTR_UMASK) {
+                       if (mask == (uint32_t) ainfo.code)
+                               return ainfo.name;
+               }
+       }
+
+       return NULL;
+}
+
+void perf_initialize(void)
+{
+       pfm_err_t err = pfm_initialize();
+
+       if (err != PFM_SUCCESS) {
+               fprintf(stderr, "Unable to initialize perfmon library: %s\n",
+                               pfm_strerror(err));
+               exit(1);
+       }
+       symbol__elf_init();
+}
+
+void perf_finalize(void)
+{
+       pfm_terminate();
+}
+
+/* This is arch-specific and maybe model specific in the future.  For some
+ * events, pfm4 gives us a pseudo encoding.  Those codes don't map to real
+ * hardware events and are meant to be interpreted by Linux for *other* HW
+ * events, e.g. in arch/x86/events/intel/core.c.
+ *
+ * While we're here, we can also take *real* encodings and treat them like
+ * pseudo encodings.  For instance, the arch event 0x3c (unhalted_core_cycles)
+ * can also be done with fixed counter 1.  This all assumes we have version 2 or
+ * later of Intel's perfmon. */
+static void x86_handle_pseudo_encoding(struct perf_eventsel *sel)
+{
+       uint8_t lower_byte;
+
+       switch (sel->ev.event & 0xffff) {
+       case 0xc0:      /* arch inst_retired */
+               sel->ev.flags |= PERFMON_FIXED_EVENT;
+               PMEV_SET_MASK(sel->ev.event, 0);
+               PMEV_SET_EVENT(sel->ev.event, 0);
+               return;
+       case 0x3c:      /* arch unhalted_core_cycles */
+               sel->ev.flags |= PERFMON_FIXED_EVENT;
+               PMEV_SET_MASK(sel->ev.event, 0);
+               PMEV_SET_EVENT(sel->ev.event, 1);
+               return;
+       case 0x13c:     /* arch unhalted_reference_cycles */
+       case 0x300:     /* pseudo encode: unhalted_reference_cycles */
+               sel->ev.flags |= PERFMON_FIXED_EVENT;
+               PMEV_SET_MASK(sel->ev.event, 0);
+               PMEV_SET_EVENT(sel->ev.event, 2);
+               return;
+       };
+       lower_byte = sel->ev.event & 0xff;
+       if ((lower_byte == 0x00) || (lower_byte == 0xff))
+               fprintf(stderr, "Unhandled pseudo encoding %d\n", lower_byte);
+}
+
+/* Parse the string using pfm's lookup functions.  Returns TRUE on success and
+ * fills in parts of sel. */
+static bool parse_pfm_encoding(const char *str, struct perf_eventsel *sel)
+{
+       pfm_pmu_encode_arg_t encode;
+       int err;
+       char *ptr;
+
+       memset(&encode, 0, sizeof(encode));
+       encode.size = sizeof(encode);
+       encode.fstr = &ptr;
+       err = pfm_get_os_event_encoding(str, PFM_PLM3 | PFM_PLM0, PFM_OS_NONE,
+                                       &encode);
+       if (err)
+               return FALSE;
+       strlcpy(sel->fq_str, ptr, MAX_FQSTR_SZ);
+       free(ptr);
+       if (encode.count == 0) {
+               fprintf(stderr, "Found event %s, but it had no codes!\n", sel->fq_str);
+               return FALSE;
+       }
+       sel->ev.event = encode.codes[0];
+       x86_handle_pseudo_encoding(sel);
+       sel->type = PERF_TYPE_RAW;
+       sel->config = PMEV_GET_MASK(sel->ev.event) | PMEV_GET_EVENT(sel->ev.event);
+       return TRUE;
+}
+
+static bool is_end_of_raw(char c)
+{
+       return (c == ':') || (c == '\0');
+}
+
+/* Helper: given a string, if the event is a raw hex code, return its numeric
+ * value.  Returns -1 if it does not match a raw code.
+ *
+ * rNN[N][N][:,\0].  Begins with r, has at least two hexdigits, up to 4, and
+ * ends with : , or \0. */
+static int extract_raw_code(const char *event)
+{
+       int i;
+       char copy[5] = {0};
+
+       if (event[0] != 'r')
+               return -1;
+       event++;
+       for (i = 0; i < 4; i++) {
+               if (isxdigit(event[i]))
+                       continue;
+               if (is_end_of_raw(event[i]))
+                       break;
+               return -1;
+       }
+       if (!is_end_of_raw(event[i]))
+               return -1;
+       /* 'i' tracks how many we found (i.e. every 'continue') */
+       if (i < 2)
+               return -1;
+       /* need a null-terminated raw code for strtol. */
+       for (int j = 0; j < i; j++)
+               copy[j] = event[j];
+       return strtol(copy, NULL, 16);
+}
+
+/* Takes any modifiers, e.g. u:k:etc, and sets the respective values in sel. */
+static void parse_modifiers(const char *str, struct perf_eventsel *sel)
+{
+       char *dup_str, *tok, *tok_save = 0;
+
+       dup_str = xstrdup(str);
+       for (tok = strtok_r(dup_str, ":", &tok_save);
+            tok;
+            tok = strtok_r(NULL, ":", &tok_save)) {
+
+               switch (tok[0]) {
+               case 'u':
+                       PMEV_SET_USR(sel->ev.event, 1);
+                       break;
+               case 'k':
+                       PMEV_SET_OS(sel->ev.event, 1);
+                       break;
+               case 'e':
+                       PMEV_SET_EDGE(sel->ev.event, 1);
+                       break;
+               case 'p':
+                       PMEV_SET_PC(sel->ev.event, 1);
+                       break;
+               case 't':
+                       PMEV_SET_ANYTH(sel->ev.event, 1);
+                       break;
+               case 'i':
+                       PMEV_SET_INVCMSK(sel->ev.event, 1);
+                       break;
+               case 'c':
+                       if (tok[1] != '=') {
+                               fprintf(stderr, "Bad cmask tok %s, ignoring\n", tok);
+                               break;
+                       }
+                       errno = 0;
+                       PMEV_SET_CMASK(sel->ev.event, strtoul(&tok[2], NULL, 0));
+                       if (errno)
+                               fprintf(stderr, "Bad cmask tok %s, trying anyway\n", tok);
+                       break;
+               }
+       }
+       free(dup_str);
+}
+
+/* Parse the string for a raw encoding.  Returns TRUE on success and fills in
+ * parts of sel.  It has basic modifiers, like pfm4, for setting bits in the
+ * event code.  This is arch specific, and is all x86 (intel) for now. */
+static bool parse_raw_encoding(const char *str, struct perf_eventsel *sel)
+{
+       int code = extract_raw_code(str);
+       char *colon;
+
+       if (code == -1)
+               return FALSE;
+       sel->ev.event = code;
+       strlcpy(sel->fq_str, str, MAX_FQSTR_SZ);
+       colon = strchr(str, ':');
+       if (colon)
+               parse_modifiers(colon + 1, sel);
+       /* Note that we do not call x86_handle_pseudo_encoding here.  We'll submit
+        * exactly what the user asked us for - which also means no fixed counters
+        * for them (unless we want a :f: token or something). */
+       sel->type = PERF_TYPE_RAW;
+       sel->config = PMEV_GET_MASK(sel->ev.event) | PMEV_GET_EVENT(sel->ev.event);
+       return TRUE;
+}
+
+/* Helper, returns true is str is a generic event string, and fills in sel with
+ * the type and config. */
+static bool generic_str_get_code(const char *str, struct perf_eventsel *sel)
+{
+       char *colon = strchr(str, ':');
+       /* if there was no :, we compare as far as we can.  generic_events.name is a
+        * string literal, so strcmp() is fine. */
+       size_t len = colon ? colon - str : SIZE_MAX;
+
+       for (int i = 0; i < COUNT_OF(generic_events); i++) {
+               if (!strncmp(generic_events[i].name, str, len)) {
+                       sel->type = generic_events[i].type;
+                       sel->config = generic_events[i].config;
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+/* TODO: this is arch-specific and possibly machine-specific. (intel for now).
+ * Basically a lot of our perf is arch-dependent. (e.g. PMEV_*). */
+static bool arch_translate_generic(struct perf_eventsel *sel)
+{
+       switch (sel->type) {
+       case PERF_TYPE_HARDWARE:
+               /* These are the intel/x86 architectural perf events */
+               switch (sel->config) {
+               case PERF_COUNT_HW_CPU_CYCLES:
+                       PMEV_SET_MASK(sel->ev.event, 0x00);
+                       PMEV_SET_EVENT(sel->ev.event, 0x3c);
+                       break;
+               case PERF_COUNT_HW_INSTRUCTIONS:
+                       PMEV_SET_MASK(sel->ev.event, 0x00);
+                       PMEV_SET_EVENT(sel->ev.event, 0xc0);
+                       break;
+               case PERF_COUNT_HW_CACHE_REFERENCES:
+                       PMEV_SET_MASK(sel->ev.event, 0x4f);
+                       PMEV_SET_EVENT(sel->ev.event, 0x2e);
+                       break;
+               case PERF_COUNT_HW_CACHE_MISSES:
+                       PMEV_SET_MASK(sel->ev.event, 0x41);
+                       PMEV_SET_EVENT(sel->ev.event, 0x2e);
+                       break;
+               case PERF_COUNT_HW_BRANCH_INSTRUCTIONS:
+                       PMEV_SET_MASK(sel->ev.event, 0x00);
+                       PMEV_SET_EVENT(sel->ev.event, 0xc4);
+                       break;
+               case PERF_COUNT_HW_BRANCH_MISSES:
+                       PMEV_SET_MASK(sel->ev.event, 0x00);
+                       PMEV_SET_EVENT(sel->ev.event, 0xc5);
+                       break;
+               case PERF_COUNT_HW_BUS_CYCLES:
+                       /* Unhalted reference cycles */
+                       PMEV_SET_MASK(sel->ev.event, 0x01);
+                       PMEV_SET_EVENT(sel->ev.event, 0x3c);
+                       break;
+               default:
+                       return FALSE;
+               };
+               break;
+       default:
+               return FALSE;
+       };
+       /* This will make sure we use fixed counters if available */
+       x86_handle_pseudo_encoding(sel);
+       return TRUE;
+}
+
+/* Parse the string for a built-in encoding.  These are the perf defaults such
+ * as 'cycles' or 'cache-references.' Returns TRUE on success and fills in parts
+ * of sel. */
+static bool parse_generic_encoding(const char *str, struct perf_eventsel *sel)
+{
+       bool ret = FALSE;
+       char *colon;
+
+       if (!generic_str_get_code(str, sel))
+               return FALSE;
+       switch (sel->type) {
+       case PERF_TYPE_HARDWARE:
+       case PERF_TYPE_HW_CACHE:
+               ret = arch_translate_generic(sel);
+               break;
+       };
+       if (!ret) {
+               fprintf(stderr, "Unsupported built-in event %s\n", str);
+               return FALSE;
+       }
+       strlcpy(sel->fq_str, str, MAX_FQSTR_SZ);
+       colon = strchr(str, ':');
+       if (colon)
+               parse_modifiers(colon + 1, sel);
+       return TRUE;
+}
+
+/* Given an event description string, fills out sel with the info from the
+ * string such that it can be submitted to the OS.
+ *
+ * The caller can set more bits if they like, such as whether or not to
+ * interrupt on overflow, the sample_period, etc.  None of those settings are
+ * part of the event string.
+ *
+ * Kills the program on failure. */
+struct perf_eventsel *perf_parse_event(const char *str)
+{
+       struct perf_eventsel *sel = xzmalloc(sizeof(struct perf_eventsel));
+
+       sel->ev.user_data = (uint64_t)sel;
+       if (parse_generic_encoding(str, sel))
+               goto success;
+       if (parse_pfm_encoding(str, sel))
+               goto success;
+       if (parse_raw_encoding(str, sel))
+               goto success;
+       free(sel);
+       fprintf(stderr, "Failed to parse event string %s\n", str);
+       exit(-1);
+success:
+       if (!PMEV_GET_OS(sel->ev.event) && !PMEV_GET_USR(sel->ev.event)) {
+               PMEV_SET_OS(sel->ev.event, 1);
+               PMEV_SET_USR(sel->ev.event, 1);
+       }
+       PMEV_SET_EN(sel->ev.event, 1);
+       return sel;
+}
+
+static void perf_get_arch_info(int perf_fd, struct perf_arch_info *pai)
+{
+       uint8_t cmdbuf[6 * sizeof(uint32_t)];
+       const uint8_t *rptr = cmdbuf;
+
+       cmdbuf[0] = PERFMON_CMD_CPU_CAPS;
+
+       xpwrite(perf_fd, cmdbuf, 1, 0);
+       xpread(perf_fd, cmdbuf, 6 * sizeof(uint32_t), 0);
+
+       rptr = get_le_u32(rptr, &pai->perfmon_version);
+       rptr = get_le_u32(rptr, &pai->proc_arch_events);
+       rptr = get_le_u32(rptr, &pai->bits_x_counter);
+       rptr = get_le_u32(rptr, &pai->counters_x_proc);
+       rptr = get_le_u32(rptr, &pai->bits_x_fix_counter);
+       rptr = get_le_u32(rptr, &pai->fix_counters_x_proc);
+}
+
+static int perf_open_event(int perf_fd, const struct core_set *cores,
+                                                  const struct perf_eventsel *sel)
+{
+       uint8_t cmdbuf[1 + 3 * sizeof(uint64_t) + sizeof(uint32_t) +
+                                  CORE_SET_SIZE];
+       uint8_t *wptr = cmdbuf;
+       const uint8_t *rptr = cmdbuf;
+       uint32_t ped;
+       int i, j;
+
+       *wptr++ = PERFMON_CMD_COUNTER_OPEN;
+       wptr = put_le_u64(wptr, sel->ev.event);
+       wptr = put_le_u64(wptr, sel->ev.flags);
+       wptr = put_le_u64(wptr, sel->ev.trigger_count);
+       wptr = put_le_u64(wptr, sel->ev.user_data);
+
+       for (i = CORE_SET_SIZE - 1; (i >= 0) && !cores->core_set[i]; i--)
+               ;
+       if (i < 0) {
+               fprintf(stderr, "Performance event CPU set must not be empty\n");
+               exit(1);
+       }
+       wptr = put_le_u32(wptr, i + 1);
+       for (j = 0; j <= i; j++)
+               *wptr++ = cores->core_set[j];
+
+       xpwrite(perf_fd, cmdbuf, wptr - cmdbuf, 0);
+       xpread(perf_fd, cmdbuf, sizeof(uint32_t), 0);
+
+       rptr = get_le_u32(rptr, &ped);
+
+       return (int) ped;
+}
+
+static uint64_t *perf_get_event_values(int perf_fd, int ped, size_t *pnvalues)
+{
+       ssize_t rsize;
+       uint32_t i, n;
+       uint64_t *values;
+       uint64_t temp;
+       size_t bufsize = sizeof(uint32_t) + MAX_NUM_CORES * sizeof(uint64_t);
+       uint8_t *cmdbuf = xmalloc(bufsize);
+       uint8_t *wptr = cmdbuf;
+       const uint8_t *rptr = cmdbuf;
+
+       *wptr++ = PERFMON_CMD_COUNTER_STATUS;
+       wptr = put_le_u32(wptr, ped);
+
+       xpwrite(perf_fd, cmdbuf, wptr - cmdbuf, 0);
+       rsize = pread(perf_fd, cmdbuf, bufsize, 0);
+
+       if (rsize < (sizeof(uint32_t))) {
+               fprintf(stderr, "Invalid read size while fetching event status: %ld\n",
+                               rsize);
+               exit(1);
+       }
+       rptr = get_le_u32(rptr, &n);
+       if (((rptr - cmdbuf) + n * sizeof(uint64_t)) > rsize) {
+               fprintf(stderr, "Invalid read size while fetching event status: %ld\n",
+                               rsize);
+               exit(1);
+       }
+       values = xmalloc(n * sizeof(uint64_t));
+       for (i = 0; i < n; i++)
+               rptr = get_le_u64(rptr, values + i);
+       free(cmdbuf);
+
+       *pnvalues = n;
+
+       return values;
+}
+
+/* Helper, returns the total count (across all cores) of the event @idx */
+uint64_t perf_get_event_count(struct perf_context *pctx, unsigned int idx)
+{
+       uint64_t total = 0;
+       size_t nvalues;
+       uint64_t *values;
+
+       values = perf_get_event_values(pctx->perf_fd, pctx->events[idx].ped,
+                                      &nvalues);
+       for (int i = 0; i < nvalues; i++)
+               total += values[i];
+       free(values);
+       return total;
+}
+
+static void perf_close_event(int perf_fd, int ped)
+{
+       uint8_t cmdbuf[1 + sizeof(uint32_t)];
+       uint8_t *wptr = cmdbuf;
+
+       *wptr++ = PERFMON_CMD_COUNTER_CLOSE;
+       wptr = put_le_u32(wptr, ped);
+
+       xpwrite(perf_fd, cmdbuf, wptr - cmdbuf, 0);
+}
+
+struct perf_context *perf_create_context(struct perf_context_config *cfg)
+{
+       struct perf_context *pctx = xzmalloc(sizeof(struct perf_context));
+
+       pctx->cfg = cfg;
+       pctx->perf_fd = xopen(cfg->perf_file, O_RDWR, 0);
+       /* perf record needs kpctl_fd, but other perf subcommands might not.  We'll
+        * delay the opening of kpctl until we need it, since kprof is picky about
+        * multiple users of kpctl. */
+       pctx->kpctl_fd = -1;
+       perf_get_arch_info(pctx->perf_fd, &pctx->pai);
+
+       return pctx;
+}
+
+void perf_free_context(struct perf_context *pctx)
+{
+       if (pctx->kpctl_fd != -1)
+               close(pctx->kpctl_fd);  /* disabled sampling */
+       close(pctx->perf_fd);   /* closes all events */
+       free(pctx);
+}
+
+void perf_context_event_submit(struct perf_context *pctx,
+                                                          const struct core_set *cores,
+                                                          const struct perf_eventsel *sel)
+{
+       struct perf_event *pevt = pctx->events + pctx->event_count;
+
+       if (pctx->event_count >= COUNT_OF(pctx->events)) {
+               fprintf(stderr, "Too many open events: %d\n", pctx->event_count);
+               exit(1);
+       }
+       pctx->event_count++;
+       pevt->cores = *cores;
+       pevt->sel = *sel;
+       pevt->ped = perf_open_event(pctx->perf_fd, cores, sel);
+       if (pevt->ped < 0) {
+               fprintf(stderr, "Unable to submit event \"%s\": %s\n", sel->fq_str,
+                       errstr());
+               exit(1);
+       }
+}
+
+void perf_stop_events(struct perf_context *pctx)
+{
+       for (int i = 0; i < pctx->event_count; i++)
+               perf_close_event(pctx->perf_fd, pctx->events[i].ped);
+}
+
+static void ensure_kpctl_is_open(struct perf_context *pctx)
+{
+       if (pctx->kpctl_fd == -1)
+               pctx->kpctl_fd = xopen(pctx->cfg->kpctl_file, O_RDWR, 0);
+}
+
+void perf_start_sampling(struct perf_context *pctx)
+{
+       static const char * const enable_str = "start";
+
+       ensure_kpctl_is_open(pctx);
+       xwrite(pctx->kpctl_fd, enable_str, strlen(enable_str));
+}
+
+void perf_stop_sampling(struct perf_context *pctx)
+{
+       static const char * const disable_str = "stop";
+
+       ensure_kpctl_is_open(pctx);
+       xwrite(pctx->kpctl_fd, disable_str, strlen(disable_str));
+}
+
+void perf_context_show_events(struct perf_context *pctx, FILE *file)
+{
+       struct perf_eventsel *sel;
+
+       for (int i = 0; i < pctx->event_count; i++) {
+               sel = &pctx->events[i].sel;
+               fprintf(file, "Event: %s, final code %p%s, trigger count %d\n",
+                       sel->fq_str, sel->ev.event,
+                       perfmon_is_fixed_event(&sel->ev) ? " (fixed)" : "",
+                       sel->ev.trigger_count);
+       }
+}
+
+static void perf_print_event_flags(const pfm_event_info_t *info, FILE *file)
+{
+       int n = 0;
+
+       if (info->is_precise) {
+               fputs("[precise] ", file);
+               n++;
+       }
+       if (!n)
+               fputs("None", file);
+}
+
+static void perf_print_attr_flags(const pfm_event_attr_info_t *info, FILE *file)
+{
+       int n = 0;
+
+       if (info->is_dfl) {
+               fputs("[default] ", file);
+               n++;
+       }
+       if (info->is_precise) {
+               fputs("[precise] ", file);
+               n++;
+       }
+       if (!n)
+               fputs("None ", file);
+}
+
+/* Ported from libpfm4 */
+static void perf_show_event_info(const pfm_event_info_t *info,
+                                                                const pfm_pmu_info_t *pinfo, FILE *file)
+{
+       static const char * const srcs[PFM_ATTR_CTRL_MAX] = {
+               [PFM_ATTR_CTRL_UNKNOWN] = "???",
+               [PFM_ATTR_CTRL_PMU] = "PMU",
+               [PFM_ATTR_CTRL_PERF_EVENT] = "perf_event",
+       };
+       pfm_event_attr_info_t ainfo;
+       int i, mod = 0, um = 0;
+
+       fprintf(file, "#-----------------------------\n"
+                       "IDX      : %d\n"
+                       "PMU name : %s (%s)\n"
+                       "Name     : %s\n"
+                       "Equiv    : %s\n",
+                       info->idx, pinfo->name, pinfo->desc,
+                       info->name, info->equiv ? info->equiv : "None");
+
+       fprintf(file, "Flags    : ");
+       perf_print_event_flags(info, file);
+       fputc('\n', file);
+
+       fprintf(file, "Desc     : %s\n", info->desc ? info->desc :
+                       "no description available");
+       fprintf(file, "Code     : 0x%"PRIx64"\n", info->code);
+
+       ZERO_DATA(ainfo);
+       ainfo.size = sizeof(ainfo);
+
+       pfm_for_each_event_attr(i, info) {
+               const char *src;
+               pfm_err_t err = pfm_get_event_attr_info(info->idx, i, PFM_OS_NONE,
+                                                                                               &ainfo);
+
+               if (err != PFM_SUCCESS) {
+                       fprintf(stderr, "Failed to get attribute info: %s\n",
+                                       pfm_strerror(err));
+                       exit(1);
+               }
+
+               if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX) {
+                       fprintf(stderr, "event: %s has unsupported attribute source %d",
+                                       info->name, ainfo.ctrl);
+                       ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN;
+               }
+               src = srcs[ainfo.ctrl];
+               switch (ainfo.type) {
+                       case PFM_ATTR_UMASK:
+                               fprintf(file, "Umask-%02u : 0x%02"PRIx64" : %s : [%s] : ",
+                                               um, ainfo.code, src, ainfo.name);
+                               perf_print_attr_flags(&ainfo, file);
+                               fputc(':', file);
+                               if (ainfo.equiv)
+                                       fprintf(file, " Alias to %s", ainfo.equiv);
+                               else
+                                       fprintf(file, " %s", ainfo.desc);
+                               fputc('\n', file);
+                               um++;
+                               break;
+                       case PFM_ATTR_MOD_BOOL:
+                               fprintf(file, "Modif-%02u : 0x%02"PRIx64" : %s : [%s] : "
+                                               "%s (boolean)\n", mod, ainfo.code, src, ainfo.name,
+                                               ainfo.desc);
+                               mod++;
+                               break;
+                       case PFM_ATTR_MOD_INTEGER:
+                               fprintf(file, "Modif-%02u : 0x%02"PRIx64" : %s : [%s] : "
+                                               "%s (integer)\n", mod, ainfo.code, src, ainfo.name,
+                                               ainfo.desc);
+                               mod++;
+                               break;
+                       default:
+                               fprintf(file, "Attr-%02u  : 0x%02"PRIx64" : %s : [%s] : %s\n",
+                                               i, ainfo.code, ainfo.name, src, ainfo.desc);
+               }
+       }
+}
+
+void perf_show_events(const char *rx, FILE *file)
+{
+       int pmu;
+       regex_t crx;
+       pfm_pmu_info_t pinfo;
+       pfm_event_info_t info;
+       char fullname[256];
+
+       if (rx && regcomp(&crx, rx, REG_ICASE)) {
+               fprintf(stderr, "Failed to compile event regex: '%s'\n", rx);
+               exit(1);
+       }
+
+    ZERO_DATA(pinfo);
+    pinfo.size = sizeof(pinfo);
+    ZERO_DATA(info);
+    info.size = sizeof(info);
+
+       pfm_for_all_pmus(pmu) {
+               pfm_err_t err = pfm_get_pmu_info(pmu, &pinfo);
+
+               if (err != PFM_SUCCESS || !pinfo.is_present)
+                       continue;
+
+               for (int i = pinfo.first_event; i != -1; i = pfm_get_event_next(i)) {
+                       err = pfm_get_event_info(i, PFM_OS_NONE, &info);
+                       if (err != PFM_SUCCESS) {
+                               fprintf(stderr, "Failed to get event info: %s\n",
+                                               pfm_strerror(err));
+                               exit(1);
+                       }
+                       snprintf(fullname, sizeof(fullname), "%s::%s", pinfo.name,
+                                        info.name);
+                       if (!rx || regexec(&crx, fullname, 0, NULL, 0) == 0)
+                               perf_show_event_info(&info, &pinfo, file);
+               }
+       }
+       if (rx)
+               regfree(&crx);
+}
+
+void perf_convert_trace_data(struct perfconv_context *cctx, const char *input,
+                                                        FILE *outfile)
+{
+       FILE *infile;
+       size_t ksize;
+
+       infile = xfopen(input, "rb");
+       if (xfsize(infile) > 0) {
+               perfconv_add_kernel_mmap(cctx);
+               perfconv_add_kernel_buildid(cctx);
+               perfconv_process_input(cctx, infile, outfile);
+       }
+       fclose(infile);
+}
diff --git a/tools/dev-util/perf/perf_core.h b/tools/dev-util/perf/perf_core.h
new file mode 100644 (file)
index 0000000..ef67083
--- /dev/null
@@ -0,0 +1,80 @@
+/* Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ */
+
+#pragma once
+
+#include <ros/arch/arch.h>
+#include <ros/arch/perfmon.h>
+#include <ros/common.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <limits.h>
+#include "akaros.h"
+#include "perfconv.h"
+
+#define MAX_CPU_EVENTS 256
+
+struct perf_arch_info {
+       uint32_t perfmon_version;
+       uint32_t proc_arch_events;
+       uint32_t bits_x_counter;
+       uint32_t counters_x_proc;
+       uint32_t bits_x_fix_counter;
+       uint32_t fix_counters_x_proc;
+};
+
+#define MAX_FQSTR_SZ 128
+struct perf_eventsel {
+       struct perfmon_event ev;
+       bool attr_emitted;
+       uint32_t type;
+       uint64_t config;
+       char fq_str[MAX_FQSTR_SZ];
+};
+
+struct perf_event {
+       struct core_set cores;
+       struct perf_eventsel sel;
+       int ped;
+};
+
+struct perf_context_config {
+       const char *perf_file;
+       const char *kpctl_file;
+       const char *kpdata_file;
+};
+
+struct perf_context {
+       struct perf_context_config *cfg;
+       int perf_fd;
+       int kpctl_fd;
+       struct perf_arch_info pai;
+       int event_count;
+       struct perf_event events[MAX_CPU_EVENTS];
+};
+
+void perf_initialize(void);
+void perf_finalize(void);
+struct perf_eventsel *perf_parse_event(const char *str);
+struct perf_context *perf_create_context(struct perf_context_config *cfg);
+void perf_free_context(struct perf_context *pctx);
+void perf_context_event_submit(struct perf_context *pctx,
+                                                          const struct core_set *cores,
+                                                          const struct perf_eventsel *sel);
+void perf_stop_events(struct perf_context *pctx);
+void perf_start_sampling(struct perf_context *pctx);
+void perf_stop_sampling(struct perf_context *pctx);
+uint64_t perf_get_event_count(struct perf_context *pctx, unsigned int idx);
+void perf_context_show_events(struct perf_context *pctx, FILE *file);
+void perf_show_events(const char *rx, FILE *file);
+void perf_convert_trace_data(struct perfconv_context *cctx, const char *input,
+                                                        FILE *outfile);
+
+static inline const struct perf_arch_info *perf_context_get_arch_info(
+       const struct perf_context *pctx)
+{
+       return &pctx->pai;
+}
diff --git a/tools/dev-util/perf/perf_format.h b/tools/dev-util/perf/perf_format.h
new file mode 100644 (file)
index 0000000..f355aa1
--- /dev/null
@@ -0,0 +1,578 @@
+/* Copyright (C) 2009-2016, the Linux Perf authors
+ * Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ *
+ * Parts of this file come, either directly, or as baseline, from the Linux
+ * kernel source file:
+ *
+ *      tools/perf/util/header.h
+ *
+ * Such file is ruled by the general Linux kernel copyright.
+ */
+
+#pragma once
+
+#define BITS_PER_LONG (8 * sizeof(long))
+#define BITS_TO_LONGS(bits) (((bits) + BITS_PER_LONG - 1) / BITS_PER_LONG)
+#define DECLARE_BITMAP(name, bits)                             \
+       unsigned long name[BITS_TO_LONGS(bits)]
+
+enum {
+       HEADER_RESERVED         = 0,    /* Always cleared */
+       HEADER_FIRST_FEATURE    = 1,
+       HEADER_TRACING_DATA     = 1,
+       HEADER_BUILD_ID,
+
+       HEADER_HOSTNAME,
+       HEADER_OSRELEASE,
+       HEADER_VERSION,
+       HEADER_ARCH,
+       HEADER_NRCPUS,
+       HEADER_CPUDESC,
+       HEADER_CPUID,
+       HEADER_TOTAL_MEM,
+       HEADER_CMDLINE,
+       HEADER_EVENT_DESC,
+       HEADER_CPU_TOPOLOGY,
+       HEADER_NUMA_TOPOLOGY,
+       HEADER_BRANCH_STACK,
+       HEADER_PMU_MAPPINGS,
+       HEADER_GROUP_DESC,
+       HEADER_LAST_FEATURE,
+       HEADER_FEAT_BITS        = 256,
+};
+
+/*
+ * Bits that can be set in attr.sample_type to request information
+ * in the overflow packets.
+ */
+enum perf_event_sample_format {
+       PERF_SAMPLE_IP                          = 1U << 0,
+       PERF_SAMPLE_TID                         = 1U << 1,
+       PERF_SAMPLE_TIME                        = 1U << 2,
+       PERF_SAMPLE_ADDR                        = 1U << 3,
+       PERF_SAMPLE_READ                        = 1U << 4,
+       PERF_SAMPLE_CALLCHAIN                   = 1U << 5,
+       PERF_SAMPLE_ID                          = 1U << 6,
+       PERF_SAMPLE_CPU                         = 1U << 7,
+       PERF_SAMPLE_PERIOD                      = 1U << 8,
+       PERF_SAMPLE_STREAM_ID                   = 1U << 9,
+       PERF_SAMPLE_RAW                         = 1U << 10,
+       PERF_SAMPLE_BRANCH_STACK                = 1U << 11,
+       PERF_SAMPLE_REGS_USER                   = 1U << 12,
+       PERF_SAMPLE_STACK_USER                  = 1U << 13,
+       PERF_SAMPLE_WEIGHT                      = 1U << 14,
+       PERF_SAMPLE_DATA_SRC                    = 1U << 15,
+       PERF_SAMPLE_IDENTIFIER                  = 1U << 16,
+       PERF_SAMPLE_TRANSACTION                 = 1U << 17,
+       PERF_SAMPLE_REGS_INTR                   = 1U << 18,
+
+       PERF_SAMPLE_MAX = 1U << 19,             /* non-ABI */
+};
+
+enum perf_event_type {
+       /*
+        * If perf_event_attr.sample_id_all is set then all event types will
+        * have the sample_type selected fields related to where/when
+        * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
+        * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
+        * just after the perf_event_header and the fields already present for
+        * the existing fields, i.e. at the end of the payload. That way a newer
+        * perf.data file will be supported by older perf tools, with these new
+        * optional fields being ignored.
+        *
+        * struct sample_id {
+        *      { u32                   pid, tid; } && PERF_SAMPLE_TID
+        *      { u64                   time;     } && PERF_SAMPLE_TIME
+        *      { u64                   id;               } && PERF_SAMPLE_ID
+        *      { u64                   stream_id;} && PERF_SAMPLE_STREAM_ID
+        *      { u32                   cpu, res; } && PERF_SAMPLE_CPU
+        *      { u64                   id;       } && PERF_SAMPLE_IDENTIFIER
+        * } && perf_event_attr::sample_id_all
+        *
+        * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.  The
+        * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
+        * relative to header.size.
+        */
+
+       /*
+        * The MMAP events record the PROT_EXEC mappings so that we can
+        * correlate userspace IPs to code. They have the following structure:
+        *
+        * struct {
+        *      struct perf_event_header        header;
+        *
+        *      u32                             pid, tid;
+        *      u64                             addr;
+        *      u64                             len;
+        *      u64                             pgoff;
+        *      char                            filename[];
+        * };
+        */
+       PERF_RECORD_MMAP                        = 1,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *      u64                             id;
+        *      u64                             lost;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_LOST                        = 2,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *
+        *      u32                             pid, tid;
+        *      char                            comm[];
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_COMM                        = 3,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *      u32                             pid, ppid;
+        *      u32                             tid, ptid;
+        *      u64                             time;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_EXIT                        = 4,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *      u64                             time;
+        *      u64                             id;
+        *      u64                             stream_id;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_THROTTLE                    = 5,
+       PERF_RECORD_UNTHROTTLE                  = 6,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *      u32                             pid, ppid;
+        *      u32                             tid, ptid;
+        *      u64                             time;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_FORK                        = 7,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *      u32                             pid, tid;
+        *
+        *      struct read_format              values;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_READ                        = 8,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *
+        *      #
+        *      # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
+        *      # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
+        *      # is fixed relative to header.
+        *      #
+        *
+        *      { u64                   id;       } && PERF_SAMPLE_IDENTIFIER
+        *      { u64                   ip;       } && PERF_SAMPLE_IP
+        *      { u32                   pid, tid; } && PERF_SAMPLE_TID
+        *      { u64                   time;     } && PERF_SAMPLE_TIME
+        *      { u64                   addr;     } && PERF_SAMPLE_ADDR
+        *      { u64                   id;       } && PERF_SAMPLE_ID
+        *      { u64                   stream_id;} && PERF_SAMPLE_STREAM_ID
+        *      { u32                   cpu, res; } && PERF_SAMPLE_CPU
+        *      { u64                   period;   } && PERF_SAMPLE_PERIOD
+        *
+        *      { struct read_format    values;   } && PERF_SAMPLE_READ
+        *
+        *      { u64                   nr,
+        *        u64                   ips[nr];  } && PERF_SAMPLE_CALLCHAIN
+        *
+        *      #
+        *      # The RAW record below is opaque data wrt the ABI
+        *      #
+        *      # That is, the ABI doesn't make any promises wrt to
+        *      # the stability of its content, it may vary depending
+        *      # on event, hardware, kernel version and phase of
+        *      # the moon.
+        *      #
+        *      # In other words, PERF_SAMPLE_RAW contents are not an ABI.
+        *      #
+        *
+        *      { u32                   size;
+        *        char                                  data[size];}&& PERF_SAMPLE_RAW
+        *
+        *      { u64                                   nr;
+        *                { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+        *
+        *      { u64                   abi; # enum perf_sample_regs_abi
+        *        u64                   regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
+        *
+        *      { u64                   size;
+        *        char                  data[size];
+        *        u64                   dyn_size; } && PERF_SAMPLE_STACK_USER
+        *
+        *      { u64                   weight;   } && PERF_SAMPLE_WEIGHT
+        *      { u64                   data_src; } && PERF_SAMPLE_DATA_SRC
+        *              { u64                                   transaction; } && PERF_SAMPLE_TRANSACTION
+        *      { u64                   abi; # enum perf_sample_regs_abi
+        *        u64                   regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR
+        * };
+        */
+       PERF_RECORD_SAMPLE                      = 9,
+
+       /*
+        * The MMAP2 records are an augmented version of MMAP, they add
+        * maj, min, ino numbers to be used to uniquely identify each mapping
+        *
+        * struct {
+        *      struct perf_event_header        header;
+        *
+        *      u32                             pid, tid;
+        *      u64                             addr;
+        *      u64                             len;
+        *      u64                             pgoff;
+        *      u32                             maj;
+        *      u32                             min;
+        *      u64                             ino;
+        *      u64                             ino_generation;
+        *      char                            filename[];
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_MMAP2                       = 10,
+
+       PERF_RECORD_MAX,                        /* non-ABI */
+};
+
+#define PERF_MAX_STACK_DEPTH           127
+
+enum perf_callchain_context {
+       PERF_CONTEXT_HV                         = (__uint64_t) -32,
+       PERF_CONTEXT_KERNEL                     = (__uint64_t) -128,
+       PERF_CONTEXT_USER                       = (__uint64_t) -512,
+
+       PERF_CONTEXT_GUEST                      = (__uint64_t) -2048,
+       PERF_CONTEXT_GUEST_KERNEL       = (__uint64_t) -2176,
+       PERF_CONTEXT_GUEST_USER         = (__uint64_t) -2560,
+
+       PERF_CONTEXT_MAX                        = (__uint64_t) -4095,
+};
+
+/*
+ * attr.type
+ */
+enum perf_type_id {
+       PERF_TYPE_HARDWARE                                              = 0,
+       PERF_TYPE_SOFTWARE                                              = 1,
+       PERF_TYPE_TRACEPOINT                                    = 2,
+       PERF_TYPE_HW_CACHE                                              = 3,
+       PERF_TYPE_RAW                                                   = 4,
+       PERF_TYPE_BREAKPOINT                                    = 5,
+       PERF_TYPE_INTEL_CQM                                             = 6,
+
+       PERF_TYPE_MAX,                                                  /* non-ABI */
+};
+
+/*
+ * Generalized performance event event_id types, used by the
+ * attr.event_id parameter of the sys_perf_event_open()
+ * syscall:
+ */
+enum perf_hw_id {
+       /*
+        * Common hardware events, generalized by the kernel:
+        */
+       PERF_COUNT_HW_CPU_CYCLES                                = 0,
+       PERF_COUNT_HW_INSTRUCTIONS                              = 1,
+       PERF_COUNT_HW_CACHE_REFERENCES                  = 2,
+       PERF_COUNT_HW_CACHE_MISSES                              = 3,
+       PERF_COUNT_HW_BRANCH_INSTRUCTIONS               = 4,
+       PERF_COUNT_HW_BRANCH_MISSES                             = 5,
+       PERF_COUNT_HW_BUS_CYCLES                                = 6,
+       PERF_COUNT_HW_STALLED_CYCLES_FRONTEND   = 7,
+       PERF_COUNT_HW_STALLED_CYCLES_BACKEND    = 8,
+       PERF_COUNT_HW_REF_CPU_CYCLES                    = 9,
+
+       PERF_COUNT_HW_MAX,                                              /* non-ABI */
+};
+
+/* We can output a bunch of different versions of perf_event_attr.  The oldest
+ * Linux perf I've run across expects version 3 and can't handle anything
+ * larger.  Since we're not using anything from versions 1 or higher, we can sit
+ * at version 0 for now. */
+#define PERF_ATTR_VER0
+
+#ifdef PERF_ATTR_VER1
+       #define __PERF_ATTR_VER1 1
+#endif
+#ifdef PERF_ATTR_VER2
+       #define __PERF_ATTR_VER1 1
+       #define __PERF_ATTR_VER2 1
+#endif
+#ifdef PERF_ATTR_VER3
+       #define __PERF_ATTR_VER1 1
+       #define __PERF_ATTR_VER2 1
+       #define __PERF_ATTR_VER3 1
+#endif
+#ifdef PERF_ATTR_VER4
+       #define __PERF_ATTR_VER1 1
+       #define __PERF_ATTR_VER2 1
+       #define __PERF_ATTR_VER3 1
+       #define __PERF_ATTR_VER4 1
+#endif
+
+/*
+ * Hardware event_id to monitor via a performance monitoring event:
+ */
+struct perf_event_attr {
+       /*
+        * Major type: hardware/software/tracepoint/etc.
+        */
+       uint32_t type;
+
+       /*
+        * Size of the attr structure, for fwd/bwd compat.
+        */
+       uint32_t size;
+
+       /*
+        * Type specific configuration information.
+        */
+       uint64_t config;
+
+       union {
+               uint64_t sample_period;
+               uint64_t sample_freq;
+       };
+
+       uint64_t sample_type;
+       uint64_t read_format;
+
+       uint64_t disabled               :  1, /* off by default            */
+               inherit            :  1, /* children inherit it   */
+               pinned             :  1, /* must always be on PMU */
+               exclusive          :  1, /* only group on PMU     */
+               exclude_user   :  1, /* don't count user          */
+               exclude_kernel :  1, /* ditto kernel              */
+               exclude_hv         :  1, /* ditto hypervisor      */
+               exclude_idle   :  1, /* don't count when idle */
+               mmap               :  1, /* include mmap data     */
+               comm               :  1, /* include comm data     */
+               freq               :  1, /* use freq, not period  */
+               inherit_stat   :  1, /* per task counts           */
+               enable_on_exec :  1, /* next exec enables         */
+               task               :  1, /* trace fork/exit               */
+               watermark          :  1, /* wakeup_watermark      */
+       /*
+        * precise_ip:
+        *
+        *      0 - SAMPLE_IP can have arbitrary skid
+        *      1 - SAMPLE_IP must have constant skid
+        *      2 - SAMPLE_IP requested to have 0 skid
+        *      3 - SAMPLE_IP must have 0 skid
+        *
+        *      See also PERF_RECORD_MISC_EXACT_IP
+        */
+               precise_ip         :  2, /* skid constraint               */
+               mmap_data          :  1, /* non-exec mmap data    */
+               sample_id_all  :  1, /* sample_type all events */
+
+               exclude_host   :  1, /* don't count in host       */
+               exclude_guest  :  1, /* don't count in guest  */
+
+               exclude_callchain_kernel : 1, /* exclude kernel callchains */
+               exclude_callchain_user   : 1, /* exclude user callchains */
+               mmap2              :  1, /* include mmap with inode data         */
+
+               __reserved_1   : 40;
+
+       union {
+               uint32_t wakeup_events;   /* wakeup every n events */
+               uint32_t wakeup_watermark; /* bytes before wakeup       */
+       };
+
+       uint32_t bp_type;
+       union {
+               uint64_t bp_addr;
+               uint64_t config1; /* extension of config */
+       };
+
+#ifdef __PERF_ATTR_VER1
+       union {
+               uint64_t bp_len;
+               uint64_t config2; /* extension of config1 */
+       };
+
+# ifdef __PERF_ATTR_VER2
+       uint64_t branch_sample_type; /* enum perf_branch_sample_type */
+
+#  ifdef __PERF_ATTR_VER3
+       /*
+        * Defines set of user regs to dump on samples.
+        * See asm/perf_regs.h for details.
+        */
+       uint64_t sample_regs_user;
+
+       /*
+        * Defines size of the user stack to dump on samples.
+        */
+       uint32_t sample_stack_user;
+
+#   ifdef __PERF_ATTR_VER4
+       /* Align to u64. */
+       uint32_t __reserved_2;
+
+       /*
+        * Defines set of regs to dump for each sample
+        * state captured on:
+        *      - precise = 0: PMU interrupt
+        *      - precise > 0: sampled instruction
+        *
+        * See asm/perf_regs.h for details.
+        */
+       uint64_t sample_regs_intr;
+#   endif /* __PERF_ATTR_VER4 */
+#  endif /* __PERF_ATTR_VER3 */
+# endif /* __PERF_ATTR_VER2 */
+#endif /* __PERF_ATTR_VER1 */
+} __attribute__((packed));
+
+#define PERF_RECORD_MISC_CPUMODE_MASK          (7 << 0)
+#define PERF_RECORD_MISC_CPUMODE_UNKNOWN       (0 << 0)
+#define PERF_RECORD_MISC_KERNEL                        (1 << 0)
+#define PERF_RECORD_MISC_USER                  (2 << 0)
+#define PERF_RECORD_MISC_HYPERVISOR            (3 << 0)
+#define PERF_RECORD_MISC_GUEST_KERNEL          (4 << 0)
+#define PERF_RECORD_MISC_GUEST_USER            (5 << 0)
+
+#define PERF_RECORD_MISC_MMAP_DATA             (1 << 13)
+/*
+ * Indicates that the content of PERF_SAMPLE_IP points to
+ * the actual instruction that triggered the event. See also
+ * perf_event_attr::precise_ip.
+ */
+#define PERF_RECORD_MISC_EXACT_IP              (1 << 14)
+/*
+ * Reserve the last bit to indicate some extended misc field
+ */
+#define PERF_RECORD_MISC_EXT_RESERVED          (1 << 15)
+
+struct perf_event_header {
+       uint32_t type;
+       uint16_t misc;
+       uint16_t size;
+} __attribute__((packed));
+
+struct perf_file_section {
+       uint64_t offset;        /* Offset from start of file */
+       uint64_t size;          /* Size of the section */
+} __attribute__((packed));
+
+#define PERF_STRING_ALIGN                                              64
+
+struct perf_header_string {
+       uint32_t len;
+       char string[0];         /* Zero terminated */
+};
+
+struct perf_header_string_list {
+       uint32_t nr;
+       struct perf_header_string strings[0];   /* Variable length records */
+};
+
+struct nr_cpus {
+       uint32_t                                        nr_cpus_online;
+       uint32_t                                        nr_cpus_available;
+};
+
+struct build_id_event {
+       struct perf_event_header        header;
+       pid_t                                           pid;
+       uint8_t                                         build_id[24];   /* BUILD_ID_SIZE aligned u64 */
+       char                                            filename[];
+};
+
+#define MAX_EVENT_NAME 64
+
+struct perf_trace_event_type {
+       uint64_t event_id;
+       char name[MAX_EVENT_NAME];
+};
+
+struct perf_file_attr {
+       struct perf_event_attr attr;
+       struct perf_file_section ids;
+};
+
+/* "PERFILE2"
+ */
+static const uint64_t PERF_MAGIC2 = 0x32454c4946524550ULL;
+
+struct perf_pipe_file_header {
+       uint64_t magic;                                 /* PERFILE2 */
+       uint64_t size;
+};
+
+struct perf_header {
+       uint64_t magic;                                 /* PERFILE2 */
+       uint64_t size;                                  /* Size of the header */
+       uint64_t attr_size;                             /* size of an attribute in attrs */
+       struct perf_file_section attrs;
+       struct perf_file_section data;
+       struct perf_file_section event_types;
+       DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+} __attribute__((packed));
+
+/* For type PERF_RECORD_MMAP
+ */
+struct perf_record_mmap {
+       struct perf_event_header header;
+       uint32_t pid;
+       uint32_t tid;
+       uint64_t addr;
+       uint64_t len;
+       uint64_t pgoff;
+       char filename[0];
+} __attribute__((packed));
+
+/* For type PERF_RECORD_COMM
+ */
+struct perf_record_comm {
+       struct perf_event_header header;
+       uint32_t pid;
+       uint32_t tid;
+       char comm[0];
+} __attribute__((packed));
+
+/* For type PERF_RECORD_SAMPLE
+ *
+ * Configured with: PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
+ * PERF_SAMPLE_ADDR | PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_CPU |
+ * PERF_SAMPLE_CALLCHAIN. */
+struct perf_record_sample {
+       struct perf_event_header header;
+       uint64_t identifier;
+       uint64_t ip;
+       uint32_t pid, tid;
+       uint64_t time;
+       uint64_t addr;
+       uint32_t cpu, res;
+       uint64_t nr;
+       uint64_t ips[0];
+} __attribute__((packed));
diff --git a/tools/dev-util/perf/perfconv.c b/tools/dev-util/perf/perfconv.c
new file mode 100644 (file)
index 0000000..f00a222
--- /dev/null
@@ -0,0 +1,833 @@
+/* Copyright (c) 2015-2016 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ *
+ * See LICENSE for details.
+ *
+ * Converts kprof profiler files into Linux perf ones. The Linux Perf file
+ * format has bee illustrated here:
+ *
+ *      https://lwn.net/Articles/644919/
+ *      https://openlab-mu-internal.web.cern.ch/openlab-mu-internal/03_Documents/
+ *                      3_Technical_Documents/Technical_Reports/2011/Urs_Fassler_report.pdf
+ *
+ */
+
+#include <ros/common.h>
+#include <ros/memops.h>
+#include <ros/profiler_records.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include "perf_format.h"
+#include "xlib.h"
+#include "perf_core.h"
+#include "perfconv.h"
+#include "elf.h"
+
+#define MAX_PERF_RECORD_SIZE (32 * 1024 * 1024)
+#define PERF_RECORD_BUFFER_SIZE 1024
+#define OFFSET_NORELOC ((uint64_t) -1)
+#define MEMFILE_BLOCK_SIZE (64 * 1024)
+
+#define MBWR_SOLID (1 << 0)
+
+char *cmd_line_save;
+
+struct perf_record {
+       uint64_t type;
+       uint64_t size;
+       char *data;
+       char buffer[PERF_RECORD_BUFFER_SIZE];
+};
+
+static void dbg_print(struct perfconv_context *cctx, int level, FILE *file,
+                                         const char *fmt, ...)
+{
+       if (cctx->debug_level >= level) {
+               va_list args;
+
+               va_start(args, fmt);
+               vfprintf(file, fmt, args);
+               va_end(args);
+       }
+}
+
+void perfconv_set_dbglevel(int level, struct perfconv_context *cctx)
+{
+       cctx->debug_level = level;
+}
+
+static void free_record(struct perf_record *pr)
+{
+       if (pr->data != pr->buffer)
+               free(pr->data);
+       pr->data = NULL;
+}
+
+static int read_record(FILE *file, struct perf_record *pr)
+{
+       if (vb_fdecode_uint64(file, &pr->type) == EOF ||
+               vb_fdecode_uint64(file, &pr->size) == EOF)
+               return EOF;
+       if (pr->size > MAX_PERF_RECORD_SIZE) {
+               fprintf(stderr, "Invalid record size: type=%lu size=%lu\n", pr->type,
+                               pr->size);
+               exit(1);
+       }
+       if (pr->size > sizeof(pr->buffer))
+               pr->data = xmalloc((size_t) pr->size);
+       else
+               pr->data = pr->buffer;
+       if (fread(pr->data, 1, (size_t) pr->size, file) != (size_t) pr->size) {
+               fprintf(stderr, "Unable to read record memory: size=%lu\n",
+                               pr->size);
+               return EOF;
+       }
+
+       return 0;
+}
+
+static struct mem_block *mem_block_alloc(size_t size)
+{
+       struct mem_block *mb = xmalloc(sizeof(struct mem_block) + size);
+
+       mb->next = NULL;
+       mb->base = mb->wptr = (char *) mb + sizeof(struct mem_block);
+       mb->top = mb->base + size;
+
+       return mb;
+}
+
+static char *mem_block_write(struct mem_block *mb, const void *data,
+                                                        size_t size)
+{
+       char *wrbase = mb->wptr;
+
+       always_assert(size <= mb->top - mb->wptr);
+
+       memcpy(mb->wptr, data, size);
+       mb->wptr += size;
+
+       return wrbase;
+}
+
+static void mem_file_init(struct mem_file *mf)
+{
+       ZERO_DATA(*mf);
+}
+
+static int mem_block_can_write(struct mem_block *mb, size_t size, int flags)
+{
+       size_t space = mb->top - mb->wptr;
+
+       return (flags & MBWR_SOLID) ? (space >= size) : (space > 0);
+}
+
+static void *mem_file_write(struct mem_file *mf, const void *data, size_t size,
+                                                       int flags)
+{
+       void *wrbase = NULL;
+
+       while (size > 0) {
+               size_t space, csize;
+               struct mem_block *mb = mf->tail;
+
+               if (!mb || !mem_block_can_write(mb, size, flags)) {
+                       mb = mem_block_alloc(max(MEMFILE_BLOCK_SIZE, size));
+                       if (!mf->tail)
+                               mf->head = mb;
+                       else
+                               mf->tail->next = mb;
+                       mf->tail = mb;
+               }
+               space = mb->top - mb->wptr;
+               csize = min(size, space);
+
+               wrbase = mem_block_write(mb, data, csize);
+               mf->size += csize;
+
+               size -= csize;
+               data = (const char *) data + csize;
+       }
+
+       return wrbase;
+}
+
+static void mem_file_sync(struct mem_file *mf, FILE *file, uint64_t rel_offset)
+{
+       struct mem_block *mb;
+
+       if (rel_offset != 0) {
+               struct mem_file_reloc *rel;
+
+               always_assert(!mf->relocs || rel_offset != OFFSET_NORELOC);
+
+               for (rel = mf->relocs; rel; rel = rel->next)
+                       *rel->ptr += rel_offset;
+       }
+
+       for (mb = mf->head; mb; mb = mb->next)
+               xfwrite(mb->base, mb->wptr - mb->base, file);
+}
+
+static struct mem_file_reloc *mem_file_add_reloc(struct mem_file *mf,
+                                                                                                uint64_t *ptr)
+{
+       struct mem_file_reloc *rel = xzmalloc(sizeof(struct mem_file_reloc));
+
+       rel->ptr = ptr;
+       rel->next = mf->relocs;
+       mf->relocs = rel;
+
+       return rel;
+}
+
+void perfconv_add_kernel_mmap(struct perfconv_context *cctx)
+{
+       char path[] = "[kernel.kallsyms]";
+       struct static_mmap64 *mm;
+
+       mm = xzmalloc(sizeof(struct static_mmap64));
+       mm->pid = -1;                           /* Linux HOST_KERNEL_ID == -1 */
+       mm->tid = 0;                            /* Default thread: swapper */
+       mm->header_misc = PERF_RECORD_MISC_KERNEL;
+       /* Linux sets addr = 0, size = 0xffffffff9fffffff, off = 0xffffffff81000000
+        * Their mmap record is also called [kernel.kallsyms]_text (I think).  They
+        * also have a _text symbol in kallsyms at ffffffff81000000 (equiv to our
+        * KERN_LOAD_ADDR (which is 0xffffffffc0000000)).  Either way, this seems to
+        * work for us; we'll see.  It's also arch-independent (for now). */
+       mm->addr = 0;
+       mm->size = 0xffffffffffffffff;
+       mm->offset = 0x0;
+       mm->path = xstrdup(path);
+
+       mm->next = cctx->static_mmaps;
+       cctx->static_mmaps = mm;
+}
+
+static void headers_init(struct perf_headers *hdrs)
+{
+       ZERO_DATA(*hdrs);
+}
+
+/* Prepends a header (the mem_block) to the list of memblocks for the given
+ * HEADER type.  They will all be concatted together during finalization. */
+static void headers_add_header(struct perf_header *ph,
+                               struct perf_headers *hdrs, size_t nhdr,
+                                                          struct mem_block *mb)
+{
+       always_assert(nhdr < HEADER_FEAT_BITS);
+
+       mb->next = hdrs->headers[nhdr];
+       hdrs->headers[nhdr] = mb;
+       set_bitno(ph->adds_features, nhdr);
+}
+
+/* Emits the headers contents to the mem_file */
+static void headers_finalize(struct perf_headers *hdrs, struct mem_file *mf)
+{
+       struct perf_file_section *header_file_secs, *file_sec;
+       struct perf_file_section *file_sec_reloc;
+       size_t nr_hdrs = 0;
+       size_t hdr_off;
+       struct mem_block *mb;
+       size_t mb_sz;
+
+       /* For each header, we need a perf_file_section.  These header file sections
+        * are right after the main perf header, and they point to actual header. */
+       for (int i = 0; i < HEADER_FEAT_BITS; i++)
+               if (hdrs->headers[i])
+                       nr_hdrs++;
+       if (!nr_hdrs)
+               return;
+       header_file_secs = xmalloc(sizeof(struct perf_file_section) * nr_hdrs);
+
+       hdr_off = sizeof(struct perf_file_section) * nr_hdrs;
+       file_sec = header_file_secs;
+
+       /* Spit out the perf_file_sections first and track relocations for all of
+        * the offsets. */
+       for (int i = 0; i < HEADER_FEAT_BITS; i++) {
+               mb = hdrs->headers[i];
+               if (!mb)
+                       continue;
+               mb_sz = mb->wptr - mb->base;
+               /* headers[i] is a chain of memblocks */
+               while (mb->next) {
+                       mb = mb->next;
+                       mb_sz += mb->wptr - mb->base;
+               }
+               file_sec->size = mb_sz;
+               file_sec->offset = hdr_off;             /* offset rel to this memfile */
+               /* When we sync the memfile, we'll need to relocate each of the offsets
+                * so that they are relative to the final file.   mem_file_write()
+                * should be returning the location of where it wrote our file section
+                * within the memfile.  that's the offset that we'll reloc later. */
+               file_sec_reloc = mem_file_write(mf, file_sec,
+                                               sizeof(struct perf_file_section), 0);
+               assert(file_sec->size == file_sec_reloc->size);
+               assert(file_sec->offset == file_sec_reloc->offset);
+               mem_file_add_reloc(mf, &file_sec_reloc->offset);
+
+               hdr_off += mb_sz;
+               file_sec++;
+       }
+       free(header_file_secs);
+
+       /* Spit out the actual headers */
+       for (int i = 0; i < HEADER_FEAT_BITS; i++) {
+               mb = hdrs->headers[i];
+               while (mb) {
+                       mem_file_write(mf, mb->base, mb->wptr - mb->base, 0);
+                       mb = mb->next;
+               }
+       }
+}
+
+/* Builds a struct perf_header_string from str and returns it in a mem_block */
+static struct mem_block *header_make_string(const char *str)
+{
+       struct perf_header_string *hdr;
+       struct mem_block *mb;
+       size_t str_sz = strlen(str) + 1;
+       size_t hdr_sz = ROUNDUP(str_sz + sizeof(struct perf_header_string),
+                               PERF_STRING_ALIGN);
+
+       mb = mem_block_alloc(hdr_sz);
+       /* Manually writing to the block to avoid another alloc.  I guess I could do
+        * two writes (len and string) and try to not screw it up, but that'd be a
+        * mess. */
+       hdr = (struct perf_header_string*)mb->wptr;
+       mb->wptr += hdr_sz;
+       hdr->len = str_sz;
+       memcpy(hdr->string, str, str_sz);
+       return mb;
+}
+
+/* Opens and reads filename, returning the contents.  Free the ret. */
+static char *get_str_from_os(const char *filename)
+{
+       int fd, ret;
+       struct stat fd_stat;
+       char *buf;
+       size_t buf_sz;
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0)
+               return 0;
+       ret = fstat(fd, &fd_stat);
+       if (ret) {
+               close(fd);
+               return 0;
+       }
+       buf_sz = fd_stat.st_size + 1;
+       buf = xmalloc(buf_sz);
+       ret = read(fd, buf, buf_sz - 1);
+       if (ret <= 0) {
+               free(buf);
+               close(fd);
+               return 0;
+       }
+       close(fd);
+       /* OS strings should be null terminated, but let's be defensive */
+       buf[ret] = 0;
+       return buf;
+}
+
+static void hdr_do_osrelease(struct perf_header *ph, struct perf_headers *hdrs)
+{
+       char *str;
+
+       str = get_str_from_os("#version/version_name");
+       if (!str)
+               return;
+       headers_add_header(ph, hdrs, HEADER_OSRELEASE, header_make_string(str));
+       free(str);
+}
+
+static void hdr_do_nrcpus(struct perf_header *ph, struct perf_headers *hdrs)
+{
+       char *str;
+       uint32_t nr_cores;
+       struct mem_block *mb;
+       struct nr_cpus *hdr;
+
+       str = get_str_from_os("#vars/num_cores!dw");
+       if (!str)
+               return;
+       nr_cores = atoi(str);
+       free(str);
+
+       mb = mem_block_alloc(sizeof(struct nr_cpus));
+       hdr = (struct nr_cpus*)mb->wptr;
+       mb->wptr += sizeof(struct nr_cpus);
+
+       hdr->nr_cpus_online = nr_cores;
+       hdr->nr_cpus_available = nr_cores;
+
+       headers_add_header(ph, hdrs, HEADER_NRCPUS, mb);
+}
+
+/* Unfortunately, HEADER_CMDLINE doesn't just take a string.  It takes a list of
+ * null-terminated perf_header_strings (i.e. argv), with the a u32 for the
+ * number of strings.  We can just send one string for the entire cmd line. */
+static void hdr_do_cmdline(struct perf_header *ph, struct perf_headers *hdrs)
+{
+       struct perf_header_string *hdr;
+       struct mem_block *mb;
+       size_t str_sz = strlen(cmd_line_save) + 1;
+       size_t hdr_sz = sizeof(uint32_t) +
+                       ROUNDUP(str_sz + sizeof(struct perf_header_string),
+                               PERF_STRING_ALIGN);
+
+       mb = mem_block_alloc(hdr_sz);
+       /* emit the nr strings (1) */
+       *(uint32_t*)mb->wptr = 1;
+       mb->wptr += sizeof(uint32_t);
+       /* emit the perf_header_string, as usual */
+       hdr = (struct perf_header_string*)mb->wptr;
+       mb->wptr += hdr_sz - sizeof(uint32_t);
+       hdr->len = str_sz;
+       memcpy(hdr->string, cmd_line_save, str_sz);
+
+       headers_add_header(ph, hdrs, HEADER_CMDLINE, mb);
+}
+
+/* Returns TRUE if we already emitted a build-id for path.  If this gets too
+ * slow (which we'll know because of perf!) we can use a hash table (don't hash
+ * on the first few bytes btw - they are usually either /bin or /lib). */
+static bool lookup_buildid(struct perfconv_context *cctx, const char *path)
+{
+       struct build_id_event *b_evt;
+       struct mem_block *mb;
+       size_t b_evt_path_sz;
+
+       mb = cctx->hdrs.headers[HEADER_BUILD_ID];
+       while (mb) {
+               b_evt = (struct build_id_event*)mb->base;
+               b_evt_path_sz = b_evt->header.size -
+                               offsetof(struct build_id_event, filename);
+               /* ignoring the last byte since we forced it to be \0 earlier. */
+               if (!strncmp(b_evt->filename, path, b_evt_path_sz - 1))
+                       return TRUE;
+               mb = mb->next;
+       }
+       return FALSE;
+}
+
+/* Helper: given a path, allocs and inits a build_id_event within a mem_block,
+ * returning both via parameters.  Caller needs to set header.misc and fill in
+ * the actual build_id. */
+static void build_id_alloc(const char *path, struct build_id_event **b_evt_p,
+                           struct mem_block **mb_p)
+{
+       struct build_id_event *b_evt;
+       struct mem_block *mb;
+       size_t path_sz, b_evt_sz;
+
+       path_sz = strlen(path) + 1;
+       b_evt_sz = path_sz + sizeof(struct build_id_event);
+
+       mb = mem_block_alloc(b_evt_sz);
+       b_evt = (struct build_id_event*)mb->wptr;
+       mb->wptr += b_evt_sz;
+
+       b_evt->header.type = 0; /* if this fails, try 67 (synthetic build id) */
+       /* header.size filled in by the caller, depending on the type */
+       b_evt->header.size = b_evt_sz;
+       strlcpy(b_evt->filename, path, path_sz);
+
+       *b_evt_p = b_evt;
+       *mb_p = mb;
+}
+
+/* Add a build-id header.  Unlike many of the other headers, this one is built
+ * on the fly as we emit other records. */
+static void hdr_add_buildid(struct perfconv_context *cctx, const char *path,
+                            int pid)
+{
+       struct build_id_event *b_evt;
+       struct mem_block *mb;
+       int ret;
+
+       if (lookup_buildid(cctx, path))
+               return;
+
+       build_id_alloc(path, &b_evt, &mb);
+       b_evt->header.misc = PERF_RECORD_MISC_USER;
+       b_evt->pid = pid;
+       ret = filename__read_build_id(path, b_evt->build_id, BUILD_ID_SIZE);
+       if (ret <= 0)
+               free(mb);
+       else
+               headers_add_header(&cctx->ph, &cctx->hdrs, HEADER_BUILD_ID, mb);
+}
+
+static void convert_str_to_binary(char *b_id_str, uint8_t *b_id_raw)
+{
+       char *c = b_id_str;
+
+       for (int i = 0; i < BUILD_ID_SIZE; i++) {
+               b_id_raw[i] = nibble_to_num(*c) << 4 | nibble_to_num(*(c + 1));
+               c += 2;
+       }
+}
+
+void perfconv_add_kernel_buildid(struct perfconv_context *cctx)
+{
+       struct build_id_event *b_evt;
+       struct mem_block *mb;
+       int ret, fd;
+       char build_id[BUILD_ID_SIZE * 2 + 1] = {0};
+
+       build_id_alloc("[kernel.kallsyms]", &b_evt, &mb);
+       b_evt->header.misc = PERF_RECORD_MISC_KERNEL;
+       b_evt->pid = -1;
+       fd = xopen("#version/build_id", O_RDONLY, 0);
+       ret = read(fd, build_id, sizeof(build_id));
+       if (ret <= 0) {
+               free(mb);
+       } else {
+               convert_str_to_binary(build_id, b_evt->build_id);
+               headers_add_header(&cctx->ph, &cctx->hdrs, HEADER_BUILD_ID, mb);
+       }
+}
+
+/* Helper: adds all the headers, marking them in PH and storing them in
+ * feat_hdrs. */
+static void headers_build(struct perf_header *ph, struct perf_headers *hdrs,
+                          struct mem_file *feat_hdrs)
+{
+       hdr_do_osrelease(ph, hdrs);
+       hdr_do_nrcpus(ph, hdrs);
+       hdr_do_cmdline(ph, hdrs);
+
+       headers_finalize(hdrs, feat_hdrs);
+}
+
+static void perf_header_init(struct perf_header *ph)
+{
+       ZERO_DATA(*ph);
+       ph->magic = PERF_MAGIC2;
+       ph->size = sizeof(*ph);
+       ph->attr_size = sizeof(struct perf_event_attr);
+}
+
+/* For each attr we emit, we push out the attr, then a perf_file_section for the
+ * id(s) for that attr.  This wasn't mentioned in
+ * https://lwn.net/Articles/644919/, but it's what Linux perf expects
+ * (util/header.c).  It looks like you can have multiple IDs per event attr.
+ * We'll only emit one.  The *contents* of the perf_file_section for the ids
+ * aren't in the attr perf_file_section, they are in a separate one
+ * (attr_id_mf).
+ *
+ * Note that *attr_mf*'s relocs are relative to the base of *attr_id_mf*, which
+ * we'll need to sort out later. */
+static void emit_attr(struct mem_file *attr_mf, struct mem_file *attr_id_mf,
+                      const struct perf_event_attr *attr, uint64_t id)
+{
+       struct perf_file_section *psids;
+       struct perf_file_section sids;
+
+       mem_file_write(attr_mf, attr, sizeof(*attr), 0);
+
+       sids.offset = attr_id_mf->size;
+       sids.size = sizeof(uint64_t);
+       mem_file_write(attr_id_mf, &id, sizeof(uint64_t), 0);
+
+       psids = mem_file_write(attr_mf, &sids, sizeof(sids), MBWR_SOLID);
+       mem_file_add_reloc(attr_mf, &psids->offset);
+}
+
+/* Given raw_info, which is what the kernel sends as user_data for a particular
+ * sample, look up the 'id' for the event/sample.  The 'id' identifies the event
+ * stream that the sample is a part of.  There are many samples per event
+ * stream, all identified by 'id.'  It doesn't matter what 'id', so long as it
+ * is unique.  We happen to use the pointer to the sample's eventsel.
+ *
+ * If this is the first time we've seen 'raw_info', we'll also add an attribute
+ * to the perf ctx.  There is one attr per 'id' / event stream. */
+static uint64_t perfconv_get_event_id(struct perfconv_context *cctx,
+                                                                         uint64_t raw_info)
+{
+       struct perf_eventsel *sel = (struct perf_eventsel*)raw_info;
+       struct perf_event_attr attr;
+       uint64_t raw_event;
+
+       assert(sel);
+       if (sel->attr_emitted)
+               return raw_info;
+       raw_event = sel->ev.event;
+       ZERO_DATA(attr);
+       attr.size = sizeof(attr);
+       attr.mmap = 1;
+       attr.comm = 1;
+       attr.sample_period = sel->ev.trigger_count;
+       /* Closely coupled with struct perf_record_sample */
+       attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
+                          PERF_SAMPLE_ADDR | PERF_SAMPLE_IDENTIFIER |
+                          PERF_SAMPLE_CPU | PERF_SAMPLE_CALLCHAIN;
+       attr.exclude_guest = 1; /* we can't trace VMs yet */
+       attr.exclude_hv = 1;    /* we aren't tracing our hypervisor, AFAIK */
+       attr.exclude_user = !PMEV_GET_USR(raw_event);
+       attr.exclude_kernel = !PMEV_GET_OS(raw_event);
+       attr.type = sel->type;
+       attr.config = sel->config;
+       emit_attr(&cctx->attrs, &cctx->attr_ids, &attr, raw_info);
+       sel->attr_emitted = TRUE;
+       return raw_info;
+}
+
+static void emit_static_mmaps(struct perfconv_context *cctx)
+{
+       struct static_mmap64 *mm;
+
+       for (mm = cctx->static_mmaps; mm; mm = mm->next) {
+               size_t size = sizeof(struct perf_record_mmap) + strlen(mm->path) + 1;
+               struct perf_record_mmap *xrec = xzmalloc(size);
+
+               xrec->header.type = PERF_RECORD_MMAP;
+               xrec->header.misc = mm->header_misc;
+               xrec->header.size = size;
+               xrec->pid = mm->pid;
+               xrec->tid = mm->tid;
+               xrec->addr = mm->addr;
+               xrec->len = mm->size;
+               xrec->pgoff = mm->offset;
+               strcpy(xrec->filename, mm->path);
+
+               mem_file_write(&cctx->data, xrec, size, 0);
+
+               free(xrec);
+       }
+}
+
+static void emit_comm(uint32_t pid, const char *comm,
+                                         struct perfconv_context *cctx)
+{
+       size_t size = sizeof(struct perf_record_comm) + strlen(comm) + 1;
+       struct perf_record_comm *xrec = xzmalloc(size);
+
+       xrec->header.type = PERF_RECORD_COMM;
+       xrec->header.misc = PERF_RECORD_MISC_USER;
+       xrec->header.size = size;
+       xrec->pid = xrec->tid = pid;
+       strcpy(xrec->comm, comm);
+
+       mem_file_write(&cctx->data, xrec, size, 0);
+
+       free(xrec);
+}
+
+static void emit_pid_mmap64(struct perf_record *pr,
+                                                       struct perfconv_context *cctx)
+{
+       struct proftype_pid_mmap64 *rec = (struct proftype_pid_mmap64 *) pr->data;
+       size_t size = sizeof(struct perf_record_mmap) +
+                     strlen((char*)rec->path) + 1;
+       struct perf_record_mmap *xrec = xzmalloc(size);
+
+       hdr_add_buildid(cctx, (char*)rec->path, rec->pid);
+
+       xrec->header.type = PERF_RECORD_MMAP;
+       xrec->header.misc = PERF_RECORD_MISC_USER;
+       xrec->header.size = size;
+       xrec->pid = xrec->tid = rec->pid;
+       xrec->addr = rec->addr;
+       xrec->len = rec->size;
+       xrec->pgoff = rec->offset;
+       strcpy(xrec->filename, (char*)rec->path);
+
+       mem_file_write(&cctx->data, xrec, size, 0);
+
+       free(xrec);
+}
+
+static void emit_kernel_trace64(struct perf_record *pr,
+                                                               struct perfconv_context *cctx)
+{
+       struct proftype_kern_trace64 *rec = (struct proftype_kern_trace64 *)
+               pr->data;
+       size_t size = sizeof(struct perf_record_sample) +
+               (rec->num_traces - 1) * sizeof(uint64_t);
+       struct perf_record_sample *xrec = xzmalloc(size);
+
+       xrec->header.type = PERF_RECORD_SAMPLE;
+       xrec->header.misc = PERF_RECORD_MISC_KERNEL;
+       xrec->header.size = size;
+       xrec->ip = rec->trace[0];
+       /* TODO: -1 means "not a process".  We could track ktasks with IDs, emit
+        * COMM events for them (probably!) and report them as the tid.  For now,
+        * tid of 0 means [swapper] to Linux. */
+       if (rec->pid == -1) {
+               xrec->pid = -1;
+               xrec->tid = 0;
+       } else {
+               xrec->pid = rec->pid;
+               xrec->tid = rec->pid;
+       }
+       xrec->time = rec->tstamp;
+       xrec->addr = rec->trace[0];
+       xrec->identifier = perfconv_get_event_id(cctx, rec->info);
+       xrec->cpu = rec->cpu;
+       xrec->nr = rec->num_traces - 1;
+       memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
+
+       mem_file_write(&cctx->data, xrec, size, 0);
+
+       free(xrec);
+}
+
+static void emit_user_trace64(struct perf_record *pr,
+                                                         struct perfconv_context *cctx)
+{
+       struct proftype_user_trace64 *rec = (struct proftype_user_trace64 *)
+               pr->data;
+       size_t size = sizeof(struct perf_record_sample) +
+               (rec->num_traces - 1) * sizeof(uint64_t);
+       struct perf_record_sample *xrec = xzmalloc(size);
+
+       xrec->header.type = PERF_RECORD_SAMPLE;
+       xrec->header.misc = PERF_RECORD_MISC_USER;
+       xrec->header.size = size;
+       xrec->ip = rec->trace[0];
+       xrec->pid = xrec->tid = rec->pid;
+       xrec->time = rec->tstamp;
+       xrec->addr = rec->trace[0];
+       xrec->identifier = perfconv_get_event_id(cctx, rec->info);
+       xrec->cpu = rec->cpu;
+       xrec->nr = rec->num_traces - 1;
+       memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
+
+       mem_file_write(&cctx->data, xrec, size, 0);
+
+       free(xrec);
+}
+
+static void emit_new_process(struct perf_record *pr,
+                                                        struct perfconv_context *cctx)
+{
+       struct proftype_new_process *rec = (struct proftype_new_process *) pr->data;
+       const char *comm;
+
+       hdr_add_buildid(cctx, (char*)rec->path, rec->pid);
+
+       comm = strrchr((char*)rec->path, '/');
+       if (!comm)
+               comm = (char*)rec->path;
+       else
+               comm++;
+       emit_comm(rec->pid, comm, cctx);
+}
+
+struct perfconv_context *perfconv_create_context(struct perf_context *pctx)
+{
+       struct perfconv_context *cctx = xzmalloc(sizeof(struct perfconv_context));
+
+       cctx->pctx = pctx;
+       perf_header_init(&cctx->ph);
+       headers_init(&cctx->hdrs);
+       mem_file_init(&cctx->fhdrs);
+       mem_file_init(&cctx->attr_ids);
+       mem_file_init(&cctx->attrs);
+       mem_file_init(&cctx->data);
+       /* event_types is ignored in newer versions of perf */
+       mem_file_init(&cctx->event_types);
+
+       return cctx;
+}
+
+void perfconv_free_context(struct perfconv_context *cctx)
+{
+       if (cctx)
+               free(cctx);
+}
+
+void perfconv_process_input(struct perfconv_context *cctx, FILE *input,
+                                                       FILE *output)
+{
+       size_t processed_records = 0;
+       uint64_t offset;
+       uint64_t attr_ids_off = sizeof(cctx->ph);
+       struct perf_record pr;
+
+       emit_static_mmaps(cctx);
+
+       while (read_record(input, &pr) == 0) {
+               dbg_print(cctx, 8, stderr, "Valid record: type=%lu size=%lu\n",
+                                 pr.type, pr.size);
+
+               processed_records++;
+
+               switch (pr.type) {
+               case PROFTYPE_KERN_TRACE64:
+                       emit_kernel_trace64(&pr, cctx);
+                       break;
+               case PROFTYPE_USER_TRACE64:
+                       emit_user_trace64(&pr, cctx);
+                       break;
+               case PROFTYPE_PID_MMAP64:
+                       emit_pid_mmap64(&pr, cctx);
+                       break;
+               case PROFTYPE_NEW_PROCESS:
+                       emit_new_process(&pr, cctx);
+                       break;
+               default:
+                       fprintf(stderr, "Unknown record: type=%lu size=%lu\n", pr.type,
+                                       pr.size);
+                       processed_records--;
+               }
+
+               free_record(&pr);
+       }
+
+       /* Add all of the headers before outputting ph */
+       headers_build(&cctx->ph, &cctx->hdrs, &cctx->fhdrs);
+
+       /* attrs, events, and data will come after attr_ids. */
+       offset = sizeof(cctx->ph) + cctx->attr_ids.size;
+
+       /* These are the perf_file_sections in the main perf header.  We need this
+        * sorted out before we emit the PH. */
+       cctx->ph.event_types.offset = offset;
+       cctx->ph.event_types.size = cctx->event_types.size;
+       offset += cctx->event_types.size;
+
+       cctx->ph.attrs.offset = offset;
+       cctx->ph.attrs.size = cctx->attrs.size;
+       offset += cctx->attrs.size;
+
+       cctx->ph.data.offset = offset;
+       cctx->ph.data.size = cctx->data.size;
+       offset += cctx->data.size;
+
+       xfwrite(&cctx->ph, sizeof(cctx->ph), output);
+
+       /* attr_ids comes right after the cctx->ph.  We need to put it before
+        * attrs, since attrs needs to know the offset of the base of attrs_ids for
+        * its relocs. */
+       assert(ftell(output) == attr_ids_off);
+       mem_file_sync(&cctx->attr_ids, output, OFFSET_NORELOC);
+       mem_file_sync(&cctx->event_types, output, OFFSET_NORELOC);
+       /* reloc is based off *attr_ids* base */
+       mem_file_sync(&cctx->attrs, output, attr_ids_off);
+       /* Keep data last, so we can append the feature headers.*/
+       mem_file_sync(&cctx->data, output, OFFSET_NORELOC);
+       /* The feature headers must be right after the data section.  I didn't see
+        * anything in the ABI about this, but Linux's perf has this line:
+        *
+        *              ph->feat_offset  = header->data.offset + header->data.size;
+        */
+       mem_file_sync(&cctx->fhdrs, output,
+                     cctx->ph.data.offset + cctx->ph.data.size);
+
+       dbg_print(cctx, 2, stderr, "Conversion succeeded: %lu records converted\n",
+                         processed_records);
+}
diff --git a/tools/dev-util/perf/perfconv.h b/tools/dev-util/perf/perfconv.h
new file mode 100644 (file)
index 0000000..107ce92
--- /dev/null
@@ -0,0 +1,78 @@
+/* Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ *
+ * Converts kprof profiler files into Linux perf ones. The Linux Perf file
+ * format has bee illustrated here:
+ *
+ *      https://lwn.net/Articles/644919/
+ *      https://openlab-mu-internal.web.cern.ch/openlab-mu-internal/03_Documents/
+ *                      3_Technical_Documents/Technical_Reports/2011/Urs_Fassler_report.pdf
+ *
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdint.h>
+#include "xlib.h"
+#include "perf_format.h"
+
+struct mem_file_reloc {
+       struct mem_file_reloc *next;
+       uint64_t *ptr;
+};
+
+struct mem_block {
+       struct mem_block *next;
+       char *base;
+       char *top;
+       char *wptr;
+};
+
+struct mem_file {
+       size_t size;
+       struct mem_block *head;
+       struct mem_block *tail;
+       struct mem_file_reloc *relocs;
+};
+
+struct static_mmap64 {
+       struct static_mmap64 *next;
+       uint64_t addr;
+       uint64_t size;
+       uint64_t offset;
+       uint32_t pid;
+       uint32_t tid;
+       uint16_t header_misc;
+       char *path;
+};
+
+struct perf_headers {
+       struct mem_block *headers[HEADER_FEAT_BITS];
+};
+
+struct perf_event_id {
+       uint64_t event;
+       uint64_t id;
+};
+
+struct perfconv_context {
+       struct perf_context *pctx;
+       int debug_level;
+       struct static_mmap64 *static_mmaps;
+       struct perf_header ph;
+       struct perf_headers hdrs;
+       struct mem_file fhdrs, attr_ids, attrs, data, event_types;
+};
+
+extern char *cmd_line_save;
+
+struct perfconv_context *perfconv_create_context(struct perf_context *pctx);
+void perfconv_free_context(struct perfconv_context *cctx);
+void perfconv_set_dbglevel(int level, struct perfconv_context *cctx);
+void perfconv_add_kernel_mmap(struct perfconv_context *cctx);
+void perfconv_add_kernel_buildid(struct perfconv_context *cctx);
+void perfconv_process_input(struct perfconv_context *cctx, FILE *input,
+                                                       FILE *output);
diff --git a/tools/dev-util/perf/symbol-elf.c b/tools/dev-util/perf/symbol-elf.c
new file mode 100644 (file)
index 0000000..6d46740
--- /dev/null
@@ -0,0 +1,301 @@
+/* Copyright (c) 2011-2015 Linux Perf Authors
+ *
+ * This source code is licensed under the GNU General Public License Version 2.
+ * See the file LICENSE-gpl-2.0.txt for more details. */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <gelf.h>
+#include <libelf.h>
+#include "xlib.h"
+#include "elf.h"
+
+#define pr_err(args...) fprintf(stderr, args)
+#define pr_debug2(args...) pr_err(args)
+
+#define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP
+
+enum map_type {
+       MAP__FUNCTION = 0,
+       MAP__VARIABLE,
+};
+
+static inline char *bfd_demangle(void *v, const char *c, int i)
+{
+       return NULL;
+}
+
+#ifndef NT_GNU_BUILD_ID
+#define NT_GNU_BUILD_ID 3
+#endif
+
+/**
+ * elf_symtab__for_each_symbol - iterate thru all the symbols
+ *
+ * @syms: struct elf_symtab instance to iterate
+ * @idx: uint32_t idx
+ * @sym: GElf_Sym iterator
+ */
+#define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
+       for (idx = 0, gelf_getsym(syms, idx, &sym);\
+            idx < nr_syms; \
+            idx++, gelf_getsym(syms, idx, &sym))
+
+static inline uint8_t elf_sym__type(const GElf_Sym *sym)
+{
+       return GELF_ST_TYPE(sym->st_info);
+}
+
+#ifndef STT_GNU_IFUNC
+#define STT_GNU_IFUNC 10
+#endif
+
+static inline int elf_sym__is_function(const GElf_Sym *sym)
+{
+       return (elf_sym__type(sym) == STT_FUNC ||
+               elf_sym__type(sym) == STT_GNU_IFUNC) &&
+              sym->st_name != 0 &&
+              sym->st_shndx != SHN_UNDEF;
+}
+
+static inline bool elf_sym__is_object(const GElf_Sym *sym)
+{
+       return elf_sym__type(sym) == STT_OBJECT &&
+               sym->st_name != 0 &&
+               sym->st_shndx != SHN_UNDEF;
+}
+
+static inline int elf_sym__is_label(const GElf_Sym *sym)
+{
+       return elf_sym__type(sym) == STT_NOTYPE &&
+               sym->st_name != 0 &&
+               sym->st_shndx != SHN_UNDEF &&
+               sym->st_shndx != SHN_ABS;
+}
+
+static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type)
+{
+       switch (type) {
+       case MAP__FUNCTION:
+               return elf_sym__is_function(sym);
+       case MAP__VARIABLE:
+               return elf_sym__is_object(sym);
+       default:
+               return false;
+       }
+}
+
+static inline const char *elf_sym__name(const GElf_Sym *sym,
+                                       const Elf_Data *symstrs)
+{
+       return symstrs->d_buf + sym->st_name;
+}
+
+static inline const char *elf_sec__name(const GElf_Shdr *shdr,
+                                       const Elf_Data *secstrs)
+{
+       return secstrs->d_buf + shdr->sh_name;
+}
+
+static inline int elf_sec__is_text(const GElf_Shdr *shdr,
+                                       const Elf_Data *secstrs)
+{
+       return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
+}
+
+static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
+                                   const Elf_Data *secstrs)
+{
+       return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
+}
+
+static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs,
+                         enum map_type type)
+{
+       switch (type) {
+       case MAP__FUNCTION:
+               return elf_sec__is_text(shdr, secstrs);
+       case MAP__VARIABLE:
+               return elf_sec__is_data(shdr, secstrs);
+       default:
+               return false;
+       }
+}
+
+static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
+{
+       Elf_Scn *sec = NULL;
+       GElf_Shdr shdr;
+       size_t cnt = 1;
+
+       while ((sec = elf_nextscn(elf, sec)) != NULL) {
+               gelf_getshdr(sec, &shdr);
+
+               if ((addr >= shdr.sh_addr) &&
+                   (addr < (shdr.sh_addr + shdr.sh_size)))
+                       return cnt;
+
+               ++cnt;
+       }
+
+       return -1;
+}
+
+Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
+                            GElf_Shdr *shp, const char *name, size_t *idx)
+{
+       Elf_Scn *sec = NULL;
+       size_t cnt = 1;
+
+       /* Elf is corrupted/truncated, avoid calling elf_strptr. */
+       if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL))
+               return NULL;
+
+       while ((sec = elf_nextscn(elf, sec)) != NULL) {
+               char *str;
+
+               gelf_getshdr(sec, shp);
+               str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
+               if (str && !strcmp(name, str)) {
+                       if (idx)
+                               *idx = cnt;
+                       return sec;
+               }
+               ++cnt;
+       }
+
+       return NULL;
+}
+
+#define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
+       for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
+            idx < nr_entries; \
+            ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
+
+#define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
+       for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
+            idx < nr_entries; \
+            ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
+
+
+/*
+ * Align offset to 4 bytes as needed for note name and descriptor data.
+ */
+#define NOTE_ALIGN(n) (((n) + 3) & -4U)
+
+static int elf_read_build_id(Elf *elf, void *bf, size_t size)
+{
+       int err = -1;
+       GElf_Ehdr ehdr;
+       GElf_Shdr shdr;
+       Elf_Data *data;
+       Elf_Scn *sec;
+       Elf_Kind ek;
+       void *ptr;
+
+       if (size < BUILD_ID_SIZE)
+               goto out;
+
+       ek = elf_kind(elf);
+       if (ek != ELF_K_ELF)
+               goto out;
+
+       if (gelf_getehdr(elf, &ehdr) == NULL) {
+               pr_err("%s: cannot get elf header.\n", __func__);
+               goto out;
+       }
+
+       /*
+        * Check following sections for notes:
+        *   '.note.gnu.build-id'
+        *   '.notes'
+        *   '.note' (VDSO specific)
+        */
+       do {
+               sec = elf_section_by_name(elf, &ehdr, &shdr,
+                                         ".note.gnu.build-id", NULL);
+               if (sec)
+                       break;
+
+               sec = elf_section_by_name(elf, &ehdr, &shdr,
+                                         ".notes", NULL);
+               if (sec)
+                       break;
+
+               sec = elf_section_by_name(elf, &ehdr, &shdr,
+                                         ".note", NULL);
+               if (sec)
+                       break;
+
+               return err;
+
+       } while (0);
+
+       data = elf_getdata(sec, NULL);
+       if (data == NULL)
+               goto out;
+
+       ptr = data->d_buf;
+       while (ptr < (data->d_buf + data->d_size)) {
+               GElf_Nhdr *nhdr = ptr;
+               size_t namesz = NOTE_ALIGN(nhdr->n_namesz),
+                      descsz = NOTE_ALIGN(nhdr->n_descsz);
+               const char *name;
+
+               ptr += sizeof(*nhdr);
+               name = ptr;
+               ptr += namesz;
+               if (nhdr->n_type == NT_GNU_BUILD_ID &&
+                   nhdr->n_namesz == sizeof("GNU")) {
+                       if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
+                               size_t sz = min(size, descsz);
+                               memcpy(bf, ptr, sz);
+                               memset(bf + sz, 0, size - sz);
+                               err = descsz;
+                               break;
+                       }
+               }
+               ptr += descsz;
+       }
+
+out:
+       return err;
+}
+
+int filename__read_build_id(const char *filename, void *bf, size_t size)
+{
+       int fd, err = -1;
+       Elf *elf;
+
+       if (size < BUILD_ID_SIZE)
+               goto out;
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0)
+               goto out;
+
+       elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+       if (elf == NULL) {
+               pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
+               goto out_close;
+       }
+
+       err = elf_read_build_id(elf, bf, size);
+
+       elf_end(elf);
+out_close:
+       close(fd);
+out:
+       return err;
+}
+
+void symbol__elf_init(void)
+{
+       elf_version(EV_CURRENT);
+}
diff --git a/tools/dev-util/perf/xlib.c b/tools/dev-util/perf/xlib.c
new file mode 100644 (file)
index 0000000..897b68e
--- /dev/null
@@ -0,0 +1,241 @@
+/* Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "xlib.h"
+
+int xopen(const char *path, int flags, mode_t mode)
+{
+       int fd = open(path, flags, mode);
+
+       if (fd < 0) {
+               perror(path);
+               exit(1);
+       }
+
+       return fd;
+}
+
+void xwrite(int fd, const void *data, size_t size)
+{
+       ssize_t wcount = write(fd, data, size);
+
+       if (size != (size_t) wcount) {
+               perror("Writing file");
+               exit(1);
+       }
+}
+
+void xread(int fd, void *data, size_t size)
+{
+       ssize_t rcount = read(fd, data, size);
+
+       if (size != (size_t) rcount) {
+               perror("Reading file");
+               exit(1);
+       }
+}
+
+void xpwrite(int fd, const void *data, size_t size, off_t off)
+{
+       ssize_t wcount = pwrite(fd, data, size, off);
+
+       if (size != (size_t) wcount) {
+               perror("Writing file");
+               exit(1);
+       }
+}
+
+void xpread(int fd, void *data, size_t size, off_t off)
+{
+       ssize_t rcount = pread(fd, data, size, off);
+
+       if (size != (size_t) rcount) {
+               perror("Reading file");
+               exit(1);
+       }
+}
+
+FILE *xfopen(const char *path, const char *mode)
+{
+       FILE *file = fopen(path, mode);
+
+       if (!file) {
+               fprintf(stderr, "Unable to open file '%s' for mode '%s': %s\n",
+                               path, mode, strerror(errno));
+               exit(1);
+       }
+
+       return file;
+}
+
+FILE *xfdopen(int fd, const char *mode)
+{
+       FILE *file = fdopen(fd, mode);
+
+       if (!file) {
+               fprintf(stderr, "Unable to reopen fd '%d' for mode '%s;: %s\n", fd,
+                       mode, strerror(errno));
+               exit(1);
+       }
+
+       return file;
+}
+
+off_t xfsize(FILE *file)
+{
+       struct stat stat_buf;
+       int fd = fileno(file);
+
+       if (fd < 0) {
+               perror("xfsize fileno");
+               exit(1);
+       }
+       if (fstat(fd, &stat_buf)) {
+               perror("xfsize fstat");
+               exit(1);
+       }
+       return stat_buf.st_size;
+}
+
+void xfwrite(const void *data, size_t size, FILE *file)
+{
+       if (fwrite(data, 1, size, file) != size) {
+               fprintf(stderr, "Unable to write %lu bytes: %s\n", size,
+                               strerror(errno));
+               exit(1);
+       }
+}
+
+void xfseek(FILE *file, off_t offset, int whence)
+{
+       if (fseeko(file, offset, whence)) {
+               int error = errno;
+
+               fprintf(stderr, "Unable to seek at offset %ld from %s (fpos=%ld): %s\n",
+                               offset, whence == SEEK_SET ? "beginning of file" :
+                               (whence == SEEK_END ? "end of file" : "current position"),
+                               ftell(file), strerror(error));
+               exit(1);
+       }
+}
+
+void *xmalloc(size_t size)
+{
+       void *data = malloc(size);
+
+       if (!data) {
+               perror("Allocating memory block");
+               exit(1);
+       }
+
+       return data;
+}
+
+void *xzmalloc(size_t size)
+{
+       void *data = xmalloc(size);
+
+       memset(data, 0, size);
+
+       return data;
+}
+
+char *xstrdup(const char *str)
+{
+       char *dstr = strdup(str);
+
+       if (dstr == NULL) {
+               perror("Duplicating a string");
+               exit(1);
+       }
+
+       return dstr;
+}
+
+const char *vb_decode_uint64(const char *data, uint64_t *pval)
+{
+       unsigned int i;
+       uint64_t val = 0;
+
+       for (i = 0; (*data & 0x80) != 0; i += 7, data++)
+               val |= (((uint64_t) *data) & 0x7f) << i;
+       *pval = val | ((uint64_t) *data) << i;
+
+       return data + 1;
+}
+
+int vb_fdecode_uint64(FILE *file, uint64_t *pval)
+{
+       unsigned int i = 0;
+       uint64_t val = 0;
+
+       for (;;) {
+               int c = fgetc(file);
+
+               if (c == EOF)
+                       return EOF;
+               val |= (((uint64_t) c) & 0x7f) << i;
+               i += 7;
+               if ((c & 0x80) == 0)
+                       break;
+       }
+       *pval = val;
+
+       return i / 7;
+}
+
+uint8_t nibble_to_num(char c)
+{
+       switch (c) {
+       case '0':
+               return 0;
+       case '1':
+               return 1;
+       case '2':
+               return 2;
+       case '3':
+               return 3;
+       case '4':
+               return 4;
+       case '5':
+               return 5;
+       case '6':
+               return 6;
+       case '7':
+               return 7;
+       case '8':
+               return 8;
+       case '9':
+               return 9;
+       case 'a':
+       case 'A':
+               return 0xa;
+       case 'b':
+       case 'B':
+               return 0xb;
+       case 'c':
+       case 'C':
+               return 0xc;
+       case 'd':
+       case 'D':
+               return 0xd;
+       case 'e':
+       case 'E':
+               return 0xe;
+       case 'f':
+       case 'F':
+               return 0xf;
+       };
+       return -1;
+}
diff --git a/tools/dev-util/perf/xlib.h b/tools/dev-util/perf/xlib.h
new file mode 100644 (file)
index 0000000..6793f26
--- /dev/null
@@ -0,0 +1,71 @@
+/* Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ */
+
+#pragma once
+
+#include <ros/common.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define min(a, b)                                                              \
+       ({ __typeof__(a) _a = (a);                                      \
+               __typeof__(b) _b = (b);                                 \
+               _a < _b ? _a : _b; })
+#define max(a, b)                                                              \
+       ({ __typeof__(a) _a = (a);                                      \
+               __typeof__(b) _b = (b);                                 \
+               _a > _b ? _a : _b; })
+#define always_assert(c)                                                                                               \
+       do {                                                                                                                            \
+               if (!(c))                                                                                                               \
+                       fprintf(stderr, "%s: %d: Assertion failed: " #c "\n",           \
+                                       __FILE__, __LINE__);                                                            \
+       } while (0)
+
+int xopen(const char *path, int flags, mode_t mode);
+void xwrite(int fd, const void *data, size_t size);
+void xread(int fd, void *data, size_t size);
+void xpwrite(int fd, const void *data, size_t size, off_t off);
+void xpread(int fd, void *data, size_t size, off_t off);
+FILE *xfopen(const char *path, const char *mode);
+FILE *xfdopen(int fd, const char *mode);
+off_t xfsize(FILE *file);
+void xfwrite(const void *data, size_t size, FILE *file);
+void xfseek(FILE *file, off_t offset, int whence);
+void *xmalloc(size_t size);
+void *xzmalloc(size_t size);
+char *xstrdup(const char *str);
+const char *vb_decode_uint64(const char *data, uint64_t *pval);
+int vb_fdecode_uint64(FILE *file, uint64_t *pval);
+uint8_t nibble_to_num(char c);
+
+static inline void cpuid(uint32_t ieax, uint32_t iecx, uint32_t *eaxp,
+                         uint32_t *ebxp, uint32_t *ecxp, uint32_t *edxp)
+{
+       uint32_t eax, ebx, ecx, edx;
+
+       asm volatile("cpuid"
+                                : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
+                                : "a" (ieax), "c" (iecx));
+       if (eaxp)
+               *eaxp = eax;
+       if (ebxp)
+               *ebxp = ebx;
+       if (ecxp)
+               *ecxp = ecx;
+       if (edxp)
+               *edxp = edx;
+}
+
+static inline void set_bitno(void *data, size_t bitno)
+{
+       ((char *) data)[bitno / 8] |= 1 << (bitno % 8);
+}
diff --git a/tools/profile/perf/.gitignore b/tools/profile/perf/.gitignore
deleted file mode 100644 (file)
index bd14107..0000000
+++ /dev/null
@@ -1 +0,0 @@
-perf
diff --git a/tools/profile/perf/Makefile b/tools/profile/perf/Makefile
deleted file mode 100644 (file)
index 6e506f6..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-include ../../Makefrag
-
-SOURCES = perf.c perfconv.c xlib.c perf_core.c akaros.c symbol-elf.c
-
-XCC = $(CROSS_COMPILE)gcc
-
-LIBS=-lperfmon -lelf
-
-PHONY := all
-all: perf
-
-
-PHONY += perf
-perf: $(SOURCES)
-       @echo "  CC      perf"
-       $(Q)$(XCC) -O2 -Wall -Wno-format -Wno-unused -Werror \
-                  -std=gnu99 -o perf $(SOURCES) $(LIBS)
-
-
-PHONY += install
-install: perf
-       @echo "  IN      perf"
-       $(Q)cp perf $(KFS_ROOT)/bin/perf
-
-
-PHONY += clean
-clean:
-       @echo "  RM      perf"
-       $(Q)rm -f perf
-
-
-PHONY += mrproper
-mrproper: clean
-
-
-.PHONY: $(PHONY)
diff --git a/tools/profile/perf/akaros.c b/tools/profile/perf/akaros.c
deleted file mode 100644 (file)
index 3ae2a22..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/* Copyright (c) 2015 Google Inc
- * Davide Libenzi <dlibenzi@google.com>
- * See LICENSE for details.
- */
-
-#include <ros/arch/arch.h>
-#include <ros/common.h>
-#include <sys/types.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <limits.h>
-#include <parlib/parlib.h>
-#include "xlib.h"
-#include "akaros.h"
-
-static const unsigned int llcores[] = {
-       0
-};
-
-void ros_get_low_latency_core_set(struct core_set *cores)
-{
-       for (size_t i = 0; i < COUNT_OF(llcores); i++)
-               ros_set_bit(cores, llcores[i]);
-}
-
-size_t ros_get_low_latency_core_count(void)
-{
-       return COUNT_OF(llcores);
-}
-
-static int guess_nr_cores(void)
-{
-       return max_vcores() + ros_get_low_latency_core_count();
-}
-
-static int get_vars_nr_cores(void)
-{
-       int fd, ret;
-       char buf[10];
-
-       fd = xopen("#vars/num_cores!dw", O_READ, 0);
-       if (fd < 0)
-               return -1;
-       if (read(fd, buf, sizeof(buf)) <= 0) {
-               close(fd);
-               return -1;
-       }
-       ret = atoi(buf);
-       return ret;
-}
-
-static int nr_cores;
-
-static void set_nr_cores(void)
-{
-       nr_cores = get_vars_nr_cores();
-       if (nr_cores == -1)
-               nr_cores = guess_nr_cores();
-}
-
-
-size_t ros_total_cores(void)
-{
-       run_once(set_nr_cores());
-       return nr_cores;
-}
-
-void ros_parse_cores(const char *str, struct core_set *cores)
-{
-       unsigned int fcpu, ncpu;
-       char *dstr = xstrdup(str);
-       char *sptr = NULL;
-       char *tok, *sptr2;
-
-       ZERO_DATA(*cores);
-       for (tok = strtok_r(dstr, ",", &sptr); tok;
-                tok = strtok_r(NULL, ",", &sptr)) {
-
-               if (strchr(tok, '-')) {
-                       if (sscanf(tok, "%u-%u", &fcpu, &ncpu) != 2) {
-                               fprintf(stderr, "Invalid CPU range: %s\n", tok);
-                               exit(1);
-                       }
-                       if (fcpu >= ros_total_cores()) {
-                               fprintf(stderr, "CPU number out of bound: %u\n", fcpu);
-                               exit(1);
-                       }
-                       if (ncpu >= ros_total_cores()) {
-                               fprintf(stderr, "CPU number out of bound: %u\n", ncpu);
-                               exit(1);
-                       }
-                       if (fcpu > ncpu) {
-                               fprintf(stderr, "CPU range is backwards: %u-%u\n", fcpu, ncpu);
-                               exit(1);
-                       }
-                       for (; fcpu <= ncpu; fcpu++)
-                               ros_set_bit(cores->core_set, fcpu);
-               } else {
-                       fcpu = atoi(tok);
-                       if (fcpu >= ros_total_cores()) {
-                               fprintf(stderr, "CPU number out of bound: %u\n",
-                                               fcpu);
-                               exit(1);
-                       }
-                       ros_set_bit(cores->core_set, fcpu);
-               }
-       }
-       free(dstr);
-}
-
-void ros_get_all_cores_set(struct core_set *cores)
-{
-       size_t max_cores = ros_total_cores();
-
-       memset(cores->core_set, 0xff, DIV_ROUND_UP(max_cores, CHAR_BIT));
-}
-
-void ros_not_core_set(struct core_set *dcs)
-{
-       size_t max_cores = ros_total_cores();
-
-       for (size_t i = 0; (max_cores > 0) && (i < sizeof(dcs->core_set)); i++) {
-               size_t nb = (max_cores >= CHAR_BIT) ? CHAR_BIT : max_cores;
-
-               dcs->core_set[i] = (~dcs->core_set[i]) & ((1 << nb) - 1);
-               max_cores -= nb;
-       }
-}
-
-void ros_and_core_sets(struct core_set *dcs, const struct core_set *scs)
-{
-       for (size_t i = 0; i < sizeof(dcs->core_set); i++)
-               dcs->core_set[i] &= scs->core_set[i];
-}
-
-void ros_or_core_sets(struct core_set *dcs, const struct core_set *scs)
-{
-       for (size_t i = 0; i < sizeof(dcs->core_set); i++)
-               dcs->core_set[i] |= scs->core_set[i];
-}
diff --git a/tools/profile/perf/akaros.h b/tools/profile/perf/akaros.h
deleted file mode 100644 (file)
index 98c9779..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright (c) 2015 Google Inc
- * Davide Libenzi <dlibenzi@google.com>
- * See LICENSE for details.
- */
-
-#pragma once
-
-#include <ros/arch/arch.h>
-#include <ros/common.h>
-#include <sys/types.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <limits.h>
-#include <parlib/parlib.h>
-
-#define CORE_SET_SIZE DIV_ROUND_UP(MAX_NUM_CORES, CHAR_BIT)
-
-/* Not using sched.h CPU set because that file has definitions for a large
- * number of APIs Akaros does not support.
- * Making Akaros core_set.h visible in userslace might be a cleaner approach.
- */
-struct core_set {
-       uint8_t core_set[CORE_SET_SIZE];
-};
-
-void ros_get_low_latency_core_set(struct core_set *cores);
-size_t ros_get_low_latency_core_count(void);
-size_t ros_total_cores(void);
-void ros_parse_cores(const char *str, struct core_set *cores);
-void ros_get_all_cores_set(struct core_set *cores);
-void ros_not_core_set(struct core_set *dcs);
-void ros_and_core_sets(struct core_set *dcs, const struct core_set *scs);
-void ros_or_core_sets(struct core_set *dcs, const struct core_set *scs);
-
-static inline void ros_set_bit(void *addr, size_t nbit)
-{
-       ((uint8_t *) addr)[nbit % CHAR_BIT] |= 1 << (nbit % CHAR_BIT);
-}
-
-static inline void ros_clear_bit(void *addr, size_t nbit)
-{
-       ((uint8_t *) addr)[nbit % CHAR_BIT] &= ~(1 << (nbit % CHAR_BIT));
-}
-
-static inline bool ros_get_bit(const void *addr, size_t nbit)
-{
-       return ((const uint8_t *) addr)[nbit % CHAR_BIT] & (1 << (nbit % CHAR_BIT));
-}
diff --git a/tools/profile/perf/elf.h b/tools/profile/perf/elf.h
deleted file mode 100644 (file)
index cc86df2..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-
-#include <stddef.h>
-
-#define BUILD_ID_SIZE   20
-
-int filename__read_build_id(const char *filename, void *bf, size_t size);
-void symbol__elf_init(void);
diff --git a/tools/profile/perf/perf.c b/tools/profile/perf/perf.c
deleted file mode 100644 (file)
index b6b3aa1..0000000
+++ /dev/null
@@ -1,620 +0,0 @@
-/* Copyright (c) 2015-2016 Google Inc
- * Barret Rhoden <brho@cs.berkeley.edu>
- * Davide Libenzi <dlibenzi@google.com>
- * See LICENSE for details.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <errno.h>
-#include <argp.h>
-#include <time.h>
-#include <parlib/parlib.h>
-#include <parlib/timing.h>
-#include "xlib.h"
-#include "akaros.h"
-#include "perfconv.h"
-#include "perf_core.h"
-
-/* Helpers */
-static void run_process_and_wait(int argc, char *argv[],
-                                                                const struct core_set *cores);
-
-/* For communicating with perf_create_context() */
-static struct perf_context_config perf_cfg = {
-       .perf_file = "#arch/perf",
-       .kpctl_file = "#kprof/kpctl",
-       .kpdata_file = "#kprof/kpdata",
-};
-
-static struct perfconv_context *cctx;
-static struct perf_context *pctx;
-extern char **environ; /* POSIX envp */
-
-struct perf_opts {
-       FILE                                            *outfile;
-       const char                                      *events;
-       char                                            **cmd_argv;
-       int                                                     cmd_argc;
-       struct core_set                         cores;
-       bool                                            got_cores;
-       bool                                            verbose;
-       bool                                            sampling;
-       bool                                            stat_bignum;
-       bool                                            record_quiet;
-       unsigned long                           record_period;
-};
-static struct perf_opts opts;
-
-struct perf_cmd {
-       char                                            *name;
-       char                                            *desc;
-       char                                            *opts;
-       int (*func)(struct perf_cmd *, int, char **);
-};
-
-static int perf_help(struct perf_cmd *cmd, int argc, char *argv[]);
-static int perf_list(struct perf_cmd *cmd, int argc, char *argv[]);
-static int perf_record(struct perf_cmd *cmd, int argc, char *argv[]);
-static int perf_stat(struct perf_cmd *cmd, int argc, char *argv[]);
-static int perf_pmu_caps(struct perf_cmd *cmd, int argc, char *argv[]);
-
-static struct perf_cmd perf_cmds[] = {
-       { .name = "help",
-         .desc = "Detailed help for commands",
-         .opts = "COMMAND",
-         .func = perf_help,
-       },
-       { .name = "list",
-         .desc = "Lists all available events",
-         .opts = "[REGEX]",
-         .func = perf_list,
-       },
-       { .name = "record",
-         .desc = "Samples events during command execution",
-         .opts = 0,
-         .func = perf_record,
-       },
-       { .name = "stat",
-         .desc = "Counts events during command execution",
-         .opts = 0,
-         .func = perf_stat,
-       },
-       { .name = "pmu_caps",
-         .desc = "Shows PMU capabilities",
-         .opts = "",
-         .func = perf_pmu_caps,
-       },
-};
-
-/**************************** perf help ****************************/
-
-static int perf_help(struct perf_cmd *cmd, int argc, char *argv[])
-{
-       char *sub_argv[2];
-
-       if (argc < 2) {
-               fprintf(stderr, "perf %s %s\n", cmd->name, cmd->opts);
-               return -1;
-       }
-       for (int i = 0; i < COUNT_OF(perf_cmds); i++) {
-               if (!strcmp(perf_cmds[i].name, argv[1])) {
-                       if (perf_cmds[i].opts) {
-                               fprintf(stdout, "perf %s %s\n", perf_cmds[i].name,
-                                       perf_cmds[i].opts);
-                               fprintf(stdout, "\t%s\n", perf_cmds[i].desc);
-                       } else {
-                               /* For argp subcommands, call their help directly. */
-                               sub_argv[0] = xstrdup(perf_cmds[i].name);
-                               sub_argv[1] = xstrdup("--help");
-                               perf_cmds[i].func(&perf_cmds[i], 2, sub_argv);
-                               free(sub_argv[0]);
-                               free(sub_argv[1]);
-                       }
-                       return 0;
-               }
-       }
-       fprintf(stderr, "Unknown perf command %s\n", argv[1]);
-       return -1;
-}
-
-/**************************** perf list ****************************/
-
-static int perf_list(struct perf_cmd *cmd, int argc, char *argv[])
-{
-       char *show_regex = NULL;
-
-       if (argc > 1)
-               show_regex = argv[1];
-       perf_show_events(show_regex, stdout);
-       return 0;
-}
-
-/**************************** perf pmu_caps ************************/
-
-static int perf_pmu_caps(struct perf_cmd *cmd, int argc, char *argv[])
-{
-       const struct perf_arch_info *pai = perf_context_get_arch_info(pctx);
-
-       fprintf(stdout,
-                       "PERF.version             = %u\n"
-                       "PERF.proc_arch_events    = %u\n"
-                       "PERF.bits_x_counter      = %u\n"
-                       "PERF.counters_x_proc     = %u\n"
-                       "PERF.bits_x_fix_counter  = %u\n"
-                       "PERF.fix_counters_x_proc = %u\n",
-                       pai->perfmon_version, pai->proc_arch_events, pai->bits_x_counter,
-                       pai->counters_x_proc, pai->bits_x_fix_counter,
-                       pai->fix_counters_x_proc);
-       return 0;
-}
-
-/**************************** Common argp ************************/
-
-/* Collection argument parsing.  These options are common to any function that
- * will collect perf events, e.g. perf record and perf stat. */
-
-static struct argp_option collect_opts[] = {
-       {"event", 'e', "EVENT", 0, "Event string, e.g. cycles:u:k"},
-       {"cores", 'C', "CORE_LIST", 0, "List of cores, e.g. 0.2.4:8-19"},
-       {"cpu", 'C', 0, OPTION_ALIAS},
-       {"all-cpus", 'a', 0, 0, "Collect events on all cores (on by default)"},
-       {"verbose", 'v', 0, 0, 0},
-       { 0 }
-};
-
-static const char *collect_args_doc = "COMMAND [ARGS]";
-
-static error_t parse_collect_opt(int key, char *arg, struct argp_state *state)
-{
-       struct perf_opts *p_opts = state->input;
-
-       /* argp doesn't pass input to the child parser(s) by default... */
-       state->child_inputs[0] = state->input;
-
-       switch (key) {
-       case 'a':
-               /* Our default operation is to track all cores; we don't follow
-                * processes yet. */
-               break;
-       case 'C':
-               ros_parse_cores(arg, &p_opts->cores);
-               p_opts->got_cores = TRUE;
-               break;
-       case 'e':
-               p_opts->events = arg;
-               break;
-       case 'v':
-               p_opts->verbose = TRUE;
-               break;
-       case ARGP_KEY_ARG:
-               p_opts->cmd_argc = state->argc - state->next + 1;
-               p_opts->cmd_argv = xmalloc(sizeof(char*) * (p_opts->cmd_argc + 1));
-               p_opts->cmd_argv[0] = arg;
-               memcpy(&p_opts->cmd_argv[1], &state->argv[state->next],
-                      sizeof(char*) * (p_opts->cmd_argc - 1));
-               p_opts->cmd_argv[p_opts->cmd_argc] = NULL;
-               state->next = state->argc;
-               break;
-       case ARGP_KEY_END:
-               if (!p_opts->cmd_argc)
-                       argp_usage(state);
-               /* By default, we set all cores (different than linux) */
-               if (!p_opts->got_cores)
-                       ros_get_all_cores_set(&p_opts->cores);
-               break;
-       default:
-               return ARGP_ERR_UNKNOWN;
-       }
-       return 0;
-}
-
-/* Helper, parses args using the collect_opts and the child parser for a given
- * cmd. */
-static void collect_argp(struct perf_cmd *cmd, int argc, char *argv[],
-                         struct argp_child *children, struct perf_opts *opts)
-{
-       struct argp collect_opt = {collect_opts, parse_collect_opt,
-                                  collect_args_doc, cmd->desc, children};
-       char *cmd_name;
-       const char *fmt = "perf %s";
-       size_t cmd_sz = strlen(cmd->name) + strlen(fmt) + 1;
-
-       /* Rewrite the command name from foo to perf foo for the --help output */
-       cmd_name = xmalloc(cmd_sz);
-       snprintf(cmd_name, cmd_sz, fmt, cmd->name);
-       cmd_name[cmd_sz - 1] = '\0';
-       argv[0] = cmd_name;
-       argp_parse(&collect_opt, argc, argv, ARGP_IN_ORDER, 0, opts);
-       /* It's possible that someone could still be using cmd_name */
-}
-
-/* Helper, submits the events in opts to the kernel for monitoring. */
-static void submit_events(struct perf_opts *opts)
-{
-       struct perf_eventsel *sel;
-       char *dup_evts, *tok, *tok_save = 0;
-
-       dup_evts = xstrdup(opts->events);
-       for (tok = strtok_r(dup_evts, ",", &tok_save);
-            tok;
-                tok = strtok_r(NULL, ",", &tok_save)) {
-
-               sel = perf_parse_event(tok);
-               PMEV_SET_INTEN(sel->ev.event, opts->sampling);
-               sel->ev.trigger_count = opts->record_period;
-               perf_context_event_submit(pctx, &opts->cores, sel);
-       }
-       free(dup_evts);
-}
-
-/**************************** perf record ************************/
-
-static struct argp_option record_opts[] = {
-       {"count", 'c', "PERIOD", 0, "Sampling period"},
-       {"output", 'o', "FILE", 0, "Output file name (default perf.data)"},
-       {"freq", 'F', "FREQUENCY", 0, "Sampling frequency (assumes cycles)"},
-       {"call-graph", 'g', 0, 0, "Backtrace recording (always on!)"},
-       {"quiet", 'q', 0, 0, "No printing to stdio"},
-       { 0 }
-};
-
-/* In lieu of adaptively changing the period to maintain a set freq, we
- * just assume they want cycles and that the TSC is close to that.
- *
- * (cycles/sec) / (samples/sec) = cycles / sample = period.
- *
- * TODO: this also assumes we're running the core at full speed. */
-static unsigned long freq_to_period(unsigned long freq)
-{
-       return get_tsc_freq() / freq;
-}
-
-static error_t parse_record_opt(int key, char *arg, struct argp_state *state)
-{
-       struct perf_opts *p_opts = state->input;
-
-       switch (key) {
-       case 'c':
-               if (p_opts->record_period)
-                       argp_error(state, "Period set.  Only use at most one of -c -F");
-               p_opts->record_period = atol(arg);
-               break;
-       case 'F':
-               if (p_opts->record_period)
-                       argp_error(state, "Period set.  Only use at most one of -c -F");
-               /* TODO: when we properly support freq, multiple events will have the
-                * same freq but different, dynamic, periods. */
-               p_opts->record_period = freq_to_period(atol(arg));
-               break;
-       case 'g':
-               /* Our default operation is to record backtraces. */
-               break;
-       case 'o':
-               p_opts->outfile = xfopen(arg, "wb");
-               break;
-       case 'q':
-               p_opts->record_quiet = TRUE;
-               break;
-       case ARGP_KEY_END:
-               if (!p_opts->events)
-                       p_opts->events = "cycles";
-               if (!p_opts->outfile)
-                       p_opts->outfile = xfopen("perf.data", "wb");
-               if (!p_opts->record_period)
-                       p_opts->record_period = freq_to_period(1000);
-               break;
-       default:
-               return ARGP_ERR_UNKNOWN;
-       }
-       return 0;
-}
-
-static int perf_record(struct perf_cmd *cmd, int argc, char *argv[])
-{
-       struct argp argp_record = {record_opts, parse_record_opt};
-       struct argp_child children[] = { {&argp_record, 0, 0, 0}, {0} };
-
-       collect_argp(cmd, argc, argv, children, &opts);
-       opts.sampling = TRUE;
-
-       /* Once a perf event is submitted, it'll start counting and firing the IRQ.
-        * However, we can control whether or not the samples are collected. */
-       submit_events(&opts);
-       perf_start_sampling(pctx);
-       run_process_and_wait(opts.cmd_argc, opts.cmd_argv, &opts.cores);
-       perf_stop_sampling(pctx);
-       if (opts.verbose)
-               perf_context_show_events(pctx, stdout);
-       /* The events are still counting and firing IRQs.  Let's be nice and turn
-        * them off to minimize our impact. */
-       perf_stop_events(pctx);
-       /* Generate the Linux perf file format with the traces which have been
-        * created during this operation. */
-       perf_convert_trace_data(cctx, perf_cfg.kpdata_file, opts.outfile);
-       fclose(opts.outfile);
-       return 0;
-}
-
-/**************************** perf stat  ************************/
-
-static struct argp_option stat_opts[] = {
-       {"big-num", 'B', 0, 0, "Formatting option"},
-       {"output", 'o', "FILE", 0, "Print output to file (default stdout)"},
-       { 0 }
-};
-
-static error_t parse_stat_opt(int key, char *arg, struct argp_state *state)
-{
-       struct perf_opts *p_opts = state->input;
-
-       switch (key) {
-       case 'B':
-               p_opts->stat_bignum = TRUE;
-               break;
-       case 'o':
-               p_opts->outfile = xfopen(arg, "w+");
-               break;
-       case ARGP_KEY_END:
-               if (!p_opts->events)
-                       p_opts->events = "cache-misses,cache-references,"
-                                        "branch-misses,branches,instructions,cycles";
-               if (!p_opts->outfile)
-                       p_opts->outfile = xfdopen(1, "w+");
-               break;
-       default:
-               return ARGP_ERR_UNKNOWN;
-       }
-       return 0;
-}
-
-struct stat_val {
-       char                                            *name;
-       uint64_t                                        count;
-};
-
-/* Helper, given a name, fetches its value as a float. */
-static float get_count_for(char *name, struct stat_val *all_vals,
-                           size_t nr_vals)
-{
-       for (int i = 0; i < nr_vals; i++) {
-               if (!strcmp(name, all_vals[i].name))
-                       return (float)all_vals[i].count;
-       }
-       return 0.0;
-}
-
-/* Helper, gets the seconds count as a float */
-static float get_seconds(struct stat_val *all_vals, size_t nr_vals)
-{
-       float sec = get_count_for("nsec", all_vals, nr_vals) / 1000000000;
-
-       /* We should never have a time of 0, but in case something went wrong, don't
-        * hand back 0 (divide by 0 errors). */
-       return sec != 0.0 ? sec : 1.0;
-}
-
-/* Prints "X per second", scaling for K, M, or G. */
-static void print_default_rate(FILE *out, struct stat_val *val,
-                               struct stat_val *all_vals, size_t nr_vals)
-{
-       float rate = val->count / get_seconds(all_vals, nr_vals);
-       char scale = ' ';
-
-       if (rate > 1000000000) {
-               rate /= 1000000000;
-               scale = 'G';
-       } else if (rate > 1000000) {
-               rate /= 1000000;
-               scale = 'M';
-       } else if (rate > 1000) {
-               rate /= 1000;
-               scale = 'K';
-       }
-       fprintf(out, "%9.3f %c/sec\n", rate, scale);
-}
-
-/* Prints a line for the given stat val.  We pass all the vals since some stats
- * will adjust their output based on *other* known values.  e.g. IPC. */
-static void stat_print_val(FILE *out, struct stat_val *val,
-                           struct stat_val *all_vals, size_t nr_vals)
-{
-       /* Everyone gets the same front part of the printout */
-       fprintf(out, "%18llu      %-25s #", val->count, val->name);
-
-       /* Based on the particular event and what other events we know, we may print
-        * something different to the summary bit after the #. */
-       if (!strcmp(val->name, "instructions")) {
-               float cycles = get_count_for("cycles", all_vals, nr_vals);
-
-               if (cycles != 0.0)
-                       fprintf(out, "%9.3f insns per cycle\n", val->count / cycles);
-               else
-                       print_default_rate(out, val, all_vals, nr_vals);
-       } else if (!strcmp(val->name, "cache-misses")) {
-               float cache_ref = get_count_for("cache-references", all_vals, nr_vals);
-
-               if (cache_ref != 0.0)
-                       fprintf(out, "%8.2f%% of all refs\n", val->count * 100 / cache_ref);
-               else
-                       print_default_rate(out, val, all_vals, nr_vals);
-       } else if (!strcmp(val->name, "branch-misses")) {
-               float branches = get_count_for("branches", all_vals, nr_vals);
-
-               if (branches != 0.0)
-                       fprintf(out, "%8.2f%% of all branches\n",
-                               val->count * 100 / branches);
-               else
-                       print_default_rate(out, val, all_vals, nr_vals);
-       } else {
-               print_default_rate(out, val, all_vals, nr_vals);
-       }
-}
-
-static char *cmd_as_str(int argc, char *const argv[])
-{
-       size_t len = 0;
-       char *str;
-
-       for (int i = 0; i < argc; i++)
-               len += strlen(argv[i]) + 1;
-       str = xzmalloc(len);
-       for (int i = 0; i < argc; i++) {
-               strlcat(str, argv[i], len);
-               if (i != argc - 1)
-                       strlcat(str, " ", len);
-       }
-       return str;
-}
-
-static struct stat_val *collect_stats(struct perf_context *pctx,
-                                      struct timespec *diff)
-{
-       struct stat_val *stat_vals;
-
-       /* the last stat is time (nsec). */
-       stat_vals = xzmalloc(sizeof(struct stat_val) * (pctx->event_count + 1));
-       for (int i = 0; i < pctx->event_count; i++) {
-               stat_vals[i].count = perf_get_event_count(pctx, i);
-               stat_vals[i].name = pctx->events[i].sel.fq_str;
-       }
-       stat_vals[pctx->event_count].name = "nsec";
-       stat_vals[pctx->event_count].count = diff->tv_sec * 1000000000 +
-                                            diff->tv_nsec;
-       return stat_vals;
-}
-
-static int perf_stat(struct perf_cmd *cmd, int argc, char *argv[])
-{
-       struct argp argp_stat = {stat_opts, parse_stat_opt};
-       struct argp_child children[] = { {&argp_stat, 0, 0, 0}, {0} };
-       FILE *out;
-       struct timespec start, end, diff;
-       struct stat_val *stat_vals;
-       char *cmd_string;
-
-       collect_argp(cmd, argc, argv, children, &opts);
-       opts.sampling = FALSE;
-       out = opts.outfile;
-
-       /* As soon as we submit one event, that event is being tracked, meaning that
-        * the setup/teardown of perf events is also tracked.  Each event (including
-        * the clock measurement) will roughly account for either the start or stop
-        * of every other event. */
-       clock_gettime(CLOCK_REALTIME, &start);
-       submit_events(&opts);
-       run_process_and_wait(opts.cmd_argc, opts.cmd_argv, &opts.cores);
-       clock_gettime(CLOCK_REALTIME, &end);
-       subtract_timespecs(&diff, &end, &start);
-       stat_vals = collect_stats(pctx, &diff);
-       perf_stop_events(pctx);
-       cmd_string = cmd_as_str(opts.cmd_argc, opts.cmd_argv);
-       fprintf(out, "\nPerformance counter stats for '%s':\n\n", cmd_string);
-       free(cmd_string);
-       for (int i = 0; i < pctx->event_count; i++)
-               stat_print_val(out, &stat_vals[i], stat_vals, pctx->event_count + 1);
-       fprintf(out, "\n%8llu.%09llu seconds time elapsed\n\n", diff.tv_sec,
-               diff.tv_nsec);
-       fclose(out);
-       free(stat_vals);
-       return 0;
-}
-
-static void run_process_and_wait(int argc, char *argv[],
-                                                                const struct core_set *cores)
-{
-       int pid, status;
-       size_t max_cores = ros_total_cores();
-       struct core_set pvcores;
-
-       pid = create_child_with_stdfds(argv[0], argc, argv, environ);
-       if (pid < 0) {
-               perror("Unable to spawn child");
-               fflush(stderr);
-               exit(1);
-       }
-
-       ros_get_low_latency_core_set(&pvcores);
-       ros_not_core_set(&pvcores);
-       ros_and_core_sets(&pvcores, cores);
-       for (size_t i = 0; i < max_cores; i++) {
-               if (ros_get_bit(&pvcores, i)) {
-                       if (sys_provision(pid, RES_CORES, i)) {
-                               fprintf(stderr,
-                                               "Unable to provision CPU %lu to PID %d: cmd='%s'\n",
-                                               i, pid, argv[0]);
-                               sys_proc_destroy(pid, -1);
-                               exit(1);
-                       }
-               }
-       }
-
-       sys_proc_run(pid);
-       waitpid(pid, &status, 0);
-}
-
-static void save_cmdline(int argc, char *argv[])
-{
-       size_t len = 0;
-       char *p;
-
-       for (int i = 0; i < argc; i++)
-               len += strlen(argv[i]) + 1;
-       cmd_line_save = xmalloc(len);
-       p = cmd_line_save;
-       for (int i = 0; i < argc; i++) {
-               strcpy(p, argv[i]);
-               p += strlen(argv[i]);
-               if (!(i == argc - 1)) {
-                       *p = ' ';       /* overwrite \0 with ' ' */
-                       p++;
-               }
-       }
-}
-
-static void global_usage(void)
-{
-       fprintf(stderr, "  Usage: perf COMMAND [ARGS]\n");
-       fprintf(stderr, "\n  Available commands:\n\n");
-       for (int i = 0; i < COUNT_OF(perf_cmds); i++)
-               fprintf(stderr, "  \t%s: %s\n", perf_cmds[i].name, perf_cmds[i].desc);
-       exit(-1);
-}
-
-int main(int argc, char *argv[])
-{
-       int i, ret = -1;
-
-       save_cmdline(argc, argv);
-
-       /* Common inits.  Some functions don't need these, but it doesn't hurt. */
-       perf_initialize();
-       pctx = perf_create_context(&perf_cfg);
-       cctx = perfconv_create_context(pctx);
-
-       if (argc < 2)
-               global_usage();
-       for (i = 0; i < COUNT_OF(perf_cmds); i++) {
-               if (!strcmp(perf_cmds[i].name, argv[1])) {
-                       ret = perf_cmds[i].func(&perf_cmds[i], argc - 1, argv + 1);
-                       break;
-               }
-       }
-       if (i == COUNT_OF(perf_cmds))
-               global_usage();
-       /* This cleanup is optional - they'll all be dealt with when the program
-        * exits.  This means its safe for us to exit(-1) at any point in the
-        * program. */
-       perf_free_context(pctx);
-       perfconv_free_context(cctx);
-       perf_finalize();
-       return ret;
-}
diff --git a/tools/profile/perf/perf_core.c b/tools/profile/perf/perf_core.c
deleted file mode 100644 (file)
index 181d2f1..0000000
+++ /dev/null
@@ -1,775 +0,0 @@
-/* Copyright (c) 2015-2016 Google Inc
- * Davide Libenzi <dlibenzi@google.com>
- * Barret Rhoden <brho@cs.berkeley.edu>
- * Stephane Eranian <eranian@gmail.com> (perf_show_event_info() from libpfm4)
- *
- * See LICENSE for details. */
-
-#include <ros/arch/msr-index.h>
-#include <ros/arch/perfmon.h>
-#include <ros/common.h>
-#include <ros/memops.h>
-#include <sys/types.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <limits.h>
-#include <errno.h>
-#include <regex.h>
-#include <parlib/parlib.h>
-#include <perfmon/err.h>
-#include <perfmon/pfmlib.h>
-#include "xlib.h"
-#include "perfconv.h"
-#include "akaros.h"
-#include "perf_core.h"
-#include "elf.h"
-
-struct perf_generic_event {
-       char                                            *name;
-       uint32_t                                        type;
-       uint32_t                                        config;
-};
-
-struct perf_generic_event generic_events[] = {
-       { .name = "cycles",
-         .type = PERF_TYPE_HARDWARE,
-         .config = PERF_COUNT_HW_CPU_CYCLES,
-       },
-       { .name = "cpu-cycles",
-         .type = PERF_TYPE_HARDWARE,
-         .config = PERF_COUNT_HW_CPU_CYCLES,
-       },
-       { .name = "instructions",
-         .type = PERF_TYPE_HARDWARE,
-         .config = PERF_COUNT_HW_INSTRUCTIONS,
-       },
-       { .name = "cache-references",
-         .type = PERF_TYPE_HARDWARE,
-         .config = PERF_COUNT_HW_CACHE_REFERENCES,
-       },
-       { .name = "cache-misses",
-         .type = PERF_TYPE_HARDWARE,
-         .config = PERF_COUNT_HW_CACHE_MISSES,
-       },
-       { .name = "branches",
-         .type = PERF_TYPE_HARDWARE,
-         .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
-       },
-       { .name = "branch-instructions",
-         .type = PERF_TYPE_HARDWARE,
-         .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
-       },
-       { .name = "branch-misses",
-         .type = PERF_TYPE_HARDWARE,
-         .config = PERF_COUNT_HW_BRANCH_MISSES,
-       },
-       { .name = "bus-cycles",
-         .type = PERF_TYPE_HARDWARE,
-         .config = PERF_COUNT_HW_BUS_CYCLES,
-       },
-};
-
-static const char *perf_get_event_mask_name(const pfm_event_info_t *einfo,
-                                                                                       uint32_t mask)
-{
-       int i;
-       pfm_event_attr_info_t ainfo;
-
-       ZERO_DATA(ainfo);
-       ainfo.size = sizeof(ainfo);
-       pfm_for_each_event_attr(i, einfo) {
-               pfm_err_t err = pfm_get_event_attr_info(einfo->idx, i, PFM_OS_NONE,
-                                                                                               &ainfo);
-
-               if (err != PFM_SUCCESS) {
-                       fprintf(stderr, "Failed to get attribute info: %s\n",
-                                       pfm_strerror(err));
-                       exit(1);
-               }
-               if (ainfo.type == PFM_ATTR_UMASK) {
-                       if (mask == (uint32_t) ainfo.code)
-                               return ainfo.name;
-               }
-       }
-
-       return NULL;
-}
-
-void perf_initialize(void)
-{
-       pfm_err_t err = pfm_initialize();
-
-       if (err != PFM_SUCCESS) {
-               fprintf(stderr, "Unable to initialize perfmon library: %s\n",
-                               pfm_strerror(err));
-               exit(1);
-       }
-       symbol__elf_init();
-}
-
-void perf_finalize(void)
-{
-       pfm_terminate();
-}
-
-/* This is arch-specific and maybe model specific in the future.  For some
- * events, pfm4 gives us a pseudo encoding.  Those codes don't map to real
- * hardware events and are meant to be interpreted by Linux for *other* HW
- * events, e.g. in arch/x86/events/intel/core.c.
- *
- * While we're here, we can also take *real* encodings and treat them like
- * pseudo encodings.  For instance, the arch event 0x3c (unhalted_core_cycles)
- * can also be done with fixed counter 1.  This all assumes we have version 2 or
- * later of Intel's perfmon. */
-static void x86_handle_pseudo_encoding(struct perf_eventsel *sel)
-{
-       uint8_t lower_byte;
-
-       switch (sel->ev.event & 0xffff) {
-       case 0xc0:      /* arch inst_retired */
-               sel->ev.flags |= PERFMON_FIXED_EVENT;
-               PMEV_SET_MASK(sel->ev.event, 0);
-               PMEV_SET_EVENT(sel->ev.event, 0);
-               return;
-       case 0x3c:      /* arch unhalted_core_cycles */
-               sel->ev.flags |= PERFMON_FIXED_EVENT;
-               PMEV_SET_MASK(sel->ev.event, 0);
-               PMEV_SET_EVENT(sel->ev.event, 1);
-               return;
-       case 0x13c:     /* arch unhalted_reference_cycles */
-       case 0x300:     /* pseudo encode: unhalted_reference_cycles */
-               sel->ev.flags |= PERFMON_FIXED_EVENT;
-               PMEV_SET_MASK(sel->ev.event, 0);
-               PMEV_SET_EVENT(sel->ev.event, 2);
-               return;
-       };
-       lower_byte = sel->ev.event & 0xff;
-       if ((lower_byte == 0x00) || (lower_byte == 0xff))
-               fprintf(stderr, "Unhandled pseudo encoding %d\n", lower_byte);
-}
-
-/* Parse the string using pfm's lookup functions.  Returns TRUE on success and
- * fills in parts of sel. */
-static bool parse_pfm_encoding(const char *str, struct perf_eventsel *sel)
-{
-       pfm_pmu_encode_arg_t encode;
-       int err;
-       char *ptr;
-
-       memset(&encode, 0, sizeof(encode));
-       encode.size = sizeof(encode);
-       encode.fstr = &ptr;
-       err = pfm_get_os_event_encoding(str, PFM_PLM3 | PFM_PLM0, PFM_OS_NONE,
-                                       &encode);
-       if (err)
-               return FALSE;
-       strlcpy(sel->fq_str, ptr, MAX_FQSTR_SZ);
-       free(ptr);
-       if (encode.count == 0) {
-               fprintf(stderr, "Found event %s, but it had no codes!\n", sel->fq_str);
-               return FALSE;
-       }
-       sel->ev.event = encode.codes[0];
-       x86_handle_pseudo_encoding(sel);
-       sel->type = PERF_TYPE_RAW;
-       sel->config = PMEV_GET_MASK(sel->ev.event) | PMEV_GET_EVENT(sel->ev.event);
-       return TRUE;
-}
-
-static bool is_end_of_raw(char c)
-{
-       return (c == ':') || (c == '\0');
-}
-
-/* Helper: given a string, if the event is a raw hex code, return its numeric
- * value.  Returns -1 if it does not match a raw code.
- *
- * rNN[N][N][:,\0].  Begins with r, has at least two hexdigits, up to 4, and
- * ends with : , or \0. */
-static int extract_raw_code(const char *event)
-{
-       int i;
-       char copy[5] = {0};
-
-       if (event[0] != 'r')
-               return -1;
-       event++;
-       for (i = 0; i < 4; i++) {
-               if (isxdigit(event[i]))
-                       continue;
-               if (is_end_of_raw(event[i]))
-                       break;
-               return -1;
-       }
-       if (!is_end_of_raw(event[i]))
-               return -1;
-       /* 'i' tracks how many we found (i.e. every 'continue') */
-       if (i < 2)
-               return -1;
-       /* need a null-terminated raw code for strtol. */
-       for (int j = 0; j < i; j++)
-               copy[j] = event[j];
-       return strtol(copy, NULL, 16);
-}
-
-/* Takes any modifiers, e.g. u:k:etc, and sets the respective values in sel. */
-static void parse_modifiers(const char *str, struct perf_eventsel *sel)
-{
-       char *dup_str, *tok, *tok_save = 0;
-
-       dup_str = xstrdup(str);
-       for (tok = strtok_r(dup_str, ":", &tok_save);
-            tok;
-            tok = strtok_r(NULL, ":", &tok_save)) {
-
-               switch (tok[0]) {
-               case 'u':
-                       PMEV_SET_USR(sel->ev.event, 1);
-                       break;
-               case 'k':
-                       PMEV_SET_OS(sel->ev.event, 1);
-                       break;
-               case 'e':
-                       PMEV_SET_EDGE(sel->ev.event, 1);
-                       break;
-               case 'p':
-                       PMEV_SET_PC(sel->ev.event, 1);
-                       break;
-               case 't':
-                       PMEV_SET_ANYTH(sel->ev.event, 1);
-                       break;
-               case 'i':
-                       PMEV_SET_INVCMSK(sel->ev.event, 1);
-                       break;
-               case 'c':
-                       if (tok[1] != '=') {
-                               fprintf(stderr, "Bad cmask tok %s, ignoring\n", tok);
-                               break;
-                       }
-                       errno = 0;
-                       PMEV_SET_CMASK(sel->ev.event, strtoul(&tok[2], NULL, 0));
-                       if (errno)
-                               fprintf(stderr, "Bad cmask tok %s, trying anyway\n", tok);
-                       break;
-               }
-       }
-       free(dup_str);
-}
-
-/* Parse the string for a raw encoding.  Returns TRUE on success and fills in
- * parts of sel.  It has basic modifiers, like pfm4, for setting bits in the
- * event code.  This is arch specific, and is all x86 (intel) for now. */
-static bool parse_raw_encoding(const char *str, struct perf_eventsel *sel)
-{
-       int code = extract_raw_code(str);
-       char *colon;
-
-       if (code == -1)
-               return FALSE;
-       sel->ev.event = code;
-       strlcpy(sel->fq_str, str, MAX_FQSTR_SZ);
-       colon = strchr(str, ':');
-       if (colon)
-               parse_modifiers(colon + 1, sel);
-       /* Note that we do not call x86_handle_pseudo_encoding here.  We'll submit
-        * exactly what the user asked us for - which also means no fixed counters
-        * for them (unless we want a :f: token or something). */
-       sel->type = PERF_TYPE_RAW;
-       sel->config = PMEV_GET_MASK(sel->ev.event) | PMEV_GET_EVENT(sel->ev.event);
-       return TRUE;
-}
-
-/* Helper, returns true is str is a generic event string, and fills in sel with
- * the type and config. */
-static bool generic_str_get_code(const char *str, struct perf_eventsel *sel)
-{
-       char *colon = strchr(str, ':');
-       /* if there was no :, we compare as far as we can.  generic_events.name is a
-        * string literal, so strcmp() is fine. */
-       size_t len = colon ? colon - str : SIZE_MAX;
-
-       for (int i = 0; i < COUNT_OF(generic_events); i++) {
-               if (!strncmp(generic_events[i].name, str, len)) {
-                       sel->type = generic_events[i].type;
-                       sel->config = generic_events[i].config;
-                       return TRUE;
-               }
-       }
-       return FALSE;
-}
-
-/* TODO: this is arch-specific and possibly machine-specific. (intel for now).
- * Basically a lot of our perf is arch-dependent. (e.g. PMEV_*). */
-static bool arch_translate_generic(struct perf_eventsel *sel)
-{
-       switch (sel->type) {
-       case PERF_TYPE_HARDWARE:
-               /* These are the intel/x86 architectural perf events */
-               switch (sel->config) {
-               case PERF_COUNT_HW_CPU_CYCLES:
-                       PMEV_SET_MASK(sel->ev.event, 0x00);
-                       PMEV_SET_EVENT(sel->ev.event, 0x3c);
-                       break;
-               case PERF_COUNT_HW_INSTRUCTIONS:
-                       PMEV_SET_MASK(sel->ev.event, 0x00);
-                       PMEV_SET_EVENT(sel->ev.event, 0xc0);
-                       break;
-               case PERF_COUNT_HW_CACHE_REFERENCES:
-                       PMEV_SET_MASK(sel->ev.event, 0x4f);
-                       PMEV_SET_EVENT(sel->ev.event, 0x2e);
-                       break;
-               case PERF_COUNT_HW_CACHE_MISSES:
-                       PMEV_SET_MASK(sel->ev.event, 0x41);
-                       PMEV_SET_EVENT(sel->ev.event, 0x2e);
-                       break;
-               case PERF_COUNT_HW_BRANCH_INSTRUCTIONS:
-                       PMEV_SET_MASK(sel->ev.event, 0x00);
-                       PMEV_SET_EVENT(sel->ev.event, 0xc4);
-                       break;
-               case PERF_COUNT_HW_BRANCH_MISSES:
-                       PMEV_SET_MASK(sel->ev.event, 0x00);
-                       PMEV_SET_EVENT(sel->ev.event, 0xc5);
-                       break;
-               case PERF_COUNT_HW_BUS_CYCLES:
-                       /* Unhalted reference cycles */
-                       PMEV_SET_MASK(sel->ev.event, 0x01);
-                       PMEV_SET_EVENT(sel->ev.event, 0x3c);
-                       break;
-               default:
-                       return FALSE;
-               };
-               break;
-       default:
-               return FALSE;
-       };
-       /* This will make sure we use fixed counters if available */
-       x86_handle_pseudo_encoding(sel);
-       return TRUE;
-}
-
-/* Parse the string for a built-in encoding.  These are the perf defaults such
- * as 'cycles' or 'cache-references.' Returns TRUE on success and fills in parts
- * of sel. */
-static bool parse_generic_encoding(const char *str, struct perf_eventsel *sel)
-{
-       bool ret = FALSE;
-       char *colon;
-
-       if (!generic_str_get_code(str, sel))
-               return FALSE;
-       switch (sel->type) {
-       case PERF_TYPE_HARDWARE:
-       case PERF_TYPE_HW_CACHE:
-               ret = arch_translate_generic(sel);
-               break;
-       };
-       if (!ret) {
-               fprintf(stderr, "Unsupported built-in event %s\n", str);
-               return FALSE;
-       }
-       strlcpy(sel->fq_str, str, MAX_FQSTR_SZ);
-       colon = strchr(str, ':');
-       if (colon)
-               parse_modifiers(colon + 1, sel);
-       return TRUE;
-}
-
-/* Given an event description string, fills out sel with the info from the
- * string such that it can be submitted to the OS.
- *
- * The caller can set more bits if they like, such as whether or not to
- * interrupt on overflow, the sample_period, etc.  None of those settings are
- * part of the event string.
- *
- * Kills the program on failure. */
-struct perf_eventsel *perf_parse_event(const char *str)
-{
-       struct perf_eventsel *sel = xzmalloc(sizeof(struct perf_eventsel));
-
-       sel->ev.user_data = (uint64_t)sel;
-       if (parse_generic_encoding(str, sel))
-               goto success;
-       if (parse_pfm_encoding(str, sel))
-               goto success;
-       if (parse_raw_encoding(str, sel))
-               goto success;
-       free(sel);
-       fprintf(stderr, "Failed to parse event string %s\n", str);
-       exit(-1);
-success:
-       if (!PMEV_GET_OS(sel->ev.event) && !PMEV_GET_USR(sel->ev.event)) {
-               PMEV_SET_OS(sel->ev.event, 1);
-               PMEV_SET_USR(sel->ev.event, 1);
-       }
-       PMEV_SET_EN(sel->ev.event, 1);
-       return sel;
-}
-
-static void perf_get_arch_info(int perf_fd, struct perf_arch_info *pai)
-{
-       uint8_t cmdbuf[6 * sizeof(uint32_t)];
-       const uint8_t *rptr = cmdbuf;
-
-       cmdbuf[0] = PERFMON_CMD_CPU_CAPS;
-
-       xpwrite(perf_fd, cmdbuf, 1, 0);
-       xpread(perf_fd, cmdbuf, 6 * sizeof(uint32_t), 0);
-
-       rptr = get_le_u32(rptr, &pai->perfmon_version);
-       rptr = get_le_u32(rptr, &pai->proc_arch_events);
-       rptr = get_le_u32(rptr, &pai->bits_x_counter);
-       rptr = get_le_u32(rptr, &pai->counters_x_proc);
-       rptr = get_le_u32(rptr, &pai->bits_x_fix_counter);
-       rptr = get_le_u32(rptr, &pai->fix_counters_x_proc);
-}
-
-static int perf_open_event(int perf_fd, const struct core_set *cores,
-                                                  const struct perf_eventsel *sel)
-{
-       uint8_t cmdbuf[1 + 3 * sizeof(uint64_t) + sizeof(uint32_t) +
-                                  CORE_SET_SIZE];
-       uint8_t *wptr = cmdbuf;
-       const uint8_t *rptr = cmdbuf;
-       uint32_t ped;
-       int i, j;
-
-       *wptr++ = PERFMON_CMD_COUNTER_OPEN;
-       wptr = put_le_u64(wptr, sel->ev.event);
-       wptr = put_le_u64(wptr, sel->ev.flags);
-       wptr = put_le_u64(wptr, sel->ev.trigger_count);
-       wptr = put_le_u64(wptr, sel->ev.user_data);
-
-       for (i = CORE_SET_SIZE - 1; (i >= 0) && !cores->core_set[i]; i--)
-               ;
-       if (i < 0) {
-               fprintf(stderr, "Performance event CPU set must not be empty\n");
-               exit(1);
-       }
-       wptr = put_le_u32(wptr, i + 1);
-       for (j = 0; j <= i; j++)
-               *wptr++ = cores->core_set[j];
-
-       xpwrite(perf_fd, cmdbuf, wptr - cmdbuf, 0);
-       xpread(perf_fd, cmdbuf, sizeof(uint32_t), 0);
-
-       rptr = get_le_u32(rptr, &ped);
-
-       return (int) ped;
-}
-
-static uint64_t *perf_get_event_values(int perf_fd, int ped, size_t *pnvalues)
-{
-       ssize_t rsize;
-       uint32_t i, n;
-       uint64_t *values;
-       uint64_t temp;
-       size_t bufsize = sizeof(uint32_t) + MAX_NUM_CORES * sizeof(uint64_t);
-       uint8_t *cmdbuf = xmalloc(bufsize);
-       uint8_t *wptr = cmdbuf;
-       const uint8_t *rptr = cmdbuf;
-
-       *wptr++ = PERFMON_CMD_COUNTER_STATUS;
-       wptr = put_le_u32(wptr, ped);
-
-       xpwrite(perf_fd, cmdbuf, wptr - cmdbuf, 0);
-       rsize = pread(perf_fd, cmdbuf, bufsize, 0);
-
-       if (rsize < (sizeof(uint32_t))) {
-               fprintf(stderr, "Invalid read size while fetching event status: %ld\n",
-                               rsize);
-               exit(1);
-       }
-       rptr = get_le_u32(rptr, &n);
-       if (((rptr - cmdbuf) + n * sizeof(uint64_t)) > rsize) {
-               fprintf(stderr, "Invalid read size while fetching event status: %ld\n",
-                               rsize);
-               exit(1);
-       }
-       values = xmalloc(n * sizeof(uint64_t));
-       for (i = 0; i < n; i++)
-               rptr = get_le_u64(rptr, values + i);
-       free(cmdbuf);
-
-       *pnvalues = n;
-
-       return values;
-}
-
-/* Helper, returns the total count (across all cores) of the event @idx */
-uint64_t perf_get_event_count(struct perf_context *pctx, unsigned int idx)
-{
-       uint64_t total = 0;
-       size_t nvalues;
-       uint64_t *values;
-
-       values = perf_get_event_values(pctx->perf_fd, pctx->events[idx].ped,
-                                      &nvalues);
-       for (int i = 0; i < nvalues; i++)
-               total += values[i];
-       free(values);
-       return total;
-}
-
-static void perf_close_event(int perf_fd, int ped)
-{
-       uint8_t cmdbuf[1 + sizeof(uint32_t)];
-       uint8_t *wptr = cmdbuf;
-
-       *wptr++ = PERFMON_CMD_COUNTER_CLOSE;
-       wptr = put_le_u32(wptr, ped);
-
-       xpwrite(perf_fd, cmdbuf, wptr - cmdbuf, 0);
-}
-
-struct perf_context *perf_create_context(struct perf_context_config *cfg)
-{
-       struct perf_context *pctx = xzmalloc(sizeof(struct perf_context));
-
-       pctx->cfg = cfg;
-       pctx->perf_fd = xopen(cfg->perf_file, O_RDWR, 0);
-       /* perf record needs kpctl_fd, but other perf subcommands might not.  We'll
-        * delay the opening of kpctl until we need it, since kprof is picky about
-        * multiple users of kpctl. */
-       pctx->kpctl_fd = -1;
-       perf_get_arch_info(pctx->perf_fd, &pctx->pai);
-
-       return pctx;
-}
-
-void perf_free_context(struct perf_context *pctx)
-{
-       if (pctx->kpctl_fd != -1)
-               close(pctx->kpctl_fd);  /* disabled sampling */
-       close(pctx->perf_fd);   /* closes all events */
-       free(pctx);
-}
-
-void perf_context_event_submit(struct perf_context *pctx,
-                                                          const struct core_set *cores,
-                                                          const struct perf_eventsel *sel)
-{
-       struct perf_event *pevt = pctx->events + pctx->event_count;
-
-       if (pctx->event_count >= COUNT_OF(pctx->events)) {
-               fprintf(stderr, "Too many open events: %d\n", pctx->event_count);
-               exit(1);
-       }
-       pctx->event_count++;
-       pevt->cores = *cores;
-       pevt->sel = *sel;
-       pevt->ped = perf_open_event(pctx->perf_fd, cores, sel);
-       if (pevt->ped < 0) {
-               fprintf(stderr, "Unable to submit event \"%s\": %s\n", sel->fq_str,
-                       errstr());
-               exit(1);
-       }
-}
-
-void perf_stop_events(struct perf_context *pctx)
-{
-       for (int i = 0; i < pctx->event_count; i++)
-               perf_close_event(pctx->perf_fd, pctx->events[i].ped);
-}
-
-static void ensure_kpctl_is_open(struct perf_context *pctx)
-{
-       if (pctx->kpctl_fd == -1)
-               pctx->kpctl_fd = xopen(pctx->cfg->kpctl_file, O_RDWR, 0);
-}
-
-void perf_start_sampling(struct perf_context *pctx)
-{
-       static const char * const enable_str = "start";
-
-       ensure_kpctl_is_open(pctx);
-       xwrite(pctx->kpctl_fd, enable_str, strlen(enable_str));
-}
-
-void perf_stop_sampling(struct perf_context *pctx)
-{
-       static const char * const disable_str = "stop";
-
-       ensure_kpctl_is_open(pctx);
-       xwrite(pctx->kpctl_fd, disable_str, strlen(disable_str));
-}
-
-void perf_context_show_events(struct perf_context *pctx, FILE *file)
-{
-       struct perf_eventsel *sel;
-
-       for (int i = 0; i < pctx->event_count; i++) {
-               sel = &pctx->events[i].sel;
-               fprintf(file, "Event: %s, final code %p%s, trigger count %d\n",
-                       sel->fq_str, sel->ev.event,
-                       perfmon_is_fixed_event(&sel->ev) ? " (fixed)" : "",
-                       sel->ev.trigger_count);
-       }
-}
-
-static void perf_print_event_flags(const pfm_event_info_t *info, FILE *file)
-{
-       int n = 0;
-
-       if (info->is_precise) {
-               fputs("[precise] ", file);
-               n++;
-       }
-       if (!n)
-               fputs("None", file);
-}
-
-static void perf_print_attr_flags(const pfm_event_attr_info_t *info, FILE *file)
-{
-       int n = 0;
-
-       if (info->is_dfl) {
-               fputs("[default] ", file);
-               n++;
-       }
-       if (info->is_precise) {
-               fputs("[precise] ", file);
-               n++;
-       }
-       if (!n)
-               fputs("None ", file);
-}
-
-/* Ported from libpfm4 */
-static void perf_show_event_info(const pfm_event_info_t *info,
-                                                                const pfm_pmu_info_t *pinfo, FILE *file)
-{
-       static const char * const srcs[PFM_ATTR_CTRL_MAX] = {
-               [PFM_ATTR_CTRL_UNKNOWN] = "???",
-               [PFM_ATTR_CTRL_PMU] = "PMU",
-               [PFM_ATTR_CTRL_PERF_EVENT] = "perf_event",
-       };
-       pfm_event_attr_info_t ainfo;
-       int i, mod = 0, um = 0;
-
-       fprintf(file, "#-----------------------------\n"
-                       "IDX      : %d\n"
-                       "PMU name : %s (%s)\n"
-                       "Name     : %s\n"
-                       "Equiv    : %s\n",
-                       info->idx, pinfo->name, pinfo->desc,
-                       info->name, info->equiv ? info->equiv : "None");
-
-       fprintf(file, "Flags    : ");
-       perf_print_event_flags(info, file);
-       fputc('\n', file);
-
-       fprintf(file, "Desc     : %s\n", info->desc ? info->desc :
-                       "no description available");
-       fprintf(file, "Code     : 0x%"PRIx64"\n", info->code);
-
-       ZERO_DATA(ainfo);
-       ainfo.size = sizeof(ainfo);
-
-       pfm_for_each_event_attr(i, info) {
-               const char *src;
-               pfm_err_t err = pfm_get_event_attr_info(info->idx, i, PFM_OS_NONE,
-                                                                                               &ainfo);
-
-               if (err != PFM_SUCCESS) {
-                       fprintf(stderr, "Failed to get attribute info: %s\n",
-                                       pfm_strerror(err));
-                       exit(1);
-               }
-
-               if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX) {
-                       fprintf(stderr, "event: %s has unsupported attribute source %d",
-                                       info->name, ainfo.ctrl);
-                       ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN;
-               }
-               src = srcs[ainfo.ctrl];
-               switch (ainfo.type) {
-                       case PFM_ATTR_UMASK:
-                               fprintf(file, "Umask-%02u : 0x%02"PRIx64" : %s : [%s] : ",
-                                               um, ainfo.code, src, ainfo.name);
-                               perf_print_attr_flags(&ainfo, file);
-                               fputc(':', file);
-                               if (ainfo.equiv)
-                                       fprintf(file, " Alias to %s", ainfo.equiv);
-                               else
-                                       fprintf(file, " %s", ainfo.desc);
-                               fputc('\n', file);
-                               um++;
-                               break;
-                       case PFM_ATTR_MOD_BOOL:
-                               fprintf(file, "Modif-%02u : 0x%02"PRIx64" : %s : [%s] : "
-                                               "%s (boolean)\n", mod, ainfo.code, src, ainfo.name,
-                                               ainfo.desc);
-                               mod++;
-                               break;
-                       case PFM_ATTR_MOD_INTEGER:
-                               fprintf(file, "Modif-%02u : 0x%02"PRIx64" : %s : [%s] : "
-                                               "%s (integer)\n", mod, ainfo.code, src, ainfo.name,
-                                               ainfo.desc);
-                               mod++;
-                               break;
-                       default:
-                               fprintf(file, "Attr-%02u  : 0x%02"PRIx64" : %s : [%s] : %s\n",
-                                               i, ainfo.code, ainfo.name, src, ainfo.desc);
-               }
-       }
-}
-
-void perf_show_events(const char *rx, FILE *file)
-{
-       int pmu;
-       regex_t crx;
-       pfm_pmu_info_t pinfo;
-       pfm_event_info_t info;
-       char fullname[256];
-
-       if (rx && regcomp(&crx, rx, REG_ICASE)) {
-               fprintf(stderr, "Failed to compile event regex: '%s'\n", rx);
-               exit(1);
-       }
-
-    ZERO_DATA(pinfo);
-    pinfo.size = sizeof(pinfo);
-    ZERO_DATA(info);
-    info.size = sizeof(info);
-
-       pfm_for_all_pmus(pmu) {
-               pfm_err_t err = pfm_get_pmu_info(pmu, &pinfo);
-
-               if (err != PFM_SUCCESS || !pinfo.is_present)
-                       continue;
-
-               for (int i = pinfo.first_event; i != -1; i = pfm_get_event_next(i)) {
-                       err = pfm_get_event_info(i, PFM_OS_NONE, &info);
-                       if (err != PFM_SUCCESS) {
-                               fprintf(stderr, "Failed to get event info: %s\n",
-                                               pfm_strerror(err));
-                               exit(1);
-                       }
-                       snprintf(fullname, sizeof(fullname), "%s::%s", pinfo.name,
-                                        info.name);
-                       if (!rx || regexec(&crx, fullname, 0, NULL, 0) == 0)
-                               perf_show_event_info(&info, &pinfo, file);
-               }
-       }
-       if (rx)
-               regfree(&crx);
-}
-
-void perf_convert_trace_data(struct perfconv_context *cctx, const char *input,
-                                                        FILE *outfile)
-{
-       FILE *infile;
-       size_t ksize;
-
-       infile = xfopen(input, "rb");
-       if (xfsize(infile) > 0) {
-               perfconv_add_kernel_mmap(cctx);
-               perfconv_add_kernel_buildid(cctx);
-               perfconv_process_input(cctx, infile, outfile);
-       }
-       fclose(infile);
-}
diff --git a/tools/profile/perf/perf_core.h b/tools/profile/perf/perf_core.h
deleted file mode 100644 (file)
index ef67083..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright (c) 2015 Google Inc
- * Davide Libenzi <dlibenzi@google.com>
- * See LICENSE for details.
- */
-
-#pragma once
-
-#include <ros/arch/arch.h>
-#include <ros/arch/perfmon.h>
-#include <ros/common.h>
-#include <sys/types.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <limits.h>
-#include "akaros.h"
-#include "perfconv.h"
-
-#define MAX_CPU_EVENTS 256
-
-struct perf_arch_info {
-       uint32_t perfmon_version;
-       uint32_t proc_arch_events;
-       uint32_t bits_x_counter;
-       uint32_t counters_x_proc;
-       uint32_t bits_x_fix_counter;
-       uint32_t fix_counters_x_proc;
-};
-
-#define MAX_FQSTR_SZ 128
-struct perf_eventsel {
-       struct perfmon_event ev;
-       bool attr_emitted;
-       uint32_t type;
-       uint64_t config;
-       char fq_str[MAX_FQSTR_SZ];
-};
-
-struct perf_event {
-       struct core_set cores;
-       struct perf_eventsel sel;
-       int ped;
-};
-
-struct perf_context_config {
-       const char *perf_file;
-       const char *kpctl_file;
-       const char *kpdata_file;
-};
-
-struct perf_context {
-       struct perf_context_config *cfg;
-       int perf_fd;
-       int kpctl_fd;
-       struct perf_arch_info pai;
-       int event_count;
-       struct perf_event events[MAX_CPU_EVENTS];
-};
-
-void perf_initialize(void);
-void perf_finalize(void);
-struct perf_eventsel *perf_parse_event(const char *str);
-struct perf_context *perf_create_context(struct perf_context_config *cfg);
-void perf_free_context(struct perf_context *pctx);
-void perf_context_event_submit(struct perf_context *pctx,
-                                                          const struct core_set *cores,
-                                                          const struct perf_eventsel *sel);
-void perf_stop_events(struct perf_context *pctx);
-void perf_start_sampling(struct perf_context *pctx);
-void perf_stop_sampling(struct perf_context *pctx);
-uint64_t perf_get_event_count(struct perf_context *pctx, unsigned int idx);
-void perf_context_show_events(struct perf_context *pctx, FILE *file);
-void perf_show_events(const char *rx, FILE *file);
-void perf_convert_trace_data(struct perfconv_context *cctx, const char *input,
-                                                        FILE *outfile);
-
-static inline const struct perf_arch_info *perf_context_get_arch_info(
-       const struct perf_context *pctx)
-{
-       return &pctx->pai;
-}
diff --git a/tools/profile/perf/perf_format.h b/tools/profile/perf/perf_format.h
deleted file mode 100644 (file)
index f355aa1..0000000
+++ /dev/null
@@ -1,578 +0,0 @@
-/* Copyright (C) 2009-2016, the Linux Perf authors
- * Copyright (c) 2015 Google Inc
- * Davide Libenzi <dlibenzi@google.com>
- * See LICENSE for details.
- *
- * Parts of this file come, either directly, or as baseline, from the Linux
- * kernel source file:
- *
- *      tools/perf/util/header.h
- *
- * Such file is ruled by the general Linux kernel copyright.
- */
-
-#pragma once
-
-#define BITS_PER_LONG (8 * sizeof(long))
-#define BITS_TO_LONGS(bits) (((bits) + BITS_PER_LONG - 1) / BITS_PER_LONG)
-#define DECLARE_BITMAP(name, bits)                             \
-       unsigned long name[BITS_TO_LONGS(bits)]
-
-enum {
-       HEADER_RESERVED         = 0,    /* Always cleared */
-       HEADER_FIRST_FEATURE    = 1,
-       HEADER_TRACING_DATA     = 1,
-       HEADER_BUILD_ID,
-
-       HEADER_HOSTNAME,
-       HEADER_OSRELEASE,
-       HEADER_VERSION,
-       HEADER_ARCH,
-       HEADER_NRCPUS,
-       HEADER_CPUDESC,
-       HEADER_CPUID,
-       HEADER_TOTAL_MEM,
-       HEADER_CMDLINE,
-       HEADER_EVENT_DESC,
-       HEADER_CPU_TOPOLOGY,
-       HEADER_NUMA_TOPOLOGY,
-       HEADER_BRANCH_STACK,
-       HEADER_PMU_MAPPINGS,
-       HEADER_GROUP_DESC,
-       HEADER_LAST_FEATURE,
-       HEADER_FEAT_BITS        = 256,
-};
-
-/*
- * Bits that can be set in attr.sample_type to request information
- * in the overflow packets.
- */
-enum perf_event_sample_format {
-       PERF_SAMPLE_IP                          = 1U << 0,
-       PERF_SAMPLE_TID                         = 1U << 1,
-       PERF_SAMPLE_TIME                        = 1U << 2,
-       PERF_SAMPLE_ADDR                        = 1U << 3,
-       PERF_SAMPLE_READ                        = 1U << 4,
-       PERF_SAMPLE_CALLCHAIN                   = 1U << 5,
-       PERF_SAMPLE_ID                          = 1U << 6,
-       PERF_SAMPLE_CPU                         = 1U << 7,
-       PERF_SAMPLE_PERIOD                      = 1U << 8,
-       PERF_SAMPLE_STREAM_ID                   = 1U << 9,
-       PERF_SAMPLE_RAW                         = 1U << 10,
-       PERF_SAMPLE_BRANCH_STACK                = 1U << 11,
-       PERF_SAMPLE_REGS_USER                   = 1U << 12,
-       PERF_SAMPLE_STACK_USER                  = 1U << 13,
-       PERF_SAMPLE_WEIGHT                      = 1U << 14,
-       PERF_SAMPLE_DATA_SRC                    = 1U << 15,
-       PERF_SAMPLE_IDENTIFIER                  = 1U << 16,
-       PERF_SAMPLE_TRANSACTION                 = 1U << 17,
-       PERF_SAMPLE_REGS_INTR                   = 1U << 18,
-
-       PERF_SAMPLE_MAX = 1U << 19,             /* non-ABI */
-};
-
-enum perf_event_type {
-       /*
-        * If perf_event_attr.sample_id_all is set then all event types will
-        * have the sample_type selected fields related to where/when
-        * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
-        * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
-        * just after the perf_event_header and the fields already present for
-        * the existing fields, i.e. at the end of the payload. That way a newer
-        * perf.data file will be supported by older perf tools, with these new
-        * optional fields being ignored.
-        *
-        * struct sample_id {
-        *      { u32                   pid, tid; } && PERF_SAMPLE_TID
-        *      { u64                   time;     } && PERF_SAMPLE_TIME
-        *      { u64                   id;               } && PERF_SAMPLE_ID
-        *      { u64                   stream_id;} && PERF_SAMPLE_STREAM_ID
-        *      { u32                   cpu, res; } && PERF_SAMPLE_CPU
-        *      { u64                   id;       } && PERF_SAMPLE_IDENTIFIER
-        * } && perf_event_attr::sample_id_all
-        *
-        * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.  The
-        * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
-        * relative to header.size.
-        */
-
-       /*
-        * The MMAP events record the PROT_EXEC mappings so that we can
-        * correlate userspace IPs to code. They have the following structure:
-        *
-        * struct {
-        *      struct perf_event_header        header;
-        *
-        *      u32                             pid, tid;
-        *      u64                             addr;
-        *      u64                             len;
-        *      u64                             pgoff;
-        *      char                            filename[];
-        * };
-        */
-       PERF_RECORD_MMAP                        = 1,
-
-       /*
-        * struct {
-        *      struct perf_event_header        header;
-        *      u64                             id;
-        *      u64                             lost;
-        *      struct sample_id                sample_id;
-        * };
-        */
-       PERF_RECORD_LOST                        = 2,
-
-       /*
-        * struct {
-        *      struct perf_event_header        header;
-        *
-        *      u32                             pid, tid;
-        *      char                            comm[];
-        *      struct sample_id                sample_id;
-        * };
-        */
-       PERF_RECORD_COMM                        = 3,
-
-       /*
-        * struct {
-        *      struct perf_event_header        header;
-        *      u32                             pid, ppid;
-        *      u32                             tid, ptid;
-        *      u64                             time;
-        *      struct sample_id                sample_id;
-        * };
-        */
-       PERF_RECORD_EXIT                        = 4,
-
-       /*
-        * struct {
-        *      struct perf_event_header        header;
-        *      u64                             time;
-        *      u64                             id;
-        *      u64                             stream_id;
-        *      struct sample_id                sample_id;
-        * };
-        */
-       PERF_RECORD_THROTTLE                    = 5,
-       PERF_RECORD_UNTHROTTLE                  = 6,
-
-       /*
-        * struct {
-        *      struct perf_event_header        header;
-        *      u32                             pid, ppid;
-        *      u32                             tid, ptid;
-        *      u64                             time;
-        *      struct sample_id                sample_id;
-        * };
-        */
-       PERF_RECORD_FORK                        = 7,
-
-       /*
-        * struct {
-        *      struct perf_event_header        header;
-        *      u32                             pid, tid;
-        *
-        *      struct read_format              values;
-        *      struct sample_id                sample_id;
-        * };
-        */
-       PERF_RECORD_READ                        = 8,
-
-       /*
-        * struct {
-        *      struct perf_event_header        header;
-        *
-        *      #
-        *      # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
-        *      # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
-        *      # is fixed relative to header.
-        *      #
-        *
-        *      { u64                   id;       } && PERF_SAMPLE_IDENTIFIER
-        *      { u64                   ip;       } && PERF_SAMPLE_IP
-        *      { u32                   pid, tid; } && PERF_SAMPLE_TID
-        *      { u64                   time;     } && PERF_SAMPLE_TIME
-        *      { u64                   addr;     } && PERF_SAMPLE_ADDR
-        *      { u64                   id;       } && PERF_SAMPLE_ID
-        *      { u64                   stream_id;} && PERF_SAMPLE_STREAM_ID
-        *      { u32                   cpu, res; } && PERF_SAMPLE_CPU
-        *      { u64                   period;   } && PERF_SAMPLE_PERIOD
-        *
-        *      { struct read_format    values;   } && PERF_SAMPLE_READ
-        *
-        *      { u64                   nr,
-        *        u64                   ips[nr];  } && PERF_SAMPLE_CALLCHAIN
-        *
-        *      #
-        *      # The RAW record below is opaque data wrt the ABI
-        *      #
-        *      # That is, the ABI doesn't make any promises wrt to
-        *      # the stability of its content, it may vary depending
-        *      # on event, hardware, kernel version and phase of
-        *      # the moon.
-        *      #
-        *      # In other words, PERF_SAMPLE_RAW contents are not an ABI.
-        *      #
-        *
-        *      { u32                   size;
-        *        char                                  data[size];}&& PERF_SAMPLE_RAW
-        *
-        *      { u64                                   nr;
-        *                { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
-        *
-        *      { u64                   abi; # enum perf_sample_regs_abi
-        *        u64                   regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
-        *
-        *      { u64                   size;
-        *        char                  data[size];
-        *        u64                   dyn_size; } && PERF_SAMPLE_STACK_USER
-        *
-        *      { u64                   weight;   } && PERF_SAMPLE_WEIGHT
-        *      { u64                   data_src; } && PERF_SAMPLE_DATA_SRC
-        *              { u64                                   transaction; } && PERF_SAMPLE_TRANSACTION
-        *      { u64                   abi; # enum perf_sample_regs_abi
-        *        u64                   regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR
-        * };
-        */
-       PERF_RECORD_SAMPLE                      = 9,
-
-       /*
-        * The MMAP2 records are an augmented version of MMAP, they add
-        * maj, min, ino numbers to be used to uniquely identify each mapping
-        *
-        * struct {
-        *      struct perf_event_header        header;
-        *
-        *      u32                             pid, tid;
-        *      u64                             addr;
-        *      u64                             len;
-        *      u64                             pgoff;
-        *      u32                             maj;
-        *      u32                             min;
-        *      u64                             ino;
-        *      u64                             ino_generation;
-        *      char                            filename[];
-        *      struct sample_id                sample_id;
-        * };
-        */
-       PERF_RECORD_MMAP2                       = 10,
-
-       PERF_RECORD_MAX,                        /* non-ABI */
-};
-
-#define PERF_MAX_STACK_DEPTH           127
-
-enum perf_callchain_context {
-       PERF_CONTEXT_HV                         = (__uint64_t) -32,
-       PERF_CONTEXT_KERNEL                     = (__uint64_t) -128,
-       PERF_CONTEXT_USER                       = (__uint64_t) -512,
-
-       PERF_CONTEXT_GUEST                      = (__uint64_t) -2048,
-       PERF_CONTEXT_GUEST_KERNEL       = (__uint64_t) -2176,
-       PERF_CONTEXT_GUEST_USER         = (__uint64_t) -2560,
-
-       PERF_CONTEXT_MAX                        = (__uint64_t) -4095,
-};
-
-/*
- * attr.type
- */
-enum perf_type_id {
-       PERF_TYPE_HARDWARE                                              = 0,
-       PERF_TYPE_SOFTWARE                                              = 1,
-       PERF_TYPE_TRACEPOINT                                    = 2,
-       PERF_TYPE_HW_CACHE                                              = 3,
-       PERF_TYPE_RAW                                                   = 4,
-       PERF_TYPE_BREAKPOINT                                    = 5,
-       PERF_TYPE_INTEL_CQM                                             = 6,
-
-       PERF_TYPE_MAX,                                                  /* non-ABI */
-};
-
-/*
- * Generalized performance event event_id types, used by the
- * attr.event_id parameter of the sys_perf_event_open()
- * syscall:
- */
-enum perf_hw_id {
-       /*
-        * Common hardware events, generalized by the kernel:
-        */
-       PERF_COUNT_HW_CPU_CYCLES                                = 0,
-       PERF_COUNT_HW_INSTRUCTIONS                              = 1,
-       PERF_COUNT_HW_CACHE_REFERENCES                  = 2,
-       PERF_COUNT_HW_CACHE_MISSES                              = 3,
-       PERF_COUNT_HW_BRANCH_INSTRUCTIONS               = 4,
-       PERF_COUNT_HW_BRANCH_MISSES                             = 5,
-       PERF_COUNT_HW_BUS_CYCLES                                = 6,
-       PERF_COUNT_HW_STALLED_CYCLES_FRONTEND   = 7,
-       PERF_COUNT_HW_STALLED_CYCLES_BACKEND    = 8,
-       PERF_COUNT_HW_REF_CPU_CYCLES                    = 9,
-
-       PERF_COUNT_HW_MAX,                                              /* non-ABI */
-};
-
-/* We can output a bunch of different versions of perf_event_attr.  The oldest
- * Linux perf I've run across expects version 3 and can't handle anything
- * larger.  Since we're not using anything from versions 1 or higher, we can sit
- * at version 0 for now. */
-#define PERF_ATTR_VER0
-
-#ifdef PERF_ATTR_VER1
-       #define __PERF_ATTR_VER1 1
-#endif
-#ifdef PERF_ATTR_VER2
-       #define __PERF_ATTR_VER1 1
-       #define __PERF_ATTR_VER2 1
-#endif
-#ifdef PERF_ATTR_VER3
-       #define __PERF_ATTR_VER1 1
-       #define __PERF_ATTR_VER2 1
-       #define __PERF_ATTR_VER3 1
-#endif
-#ifdef PERF_ATTR_VER4
-       #define __PERF_ATTR_VER1 1
-       #define __PERF_ATTR_VER2 1
-       #define __PERF_ATTR_VER3 1
-       #define __PERF_ATTR_VER4 1
-#endif
-
-/*
- * Hardware event_id to monitor via a performance monitoring event:
- */
-struct perf_event_attr {
-       /*
-        * Major type: hardware/software/tracepoint/etc.
-        */
-       uint32_t type;
-
-       /*
-        * Size of the attr structure, for fwd/bwd compat.
-        */
-       uint32_t size;
-
-       /*
-        * Type specific configuration information.
-        */
-       uint64_t config;
-
-       union {
-               uint64_t sample_period;
-               uint64_t sample_freq;
-       };
-
-       uint64_t sample_type;
-       uint64_t read_format;
-
-       uint64_t disabled               :  1, /* off by default            */
-               inherit            :  1, /* children inherit it   */
-               pinned             :  1, /* must always be on PMU */
-               exclusive          :  1, /* only group on PMU     */
-               exclude_user   :  1, /* don't count user          */
-               exclude_kernel :  1, /* ditto kernel              */
-               exclude_hv         :  1, /* ditto hypervisor      */
-               exclude_idle   :  1, /* don't count when idle */
-               mmap               :  1, /* include mmap data     */
-               comm               :  1, /* include comm data     */
-               freq               :  1, /* use freq, not period  */
-               inherit_stat   :  1, /* per task counts           */
-               enable_on_exec :  1, /* next exec enables         */
-               task               :  1, /* trace fork/exit               */
-               watermark          :  1, /* wakeup_watermark      */
-       /*
-        * precise_ip:
-        *
-        *      0 - SAMPLE_IP can have arbitrary skid
-        *      1 - SAMPLE_IP must have constant skid
-        *      2 - SAMPLE_IP requested to have 0 skid
-        *      3 - SAMPLE_IP must have 0 skid
-        *
-        *      See also PERF_RECORD_MISC_EXACT_IP
-        */
-               precise_ip         :  2, /* skid constraint               */
-               mmap_data          :  1, /* non-exec mmap data    */
-               sample_id_all  :  1, /* sample_type all events */
-
-               exclude_host   :  1, /* don't count in host       */
-               exclude_guest  :  1, /* don't count in guest  */
-
-               exclude_callchain_kernel : 1, /* exclude kernel callchains */
-               exclude_callchain_user   : 1, /* exclude user callchains */
-               mmap2              :  1, /* include mmap with inode data         */
-
-               __reserved_1   : 40;
-
-       union {
-               uint32_t wakeup_events;   /* wakeup every n events */
-               uint32_t wakeup_watermark; /* bytes before wakeup       */
-       };
-
-       uint32_t bp_type;
-       union {
-               uint64_t bp_addr;
-               uint64_t config1; /* extension of config */
-       };
-
-#ifdef __PERF_ATTR_VER1
-       union {
-               uint64_t bp_len;
-               uint64_t config2; /* extension of config1 */
-       };
-
-# ifdef __PERF_ATTR_VER2
-       uint64_t branch_sample_type; /* enum perf_branch_sample_type */
-
-#  ifdef __PERF_ATTR_VER3
-       /*
-        * Defines set of user regs to dump on samples.
-        * See asm/perf_regs.h for details.
-        */
-       uint64_t sample_regs_user;
-
-       /*
-        * Defines size of the user stack to dump on samples.
-        */
-       uint32_t sample_stack_user;
-
-#   ifdef __PERF_ATTR_VER4
-       /* Align to u64. */
-       uint32_t __reserved_2;
-
-       /*
-        * Defines set of regs to dump for each sample
-        * state captured on:
-        *      - precise = 0: PMU interrupt
-        *      - precise > 0: sampled instruction
-        *
-        * See asm/perf_regs.h for details.
-        */
-       uint64_t sample_regs_intr;
-#   endif /* __PERF_ATTR_VER4 */
-#  endif /* __PERF_ATTR_VER3 */
-# endif /* __PERF_ATTR_VER2 */
-#endif /* __PERF_ATTR_VER1 */
-} __attribute__((packed));
-
-#define PERF_RECORD_MISC_CPUMODE_MASK          (7 << 0)
-#define PERF_RECORD_MISC_CPUMODE_UNKNOWN       (0 << 0)
-#define PERF_RECORD_MISC_KERNEL                        (1 << 0)
-#define PERF_RECORD_MISC_USER                  (2 << 0)
-#define PERF_RECORD_MISC_HYPERVISOR            (3 << 0)
-#define PERF_RECORD_MISC_GUEST_KERNEL          (4 << 0)
-#define PERF_RECORD_MISC_GUEST_USER            (5 << 0)
-
-#define PERF_RECORD_MISC_MMAP_DATA             (1 << 13)
-/*
- * Indicates that the content of PERF_SAMPLE_IP points to
- * the actual instruction that triggered the event. See also
- * perf_event_attr::precise_ip.
- */
-#define PERF_RECORD_MISC_EXACT_IP              (1 << 14)
-/*
- * Reserve the last bit to indicate some extended misc field
- */
-#define PERF_RECORD_MISC_EXT_RESERVED          (1 << 15)
-
-struct perf_event_header {
-       uint32_t type;
-       uint16_t misc;
-       uint16_t size;
-} __attribute__((packed));
-
-struct perf_file_section {
-       uint64_t offset;        /* Offset from start of file */
-       uint64_t size;          /* Size of the section */
-} __attribute__((packed));
-
-#define PERF_STRING_ALIGN                                              64
-
-struct perf_header_string {
-       uint32_t len;
-       char string[0];         /* Zero terminated */
-};
-
-struct perf_header_string_list {
-       uint32_t nr;
-       struct perf_header_string strings[0];   /* Variable length records */
-};
-
-struct nr_cpus {
-       uint32_t                                        nr_cpus_online;
-       uint32_t                                        nr_cpus_available;
-};
-
-struct build_id_event {
-       struct perf_event_header        header;
-       pid_t                                           pid;
-       uint8_t                                         build_id[24];   /* BUILD_ID_SIZE aligned u64 */
-       char                                            filename[];
-};
-
-#define MAX_EVENT_NAME 64
-
-struct perf_trace_event_type {
-       uint64_t event_id;
-       char name[MAX_EVENT_NAME];
-};
-
-struct perf_file_attr {
-       struct perf_event_attr attr;
-       struct perf_file_section ids;
-};
-
-/* "PERFILE2"
- */
-static const uint64_t PERF_MAGIC2 = 0x32454c4946524550ULL;
-
-struct perf_pipe_file_header {
-       uint64_t magic;                                 /* PERFILE2 */
-       uint64_t size;
-};
-
-struct perf_header {
-       uint64_t magic;                                 /* PERFILE2 */
-       uint64_t size;                                  /* Size of the header */
-       uint64_t attr_size;                             /* size of an attribute in attrs */
-       struct perf_file_section attrs;
-       struct perf_file_section data;
-       struct perf_file_section event_types;
-       DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
-} __attribute__((packed));
-
-/* For type PERF_RECORD_MMAP
- */
-struct perf_record_mmap {
-       struct perf_event_header header;
-       uint32_t pid;
-       uint32_t tid;
-       uint64_t addr;
-       uint64_t len;
-       uint64_t pgoff;
-       char filename[0];
-} __attribute__((packed));
-
-/* For type PERF_RECORD_COMM
- */
-struct perf_record_comm {
-       struct perf_event_header header;
-       uint32_t pid;
-       uint32_t tid;
-       char comm[0];
-} __attribute__((packed));
-
-/* For type PERF_RECORD_SAMPLE
- *
- * Configured with: PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
- * PERF_SAMPLE_ADDR | PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_CPU |
- * PERF_SAMPLE_CALLCHAIN. */
-struct perf_record_sample {
-       struct perf_event_header header;
-       uint64_t identifier;
-       uint64_t ip;
-       uint32_t pid, tid;
-       uint64_t time;
-       uint64_t addr;
-       uint32_t cpu, res;
-       uint64_t nr;
-       uint64_t ips[0];
-} __attribute__((packed));
diff --git a/tools/profile/perf/perfconv.c b/tools/profile/perf/perfconv.c
deleted file mode 100644 (file)
index f00a222..0000000
+++ /dev/null
@@ -1,833 +0,0 @@
-/* Copyright (c) 2015-2016 Google Inc
- * Davide Libenzi <dlibenzi@google.com>
- * Barret Rhoden <brho@cs.berkeley.edu>
- *
- * See LICENSE for details.
- *
- * Converts kprof profiler files into Linux perf ones. The Linux Perf file
- * format has bee illustrated here:
- *
- *      https://lwn.net/Articles/644919/
- *      https://openlab-mu-internal.web.cern.ch/openlab-mu-internal/03_Documents/
- *                      3_Technical_Documents/Technical_Reports/2011/Urs_Fassler_report.pdf
- *
- */
-
-#include <ros/common.h>
-#include <ros/memops.h>
-#include <ros/profiler_records.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <stdarg.h>
-#include "perf_format.h"
-#include "xlib.h"
-#include "perf_core.h"
-#include "perfconv.h"
-#include "elf.h"
-
-#define MAX_PERF_RECORD_SIZE (32 * 1024 * 1024)
-#define PERF_RECORD_BUFFER_SIZE 1024
-#define OFFSET_NORELOC ((uint64_t) -1)
-#define MEMFILE_BLOCK_SIZE (64 * 1024)
-
-#define MBWR_SOLID (1 << 0)
-
-char *cmd_line_save;
-
-struct perf_record {
-       uint64_t type;
-       uint64_t size;
-       char *data;
-       char buffer[PERF_RECORD_BUFFER_SIZE];
-};
-
-static void dbg_print(struct perfconv_context *cctx, int level, FILE *file,
-                                         const char *fmt, ...)
-{
-       if (cctx->debug_level >= level) {
-               va_list args;
-
-               va_start(args, fmt);
-               vfprintf(file, fmt, args);
-               va_end(args);
-       }
-}
-
-void perfconv_set_dbglevel(int level, struct perfconv_context *cctx)
-{
-       cctx->debug_level = level;
-}
-
-static void free_record(struct perf_record *pr)
-{
-       if (pr->data != pr->buffer)
-               free(pr->data);
-       pr->data = NULL;
-}
-
-static int read_record(FILE *file, struct perf_record *pr)
-{
-       if (vb_fdecode_uint64(file, &pr->type) == EOF ||
-               vb_fdecode_uint64(file, &pr->size) == EOF)
-               return EOF;
-       if (pr->size > MAX_PERF_RECORD_SIZE) {
-               fprintf(stderr, "Invalid record size: type=%lu size=%lu\n", pr->type,
-                               pr->size);
-               exit(1);
-       }
-       if (pr->size > sizeof(pr->buffer))
-               pr->data = xmalloc((size_t) pr->size);
-       else
-               pr->data = pr->buffer;
-       if (fread(pr->data, 1, (size_t) pr->size, file) != (size_t) pr->size) {
-               fprintf(stderr, "Unable to read record memory: size=%lu\n",
-                               pr->size);
-               return EOF;
-       }
-
-       return 0;
-}
-
-static struct mem_block *mem_block_alloc(size_t size)
-{
-       struct mem_block *mb = xmalloc(sizeof(struct mem_block) + size);
-
-       mb->next = NULL;
-       mb->base = mb->wptr = (char *) mb + sizeof(struct mem_block);
-       mb->top = mb->base + size;
-
-       return mb;
-}
-
-static char *mem_block_write(struct mem_block *mb, const void *data,
-                                                        size_t size)
-{
-       char *wrbase = mb->wptr;
-
-       always_assert(size <= mb->top - mb->wptr);
-
-       memcpy(mb->wptr, data, size);
-       mb->wptr += size;
-
-       return wrbase;
-}
-
-static void mem_file_init(struct mem_file *mf)
-{
-       ZERO_DATA(*mf);
-}
-
-static int mem_block_can_write(struct mem_block *mb, size_t size, int flags)
-{
-       size_t space = mb->top - mb->wptr;
-
-       return (flags & MBWR_SOLID) ? (space >= size) : (space > 0);
-}
-
-static void *mem_file_write(struct mem_file *mf, const void *data, size_t size,
-                                                       int flags)
-{
-       void *wrbase = NULL;
-
-       while (size > 0) {
-               size_t space, csize;
-               struct mem_block *mb = mf->tail;
-
-               if (!mb || !mem_block_can_write(mb, size, flags)) {
-                       mb = mem_block_alloc(max(MEMFILE_BLOCK_SIZE, size));
-                       if (!mf->tail)
-                               mf->head = mb;
-                       else
-                               mf->tail->next = mb;
-                       mf->tail = mb;
-               }
-               space = mb->top - mb->wptr;
-               csize = min(size, space);
-
-               wrbase = mem_block_write(mb, data, csize);
-               mf->size += csize;
-
-               size -= csize;
-               data = (const char *) data + csize;
-       }
-
-       return wrbase;
-}
-
-static void mem_file_sync(struct mem_file *mf, FILE *file, uint64_t rel_offset)
-{
-       struct mem_block *mb;
-
-       if (rel_offset != 0) {
-               struct mem_file_reloc *rel;
-
-               always_assert(!mf->relocs || rel_offset != OFFSET_NORELOC);
-
-               for (rel = mf->relocs; rel; rel = rel->next)
-                       *rel->ptr += rel_offset;
-       }
-
-       for (mb = mf->head; mb; mb = mb->next)
-               xfwrite(mb->base, mb->wptr - mb->base, file);
-}
-
-static struct mem_file_reloc *mem_file_add_reloc(struct mem_file *mf,
-                                                                                                uint64_t *ptr)
-{
-       struct mem_file_reloc *rel = xzmalloc(sizeof(struct mem_file_reloc));
-
-       rel->ptr = ptr;
-       rel->next = mf->relocs;
-       mf->relocs = rel;
-
-       return rel;
-}
-
-void perfconv_add_kernel_mmap(struct perfconv_context *cctx)
-{
-       char path[] = "[kernel.kallsyms]";
-       struct static_mmap64 *mm;
-
-       mm = xzmalloc(sizeof(struct static_mmap64));
-       mm->pid = -1;                           /* Linux HOST_KERNEL_ID == -1 */
-       mm->tid = 0;                            /* Default thread: swapper */
-       mm->header_misc = PERF_RECORD_MISC_KERNEL;
-       /* Linux sets addr = 0, size = 0xffffffff9fffffff, off = 0xffffffff81000000
-        * Their mmap record is also called [kernel.kallsyms]_text (I think).  They
-        * also have a _text symbol in kallsyms at ffffffff81000000 (equiv to our
-        * KERN_LOAD_ADDR (which is 0xffffffffc0000000)).  Either way, this seems to
-        * work for us; we'll see.  It's also arch-independent (for now). */
-       mm->addr = 0;
-       mm->size = 0xffffffffffffffff;
-       mm->offset = 0x0;
-       mm->path = xstrdup(path);
-
-       mm->next = cctx->static_mmaps;
-       cctx->static_mmaps = mm;
-}
-
-static void headers_init(struct perf_headers *hdrs)
-{
-       ZERO_DATA(*hdrs);
-}
-
-/* Prepends a header (the mem_block) to the list of memblocks for the given
- * HEADER type.  They will all be concatted together during finalization. */
-static void headers_add_header(struct perf_header *ph,
-                               struct perf_headers *hdrs, size_t nhdr,
-                                                          struct mem_block *mb)
-{
-       always_assert(nhdr < HEADER_FEAT_BITS);
-
-       mb->next = hdrs->headers[nhdr];
-       hdrs->headers[nhdr] = mb;
-       set_bitno(ph->adds_features, nhdr);
-}
-
-/* Emits the headers contents to the mem_file */
-static void headers_finalize(struct perf_headers *hdrs, struct mem_file *mf)
-{
-       struct perf_file_section *header_file_secs, *file_sec;
-       struct perf_file_section *file_sec_reloc;
-       size_t nr_hdrs = 0;
-       size_t hdr_off;
-       struct mem_block *mb;
-       size_t mb_sz;
-
-       /* For each header, we need a perf_file_section.  These header file sections
-        * are right after the main perf header, and they point to actual header. */
-       for (int i = 0; i < HEADER_FEAT_BITS; i++)
-               if (hdrs->headers[i])
-                       nr_hdrs++;
-       if (!nr_hdrs)
-               return;
-       header_file_secs = xmalloc(sizeof(struct perf_file_section) * nr_hdrs);
-
-       hdr_off = sizeof(struct perf_file_section) * nr_hdrs;
-       file_sec = header_file_secs;
-
-       /* Spit out the perf_file_sections first and track relocations for all of
-        * the offsets. */
-       for (int i = 0; i < HEADER_FEAT_BITS; i++) {
-               mb = hdrs->headers[i];
-               if (!mb)
-                       continue;
-               mb_sz = mb->wptr - mb->base;
-               /* headers[i] is a chain of memblocks */
-               while (mb->next) {
-                       mb = mb->next;
-                       mb_sz += mb->wptr - mb->base;
-               }
-               file_sec->size = mb_sz;
-               file_sec->offset = hdr_off;             /* offset rel to this memfile */
-               /* When we sync the memfile, we'll need to relocate each of the offsets
-                * so that they are relative to the final file.   mem_file_write()
-                * should be returning the location of where it wrote our file section
-                * within the memfile.  that's the offset that we'll reloc later. */
-               file_sec_reloc = mem_file_write(mf, file_sec,
-                                               sizeof(struct perf_file_section), 0);
-               assert(file_sec->size == file_sec_reloc->size);
-               assert(file_sec->offset == file_sec_reloc->offset);
-               mem_file_add_reloc(mf, &file_sec_reloc->offset);
-
-               hdr_off += mb_sz;
-               file_sec++;
-       }
-       free(header_file_secs);
-
-       /* Spit out the actual headers */
-       for (int i = 0; i < HEADER_FEAT_BITS; i++) {
-               mb = hdrs->headers[i];
-               while (mb) {
-                       mem_file_write(mf, mb->base, mb->wptr - mb->base, 0);
-                       mb = mb->next;
-               }
-       }
-}
-
-/* Builds a struct perf_header_string from str and returns it in a mem_block */
-static struct mem_block *header_make_string(const char *str)
-{
-       struct perf_header_string *hdr;
-       struct mem_block *mb;
-       size_t str_sz = strlen(str) + 1;
-       size_t hdr_sz = ROUNDUP(str_sz + sizeof(struct perf_header_string),
-                               PERF_STRING_ALIGN);
-
-       mb = mem_block_alloc(hdr_sz);
-       /* Manually writing to the block to avoid another alloc.  I guess I could do
-        * two writes (len and string) and try to not screw it up, but that'd be a
-        * mess. */
-       hdr = (struct perf_header_string*)mb->wptr;
-       mb->wptr += hdr_sz;
-       hdr->len = str_sz;
-       memcpy(hdr->string, str, str_sz);
-       return mb;
-}
-
-/* Opens and reads filename, returning the contents.  Free the ret. */
-static char *get_str_from_os(const char *filename)
-{
-       int fd, ret;
-       struct stat fd_stat;
-       char *buf;
-       size_t buf_sz;
-
-       fd = open(filename, O_RDONLY);
-       if (fd < 0)
-               return 0;
-       ret = fstat(fd, &fd_stat);
-       if (ret) {
-               close(fd);
-               return 0;
-       }
-       buf_sz = fd_stat.st_size + 1;
-       buf = xmalloc(buf_sz);
-       ret = read(fd, buf, buf_sz - 1);
-       if (ret <= 0) {
-               free(buf);
-               close(fd);
-               return 0;
-       }
-       close(fd);
-       /* OS strings should be null terminated, but let's be defensive */
-       buf[ret] = 0;
-       return buf;
-}
-
-static void hdr_do_osrelease(struct perf_header *ph, struct perf_headers *hdrs)
-{
-       char *str;
-
-       str = get_str_from_os("#version/version_name");
-       if (!str)
-               return;
-       headers_add_header(ph, hdrs, HEADER_OSRELEASE, header_make_string(str));
-       free(str);
-}
-
-static void hdr_do_nrcpus(struct perf_header *ph, struct perf_headers *hdrs)
-{
-       char *str;
-       uint32_t nr_cores;
-       struct mem_block *mb;
-       struct nr_cpus *hdr;
-
-       str = get_str_from_os("#vars/num_cores!dw");
-       if (!str)
-               return;
-       nr_cores = atoi(str);
-       free(str);
-
-       mb = mem_block_alloc(sizeof(struct nr_cpus));
-       hdr = (struct nr_cpus*)mb->wptr;
-       mb->wptr += sizeof(struct nr_cpus);
-
-       hdr->nr_cpus_online = nr_cores;
-       hdr->nr_cpus_available = nr_cores;
-
-       headers_add_header(ph, hdrs, HEADER_NRCPUS, mb);
-}
-
-/* Unfortunately, HEADER_CMDLINE doesn't just take a string.  It takes a list of
- * null-terminated perf_header_strings (i.e. argv), with the a u32 for the
- * number of strings.  We can just send one string for the entire cmd line. */
-static void hdr_do_cmdline(struct perf_header *ph, struct perf_headers *hdrs)
-{
-       struct perf_header_string *hdr;
-       struct mem_block *mb;
-       size_t str_sz = strlen(cmd_line_save) + 1;
-       size_t hdr_sz = sizeof(uint32_t) +
-                       ROUNDUP(str_sz + sizeof(struct perf_header_string),
-                               PERF_STRING_ALIGN);
-
-       mb = mem_block_alloc(hdr_sz);
-       /* emit the nr strings (1) */
-       *(uint32_t*)mb->wptr = 1;
-       mb->wptr += sizeof(uint32_t);
-       /* emit the perf_header_string, as usual */
-       hdr = (struct perf_header_string*)mb->wptr;
-       mb->wptr += hdr_sz - sizeof(uint32_t);
-       hdr->len = str_sz;
-       memcpy(hdr->string, cmd_line_save, str_sz);
-
-       headers_add_header(ph, hdrs, HEADER_CMDLINE, mb);
-}
-
-/* Returns TRUE if we already emitted a build-id for path.  If this gets too
- * slow (which we'll know because of perf!) we can use a hash table (don't hash
- * on the first few bytes btw - they are usually either /bin or /lib). */
-static bool lookup_buildid(struct perfconv_context *cctx, const char *path)
-{
-       struct build_id_event *b_evt;
-       struct mem_block *mb;
-       size_t b_evt_path_sz;
-
-       mb = cctx->hdrs.headers[HEADER_BUILD_ID];
-       while (mb) {
-               b_evt = (struct build_id_event*)mb->base;
-               b_evt_path_sz = b_evt->header.size -
-                               offsetof(struct build_id_event, filename);
-               /* ignoring the last byte since we forced it to be \0 earlier. */
-               if (!strncmp(b_evt->filename, path, b_evt_path_sz - 1))
-                       return TRUE;
-               mb = mb->next;
-       }
-       return FALSE;
-}
-
-/* Helper: given a path, allocs and inits a build_id_event within a mem_block,
- * returning both via parameters.  Caller needs to set header.misc and fill in
- * the actual build_id. */
-static void build_id_alloc(const char *path, struct build_id_event **b_evt_p,
-                           struct mem_block **mb_p)
-{
-       struct build_id_event *b_evt;
-       struct mem_block *mb;
-       size_t path_sz, b_evt_sz;
-
-       path_sz = strlen(path) + 1;
-       b_evt_sz = path_sz + sizeof(struct build_id_event);
-
-       mb = mem_block_alloc(b_evt_sz);
-       b_evt = (struct build_id_event*)mb->wptr;
-       mb->wptr += b_evt_sz;
-
-       b_evt->header.type = 0; /* if this fails, try 67 (synthetic build id) */
-       /* header.size filled in by the caller, depending on the type */
-       b_evt->header.size = b_evt_sz;
-       strlcpy(b_evt->filename, path, path_sz);
-
-       *b_evt_p = b_evt;
-       *mb_p = mb;
-}
-
-/* Add a build-id header.  Unlike many of the other headers, this one is built
- * on the fly as we emit other records. */
-static void hdr_add_buildid(struct perfconv_context *cctx, const char *path,
-                            int pid)
-{
-       struct build_id_event *b_evt;
-       struct mem_block *mb;
-       int ret;
-
-       if (lookup_buildid(cctx, path))
-               return;
-
-       build_id_alloc(path, &b_evt, &mb);
-       b_evt->header.misc = PERF_RECORD_MISC_USER;
-       b_evt->pid = pid;
-       ret = filename__read_build_id(path, b_evt->build_id, BUILD_ID_SIZE);
-       if (ret <= 0)
-               free(mb);
-       else
-               headers_add_header(&cctx->ph, &cctx->hdrs, HEADER_BUILD_ID, mb);
-}
-
-static void convert_str_to_binary(char *b_id_str, uint8_t *b_id_raw)
-{
-       char *c = b_id_str;
-
-       for (int i = 0; i < BUILD_ID_SIZE; i++) {
-               b_id_raw[i] = nibble_to_num(*c) << 4 | nibble_to_num(*(c + 1));
-               c += 2;
-       }
-}
-
-void perfconv_add_kernel_buildid(struct perfconv_context *cctx)
-{
-       struct build_id_event *b_evt;
-       struct mem_block *mb;
-       int ret, fd;
-       char build_id[BUILD_ID_SIZE * 2 + 1] = {0};
-
-       build_id_alloc("[kernel.kallsyms]", &b_evt, &mb);
-       b_evt->header.misc = PERF_RECORD_MISC_KERNEL;
-       b_evt->pid = -1;
-       fd = xopen("#version/build_id", O_RDONLY, 0);
-       ret = read(fd, build_id, sizeof(build_id));
-       if (ret <= 0) {
-               free(mb);
-       } else {
-               convert_str_to_binary(build_id, b_evt->build_id);
-               headers_add_header(&cctx->ph, &cctx->hdrs, HEADER_BUILD_ID, mb);
-       }
-}
-
-/* Helper: adds all the headers, marking them in PH and storing them in
- * feat_hdrs. */
-static void headers_build(struct perf_header *ph, struct perf_headers *hdrs,
-                          struct mem_file *feat_hdrs)
-{
-       hdr_do_osrelease(ph, hdrs);
-       hdr_do_nrcpus(ph, hdrs);
-       hdr_do_cmdline(ph, hdrs);
-
-       headers_finalize(hdrs, feat_hdrs);
-}
-
-static void perf_header_init(struct perf_header *ph)
-{
-       ZERO_DATA(*ph);
-       ph->magic = PERF_MAGIC2;
-       ph->size = sizeof(*ph);
-       ph->attr_size = sizeof(struct perf_event_attr);
-}
-
-/* For each attr we emit, we push out the attr, then a perf_file_section for the
- * id(s) for that attr.  This wasn't mentioned in
- * https://lwn.net/Articles/644919/, but it's what Linux perf expects
- * (util/header.c).  It looks like you can have multiple IDs per event attr.
- * We'll only emit one.  The *contents* of the perf_file_section for the ids
- * aren't in the attr perf_file_section, they are in a separate one
- * (attr_id_mf).
- *
- * Note that *attr_mf*'s relocs are relative to the base of *attr_id_mf*, which
- * we'll need to sort out later. */
-static void emit_attr(struct mem_file *attr_mf, struct mem_file *attr_id_mf,
-                      const struct perf_event_attr *attr, uint64_t id)
-{
-       struct perf_file_section *psids;
-       struct perf_file_section sids;
-
-       mem_file_write(attr_mf, attr, sizeof(*attr), 0);
-
-       sids.offset = attr_id_mf->size;
-       sids.size = sizeof(uint64_t);
-       mem_file_write(attr_id_mf, &id, sizeof(uint64_t), 0);
-
-       psids = mem_file_write(attr_mf, &sids, sizeof(sids), MBWR_SOLID);
-       mem_file_add_reloc(attr_mf, &psids->offset);
-}
-
-/* Given raw_info, which is what the kernel sends as user_data for a particular
- * sample, look up the 'id' for the event/sample.  The 'id' identifies the event
- * stream that the sample is a part of.  There are many samples per event
- * stream, all identified by 'id.'  It doesn't matter what 'id', so long as it
- * is unique.  We happen to use the pointer to the sample's eventsel.
- *
- * If this is the first time we've seen 'raw_info', we'll also add an attribute
- * to the perf ctx.  There is one attr per 'id' / event stream. */
-static uint64_t perfconv_get_event_id(struct perfconv_context *cctx,
-                                                                         uint64_t raw_info)
-{
-       struct perf_eventsel *sel = (struct perf_eventsel*)raw_info;
-       struct perf_event_attr attr;
-       uint64_t raw_event;
-
-       assert(sel);
-       if (sel->attr_emitted)
-               return raw_info;
-       raw_event = sel->ev.event;
-       ZERO_DATA(attr);
-       attr.size = sizeof(attr);
-       attr.mmap = 1;
-       attr.comm = 1;
-       attr.sample_period = sel->ev.trigger_count;
-       /* Closely coupled with struct perf_record_sample */
-       attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
-                          PERF_SAMPLE_ADDR | PERF_SAMPLE_IDENTIFIER |
-                          PERF_SAMPLE_CPU | PERF_SAMPLE_CALLCHAIN;
-       attr.exclude_guest = 1; /* we can't trace VMs yet */
-       attr.exclude_hv = 1;    /* we aren't tracing our hypervisor, AFAIK */
-       attr.exclude_user = !PMEV_GET_USR(raw_event);
-       attr.exclude_kernel = !PMEV_GET_OS(raw_event);
-       attr.type = sel->type;
-       attr.config = sel->config;
-       emit_attr(&cctx->attrs, &cctx->attr_ids, &attr, raw_info);
-       sel->attr_emitted = TRUE;
-       return raw_info;
-}
-
-static void emit_static_mmaps(struct perfconv_context *cctx)
-{
-       struct static_mmap64 *mm;
-
-       for (mm = cctx->static_mmaps; mm; mm = mm->next) {
-               size_t size = sizeof(struct perf_record_mmap) + strlen(mm->path) + 1;
-               struct perf_record_mmap *xrec = xzmalloc(size);
-
-               xrec->header.type = PERF_RECORD_MMAP;
-               xrec->header.misc = mm->header_misc;
-               xrec->header.size = size;
-               xrec->pid = mm->pid;
-               xrec->tid = mm->tid;
-               xrec->addr = mm->addr;
-               xrec->len = mm->size;
-               xrec->pgoff = mm->offset;
-               strcpy(xrec->filename, mm->path);
-
-               mem_file_write(&cctx->data, xrec, size, 0);
-
-               free(xrec);
-       }
-}
-
-static void emit_comm(uint32_t pid, const char *comm,
-                                         struct perfconv_context *cctx)
-{
-       size_t size = sizeof(struct perf_record_comm) + strlen(comm) + 1;
-       struct perf_record_comm *xrec = xzmalloc(size);
-
-       xrec->header.type = PERF_RECORD_COMM;
-       xrec->header.misc = PERF_RECORD_MISC_USER;
-       xrec->header.size = size;
-       xrec->pid = xrec->tid = pid;
-       strcpy(xrec->comm, comm);
-
-       mem_file_write(&cctx->data, xrec, size, 0);
-
-       free(xrec);
-}
-
-static void emit_pid_mmap64(struct perf_record *pr,
-                                                       struct perfconv_context *cctx)
-{
-       struct proftype_pid_mmap64 *rec = (struct proftype_pid_mmap64 *) pr->data;
-       size_t size = sizeof(struct perf_record_mmap) +
-                     strlen((char*)rec->path) + 1;
-       struct perf_record_mmap *xrec = xzmalloc(size);
-
-       hdr_add_buildid(cctx, (char*)rec->path, rec->pid);
-
-       xrec->header.type = PERF_RECORD_MMAP;
-       xrec->header.misc = PERF_RECORD_MISC_USER;
-       xrec->header.size = size;
-       xrec->pid = xrec->tid = rec->pid;
-       xrec->addr = rec->addr;
-       xrec->len = rec->size;
-       xrec->pgoff = rec->offset;
-       strcpy(xrec->filename, (char*)rec->path);
-
-       mem_file_write(&cctx->data, xrec, size, 0);
-
-       free(xrec);
-}
-
-static void emit_kernel_trace64(struct perf_record *pr,
-                                                               struct perfconv_context *cctx)
-{
-       struct proftype_kern_trace64 *rec = (struct proftype_kern_trace64 *)
-               pr->data;
-       size_t size = sizeof(struct perf_record_sample) +
-               (rec->num_traces - 1) * sizeof(uint64_t);
-       struct perf_record_sample *xrec = xzmalloc(size);
-
-       xrec->header.type = PERF_RECORD_SAMPLE;
-       xrec->header.misc = PERF_RECORD_MISC_KERNEL;
-       xrec->header.size = size;
-       xrec->ip = rec->trace[0];
-       /* TODO: -1 means "not a process".  We could track ktasks with IDs, emit
-        * COMM events for them (probably!) and report them as the tid.  For now,
-        * tid of 0 means [swapper] to Linux. */
-       if (rec->pid == -1) {
-               xrec->pid = -1;
-               xrec->tid = 0;
-       } else {
-               xrec->pid = rec->pid;
-               xrec->tid = rec->pid;
-       }
-       xrec->time = rec->tstamp;
-       xrec->addr = rec->trace[0];
-       xrec->identifier = perfconv_get_event_id(cctx, rec->info);
-       xrec->cpu = rec->cpu;
-       xrec->nr = rec->num_traces - 1;
-       memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
-
-       mem_file_write(&cctx->data, xrec, size, 0);
-
-       free(xrec);
-}
-
-static void emit_user_trace64(struct perf_record *pr,
-                                                         struct perfconv_context *cctx)
-{
-       struct proftype_user_trace64 *rec = (struct proftype_user_trace64 *)
-               pr->data;
-       size_t size = sizeof(struct perf_record_sample) +
-               (rec->num_traces - 1) * sizeof(uint64_t);
-       struct perf_record_sample *xrec = xzmalloc(size);
-
-       xrec->header.type = PERF_RECORD_SAMPLE;
-       xrec->header.misc = PERF_RECORD_MISC_USER;
-       xrec->header.size = size;
-       xrec->ip = rec->trace[0];
-       xrec->pid = xrec->tid = rec->pid;
-       xrec->time = rec->tstamp;
-       xrec->addr = rec->trace[0];
-       xrec->identifier = perfconv_get_event_id(cctx, rec->info);
-       xrec->cpu = rec->cpu;
-       xrec->nr = rec->num_traces - 1;
-       memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
-
-       mem_file_write(&cctx->data, xrec, size, 0);
-
-       free(xrec);
-}
-
-static void emit_new_process(struct perf_record *pr,
-                                                        struct perfconv_context *cctx)
-{
-       struct proftype_new_process *rec = (struct proftype_new_process *) pr->data;
-       const char *comm;
-
-       hdr_add_buildid(cctx, (char*)rec->path, rec->pid);
-
-       comm = strrchr((char*)rec->path, '/');
-       if (!comm)
-               comm = (char*)rec->path;
-       else
-               comm++;
-       emit_comm(rec->pid, comm, cctx);
-}
-
-struct perfconv_context *perfconv_create_context(struct perf_context *pctx)
-{
-       struct perfconv_context *cctx = xzmalloc(sizeof(struct perfconv_context));
-
-       cctx->pctx = pctx;
-       perf_header_init(&cctx->ph);
-       headers_init(&cctx->hdrs);
-       mem_file_init(&cctx->fhdrs);
-       mem_file_init(&cctx->attr_ids);
-       mem_file_init(&cctx->attrs);
-       mem_file_init(&cctx->data);
-       /* event_types is ignored in newer versions of perf */
-       mem_file_init(&cctx->event_types);
-
-       return cctx;
-}
-
-void perfconv_free_context(struct perfconv_context *cctx)
-{
-       if (cctx)
-               free(cctx);
-}
-
-void perfconv_process_input(struct perfconv_context *cctx, FILE *input,
-                                                       FILE *output)
-{
-       size_t processed_records = 0;
-       uint64_t offset;
-       uint64_t attr_ids_off = sizeof(cctx->ph);
-       struct perf_record pr;
-
-       emit_static_mmaps(cctx);
-
-       while (read_record(input, &pr) == 0) {
-               dbg_print(cctx, 8, stderr, "Valid record: type=%lu size=%lu\n",
-                                 pr.type, pr.size);
-
-               processed_records++;
-
-               switch (pr.type) {
-               case PROFTYPE_KERN_TRACE64:
-                       emit_kernel_trace64(&pr, cctx);
-                       break;
-               case PROFTYPE_USER_TRACE64:
-                       emit_user_trace64(&pr, cctx);
-                       break;
-               case PROFTYPE_PID_MMAP64:
-                       emit_pid_mmap64(&pr, cctx);
-                       break;
-               case PROFTYPE_NEW_PROCESS:
-                       emit_new_process(&pr, cctx);
-                       break;
-               default:
-                       fprintf(stderr, "Unknown record: type=%lu size=%lu\n", pr.type,
-                                       pr.size);
-                       processed_records--;
-               }
-
-               free_record(&pr);
-       }
-
-       /* Add all of the headers before outputting ph */
-       headers_build(&cctx->ph, &cctx->hdrs, &cctx->fhdrs);
-
-       /* attrs, events, and data will come after attr_ids. */
-       offset = sizeof(cctx->ph) + cctx->attr_ids.size;
-
-       /* These are the perf_file_sections in the main perf header.  We need this
-        * sorted out before we emit the PH. */
-       cctx->ph.event_types.offset = offset;
-       cctx->ph.event_types.size = cctx->event_types.size;
-       offset += cctx->event_types.size;
-
-       cctx->ph.attrs.offset = offset;
-       cctx->ph.attrs.size = cctx->attrs.size;
-       offset += cctx->attrs.size;
-
-       cctx->ph.data.offset = offset;
-       cctx->ph.data.size = cctx->data.size;
-       offset += cctx->data.size;
-
-       xfwrite(&cctx->ph, sizeof(cctx->ph), output);
-
-       /* attr_ids comes right after the cctx->ph.  We need to put it before
-        * attrs, since attrs needs to know the offset of the base of attrs_ids for
-        * its relocs. */
-       assert(ftell(output) == attr_ids_off);
-       mem_file_sync(&cctx->attr_ids, output, OFFSET_NORELOC);
-       mem_file_sync(&cctx->event_types, output, OFFSET_NORELOC);
-       /* reloc is based off *attr_ids* base */
-       mem_file_sync(&cctx->attrs, output, attr_ids_off);
-       /* Keep data last, so we can append the feature headers.*/
-       mem_file_sync(&cctx->data, output, OFFSET_NORELOC);
-       /* The feature headers must be right after the data section.  I didn't see
-        * anything in the ABI about this, but Linux's perf has this line:
-        *
-        *              ph->feat_offset  = header->data.offset + header->data.size;
-        */
-       mem_file_sync(&cctx->fhdrs, output,
-                     cctx->ph.data.offset + cctx->ph.data.size);
-
-       dbg_print(cctx, 2, stderr, "Conversion succeeded: %lu records converted\n",
-                         processed_records);
-}
diff --git a/tools/profile/perf/perfconv.h b/tools/profile/perf/perfconv.h
deleted file mode 100644 (file)
index 107ce92..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/* Copyright (c) 2015 Google Inc
- * Davide Libenzi <dlibenzi@google.com>
- * See LICENSE for details.
- *
- * Converts kprof profiler files into Linux perf ones. The Linux Perf file
- * format has bee illustrated here:
- *
- *      https://lwn.net/Articles/644919/
- *      https://openlab-mu-internal.web.cern.ch/openlab-mu-internal/03_Documents/
- *                      3_Technical_Documents/Technical_Reports/2011/Urs_Fassler_report.pdf
- *
- */
-
-#pragma once
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdint.h>
-#include "xlib.h"
-#include "perf_format.h"
-
-struct mem_file_reloc {
-       struct mem_file_reloc *next;
-       uint64_t *ptr;
-};
-
-struct mem_block {
-       struct mem_block *next;
-       char *base;
-       char *top;
-       char *wptr;
-};
-
-struct mem_file {
-       size_t size;
-       struct mem_block *head;
-       struct mem_block *tail;
-       struct mem_file_reloc *relocs;
-};
-
-struct static_mmap64 {
-       struct static_mmap64 *next;
-       uint64_t addr;
-       uint64_t size;
-       uint64_t offset;
-       uint32_t pid;
-       uint32_t tid;
-       uint16_t header_misc;
-       char *path;
-};
-
-struct perf_headers {
-       struct mem_block *headers[HEADER_FEAT_BITS];
-};
-
-struct perf_event_id {
-       uint64_t event;
-       uint64_t id;
-};
-
-struct perfconv_context {
-       struct perf_context *pctx;
-       int debug_level;
-       struct static_mmap64 *static_mmaps;
-       struct perf_header ph;
-       struct perf_headers hdrs;
-       struct mem_file fhdrs, attr_ids, attrs, data, event_types;
-};
-
-extern char *cmd_line_save;
-
-struct perfconv_context *perfconv_create_context(struct perf_context *pctx);
-void perfconv_free_context(struct perfconv_context *cctx);
-void perfconv_set_dbglevel(int level, struct perfconv_context *cctx);
-void perfconv_add_kernel_mmap(struct perfconv_context *cctx);
-void perfconv_add_kernel_buildid(struct perfconv_context *cctx);
-void perfconv_process_input(struct perfconv_context *cctx, FILE *input,
-                                                       FILE *output);
diff --git a/tools/profile/perf/symbol-elf.c b/tools/profile/perf/symbol-elf.c
deleted file mode 100644 (file)
index 6d46740..0000000
+++ /dev/null
@@ -1,301 +0,0 @@
-/* Copyright (c) 2011-2015 Linux Perf Authors
- *
- * This source code is licensed under the GNU General Public License Version 2.
- * See the file LICENSE-gpl-2.0.txt for more details. */
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <inttypes.h>
-#include <stdbool.h>
-
-#include <gelf.h>
-#include <libelf.h>
-#include "xlib.h"
-#include "elf.h"
-
-#define pr_err(args...) fprintf(stderr, args)
-#define pr_debug2(args...) pr_err(args)
-
-#define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP
-
-enum map_type {
-       MAP__FUNCTION = 0,
-       MAP__VARIABLE,
-};
-
-static inline char *bfd_demangle(void *v, const char *c, int i)
-{
-       return NULL;
-}
-
-#ifndef NT_GNU_BUILD_ID
-#define NT_GNU_BUILD_ID 3
-#endif
-
-/**
- * elf_symtab__for_each_symbol - iterate thru all the symbols
- *
- * @syms: struct elf_symtab instance to iterate
- * @idx: uint32_t idx
- * @sym: GElf_Sym iterator
- */
-#define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
-       for (idx = 0, gelf_getsym(syms, idx, &sym);\
-            idx < nr_syms; \
-            idx++, gelf_getsym(syms, idx, &sym))
-
-static inline uint8_t elf_sym__type(const GElf_Sym *sym)
-{
-       return GELF_ST_TYPE(sym->st_info);
-}
-
-#ifndef STT_GNU_IFUNC
-#define STT_GNU_IFUNC 10
-#endif
-
-static inline int elf_sym__is_function(const GElf_Sym *sym)
-{
-       return (elf_sym__type(sym) == STT_FUNC ||
-               elf_sym__type(sym) == STT_GNU_IFUNC) &&
-              sym->st_name != 0 &&
-              sym->st_shndx != SHN_UNDEF;
-}
-
-static inline bool elf_sym__is_object(const GElf_Sym *sym)
-{
-       return elf_sym__type(sym) == STT_OBJECT &&
-               sym->st_name != 0 &&
-               sym->st_shndx != SHN_UNDEF;
-}
-
-static inline int elf_sym__is_label(const GElf_Sym *sym)
-{
-       return elf_sym__type(sym) == STT_NOTYPE &&
-               sym->st_name != 0 &&
-               sym->st_shndx != SHN_UNDEF &&
-               sym->st_shndx != SHN_ABS;
-}
-
-static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type)
-{
-       switch (type) {
-       case MAP__FUNCTION:
-               return elf_sym__is_function(sym);
-       case MAP__VARIABLE:
-               return elf_sym__is_object(sym);
-       default:
-               return false;
-       }
-}
-
-static inline const char *elf_sym__name(const GElf_Sym *sym,
-                                       const Elf_Data *symstrs)
-{
-       return symstrs->d_buf + sym->st_name;
-}
-
-static inline const char *elf_sec__name(const GElf_Shdr *shdr,
-                                       const Elf_Data *secstrs)
-{
-       return secstrs->d_buf + shdr->sh_name;
-}
-
-static inline int elf_sec__is_text(const GElf_Shdr *shdr,
-                                       const Elf_Data *secstrs)
-{
-       return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
-}
-
-static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
-                                   const Elf_Data *secstrs)
-{
-       return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
-}
-
-static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs,
-                         enum map_type type)
-{
-       switch (type) {
-       case MAP__FUNCTION:
-               return elf_sec__is_text(shdr, secstrs);
-       case MAP__VARIABLE:
-               return elf_sec__is_data(shdr, secstrs);
-       default:
-               return false;
-       }
-}
-
-static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
-{
-       Elf_Scn *sec = NULL;
-       GElf_Shdr shdr;
-       size_t cnt = 1;
-
-       while ((sec = elf_nextscn(elf, sec)) != NULL) {
-               gelf_getshdr(sec, &shdr);
-
-               if ((addr >= shdr.sh_addr) &&
-                   (addr < (shdr.sh_addr + shdr.sh_size)))
-                       return cnt;
-
-               ++cnt;
-       }
-
-       return -1;
-}
-
-Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
-                            GElf_Shdr *shp, const char *name, size_t *idx)
-{
-       Elf_Scn *sec = NULL;
-       size_t cnt = 1;
-
-       /* Elf is corrupted/truncated, avoid calling elf_strptr. */
-       if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL))
-               return NULL;
-
-       while ((sec = elf_nextscn(elf, sec)) != NULL) {
-               char *str;
-
-               gelf_getshdr(sec, shp);
-               str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
-               if (str && !strcmp(name, str)) {
-                       if (idx)
-                               *idx = cnt;
-                       return sec;
-               }
-               ++cnt;
-       }
-
-       return NULL;
-}
-
-#define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
-       for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
-            idx < nr_entries; \
-            ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
-
-#define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
-       for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
-            idx < nr_entries; \
-            ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
-
-
-/*
- * Align offset to 4 bytes as needed for note name and descriptor data.
- */
-#define NOTE_ALIGN(n) (((n) + 3) & -4U)
-
-static int elf_read_build_id(Elf *elf, void *bf, size_t size)
-{
-       int err = -1;
-       GElf_Ehdr ehdr;
-       GElf_Shdr shdr;
-       Elf_Data *data;
-       Elf_Scn *sec;
-       Elf_Kind ek;
-       void *ptr;
-
-       if (size < BUILD_ID_SIZE)
-               goto out;
-
-       ek = elf_kind(elf);
-       if (ek != ELF_K_ELF)
-               goto out;
-
-       if (gelf_getehdr(elf, &ehdr) == NULL) {
-               pr_err("%s: cannot get elf header.\n", __func__);
-               goto out;
-       }
-
-       /*
-        * Check following sections for notes:
-        *   '.note.gnu.build-id'
-        *   '.notes'
-        *   '.note' (VDSO specific)
-        */
-       do {
-               sec = elf_section_by_name(elf, &ehdr, &shdr,
-                                         ".note.gnu.build-id", NULL);
-               if (sec)
-                       break;
-
-               sec = elf_section_by_name(elf, &ehdr, &shdr,
-                                         ".notes", NULL);
-               if (sec)
-                       break;
-
-               sec = elf_section_by_name(elf, &ehdr, &shdr,
-                                         ".note", NULL);
-               if (sec)
-                       break;
-
-               return err;
-
-       } while (0);
-
-       data = elf_getdata(sec, NULL);
-       if (data == NULL)
-               goto out;
-
-       ptr = data->d_buf;
-       while (ptr < (data->d_buf + data->d_size)) {
-               GElf_Nhdr *nhdr = ptr;
-               size_t namesz = NOTE_ALIGN(nhdr->n_namesz),
-                      descsz = NOTE_ALIGN(nhdr->n_descsz);
-               const char *name;
-
-               ptr += sizeof(*nhdr);
-               name = ptr;
-               ptr += namesz;
-               if (nhdr->n_type == NT_GNU_BUILD_ID &&
-                   nhdr->n_namesz == sizeof("GNU")) {
-                       if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
-                               size_t sz = min(size, descsz);
-                               memcpy(bf, ptr, sz);
-                               memset(bf + sz, 0, size - sz);
-                               err = descsz;
-                               break;
-                       }
-               }
-               ptr += descsz;
-       }
-
-out:
-       return err;
-}
-
-int filename__read_build_id(const char *filename, void *bf, size_t size)
-{
-       int fd, err = -1;
-       Elf *elf;
-
-       if (size < BUILD_ID_SIZE)
-               goto out;
-
-       fd = open(filename, O_RDONLY);
-       if (fd < 0)
-               goto out;
-
-       elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
-       if (elf == NULL) {
-               pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
-               goto out_close;
-       }
-
-       err = elf_read_build_id(elf, bf, size);
-
-       elf_end(elf);
-out_close:
-       close(fd);
-out:
-       return err;
-}
-
-void symbol__elf_init(void)
-{
-       elf_version(EV_CURRENT);
-}
diff --git a/tools/profile/perf/xlib.c b/tools/profile/perf/xlib.c
deleted file mode 100644 (file)
index 897b68e..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/* Copyright (c) 2015 Google Inc
- * Davide Libenzi <dlibenzi@google.com>
- * See LICENSE for details.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include "xlib.h"
-
-int xopen(const char *path, int flags, mode_t mode)
-{
-       int fd = open(path, flags, mode);
-
-       if (fd < 0) {
-               perror(path);
-               exit(1);
-       }
-
-       return fd;
-}
-
-void xwrite(int fd, const void *data, size_t size)
-{
-       ssize_t wcount = write(fd, data, size);
-
-       if (size != (size_t) wcount) {
-               perror("Writing file");
-               exit(1);
-       }
-}
-
-void xread(int fd, void *data, size_t size)
-{
-       ssize_t rcount = read(fd, data, size);
-
-       if (size != (size_t) rcount) {
-               perror("Reading file");
-               exit(1);
-       }
-}
-
-void xpwrite(int fd, const void *data, size_t size, off_t off)
-{
-       ssize_t wcount = pwrite(fd, data, size, off);
-
-       if (size != (size_t) wcount) {
-               perror("Writing file");
-               exit(1);
-       }
-}
-
-void xpread(int fd, void *data, size_t size, off_t off)
-{
-       ssize_t rcount = pread(fd, data, size, off);
-
-       if (size != (size_t) rcount) {
-               perror("Reading file");
-               exit(1);
-       }
-}
-
-FILE *xfopen(const char *path, const char *mode)
-{
-       FILE *file = fopen(path, mode);
-
-       if (!file) {
-               fprintf(stderr, "Unable to open file '%s' for mode '%s': %s\n",
-                               path, mode, strerror(errno));
-               exit(1);
-       }
-
-       return file;
-}
-
-FILE *xfdopen(int fd, const char *mode)
-{
-       FILE *file = fdopen(fd, mode);
-
-       if (!file) {
-               fprintf(stderr, "Unable to reopen fd '%d' for mode '%s;: %s\n", fd,
-                       mode, strerror(errno));
-               exit(1);
-       }
-
-       return file;
-}
-
-off_t xfsize(FILE *file)
-{
-       struct stat stat_buf;
-       int fd = fileno(file);
-
-       if (fd < 0) {
-               perror("xfsize fileno");
-               exit(1);
-       }
-       if (fstat(fd, &stat_buf)) {
-               perror("xfsize fstat");
-               exit(1);
-       }
-       return stat_buf.st_size;
-}
-
-void xfwrite(const void *data, size_t size, FILE *file)
-{
-       if (fwrite(data, 1, size, file) != size) {
-               fprintf(stderr, "Unable to write %lu bytes: %s\n", size,
-                               strerror(errno));
-               exit(1);
-       }
-}
-
-void xfseek(FILE *file, off_t offset, int whence)
-{
-       if (fseeko(file, offset, whence)) {
-               int error = errno;
-
-               fprintf(stderr, "Unable to seek at offset %ld from %s (fpos=%ld): %s\n",
-                               offset, whence == SEEK_SET ? "beginning of file" :
-                               (whence == SEEK_END ? "end of file" : "current position"),
-                               ftell(file), strerror(error));
-               exit(1);
-       }
-}
-
-void *xmalloc(size_t size)
-{
-       void *data = malloc(size);
-
-       if (!data) {
-               perror("Allocating memory block");
-               exit(1);
-       }
-
-       return data;
-}
-
-void *xzmalloc(size_t size)
-{
-       void *data = xmalloc(size);
-
-       memset(data, 0, size);
-
-       return data;
-}
-
-char *xstrdup(const char *str)
-{
-       char *dstr = strdup(str);
-
-       if (dstr == NULL) {
-               perror("Duplicating a string");
-               exit(1);
-       }
-
-       return dstr;
-}
-
-const char *vb_decode_uint64(const char *data, uint64_t *pval)
-{
-       unsigned int i;
-       uint64_t val = 0;
-
-       for (i = 0; (*data & 0x80) != 0; i += 7, data++)
-               val |= (((uint64_t) *data) & 0x7f) << i;
-       *pval = val | ((uint64_t) *data) << i;
-
-       return data + 1;
-}
-
-int vb_fdecode_uint64(FILE *file, uint64_t *pval)
-{
-       unsigned int i = 0;
-       uint64_t val = 0;
-
-       for (;;) {
-               int c = fgetc(file);
-
-               if (c == EOF)
-                       return EOF;
-               val |= (((uint64_t) c) & 0x7f) << i;
-               i += 7;
-               if ((c & 0x80) == 0)
-                       break;
-       }
-       *pval = val;
-
-       return i / 7;
-}
-
-uint8_t nibble_to_num(char c)
-{
-       switch (c) {
-       case '0':
-               return 0;
-       case '1':
-               return 1;
-       case '2':
-               return 2;
-       case '3':
-               return 3;
-       case '4':
-               return 4;
-       case '5':
-               return 5;
-       case '6':
-               return 6;
-       case '7':
-               return 7;
-       case '8':
-               return 8;
-       case '9':
-               return 9;
-       case 'a':
-       case 'A':
-               return 0xa;
-       case 'b':
-       case 'B':
-               return 0xb;
-       case 'c':
-       case 'C':
-               return 0xc;
-       case 'd':
-       case 'D':
-               return 0xd;
-       case 'e':
-       case 'E':
-               return 0xe;
-       case 'f':
-       case 'F':
-               return 0xf;
-       };
-       return -1;
-}
diff --git a/tools/profile/perf/xlib.h b/tools/profile/perf/xlib.h
deleted file mode 100644 (file)
index 6793f26..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/* Copyright (c) 2015 Google Inc
- * Davide Libenzi <dlibenzi@google.com>
- * See LICENSE for details.
- */
-
-#pragma once
-
-#include <ros/common.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#define min(a, b)                                                              \
-       ({ __typeof__(a) _a = (a);                                      \
-               __typeof__(b) _b = (b);                                 \
-               _a < _b ? _a : _b; })
-#define max(a, b)                                                              \
-       ({ __typeof__(a) _a = (a);                                      \
-               __typeof__(b) _b = (b);                                 \
-               _a > _b ? _a : _b; })
-#define always_assert(c)                                                                                               \
-       do {                                                                                                                            \
-               if (!(c))                                                                                                               \
-                       fprintf(stderr, "%s: %d: Assertion failed: " #c "\n",           \
-                                       __FILE__, __LINE__);                                                            \
-       } while (0)
-
-int xopen(const char *path, int flags, mode_t mode);
-void xwrite(int fd, const void *data, size_t size);
-void xread(int fd, void *data, size_t size);
-void xpwrite(int fd, const void *data, size_t size, off_t off);
-void xpread(int fd, void *data, size_t size, off_t off);
-FILE *xfopen(const char *path, const char *mode);
-FILE *xfdopen(int fd, const char *mode);
-off_t xfsize(FILE *file);
-void xfwrite(const void *data, size_t size, FILE *file);
-void xfseek(FILE *file, off_t offset, int whence);
-void *xmalloc(size_t size);
-void *xzmalloc(size_t size);
-char *xstrdup(const char *str);
-const char *vb_decode_uint64(const char *data, uint64_t *pval);
-int vb_fdecode_uint64(FILE *file, uint64_t *pval);
-uint8_t nibble_to_num(char c);
-
-static inline void cpuid(uint32_t ieax, uint32_t iecx, uint32_t *eaxp,
-                         uint32_t *ebxp, uint32_t *ecxp, uint32_t *edxp)
-{
-       uint32_t eax, ebx, ecx, edx;
-
-       asm volatile("cpuid"
-                                : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
-                                : "a" (ieax), "c" (iecx));
-       if (eaxp)
-               *eaxp = eax;
-       if (ebxp)
-               *ebxp = ebx;
-       if (ecxp)
-               *ecxp = ecx;
-       if (edxp)
-               *edxp = edx;
-}
-
-static inline void set_bitno(void *data, size_t bitno)
-{
-       ((char *) data)[bitno / 8] |= 1 << (bitno % 8);
-}