| /* |
| * Check verbose decoding of perf_event_open syscall. |
| * |
| * Copyright (c) 2016 Eugene Syromyatnikov <[email protected]> |
| * Copyright (c) 2016-2018 The strace developers. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "tests.h" |
| #include <asm/unistd.h> |
| |
| #if defined(__NR_perf_event_open) && defined(HAVE_LINUX_PERF_EVENT_H) |
| |
| # include <inttypes.h> |
| # include <limits.h> |
| # include <stddef.h> |
| # include <stdio.h> |
| # include <stdlib.h> |
| # include <string.h> |
| # include <unistd.h> |
| |
| # include <linux/perf_event.h> |
| |
| # include "xlat.h" |
| # include "xlat/perf_event_open_flags.h" |
| # include "xlat/perf_attr_size.h" |
| |
| # if ULONG_MAX > UINT_MAX /* Poor man's "whether long is 8 bytes?" */ |
| # define LONG_STR_PREFIX "ffffffff" |
| # else /* !(ULONG_MAX > UINT_MAX) */ |
| # define LONG_STR_PREFIX "" |
| # endif /* ULONG_MAX > UINT_MAX */ |
| |
| # ifndef PERF_TYPE_BREAKPOINT |
| # define PERF_TYPE_BREAKPOINT 5 |
| # endif |
| |
| struct s32_val_str { |
| int32_t val; |
| const char *str; |
| }; |
| |
| struct u32_val_str { |
| uint32_t val; |
| const char *str; |
| }; |
| |
| struct u64_val_str { |
| uint64_t val; |
| const char *str; |
| }; |
| |
| /* In order to avoid endianness-specific hackery. */ |
| struct pea_flags { |
| uint64_t disabled :1, |
| inherit :1, |
| pinned :1, |
| exclusive :1, |
| exclude_user :1, |
| exclude_kernel :1, |
| exclude_hv :1, |
| exclude_idle :1, |
| mmap :1, |
| comm :1, |
| freq :1, |
| inherit_stat :1, |
| enable_on_exec :1, |
| task :1, |
| watermark :1, |
| precise_ip :2, |
| mmap_data :1, |
| sample_id_all :1, |
| exclude_host :1, |
| exclude_guest :1, |
| exclude_callchain_kernel :1, |
| exclude_callchain_user :1, |
| mmap2 :1, |
| comm_exec :1, |
| use_clockid :1, |
| context_switch :1, |
| write_backward :1, |
| namespaces :1, |
| __reserved_1 :35; |
| }; |
| |
| static const char * |
| printaddr(void *ptr) |
| { |
| static char buf[sizeof("0x") + sizeof(void *) * 2]; |
| |
| if (ptr == NULL) |
| return "NULL"; |
| |
| snprintf(buf, sizeof(buf), "%#lx", (unsigned long)ptr); |
| |
| return buf; |
| } |
| |
| /* |
| * Checklist: |
| * |
| * type - 8 IDs |
| * config - 13 IDs (0..11 + random), depends on type |
| * sample type - bitmask, up to 20 bits |
| * read_format - 5 IDs |
| * bp_type - 6, weird semantics (invalid/unknown) |
| * branch_sample_type - bitmask, 16 bits |
| * clockid - 13 values |
| * |
| * Unions: |
| * sample_period/sample_freq |
| * wakeup_event/wakeup_watermark |
| * bp_addr/config1 |
| * bp_len/config2 |
| */ |
| |
| /* |
| * The main idea behind all those numerous ifdefs is checking against version of |
| * structure provided in kernel headers and not use one defined in strace |
| * headers (assume the case when suddenly we add flag without proper update of |
| * __reserved_1 field or something like this). |
| */ |
| static void |
| print_event_attr(struct perf_event_attr *attr_ptr, size_t size, |
| const char *type, const char *config, const char *sample_type, |
| const char *read_format, const char *precise_ip_desc, |
| const char *bp_type, const char *branch_sample_type, |
| const char *clockid, uint32_t available_size) |
| { |
| /* |
| * Currently, strace supports version 5 of the structure, which is |
| * 112 bytes in size. |
| */ |
| enum { |
| STRACE_PEA_ABBREV_SIZE = |
| offsetof(struct perf_event_attr, config) + |
| sizeof(attr_ptr->config), |
| STRACE_PEA_SIZE = 112, |
| }; |
| |
| uint32_t read_size; |
| struct perf_event_attr *attr; |
| # if VERBOSE |
| uint32_t cutoff; |
| uint64_t val; |
| uint64_t use_clockid; |
| union { |
| struct pea_flags flags; |
| uint64_t raw; |
| } flags_data; |
| # endif |
| |
| read_size = |
| # if !VERBOSE |
| STRACE_PEA_ABBREV_SIZE; |
| # else |
| size < STRACE_PEA_SIZE ? |
| (size ? size : PERF_ATTR_SIZE_VER0) : STRACE_PEA_SIZE; |
| # endif |
| |
| if (read_size > available_size) { |
| printf("%s", printaddr(attr_ptr)); |
| return; |
| } |
| |
| /* |
| * Replicate kernel's behaviour regarding copying structure from |
| * userspace. |
| */ |
| attr = calloc(1, STRACE_PEA_SIZE); |
| |
| if (!attr) |
| error_msg_and_fail("calloc"); |
| |
| |
| memcpy(attr, attr_ptr, read_size); |
| |
| if (size && (size < PERF_ATTR_SIZE_VER0)) { |
| printf("%s", printaddr(attr_ptr)); |
| free(attr); |
| return; |
| } |
| |
| printf("{type=%s, size=", type); |
| if (size != attr->size) { |
| printxval(perf_attr_size, size, "PERF_ATTR_SIZE_???"); |
| printf(" => "); |
| } |
| printxval(perf_attr_size, attr->size, "PERF_ATTR_SIZE_???"); |
| printf(", config=%s, ", config); |
| |
| if (!size) |
| size = PERF_ATTR_SIZE_VER0; |
| |
| # if !VERBOSE |
| printf("...}"); |
| # else /* !VERBOSE */ |
| printf("%s=%" PRI__u64", sample_type=%s, read_format=%s", |
| attr->freq ? "sample_freq" : "sample_period", |
| attr->freq ? attr->sample_freq : attr->sample_period, |
| sample_type, read_format); |
| |
| printf(", disabled=%u" |
| ", inherit=%u" |
| ", pinned=%u" |
| ", exclusive=%u" |
| ", exclusive_user=%u" |
| ", exclude_kernel=%u" |
| ", exclude_hv=%u" |
| ", exclude_idle=%u" |
| ", mmap=%u" |
| ", comm=%u" |
| ", freq=%u" |
| ", inherit_stat=%u" |
| ", enable_on_exec=%u" |
| ", task=%u" |
| ", watermark=%u", |
| attr->disabled, |
| attr->inherit, |
| attr->pinned, |
| attr->exclusive, |
| attr->exclude_user, |
| attr->exclude_kernel, |
| attr->exclude_hv, |
| attr->exclude_idle, |
| attr->mmap, |
| attr->comm, |
| attr->freq, |
| attr->inherit_stat, |
| attr->enable_on_exec, |
| attr->task, |
| attr->watermark); |
| |
| flags_data.raw = ((uint64_t *) attr)[5]; |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_PRECISE_IP |
| attr->precise_ip; |
| # else |
| flags_data.flags.precise_ip; |
| # endif |
| printf(", precise_ip=%" PRIu64 " /* %s */", val, precise_ip_desc); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_MMAP_DATA |
| attr->mmap_data; |
| # else |
| flags_data.flags.mmap_data; |
| # endif |
| printf(", mmap_data=%" PRIu64, val); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_SAMPLE_ID_ALL |
| attr->sample_id_all; |
| # else |
| flags_data.flags.sample_id_all; |
| # endif |
| printf(", sample_id_all=%" PRIu64, val); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_EXCLUDE_HOST |
| attr->exclude_host; |
| # else |
| flags_data.flags.exclude_host; |
| # endif |
| printf(", exclude_host=%" PRIu64, val); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_EXCLUDE_GUEST |
| attr->exclude_guest; |
| # else |
| flags_data.flags.exclude_guest; |
| # endif |
| printf(", exclude_guest=%" PRIu64, val); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_EXCLUDE_CALLCHAIN_KERNEL |
| attr->exclude_callchain_kernel; |
| # else |
| flags_data.flags.exclude_callchain_kernel; |
| # endif |
| printf(", exclude_callchain_kernel=%" PRIu64, val); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_EXCLUDE_CALLCHAIN_USER |
| attr->exclude_callchain_user; |
| # else |
| flags_data.flags.exclude_callchain_user; |
| # endif |
| printf(", exclude_callchain_user=%" PRIu64, val); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_MMAP2 |
| attr->mmap2; |
| # else |
| flags_data.flags.mmap2; |
| # endif |
| printf(", mmap2=%" PRIu64, val); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_COMM_EXEC |
| attr->comm_exec; |
| # else |
| flags_data.flags.comm_exec; |
| # endif |
| printf(", comm_exec=%" PRIu64, val); |
| |
| use_clockid = val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_USE_CLOCKID |
| attr->use_clockid; |
| # else |
| flags_data.flags.use_clockid; |
| # endif |
| printf(", use_clockid=%" PRIu64, val); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_CONTEXT_SWITCH |
| attr->context_switch; |
| # else |
| flags_data.flags.context_switch; |
| # endif |
| printf(", context_switch=%" PRIu64, val); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_WRITE_BACKWARD |
| attr->write_backward; |
| # else |
| flags_data.flags.write_backward; |
| # endif |
| printf(", write_backward=%" PRIu64, val); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_NAMESPACES |
| attr->namespaces; |
| # else |
| flags_data.flags.namespaces; |
| # endif |
| printf(", namespaces=%" PRIu64, val); |
| |
| val = flags_data.flags.__reserved_1; |
| if (val) |
| printf(", __reserved_1=%#" PRIx64 " /* Bits 63..29 */", val); |
| |
| printf(", %s=%u", |
| attr->watermark ? "wakeup_watermark" : "wakeup_events", |
| attr->watermark ? attr->wakeup_watermark : attr->wakeup_events); |
| |
| if (attr->type == PERF_TYPE_BREAKPOINT) |
| printf(", bp_type=%s", bp_type); |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_CONFIG1 |
| attr->config1; |
| # else |
| ((uint64_t *) attr)[56 / sizeof(uint64_t)]; |
| # endif |
| printf(", %s=%#" PRIx64, |
| attr->type == PERF_TYPE_BREAKPOINT ? "bp_addr" : "config1", |
| val); |
| |
| /* End of version 0 of the structure */ |
| if (size <= 64) { |
| cutoff = 64; |
| goto end; |
| } |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_CONFIG2 |
| attr->config2; |
| # else |
| ((uint64_t *) attr)[64 / sizeof(uint64_t)]; |
| # endif |
| if (attr->type == PERF_TYPE_BREAKPOINT) |
| printf(", bp_len=%" PRIu64, val); |
| else |
| printf(", config2=%#" PRIx64, val); |
| |
| /* End of version 1 of the structure */ |
| if (size <= 72) { |
| cutoff = 72; |
| goto end; |
| } |
| |
| /* |
| * Print branch sample type only in case PERF_SAMPLE_BRANCH_STACK |
| * is set in the sample_type field. |
| */ |
| if (attr->sample_type & (1 << 11)) |
| printf(", branch_sample_type=%s", branch_sample_type); |
| |
| /* End of version 2 of the structure */ |
| if (size <= 80) { |
| cutoff = 80; |
| goto end; |
| } |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_SAMPLE_REGS_USER |
| attr->sample_regs_user; |
| # else |
| ((uint64_t *) attr)[80 / sizeof(uint64_t)]; |
| # endif |
| printf(", sample_regs_user=%#" PRIx64, val); |
| |
| if (size <= 88) { |
| cutoff = 88; |
| goto end; |
| } |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_SAMPLE_STACK_USER |
| attr->sample_stack_user; |
| # else |
| ((uint32_t *) attr)[88 / sizeof(uint32_t)]; |
| # endif |
| /* |
| * Print branch sample type only in case PERF_SAMPLE_STACK_USER |
| * is set in the sample_type field. |
| */ |
| if (attr->sample_type & (1 << 13)) |
| printf(", sample_stack_user=%#" PRIx32, (uint32_t) val); |
| |
| if (size <= 92) { |
| cutoff = 92; |
| goto end; |
| } |
| |
| if (use_clockid) |
| printf(", clockid=%s", clockid); |
| |
| /* End of version 3 of the structure */ |
| if (size <= 96) { |
| cutoff = 96; |
| goto end; |
| } |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_SAMPLE_REGS_INTR |
| attr->sample_regs_intr; |
| # else |
| ((uint64_t *) attr)[96 / sizeof(uint64_t)]; |
| # endif |
| printf(", sample_regs_intr=%#" PRIx64, val); |
| |
| /* End of version 4 of the structure */ |
| if (size <= 104) { |
| cutoff = 104; |
| goto end; |
| } |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_AUX_WATERMARK |
| attr->aux_watermark; |
| # else |
| ((uint32_t *) attr)[104 / sizeof(uint32_t)]; |
| # endif |
| printf(", aux_watermark=%" PRIu32, (uint32_t) val); |
| |
| if (size <= 108) { |
| cutoff = 108; |
| goto end; |
| } |
| |
| val = |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_SAMPLE_MAX_STACK |
| attr->sample_max_stack; |
| # else |
| ((uint16_t *) attr)[108 / sizeof(uint16_t)]; |
| # endif |
| printf(", sample_max_stack=%" PRIu16, (uint16_t) val); |
| |
| if (size <= 110) { |
| cutoff = 110; |
| goto end; |
| } |
| |
| cutoff = STRACE_PEA_SIZE; |
| |
| end: |
| if (size > cutoff) |
| printf(", ..."); |
| |
| printf("}"); |
| # endif /* !VERBOSE */ |
| |
| free(attr); |
| } |
| |
| /* These require aligned access, so no byte-grain checks possible */ |
| # if defined SPARC || defined SPARC64 || defined POWERPC || defined POWERPC64 |
| # define ATTR_REC(sz) { tail_alloc((sz + 7) & ~7), sz } |
| # else |
| # define ATTR_REC(sz) { tail_alloc(sz), sz } |
| # endif |
| |
| # define BRANCH_TYPE_ALL \ |
| "PERF_SAMPLE_BRANCH_USER|" \ |
| "PERF_SAMPLE_BRANCH_KERNEL|" \ |
| "PERF_SAMPLE_BRANCH_HV|" \ |
| "PERF_SAMPLE_BRANCH_ANY|" \ |
| "PERF_SAMPLE_BRANCH_ANY_CALL|" \ |
| "PERF_SAMPLE_BRANCH_ANY_RETURN|" \ |
| "PERF_SAMPLE_BRANCH_IND_CALL|" \ |
| "PERF_SAMPLE_BRANCH_ABORT_TX|" \ |
| "PERF_SAMPLE_BRANCH_IN_TX|" \ |
| "PERF_SAMPLE_BRANCH_NO_TX|" \ |
| "PERF_SAMPLE_BRANCH_COND|" \ |
| "PERF_SAMPLE_BRANCH_CALL_STACK|" \ |
| "PERF_SAMPLE_BRANCH_IND_JUMP|" \ |
| "PERF_SAMPLE_BRANCH_CALL|" \ |
| "PERF_SAMPLE_BRANCH_NO_FLAGS|" \ |
| "PERF_SAMPLE_BRANCH_NO_CYCLES|" \ |
| "PERF_SAMPLE_BRANCH_TYPE_SAVE" |
| |
| int |
| main(void) |
| { |
| static const size_t attr_small_size = PERF_ATTR_SIZE_VER0 - 8; |
| static const size_t attr_v0_size = PERF_ATTR_SIZE_VER0; |
| static const size_t attr_v1_size = PERF_ATTR_SIZE_VER1; |
| static const size_t attr_v2_size = PERF_ATTR_SIZE_VER2; |
| static const size_t attr_v2_5_size = PERF_ATTR_SIZE_VER2 + 8; |
| static const size_t attr_v2_75_size = PERF_ATTR_SIZE_VER2 + 12; |
| static const size_t attr_v3_size = PERF_ATTR_SIZE_VER3; |
| static const size_t attr_v4_size = PERF_ATTR_SIZE_VER4; |
| static const size_t attr_v4_5_size = PERF_ATTR_SIZE_VER4 + 4; |
| static const size_t attr_v4_625_size = PERF_ATTR_SIZE_VER4 + 5; |
| static const size_t attr_v4_875_size = PERF_ATTR_SIZE_VER4 + 7; |
| static const size_t attr_v5_size = PERF_ATTR_SIZE_VER5; |
| static const size_t attr_big_size = PERF_ATTR_SIZE_VER5 + 32; |
| |
| static const struct u64_val_str attr_types[] = { |
| { ARG_STR(PERF_TYPE_HARDWARE) }, |
| { ARG_STR(PERF_TYPE_SOFTWARE) }, |
| { ARG_STR(PERF_TYPE_TRACEPOINT) }, |
| { ARG_STR(PERF_TYPE_HW_CACHE) }, |
| { ARG_STR(PERF_TYPE_RAW) }, |
| { ARG_STR(PERF_TYPE_BREAKPOINT) }, |
| { ARG_STR(0x6) " /* PERF_TYPE_??? */" }, |
| { ARG_STR(0xdeadc0de) " /* PERF_TYPE_??? */" }, |
| }; |
| static const struct u64_val_str |
| attr_configs[ARRAY_SIZE(attr_types)][3] = { |
| /* PERF_TYPE_HARDWARE */ { |
| { 9, "PERF_COUNT_HW_REF_CPU_CYCLES" }, |
| { 10, "0xa /* PERF_COUNT_HW_??? */" }, |
| { ARG_ULL_STR(0xfaceca75deadb0d4) |
| " /* PERF_COUNT_HW_??? */" }, |
| }, |
| /* PERF_TYPE_SOFTWARE */ { |
| { 10, "PERF_COUNT_SW_BPF_OUTPUT" }, |
| { 11, "0xb /* PERF_COUNT_SW_??? */" }, |
| { ARG_ULL_STR(0xdec0ded1dec0ded2) |
| " /* PERF_COUNT_SW_??? */" }, |
| }, |
| /* PERF_TYPE_TRACEPOINT */ { |
| { ARG_STR(0) }, |
| { 4207856245U, "4207856245" }, |
| { ARG_ULL_STR(16051074073505095380) }, |
| }, |
| /* PERF_TYPE_HW_CACHE */ { |
| { 0, "PERF_COUNT_HW_CACHE_L1D|" |
| "PERF_COUNT_HW_CACHE_OP_READ<<8|" |
| "PERF_COUNT_HW_CACHE_RESULT_ACCESS<<16" }, |
| { 0x020207, "0x7 /* PERF_COUNT_HW_CACHE_??? */|" |
| "PERF_COUNT_HW_CACHE_OP_PREFETCH<<8|" |
| "0x2 /* PERF_COUNT_HW_CACHE_RESULT_??? */<<16" }, |
| { 0xdeadf157ed010306ULL, "PERF_COUNT_HW_CACHE_NODE|" |
| "0x3 /* PERF_COUNT_HW_CACHE_OP_??? */<<8|" |
| "PERF_COUNT_HW_CACHE_RESULT_MISS<<16|" |
| "0xdeadf157ed<<24 " |
| "/* PERF_COUNT_HW_CACHE_??? */" }, |
| }, |
| /* PERF_TYPE_RAW */ { |
| { ARG_STR(0) }, |
| { ARG_STR(0xda7a1057) }, |
| { ARG_ULL_STR(0xdec0ded7dec0ded8) }, |
| }, |
| /* PERF_TYPE_BREAKPOINT */ { |
| { ARG_STR(0) }, |
| { ARG_STR(0xbadc0ded) }, |
| { ARG_ULL_STR(0xdec0ded9dec0deda) }, |
| }, |
| /* invalid 1 */ { |
| { ARG_STR(0) }, |
| { ARG_STR(0xbeeff00d) }, |
| { ARG_ULL_STR(0xdec0dedbdec0dedc) }, |
| }, |
| /* invalid 2 */ { |
| { ARG_STR(0) }, |
| { ARG_STR(0xca75dead) }, |
| { ARG_ULL_STR(0xdec0dedddec0dede) }, |
| }, |
| }; |
| static const struct u64_val_str sample_types[] = { |
| { ARG_STR(0) }, |
| { 0x800, "PERF_SAMPLE_BRANCH_STACK" }, |
| { ARG_ULL_STR(0xdeadc0deda700000) " /* PERF_SAMPLE_??? */" }, |
| { 0xffffffffffffffffULL, |
| "PERF_SAMPLE_IP|PERF_SAMPLE_TID|PERF_SAMPLE_TIME|" |
| "PERF_SAMPLE_ADDR|PERF_SAMPLE_READ|" |
| "PERF_SAMPLE_CALLCHAIN|PERF_SAMPLE_ID|PERF_SAMPLE_CPU|" |
| "PERF_SAMPLE_PERIOD|PERF_SAMPLE_STREAM_ID|" |
| "PERF_SAMPLE_RAW|PERF_SAMPLE_BRANCH_STACK|" |
| "PERF_SAMPLE_REGS_USER|PERF_SAMPLE_STACK_USER|" |
| "PERF_SAMPLE_WEIGHT|PERF_SAMPLE_DATA_SRC|" |
| "PERF_SAMPLE_IDENTIFIER|PERF_SAMPLE_TRANSACTION|" |
| "PERF_SAMPLE_REGS_INTR|PERF_SAMPLE_PHYS_ADDR|" |
| "0xfffffffffff00000" }, |
| }; |
| static const struct u64_val_str read_formats[] = { |
| { ARG_STR(0) }, |
| { ARG_STR(PERF_FORMAT_TOTAL_TIME_ENABLED) }, |
| { 0xf, "PERF_FORMAT_TOTAL_TIME_ENABLED|" |
| "PERF_FORMAT_TOTAL_TIME_RUNNING|" |
| "PERF_FORMAT_ID|PERF_FORMAT_GROUP" }, |
| { ARG_ULL_STR(0xdeadf157dec0ded0) " /* PERF_FORMAT_??? */" }, |
| { 0xffffffffffffffffULL, |
| "PERF_FORMAT_TOTAL_TIME_ENABLED|" |
| "PERF_FORMAT_TOTAL_TIME_RUNNING|" |
| "PERF_FORMAT_ID|PERF_FORMAT_GROUP|" |
| "0xfffffffffffffff0" }, |
| }; |
| static const char *precise_ip_descs[] = { |
| "arbitrary skid", |
| "constant skid", |
| "requested to have 0 skid", |
| "must have 0 skid", |
| }; |
| static const struct u32_val_str bp_types[] = { |
| { 0, "HW_BREAKPOINT_EMPTY" }, |
| { 1, "HW_BREAKPOINT_R" }, |
| { 3, "HW_BREAKPOINT_RW" }, |
| { 5, "0x5 /* HW_BREAKPOINT_INVALID */" }, |
| { 8, "0x8 /* HW_BREAKPOINT_??? */" }, |
| { ARG_STR(0xface1e55) " /* HW_BREAKPOINT_??? */" }, |
| }; |
| static const struct u64_val_str branch_sample_types[] = { |
| { ARG_STR(0) }, |
| { 0x80, "PERF_SAMPLE_BRANCH_ABORT_TX" }, |
| { 0x1ffff, BRANCH_TYPE_ALL }, |
| { ARG_ULL_STR(0xdeadcaffeeec0000) |
| " /* PERF_SAMPLE_BRANCH_??? */" }, |
| { 0xffffffffffffffffULL, |
| BRANCH_TYPE_ALL "|0xfffffffffffe0000" } |
| }; |
| static const struct s32_val_str clockids[] = { |
| { 11, "CLOCK_TAI" }, |
| { ARG_STR(0xc) " /* CLOCK_??? */" }, |
| { ARG_STR(0xbeeffeed) " /* CLOCK_??? */" }, |
| }; |
| |
| |
| struct { |
| struct perf_event_attr *ptr; |
| size_t size; |
| } attrs[] = { |
| ATTR_REC(sizeof(struct perf_event_attr)), |
| ATTR_REC(attr_v0_size), |
| ATTR_REC(attr_v1_size), |
| ATTR_REC(attr_v2_size), |
| ATTR_REC(attr_v2_5_size), |
| ATTR_REC(attr_v2_75_size), |
| ATTR_REC(attr_v3_size), |
| ATTR_REC(attr_v4_size), |
| ATTR_REC(attr_v4_5_size), |
| ATTR_REC(attr_v4_625_size), |
| ATTR_REC(attr_v4_875_size), |
| ATTR_REC(attr_v5_size), |
| ATTR_REC(attr_big_size), |
| }; |
| |
| TAIL_ALLOC_OBJECT_CONST_PTR(struct perf_event_attr, small_attr); |
| |
| struct { |
| struct perf_event_attr *attr; |
| pid_t pid; |
| int cpu; |
| int group_fd; |
| unsigned long flags; |
| const char *flags_str; |
| } args[] = { |
| { NULL, 0xfacef00d, 0xbadabba7, -1, |
| (unsigned long) 0xFFFFFFFFFFFFFFFFLLU, |
| "PERF_FLAG_FD_NO_GROUP|PERF_FLAG_FD_OUTPUT|" |
| "PERF_FLAG_PID_CGROUP|PERF_FLAG_FD_CLOEXEC|" |
| "0x" LONG_STR_PREFIX "fffffff0" |
| }, |
| { small_attr + 1, 0, 0, 0, |
| 0, "0" }, |
| { small_attr, -1, -1, 1, |
| PERF_FLAG_FD_NO_GROUP | PERF_FLAG_FD_OUTPUT | |
| PERF_FLAG_PID_CGROUP | PERF_FLAG_FD_CLOEXEC, |
| "PERF_FLAG_FD_NO_GROUP|PERF_FLAG_FD_OUTPUT|" |
| "PERF_FLAG_PID_CGROUP|PERF_FLAG_FD_CLOEXEC" }, |
| { (struct perf_event_attr *) (uintptr_t) 0xfffffacefffffeedULL, |
| -100, 100, 0xface1e55, |
| PERF_FLAG_FD_CLOEXEC, "PERF_FLAG_FD_CLOEXEC" }, |
| }; |
| |
| size_t i; |
| int rc; |
| |
| fill_memory(small_attr, sizeof(*small_attr)); |
| small_attr->size = attr_small_size; |
| |
| for (i = 0; i < ARRAY_SIZE(args); i++) { |
| rc = syscall(__NR_perf_event_open, args[i].attr, args[i].pid, |
| args[i].cpu, args[i].group_fd, args[i].flags); |
| printf("perf_event_open(%s, %d, %d, %d, %s) = %s\n", |
| printaddr(args[i].attr), args[i].pid, args[i].cpu, |
| args[i].group_fd, args[i].flags_str, sprintrc(rc)); |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(attrs) * ARRAY_SIZE(attr_types) * |
| ARRAY_SIZE(attr_configs[0]) + 1; i++) { |
| struct perf_event_attr *attr = attrs[i % ARRAY_SIZE(attrs)].ptr; |
| uint32_t size = attrs[i % ARRAY_SIZE(attrs)].size; |
| unsigned char fill_start = 0x80 + i; |
| size_t type_idx = i % ARRAY_SIZE(attr_types); |
| size_t config_idx = i % ARRAY_SIZE(attr_configs[0]); |
| size_t sample_type_idx = i % ARRAY_SIZE(sample_types); |
| size_t read_format_idx = i % ARRAY_SIZE(read_formats); |
| size_t bp_type_idx = (i / ARRAY_SIZE(attr_configs[0])) % |
| ARRAY_SIZE(bp_types); |
| size_t branch_sample_type_idx = (i / ARRAY_SIZE(sample_types)) % |
| ARRAY_SIZE(branch_sample_types); |
| size_t clockid_idx = i % ARRAY_SIZE(clockids); |
| size_t args_idx = i % ARRAY_SIZE(args); |
| const char *ip_desc_str; |
| |
| fill_memory_ex(attr, size, fill_start, 0xff); |
| |
| attr->type = attr_types[type_idx].val; |
| attr->size = size; |
| attr->config = attr_configs[type_idx][config_idx].val; |
| attr->sample_type = sample_types[sample_type_idx].val; |
| attr->read_format = read_formats[read_format_idx].val; |
| |
| if ((i % 11) == 5) |
| attr->__reserved_1 = 0; |
| |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_BP_TYPE |
| attr->bp_type = |
| # else |
| ((uint32_t *) attr)[52 / sizeof(uint32_t)] = |
| # endif |
| bp_types[bp_type_idx].val; |
| |
| if (size >= 80) |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_BRANCH_SAMPLE_TYPE |
| attr->branch_sample_type = |
| # else |
| ((uint64_t *) attr)[72 / sizeof(uint64_t)] = |
| # endif |
| branch_sample_types[branch_sample_type_idx].val; |
| |
| if (size >= 96) |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_CLOCKID |
| attr->clockid = |
| # else |
| ((uint32_t *) attr)[92 / sizeof(uint32_t)] = |
| # endif |
| clockids[clockid_idx].val; |
| |
| # ifdef HAVE_STRUCT_PERF_EVENT_ATTR_PRECISE_IP |
| ip_desc_str = precise_ip_descs[attr->precise_ip]; |
| # else |
| union { |
| struct pea_flags flags; |
| uint64_t raw; |
| } flags_data = { .raw = ((uint64_t *) attr)[5] }; |
| |
| ip_desc_str = precise_ip_descs[flags_data.flags.precise_ip]; |
| # endif |
| |
| if (i == 0) |
| attr->size = size + 8; |
| |
| if (i == 1) |
| attr->size = 0; |
| |
| rc = syscall(__NR_perf_event_open, attr, args[args_idx].pid, |
| args[args_idx].cpu, args[args_idx].group_fd, |
| args[args_idx].flags); |
| |
| printf("perf_event_open("); |
| print_event_attr(attr, i ? ((i == 1) ? 0 : size) : size + 8, |
| attr_types[type_idx].str, |
| attr_configs[type_idx][config_idx].str, |
| sample_types[sample_type_idx].str, |
| read_formats[read_format_idx].str, |
| ip_desc_str, |
| bp_types[bp_type_idx].str, |
| branch_sample_types[branch_sample_type_idx].str, |
| clockids[clockid_idx].str, size); |
| printf(", %d, %d, %d, %s) = %s\n", args[args_idx].pid, |
| args[args_idx].cpu, args[args_idx].group_fd, |
| args[args_idx].flags_str, sprintrc(rc)); |
| } |
| |
| puts("+++ exited with 0 +++"); |
| return 0; |
| } |
| |
| #else |
| |
| SKIP_MAIN_UNDEFINED("__NR_perf_event_open && HAVE_LINUX_PERF_EVENT_H"); |
| |
| #endif |