root/tools/perf/util/event.c
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <perf/cpumap.h>
#include <perf/event.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <uapi/linux/mman.h> /* To get things like MAP_HUGETLB even on older libc headers */
#include <linux/perf_event.h>
#include <linux/zalloc.h>
#include "cpumap.h"
#include "dso.h"
#include "event.h"
#include "debug.h"
#include "hist.h"
#include "machine.h"
#include "sort.h"
#include "string2.h"
#include "strlist.h"
#include "thread.h"
#include "thread_map.h"
#include "time-utils.h"
#include <linux/ctype.h>
#include "map.h"
#include "util/namespaces.h"
#include "symbol.h"
#include "symbol/kallsyms.h"
#include "asm/bug.h"
#include "stat.h"
#include "session.h"
#include "bpf-event.h"
#include "print_binary.h"
#include "tool.h"
#include "util.h"

static const char *perf_event__names[] = {
        [0]                                     = "TOTAL",
        [PERF_RECORD_MMAP]                      = "MMAP",
        [PERF_RECORD_MMAP2]                     = "MMAP2",
        [PERF_RECORD_LOST]                      = "LOST",
        [PERF_RECORD_COMM]                      = "COMM",
        [PERF_RECORD_EXIT]                      = "EXIT",
        [PERF_RECORD_THROTTLE]                  = "THROTTLE",
        [PERF_RECORD_UNTHROTTLE]                = "UNTHROTTLE",
        [PERF_RECORD_FORK]                      = "FORK",
        [PERF_RECORD_READ]                      = "READ",
        [PERF_RECORD_SAMPLE]                    = "SAMPLE",
        [PERF_RECORD_AUX]                       = "AUX",
        [PERF_RECORD_ITRACE_START]              = "ITRACE_START",
        [PERF_RECORD_LOST_SAMPLES]              = "LOST_SAMPLES",
        [PERF_RECORD_SWITCH]                    = "SWITCH",
        [PERF_RECORD_SWITCH_CPU_WIDE]           = "SWITCH_CPU_WIDE",
        [PERF_RECORD_NAMESPACES]                = "NAMESPACES",
        [PERF_RECORD_KSYMBOL]                   = "KSYMBOL",
        [PERF_RECORD_BPF_EVENT]                 = "BPF_EVENT",
        [PERF_RECORD_CGROUP]                    = "CGROUP",
        [PERF_RECORD_TEXT_POKE]                 = "TEXT_POKE",
        [PERF_RECORD_AUX_OUTPUT_HW_ID]          = "AUX_OUTPUT_HW_ID",
        [PERF_RECORD_CALLCHAIN_DEFERRED]        = "CALLCHAIN_DEFERRED",
        [PERF_RECORD_HEADER_ATTR]               = "ATTR",
        [PERF_RECORD_HEADER_EVENT_TYPE]         = "EVENT_TYPE",
        [PERF_RECORD_HEADER_TRACING_DATA]       = "TRACING_DATA",
        [PERF_RECORD_HEADER_BUILD_ID]           = "BUILD_ID",
        [PERF_RECORD_FINISHED_ROUND]            = "FINISHED_ROUND",
        [PERF_RECORD_ID_INDEX]                  = "ID_INDEX",
        [PERF_RECORD_AUXTRACE_INFO]             = "AUXTRACE_INFO",
        [PERF_RECORD_AUXTRACE]                  = "AUXTRACE",
        [PERF_RECORD_AUXTRACE_ERROR]            = "AUXTRACE_ERROR",
        [PERF_RECORD_THREAD_MAP]                = "THREAD_MAP",
        [PERF_RECORD_CPU_MAP]                   = "CPU_MAP",
        [PERF_RECORD_STAT_CONFIG]               = "STAT_CONFIG",
        [PERF_RECORD_STAT]                      = "STAT",
        [PERF_RECORD_STAT_ROUND]                = "STAT_ROUND",
        [PERF_RECORD_EVENT_UPDATE]              = "EVENT_UPDATE",
        [PERF_RECORD_TIME_CONV]                 = "TIME_CONV",
        [PERF_RECORD_HEADER_FEATURE]            = "FEATURE",
        [PERF_RECORD_COMPRESSED]                = "COMPRESSED",
        [PERF_RECORD_FINISHED_INIT]             = "FINISHED_INIT",
        [PERF_RECORD_COMPRESSED2]               = "COMPRESSED2",
        [PERF_RECORD_BPF_METADATA]              = "BPF_METADATA",
        [PERF_RECORD_SCHEDSTAT_CPU]             = "SCHEDSTAT_CPU",
        [PERF_RECORD_SCHEDSTAT_DOMAIN]          = "SCHEDSTAT_DOMAIN",
};

