|  | /* 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 <parlib/core_set.h> | 
|  | #include <perfmon/err.h> | 
|  | #include <perfmon/pfmlib.h> | 
|  | #include "xlib.h" | 
|  | #include "perfconv.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) << 8) | | 
|  | 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) << 8) | | 
|  | 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); | 
|  | } |