tests: add tests for perf_event_attr structure decoding in perf_event_open
* configure.ac: Add checks for presence of various fields of struct perf_event_attr defined in kernel headers. * tests/perf_event_open.c: New file. * tests/perf_event_open_unabbrev.c: Likewise. * tests/perf_event_open.test: New test. * tests/perf_event_open_unabbrev.test: Likewise. * tests/.gitignore: Add perf_event_open, perf_event_open_unabbrev. * tests/Makefile.am (check_PROGRAMS): Likewise. (DECODER_TESTS): Add perf_event_open.test, perf_event_open_unabbrev.test.
This commit is contained in:
parent
6ed2a84aab
commit
015aee943e
24
configure.ac
24
configure.ac
@ -347,6 +347,30 @@ AC_CHECK_MEMBERS(m4_normalize([
|
||||
siginfo_t.si_overrun
|
||||
]),,, [#include <signal.h>])
|
||||
|
||||
AC_CHECK_MEMBERS(m4_normalize([
|
||||
struct perf_event_attr.precise_ip,
|
||||
struct perf_event_attr.mmap_data,
|
||||
struct perf_event_attr.sample_id_all,
|
||||
struct perf_event_attr.exclude_host,
|
||||
struct perf_event_attr.exclude_guest,
|
||||
struct perf_event_attr.exclude_callchain_kernel,
|
||||
struct perf_event_attr.exclude_callchain_user,
|
||||
struct perf_event_attr.mmap2,
|
||||
struct perf_event_attr.comm_exec,
|
||||
struct perf_event_attr.use_clockid,
|
||||
struct perf_event_attr.context_switch,
|
||||
struct perf_event_attr.write_backward,
|
||||
struct perf_event_attr.bp_type,
|
||||
struct perf_event_attr.config1,
|
||||
struct perf_event_attr.config2,
|
||||
struct perf_event_attr.branch_sample_type,
|
||||
struct perf_event_attr.sample_regs_user,
|
||||
struct perf_event_attr.sample_stack_user,
|
||||
struct perf_event_attr.sample_regs_intr,
|
||||
struct perf_event_attr.aux_watermark,
|
||||
struct perf_event_attr.sample_max_stack
|
||||
]),,, [#include <linux/perf_event.h>])
|
||||
|
||||
AC_CHECK_HEADERS(m4_normalize([
|
||||
asm/cachectl.h
|
||||
asm/sysmips.h
|
||||
|
2
tests/.gitignore
vendored
2
tests/.gitignore
vendored
@ -192,6 +192,8 @@ openat
|
||||
pause
|
||||
pc
|
||||
perf_event_open_nonverbose
|
||||
perf_event_open
|
||||
perf_event_open_unabbrev
|
||||
personality
|
||||
pipe
|
||||
poll
|
||||
|
@ -250,6 +250,8 @@ check_PROGRAMS = \
|
||||
pause \
|
||||
pc \
|
||||
perf_event_open_nonverbose \
|
||||
perf_event_open \
|
||||
perf_event_open_unabbrev \
|
||||
personality \
|
||||
pipe \
|
||||
poll \
|
||||
@ -609,6 +611,8 @@ DECODER_TESTS = \
|
||||
openat.test \
|
||||
pause.test \
|
||||
perf_event_open_nonverbose.test \
|
||||
perf_event_open.test \
|
||||
perf_event_open_unabbrev.test \
|
||||
personality.test \
|
||||
pipe.test \
|
||||
poll.test \
|
||||
|
822
tests/perf_event_open.c
Normal file
822
tests/perf_event_open.c
Normal file
@ -0,0 +1,822 @@
|
||||
/*
|
||||
* Check verbose decoding of perf_event_open syscall.
|
||||
*
|
||||
* Copyright (c) 2016 Eugene Syromyatnikov <evgsyr@gmail.com>
|
||||
* 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 <stdbool.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 endianess-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,
|
||||
__reserved_1 :36;
|
||||
};
|
||||
|
||||
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 = flags_data.flags.__reserved_1;
|
||||
if (val)
|
||||
printf(", __reserved_1=%#" PRIx64 " /* Bits 63..28 */", 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"
|
||||
|
||||
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(0xdeadc0deda780000) " /* 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|0xfffffffffff80000" },
|
||||
};
|
||||
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" },
|
||||
{ 0xffff, BRANCH_TYPE_ALL },
|
||||
{ ARG_ULL_STR(0xdeadcaffeeed0000)
|
||||
" /* PERF_SAMPLE_BRANCH_??? */" },
|
||||
{ 0xffffffffffffffffULL,
|
||||
BRANCH_TYPE_ALL "|0xffffffffffff0000" }
|
||||
};
|
||||
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),
|
||||
};
|
||||
|
||||
struct perf_event_attr *small_attr = tail_alloc(sizeof(*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((char *) 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((char *) 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
|
7
tests/perf_event_open.test
Executable file
7
tests/perf_event_open.test
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check perf_event_open syscall decoding.
|
||||
|
||||
. "${srcdir=.}/init.sh"
|
||||
|
||||
run_strace_match_diff -a1
|
2
tests/perf_event_open_unabbrev.c
Normal file
2
tests/perf_event_open_unabbrev.c
Normal file
@ -0,0 +1,2 @@
|
||||
#define VERBOSE 1
|
||||
#include "perf_event_open.c"
|
7
tests/perf_event_open_unabbrev.test
Executable file
7
tests/perf_event_open_unabbrev.test
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check perf_event_open syscall decoding.
|
||||
|
||||
. "${srcdir=.}/init.sh"
|
||||
|
||||
run_strace_match_diff -a1 -v -e trace=perf_event_open
|
Loading…
Reference in New Issue
Block a user