const char *perf_event__name(unsigned int id)
{
        if (id >= ARRAY_SIZE(perf_event__names))
                return "INVALID";
        if (!perf_event__names[id])
                return "UNKNOWN";
        return perf_event__names[id];
}

struct process_symbol_args {
        const char *name;
        u64        start;
};

static int find_func_symbol_cb(void *arg, const char *name, char type,
                               u64 start)
{
        struct process_symbol_args *args = arg;

        /*
         * Must be a function or at least an alias, as in PARISC64, where "_text" is
         * an 'A' to the same address as "_stext".
         */
        if (!(kallsyms__is_function(type) ||
              type == 'A') || strcmp(name, args->name))
                return 0;

        args->start = start;
        return 1;
}

static int find_any_symbol_cb(void *arg, const char *name,
                              char type __maybe_unused, u64 start)
{
        struct process_symbol_args *args = arg;

        if (strcmp(name, args->name))
                return 0;

        args->start = start;
        return 1;
}

int kallsyms__get_function_start(const char *kallsyms_filename,
                                 const char *symbol_name, u64 *addr)
{
        struct process_symbol_args args = { .name = symbol_name, };

        if (kallsyms__parse(kallsyms_filename, &args, find_func_symbol_cb) <= 0)
                return -1;

        *addr = args.start;
        return 0;
}

int kallsyms__get_symbol_start(const char *kallsyms_filename,
                               const char *symbol_name, u64 *addr)
{
        struct process_symbol_args args = { .name = symbol_name, };

        if (kallsyms__parse(kallsyms_filename, &args, find_any_symbol_cb) <= 0)
                return -1;

        *addr = args.start;
        return 0;
}

void perf_event__read_stat_config(struct perf_stat_config *config,
                                  struct perf_record_stat_config *event)
{
        unsigned i;

        for (i = 0; i < event->nr; i++) {

                switch (event->data[i].tag) {
#define CASE(__term, __val)                                     \
                case PERF_STAT_CONFIG_TERM__##__term:           \
                        config->__val = event->data[i].val;     \
                        break;

                CASE(AGGR_MODE,  aggr_mode)
                CASE(SCALE,      scale)
                CASE(INTERVAL,   interval)
                CASE(AGGR_LEVEL, aggr_level)
#undef CASE
                default:
                        pr_warning("unknown stat config term %" PRI_lu64 "\n",
                                   event->data[i].tag);
                }
        }
}

size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp)
{
        const char *s;

        if (event->header.misc & PERF_RECORD_MISC_COMM_EXEC)
                s = " exec";
        else
                s = "";

        return fprintf(fp, "%s: %s:%d/%d\n", s, event->comm.comm, event->comm.pid, event->comm.tid);
}

size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp)
{
        size_t ret = 0;
        struct perf_ns_link_info *ns_link_info;
        u32 nr_namespaces, idx;

        ns_link_info = event->namespaces.link_info;
        nr_namespaces = event->namespaces.nr_namespaces;

        ret += fprintf(fp, " %d/%d - nr_namespaces: %u\n\t\t[",
                       event->namespaces.pid,
                       event->namespaces.tid,
                       nr_namespaces);

        for (idx = 0; idx < nr_namespaces; idx++) {
                if (idx && (idx % 4 == 0))
                        ret += fprintf(fp, "\n\t\t ");

                ret  += fprintf(fp, "%u/%s: %" PRIu64 "/%#" PRIx64 "%s", idx,
                                perf_ns__name(idx), (u64)ns_link_info[idx].dev,
                                (u64)ns_link_info[idx].ino,
                                ((idx + 1) != nr_namespaces) ? ", " : "]\n");
        }

        return ret;
}

