linux/tools/perf/tests/wp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <stdlib.h>
   3#include <string.h>
   4#include <unistd.h>
   5#include <sys/ioctl.h>
   6#include <linux/hw_breakpoint.h>
   7#include <linux/kernel.h>
   8#include "tests.h"
   9#include "debug.h"
  10#include "event.h"
  11#include "cloexec.h"
  12#include "../perf-sys.h"
  13
  14#define WP_TEST_ASSERT_VAL(fd, text, val)       \
  15do {                                            \
  16        long long count;                        \
  17        wp_read(fd, &count, sizeof(long long)); \
  18        TEST_ASSERT_VAL(text, count == val);    \
  19} while (0)
  20
  21volatile u64 data1;
  22volatile u8 data2[3];
  23
  24static int wp_read(int fd, long long *count, int size)
  25{
  26        int ret = read(fd, count, size);
  27
  28        if (ret != size) {
  29                pr_debug("failed to read: %d\n", ret);
  30                return -1;
  31        }
  32        return 0;
  33}
  34
  35static void get__perf_event_attr(struct perf_event_attr *attr, int wp_type,
  36                                 void *wp_addr, unsigned long wp_len)
  37{
  38        memset(attr, 0, sizeof(struct perf_event_attr));
  39        attr->type           = PERF_TYPE_BREAKPOINT;
  40        attr->size           = sizeof(struct perf_event_attr);
  41        attr->config         = 0;
  42        attr->bp_type        = wp_type;
  43        attr->bp_addr        = (unsigned long)wp_addr;
  44        attr->bp_len         = wp_len;
  45        attr->sample_period  = 1;
  46        attr->sample_type    = PERF_SAMPLE_IP;
  47        attr->exclude_kernel = 1;
  48        attr->exclude_hv     = 1;
  49}
  50
  51static int __event(int wp_type, void *wp_addr, unsigned long wp_len)
  52{
  53        int fd;
  54        struct perf_event_attr attr;
  55
  56        get__perf_event_attr(&attr, wp_type, wp_addr, wp_len);
  57        fd = sys_perf_event_open(&attr, 0, -1, -1,
  58                                 perf_event_open_cloexec_flag());
  59        if (fd < 0)
  60                pr_debug("failed opening event %x\n", attr.bp_type);
  61
  62        return fd;
  63}
  64
  65static int wp_ro_test(void)
  66{
  67        int fd;
  68        unsigned long tmp, tmp1 = rand();
  69
  70        fd = __event(HW_BREAKPOINT_R, (void *)&data1, sizeof(data1));
  71        if (fd < 0)
  72                return -1;
  73
  74        tmp = data1;
  75        WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1);
  76
  77        data1 = tmp1 + tmp;
  78        WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1);
  79
  80        close(fd);
  81        return 0;
  82}
  83
  84static int wp_wo_test(void)
  85{
  86        int fd;
  87        unsigned long tmp, tmp1 = rand();
  88
  89        fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1));
  90        if (fd < 0)
  91                return -1;
  92
  93        tmp = data1;
  94        WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 0);
  95
  96        data1 = tmp1 + tmp;
  97        WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 1);
  98
  99        close(fd);
 100        return 0;
 101}
 102
 103static int wp_rw_test(void)
 104{
 105        int fd;
 106        unsigned long tmp, tmp1 = rand();
 107
 108        fd = __event(HW_BREAKPOINT_R | HW_BREAKPOINT_W, (void *)&data1,
 109                     sizeof(data1));
 110        if (fd < 0)
 111                return -1;
 112
 113        tmp = data1;
 114        WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 1);
 115
 116        data1 = tmp1 + tmp;
 117        WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 2);
 118
 119        close(fd);
 120        return 0;
 121}
 122
 123static int wp_modify_test(void)
 124{
 125        int fd, ret;
 126        unsigned long tmp = rand();
 127        struct perf_event_attr new_attr;
 128
 129        fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1));
 130        if (fd < 0)
 131                return -1;
 132
 133        data1 = tmp;
 134        WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1);
 135
 136        /* Modify watchpoint with disabled = 1 */
 137        get__perf_event_attr(&new_attr, HW_BREAKPOINT_W, (void *)&data2[0],
 138                             sizeof(u8) * 2);
 139        new_attr.disabled = 1;
 140        ret = ioctl(fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &new_attr);
 141        if (ret < 0) {
 142                pr_debug("ioctl(PERF_EVENT_IOC_MODIFY_ATTRIBUTES) failed\n");
 143                close(fd);
 144                return ret;
 145        }
 146
 147        data2[1] = tmp; /* Not Counted */
 148        WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1);
 149
 150        /* Enable the event */
 151        ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
 152        if (ret < 0) {
 153                pr_debug("Failed to enable event\n");
 154                close(fd);
 155                return ret;
 156        }
 157
 158        data2[1] = tmp; /* Counted */
 159        WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2);
 160
 161        data2[2] = tmp; /* Not Counted */
 162        WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2);
 163
 164        close(fd);
 165        return 0;
 166}
 167
 168static bool wp_ro_supported(void)
 169{
 170#if defined (__x86_64__) || defined (__i386__)
 171        return false;
 172#else
 173        return true;
 174#endif
 175}
 176
 177static const char *wp_ro_skip_msg(void)
 178{
 179#if defined (__x86_64__) || defined (__i386__)
 180        return "missing hardware support";
 181#else
 182        return NULL;
 183#endif
 184}
 185
 186static struct {
 187        const char *desc;
 188        int (*target_func)(void);
 189        bool (*is_supported)(void);
 190        const char *(*skip_msg)(void);
 191} wp_testcase_table[] = {
 192        {
 193                .desc = "Read Only Watchpoint",
 194                .target_func = &wp_ro_test,
 195                .is_supported = &wp_ro_supported,
 196                .skip_msg = &wp_ro_skip_msg,
 197        },
 198        {
 199                .desc = "Write Only Watchpoint",
 200                .target_func = &wp_wo_test,
 201        },
 202        {
 203                .desc = "Read / Write Watchpoint",
 204                .target_func = &wp_rw_test,
 205        },
 206        {
 207                .desc = "Modify Watchpoint",
 208                .target_func = &wp_modify_test,
 209        },
 210};
 211
 212int test__wp_subtest_get_nr(void)
 213{
 214        return (int)ARRAY_SIZE(wp_testcase_table);
 215}
 216
 217const char *test__wp_subtest_get_desc(int i)
 218{
 219        if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table))
 220                return NULL;
 221        return wp_testcase_table[i].desc;
 222}
 223
 224const char *test__wp_subtest_skip_reason(int i)
 225{
 226        if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table))
 227                return NULL;
 228        if (!wp_testcase_table[i].skip_msg)
 229                return NULL;
 230        return wp_testcase_table[i].skip_msg();
 231}
 232
 233int test__wp(struct test *test __maybe_unused, int i)
 234{
 235        if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table))
 236                return TEST_FAIL;
 237
 238        if (wp_testcase_table[i].is_supported &&
 239            !wp_testcase_table[i].is_supported())
 240                return TEST_SKIP;
 241
 242        return !wp_testcase_table[i].target_func() ? TEST_OK : TEST_FAIL;
 243}
 244
 245/* The s390 so far does not have support for
 246 * instruction breakpoint using the perf_event_open() system call.
 247 */
 248bool test__wp_is_supported(void)
 249{
 250#if defined(__s390x__)
 251        return false;
 252#else
 253        return true;
 254#endif
 255}
 256