size_t perf_event__fprintf_cgroup(union perf_event *event, FILE *fp)
{
        return fprintf(fp, " cgroup: %" PRI_lu64 " %s\n",
                       event->cgroup.id, event->cgroup.path);
}

int perf_event__process_comm(const struct perf_tool *tool __maybe_unused,
                             union perf_event *event,
                             struct perf_sample *sample,
                             struct machine *machine)
{
        return machine__process_comm_event(machine, event, sample);
}

int perf_event__process_namespaces(const struct perf_tool *tool __maybe_unused,
                                   union perf_event *event,
                                   struct perf_sample *sample,
                                   struct machine *machine)
{
        return machine__process_namespaces_event(machine, event, sample);
}

int perf_event__process_cgroup(const struct perf_tool *tool __maybe_unused,
                               union perf_event *event,
                               struct perf_sample *sample,
                               struct machine *machine)
{
        return machine__process_cgroup_event(machine, event, sample);
}

int perf_event__process_lost(const struct perf_tool *tool __maybe_unused,
                             union perf_event *event,
                             struct perf_sample *sample,
                             struct machine *machine)
{
        return machine__process_lost_event(machine, event, sample);
}

int perf_event__process_aux(const struct perf_tool *tool __maybe_unused,
                            union perf_event *event,
                            struct perf_sample *sample __maybe_unused,
                            struct machine *machine)
{
        return machine__process_aux_event(machine, event);
}

int perf_event__process_itrace_start(const struct perf_tool *tool __maybe_unused,
                                     union perf_event *event,
                                     struct perf_sample *sample __maybe_unused,
                                     struct machine *machine)
{
        return machine__process_itrace_start_event(machine, event);
}

int perf_event__process_aux_output_hw_id(const struct perf_tool *tool __maybe_unused,
                                         union perf_event *event,
                                         struct perf_sample *sample __maybe_unused,
                                         struct machine *machine)
{
        return machine__process_aux_output_hw_id_event(machine, event);
}

int perf_event__process_lost_samples(const struct perf_tool *tool __maybe_unused,
                                     union perf_event *event,
                                     struct perf_sample *sample,
                                     struct machine *machine)
{
        return machine__process_lost_samples_event(machine, event, sample);
}

int perf_event__process_switch(const struct perf_tool *tool __maybe_unused,
                               union perf_event *event,
                               struct perf_sample *sample __maybe_unused,
                               struct machine *machine)
{
        return machine__process_switch_event(machine, event);
}

int perf_event__process_ksymbol(const struct perf_tool *tool __maybe_unused,
                                union perf_event *event,
                                struct perf_sample *sample __maybe_unused,
                                struct machine *machine)
{
        return machine__process_ksymbol(machine, event, sample);
}

int perf_event__process_bpf(const struct perf_tool *tool __maybe_unused,
                            union perf_event *event,
                            struct perf_sample *sample,
                            struct machine *machine)
{
        return machine__process_bpf(machine, event, sample);
}

int perf_event__process_text_poke(const struct perf_tool *tool __maybe_unused,
                                  union perf_event *event,
                                  struct perf_sample *sample,
                                  struct machine *machine)
{
        return machine__process_text_poke(machine, event, sample);
}

size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
{
        return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64 "]: %c %s\n",
                       event->mmap.pid, event->mmap.tid, event->mmap.start,
                       event->mmap.len, event->mmap.pgoff,
                       (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x',
                       event->mmap.filename);
}

size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)
{
        if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) {
                char sbuild_id[SBUILD_ID_SIZE];
                struct build_id bid;

                build_id__init(&bid, event->mmap2.build_id,
                               event->mmap2.build_id_size);
                build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id));

                return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64
                                   " <%s>]: %c%c%c%c %s\n",
                               event->mmap2.pid, event->mmap2.tid, event->mmap2.start,
                               event->mmap2.len, event->mmap2.pgoff, sbuild_id,
                               (event->mmap2.prot & PROT_READ) ? 'r' : '-',
                               (event->mmap2.prot & PROT_WRITE) ? 'w' : '-',
                               (event->mmap2.prot & PROT_EXEC) ? 'x' : '-',
                               (event->mmap2.flags & MAP_SHARED) ? 's' : 'p',
                               event->mmap2.filename);
        } else {
                return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64
                                   " %02x:%02x %"PRI_lu64" %"PRI_lu64"]: %c%c%c%c %s\n",
                               event->mmap2.pid, event->mmap2.tid, event->mmap2.start,
                               event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj,
                               event->mmap2.min, event->mmap2.ino,
                               event->mmap2.ino_generation,
                               (event->mmap2.prot & PROT_READ) ? 'r' : '-',
                               (event->mmap2.prot & PROT_WRITE) ? 'w' : '-',
                               (event->mmap2.prot & PROT_EXEC) ? 'x' : '-',
                               (event->mmap2.flags & MAP_SHARED) ? 's' : 'p',
                               event->mmap2.filename);
        }
}

size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp)
{
        struct perf_thread_map *threads = thread_map__new_event(&event->thread_map);
        size_t ret;

        ret = fprintf(fp, " nr: ");

        if (threads)
                ret += thread_map__fprintf(threads, fp);
        else
                ret += fprintf(fp, "failed to get threads from event\n");

        perf_thread_map__put(threads);
        return ret;
}

size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp)
{
        struct perf_cpu_map *cpus = cpu_map__new_data(&event->cpu_map.data);
        size_t ret;

        ret = fprintf(fp, ": ");

        if (cpus)
                ret += cpu_map__fprintf(cpus, fp);
        else
                ret += fprintf(fp, "failed to get cpumap from event\n");

        perf_cpu_map__put(cpus);
        return ret;
}

int perf_event__process_mmap(const struct perf_tool *tool __maybe_unused,
                             union perf_event *event,
                             struct perf_sample *sample,
                             struct machine *machine)
{
        return machine__process_mmap_event(machine, event, sample);
}

int perf_event__process_mmap2(const struct perf_tool *tool __maybe_unused,
                             union perf_event *event,
                             struct perf_sample *sample,
                             struct machine *machine)
{
        return machine__process_mmap2_event(machine, event, sample);
}

size_t perf_event__fprintf_task(union perf_event *event, FILE *fp)
{
        return fprintf(fp, "(%d:%d):(%d:%d)\n",
                       event->fork.pid, event->fork.tid,
                       event->fork.ppid, event->fork.ptid);
}

int perf_event__process_fork(const struct perf_tool *tool __maybe_unused,
                             union perf_event *event,
                             struct perf_sample *sample,
                             struct machine *machine)
{
        return machine__process_fork_event(machine, event, sample);
}

int perf_event__process_exit(const struct perf_tool *tool __maybe_unused,
                             union perf_event *event,
                             struct perf_sample *sample,
                             struct machine *machine)
{
        return machine__process_exit_event(machine, event, sample);
}

int perf_event__exit_del_thread(const struct perf_tool *tool __maybe_unused,
                                union perf_event *event,
                                struct perf_sample *sample __maybe_unused,
                                struct machine *machine)
{
        struct thread *thread = machine__findnew_thread(machine,
                                                        event->fork.pid,
                                                        event->fork.tid);

        dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid,
                    event->fork.ppid, event->fork.ptid);

        if (thread) {
                machine__remove_thread(machine, thread);
                thread__put(thread);
        }

        return 0;
}

size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp)
{
        return fprintf(fp, " offset: %#"PRI_lx64" size: %#"PRI_lx64" flags: %#"PRI_lx64" [%s%s%s%s]\n",
                       event->aux.aux_offset, event->aux.aux_size,
                       event->aux.flags,
                       event->aux.flags & PERF_AUX_FLAG_TRUNCATED ? "T" : "",
                       event->aux.flags & PERF_AUX_FLAG_OVERWRITE ? "O" : "",
                       event->aux.flags & PERF_AUX_FLAG_PARTIAL   ? "P" : "",
                       event->aux.flags & PERF_AUX_FLAG_COLLISION ? "C" : "");
}

size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp)
{
        return fprintf(fp, " pid: %u tid: %u\n",
                       event->itrace_start.pid, event->itrace_start.tid);
}

size_t perf_event__fprintf_aux_output_hw_id(union perf_event *event, FILE *fp)
{
        return fprintf(fp, " hw_id: %#"PRI_lx64"\n",
                       event->aux_output_hw_id.hw_id);
}

size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp)
{
        bool out = event->header.misc & PERF_RECORD_MISC_SWITCH_OUT;
        const char *in_out = !out ? "IN         " :
                !(event->header.misc & PERF_RECORD_MISC_SWITCH_OUT_PREEMPT) ?
                                    "OUT        " : "OUT preempt";

        if (event->header.type == PERF_RECORD_SWITCH)
                return fprintf(fp, " %s\n", in_out);

        return fprintf(fp, " %s  %s pid/tid: %5d/%-5d\n",
                       in_out, out ? "next" : "prev",
                       event->context_switch.next_prev_pid,
                       event->context_switch.next_prev_tid);
}

static size_t perf_event__fprintf_lost(union perf_event *event, FILE *fp)
{
        return fprintf(fp, " lost %" PRI_lu64 "\n", event->lost.lost);
}

size_t perf_event__fprintf_ksymbol(union perf_event *event, FILE *fp)
{
        return fprintf(fp, " addr %" PRI_lx64 " len %u type %u flags 0x%x name %s\n",
                       event->ksymbol.addr, event->ksymbol.len,
                       event->ksymbol.ksym_type,
                       event->ksymbol.flags, event->ksymbol.name);
}

size_t perf_event__fprintf_bpf(union perf_event *event, FILE *fp)
{
        return fprintf(fp, " type %u, flags %u, id %u\n",
                       event->bpf.type, event->bpf.flags, event->bpf.id);
}

size_t perf_event__fprintf_bpf_metadata(union perf_event *event, FILE *fp)
{
        struct perf_record_bpf_metadata *metadata = &event->bpf_metadata;
        size_t ret;

        ret = fprintf(fp, " prog %s\n", metadata->prog_name);
        for (__u32 i = 0; i < metadata->nr_entries; i++) {
                ret += fprintf(fp, "  entry %d: %20s = %s\n", i,
                               metadata->entries[i].key,
                               metadata->entries[i].value);
        }
        return ret;
}

static int text_poke_printer(enum binary_printer_ops op, unsigned int val,
                             void *extra, FILE *fp)
{
        bool old = *(bool *)extra;

        switch ((int)op) {
        case BINARY_PRINT_LINE_BEGIN:
                return fprintf(fp, "            %s bytes:", old ? "Old" : "New");
        case BINARY_PRINT_NUM_DATA:
                return fprintf(fp, " %02x", val);
        case BINARY_PRINT_LINE_END:
                return fprintf(fp, "\n");
        default:
                return 0;
        }
}

size_t perf_event__fprintf_text_poke(union perf_event *event, struct machine *machine, FILE *fp)
{
        struct perf_record_text_poke_event *tp = &event->text_poke;
        size_t ret;
        bool old;

        ret = fprintf(fp, " %" PRI_lx64 " ", tp->addr);
        if (machine) {
                struct addr_location al;

                addr_location__init(&al);
                al.map = maps__find(machine__kernel_maps(machine), tp->addr);
                if (al.map && map__load(al.map) >= 0) {
                        al.addr = map__map_ip(al.map, tp->addr);
                        al.sym = map__find_symbol(al.map, al.addr);
                        if (al.sym)
                                ret += symbol__fprintf_symname_offs(al.sym, &al, fp);
                }
                addr_location__exit(&al);
        }
        ret += fprintf(fp, " old len %u new len %u\n", tp->old_len, tp->new_len);
        old = true;
        ret += binary__fprintf(tp->bytes, tp->old_len, 16, text_poke_printer,
                               &old, fp);
        old = false;
        ret += binary__fprintf(tp->bytes + tp->old_len, tp->new_len, 16,
                               text_poke_printer, &old, fp);
        return ret;
}

size_t perf_event__fprintf_schedstat_cpu(union perf_event *event, FILE *fp)
{
        struct perf_record_schedstat_cpu *cs = &event->schedstat_cpu;
        size_t size = fprintf(fp, "\ncpu%u ", cs->cpu);
        __u16 version = cs->version;

#define CPU_FIELD(_type, _name, _desc, _format, _is_pct, _pct_of, _ver)         \
        size += fprintf(fp, "%" PRIu64 " ", (uint64_t)cs->_ver._name)

        if (version == 15) {
#include <perf/schedstat-v15.h>
                return size;
        } else if (version == 16) {
#include <perf/schedstat-v16.h>
                return size;
        } else if (version == 17) {
#include <perf/schedstat-v17.h>
                return size;
        }
#undef CPU_FIELD

        return fprintf(fp, "Unsupported /proc/schedstat version %d.\n",
                       event->schedstat_cpu.version);
}

size_t perf_event__fprintf_schedstat_domain(union perf_event *event, FILE *fp)
{
        struct perf_record_schedstat_domain *ds = &event->schedstat_domain;
        __u16 version = ds->version;
        size_t size = fprintf(fp, "\ndomain%u ", ds->domain);

#define DOMAIN_FIELD(_type, _name, _desc, _format, _is_jiffies, _ver)           \
        size += fprintf(fp, "%" PRIu64 " ", (uint64_t)ds->_ver._name)

        if (version == 15) {
#include <perf/schedstat-v15.h>
                return size;
        } else if (version == 16) {
#include <perf/schedstat-v16.h>
                return size;
        } else if (version == 17) {
#include <perf/schedstat-v17.h>
                return size;
        }
#undef DOMAIN_FIELD

        return fprintf(fp, "Unsupported /proc/schedstat version %d.\n",
                       event->schedstat_domain.version);
}

size_t perf_event__fprintf(union perf_event *event, struct machine *machine, FILE *fp)
{
        size_t ret = fprintf(fp, "PERF_RECORD_%s",
                             perf_event__name(event->header.type));

        switch (event->header.type) {
        case PERF_RECORD_COMM:
                ret += perf_event__fprintf_comm(event, fp);
                break;
        case PERF_RECORD_FORK:
        case PERF_RECORD_EXIT:
                ret += perf_event__fprintf_task(event, fp);
                break;
        case PERF_RECORD_MMAP:
                ret += perf_event__fprintf_mmap(event, fp);
                break;
        case PERF_RECORD_NAMESPACES:
                ret += perf_event__fprintf_namespaces(event, fp);
                break;
        case PERF_RECORD_CGROUP:
                ret += perf_event__fprintf_cgroup(event, fp);
                break;
        case PERF_RECORD_MMAP2:
                ret += perf_event__fprintf_mmap2(event, fp);
                break;
        case PERF_RECORD_AUX:
                ret += perf_event__fprintf_aux(event, fp);
                break;
        case PERF_RECORD_ITRACE_START:
                ret += perf_event__fprintf_itrace_start(event, fp);
                break;
        case PERF_RECORD_SWITCH:
        case PERF_RECORD_SWITCH_CPU_WIDE:
                ret += perf_event__fprintf_switch(event, fp);
                break;
        case PERF_RECORD_LOST:
                ret += perf_event__fprintf_lost(event, fp);
                break;
        case PERF_RECORD_KSYMBOL:
                ret += perf_event__fprintf_ksymbol(event, fp);
                break;
        case PERF_RECORD_BPF_EVENT:
                ret += perf_event__fprintf_bpf(event, fp);
                break;
        case PERF_RECORD_TEXT_POKE:
                ret += perf_event__fprintf_text_poke(event, machine, fp);
                break;
        case PERF_RECORD_AUX_OUTPUT_HW_ID:
                ret += perf_event__fprintf_aux_output_hw_id(event, fp);
                break;
        case PERF_RECORD_BPF_METADATA:
                ret += perf_event__fprintf_bpf_metadata(event, fp);
                break;
        default:
                ret += fprintf(fp, "\n");
        }

        return ret;
}

int perf_event__process(const struct perf_tool *tool __maybe_unused,
                        union perf_event *event,
                        struct perf_sample *sample,
                        struct machine *machine)
{
        return machine__process_event(machine, event, sample);
}

struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr,
                             struct addr_location *al)
{
        struct maps *maps = thread__maps(thread);
        struct machine *machine = maps__machine(maps);
        bool load_map = false;

        map__zput(al->map);
        thread__zput(al->thread);
        al->thread = thread__get(thread);

        al->addr = addr;
        al->cpumode = cpumode;
        al->filtered = 0;

        if (machine == NULL)
                return NULL;

        if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {
                al->level = 'k';
                maps = machine__kernel_maps(machine);
                load_map = !symbol_conf.lazy_load_kernel_maps;
        } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) {
                al->level = '.';
        } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {
                al->level = 'g';
                maps = machine__kernel_maps(machine);
                load_map = !symbol_conf.lazy_load_kernel_maps;
        } else if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) {
                al->level = 'u';
        } else {
                al->level = 'H';

                if ((cpumode == PERF_RECORD_MISC_GUEST_USER ||
                        cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&
                        !perf_guest)
                        al->filtered |= (1 << HIST_FILTER__GUEST);
                if ((cpumode == PERF_RECORD_MISC_USER ||
                        cpumode == PERF_RECORD_MISC_KERNEL) &&
                        !perf_host)
                        al->filtered |= (1 << HIST_FILTER__HOST);

                return NULL;
        }
        al->map = maps__find(maps, al->addr);
        if (al->map != NULL) {
                /*
                 * Kernel maps might be changed when loading symbols so loading
                 * must be done prior to using kernel maps.
                 */
                if (load_map)
                        map__load(al->map);
                al->addr = map__map_ip(al->map, al->addr);
        }

        return al->map;
}

/*
 * For branch stacks or branch samples, the sample cpumode might not be correct
 * because it applies only to the sample 'ip' and not necessary to 'addr' or
 * branch stack addresses. If possible, use a fallback to deal with those cases.
 */
struct map *thread__find_map_fb(struct thread *thread, u8 cpumode, u64 addr,
                                struct addr_location *al)
{
        struct map *map = thread__find_map(thread, cpumode, addr, al);
        struct machine *machine = maps__machine(thread__maps(thread));
        u8 addr_cpumode = machine__addr_cpumode(machine, cpumode, addr);

        if (map || addr_cpumode == cpumode)
                return map;

        return thread__find_map(thread, addr_cpumode, addr, al);
}

struct symbol *thread__find_symbol(struct thread *thread, u8 cpumode,
                                   u64 addr, struct addr_location *al)
{
        al->sym = NULL;
        if (thread__find_map(thread, cpumode, addr, al))
                al->sym = map__find_symbol(al->map, al->addr);
        return al->sym;
}

struct symbol *thread__find_symbol_fb(struct thread *thread, u8 cpumode,
                                      u64 addr, struct addr_location *al)
{
        al->sym = NULL;
        if (thread__find_map_fb(thread, cpumode, addr, al))
                al->sym = map__find_symbol(al->map, al->addr);
        return al->sym;
}

static bool check_address_range(struct intlist *addr_list, int addr_range,
                                unsigned long addr)
{
        struct int_node *pos;

        intlist__for_each_entry(pos, addr_list) {
                if (addr >= pos->i && addr < pos->i + addr_range)
                        return true;
        }

        return false;
}

/*
 * Callers need to drop the reference to al->thread, obtained in
 * machine__findnew_thread()
 */
int machine__resolve(struct machine *machine, struct addr_location *al,
                     struct perf_sample *sample)
{
        struct thread *thread;
        struct dso *dso;

        if (symbol_conf.guest_code && !machine__is_host(machine))
                thread = machine__findnew_guest_code(machine, sample->pid);
        else
                thread = machine__findnew_thread(machine, sample->pid, sample->tid);
        if (thread == NULL)
                return -1;

        dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread__tid(thread));
        thread__find_map(thread, sample->cpumode, sample->ip, al);
        dso = al->map ? map__dso(al->map) : NULL;
        dump_printf(" ...... dso: %s\n",
                dso
                ? dso__long_name(dso)
                : (al->level == 'H' ? "[hypervisor]" : "<not found>"));

        if (thread__is_filtered(thread))
                al->filtered |= (1 << HIST_FILTER__THREAD);

        thread__put(thread);
        thread = NULL;

        al->sym = NULL;
        al->cpu = sample->cpu;
        al->socket = -1;
        al->srcline = NULL;

        if (al->cpu >= 0) {
                struct perf_env *env = machine->env;

                if (env && env->cpu)
                        al->socket = env->cpu[al->cpu].socket_id;
        }

        /* Account for possible out-of-order switch events. */
        al->parallelism = max(1, min(machine->parallelism, machine__nr_cpus_avail(machine)));
        if (test_bit(al->parallelism, symbol_conf.parallelism_filter))
                al->filtered |= (1 << HIST_FILTER__PARALLELISM);
        /*
         * Multiply it by some const to avoid precision loss or dealing
         * with floats. The multiplier does not matter otherwise since
         * we only print it as percents.
         */
        al->latency = sample->period * 1000 / al->parallelism;

        if (al->map) {
                if (symbol_conf.dso_list &&
                    (!dso || !(strlist__has_entry(symbol_conf.dso_list,
                                                  dso__short_name(dso)) ||
                               (dso__short_name(dso) != dso__long_name(dso) &&
                                strlist__has_entry(symbol_conf.dso_list,
                                                   dso__long_name(dso)))))) {
                        al->filtered |= (1 << HIST_FILTER__DSO);
                }

                al->sym = map__find_symbol(al->map, al->addr);
        } else if (symbol_conf.dso_list) {
                al->filtered |= (1 << HIST_FILTER__DSO);
        }

        if (symbol_conf.sym_list) {
                int ret = 0;
                char al_addr_str[32];
                size_t sz = sizeof(al_addr_str);

                if (al->sym) {
                        ret = strlist__has_entry(symbol_conf.sym_list,
                                                al->sym->name);
                }
                if (!ret && al->sym) {
                        snprintf(al_addr_str, sz, "0x%"PRIx64,
                                 map__unmap_ip(al->map, al->sym->start));
                        ret = strlist__has_entry(symbol_conf.sym_list,
                                                al_addr_str);
                }
                if (!ret && symbol_conf.addr_list && al->map) {
                        unsigned long addr = map__unmap_ip(al->map, al->addr);

                        ret = intlist__has_entry(symbol_conf.addr_list, addr);
                        if (!ret && symbol_conf.addr_range) {
                                ret = check_address_range(symbol_conf.addr_list,
                                                          symbol_conf.addr_range,
                                                          addr);
                        }
                }

                if (!ret)
                        al->filtered |= (1 << HIST_FILTER__SYMBOL);
        }

        return 0;
}

bool is_bts_event(struct perf_event_attr *attr)
{
        return attr->type == PERF_TYPE_HARDWARE &&
               (attr->config & PERF_COUNT_HW_BRANCH_INSTRUCTIONS) &&
               attr->sample_period == 1;
}

bool sample_addr_correlates_sym(struct perf_event_attr *attr)
{
        if (attr->type == PERF_TYPE_SOFTWARE &&
            (attr->config == PERF_COUNT_SW_PAGE_FAULTS ||
             attr->config == PERF_COUNT_SW_PAGE_FAULTS_MIN ||
             attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ))
                return true;

        if (is_bts_event(attr))
                return true;

        return false;
}

void thread__resolve(struct thread *thread, struct addr_location *al,
                     struct perf_sample *sample)
{
        thread__find_map_fb(thread, sample->cpumode, sample->addr, al);

        al->cpu = sample->cpu;
        al->sym = NULL;

        if (al->map)
                al->sym = map__find_symbol(al->map, al->addr);
}