linux/tools/perf/tests/bp_account.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select
   4 * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu.
   5 */
   6#define __SANE_USERSPACE_TYPES__
   7
   8#include <stdlib.h>
   9#include <stdio.h>
  10#include <unistd.h>
  11#include <string.h>
  12#include <sys/ioctl.h>
  13#include <fcntl.h>
  14#include <linux/hw_breakpoint.h>
  15
  16#include "tests.h"
  17#include "debug.h"
  18#include "event.h"
  19#include "../perf-sys.h"
  20#include "cloexec.h"
  21
  22static volatile long the_var;
  23
  24static noinline int test_function(void)
  25{
  26        return 0;
  27}
  28
  29static int __event(bool is_x, void *addr, struct perf_event_attr *attr)
  30{
  31        int fd;
  32
  33        memset(attr, 0, sizeof(struct perf_event_attr));
  34        attr->type = PERF_TYPE_BREAKPOINT;
  35        attr->size = sizeof(struct perf_event_attr);
  36
  37        attr->config = 0;
  38        attr->bp_type = is_x ? HW_BREAKPOINT_X : HW_BREAKPOINT_W;
  39        attr->bp_addr = (unsigned long) addr;
  40        attr->bp_len = sizeof(long);
  41
  42        attr->sample_period = 1;
  43        attr->sample_type = PERF_SAMPLE_IP;
  44
  45        attr->exclude_kernel = 1;
  46        attr->exclude_hv = 1;
  47
  48        fd = sys_perf_event_open(attr, -1, 0, -1,
  49                                 perf_event_open_cloexec_flag());
  50        if (fd < 0) {
  51                pr_debug("failed opening event %llx\n", attr->config);
  52                return TEST_FAIL;
  53        }
  54
  55        return fd;
  56}
  57
  58static int wp_event(void *addr, struct perf_event_attr *attr)
  59{
  60        return __event(false, addr, attr);
  61}
  62
  63static int bp_event(void *addr, struct perf_event_attr *attr)
  64{
  65        return __event(true, addr, attr);
  66}
  67
  68static int bp_accounting(int wp_cnt, int share)
  69{
  70        struct perf_event_attr attr, attr_mod, attr_new;
  71        int i, fd[wp_cnt], fd_wp, ret;
  72
  73        for (i = 0; i < wp_cnt; i++) {
  74                fd[i] = wp_event((void *)&the_var, &attr);
  75                TEST_ASSERT_VAL("failed to create wp\n", fd[i] != -1);
  76                pr_debug("wp %d created\n", i);
  77        }
  78
  79        attr_mod = attr;
  80        attr_mod.bp_type = HW_BREAKPOINT_X;
  81        attr_mod.bp_addr = (unsigned long) test_function;
  82
  83        ret = ioctl(fd[0], PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &attr_mod);
  84        TEST_ASSERT_VAL("failed to modify wp\n", ret == 0);
  85
  86        pr_debug("wp 0 modified to bp\n");
  87
  88        if (!share) {
  89                fd_wp = wp_event((void *)&the_var, &attr_new);
  90                TEST_ASSERT_VAL("failed to create max wp\n", fd_wp != -1);
  91                pr_debug("wp max created\n");
  92        }
  93
  94        for (i = 0; i < wp_cnt; i++)
  95                close(fd[i]);
  96
  97        return 0;
  98}
  99
 100static int detect_cnt(bool is_x)
 101{
 102        struct perf_event_attr attr;
 103        void *addr = is_x ? (void *)test_function : (void *)&the_var;
 104        int fd[100], cnt = 0, i;
 105
 106        while (1) {
 107                if (cnt == 100) {
 108                        pr_debug("way too many debug registers, fix the test\n");
 109                        return 0;
 110                }
 111                fd[cnt] = __event(is_x, addr, &attr);
 112
 113                if (fd[cnt] < 0)
 114                        break;
 115                cnt++;
 116        }
 117
 118        for (i = 0; i < cnt; i++)
 119                close(fd[i]);
 120
 121        return cnt;
 122}
 123
 124static int detect_ioctl(void)
 125{
 126        struct perf_event_attr attr;
 127        int fd, ret = 1;
 128
 129        fd = wp_event((void *) &the_var, &attr);
 130        if (fd > 0) {
 131                ret = ioctl(fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &attr);
 132                close(fd);
 133        }
 134
 135        return ret ? 0 : 1;
 136}
 137
 138static int detect_share(int wp_cnt, int bp_cnt)
 139{
 140        struct perf_event_attr attr;
 141        int i, fd[wp_cnt + bp_cnt], ret;
 142
 143        for (i = 0; i < wp_cnt; i++) {
 144                fd[i] = wp_event((void *)&the_var, &attr);
 145                TEST_ASSERT_VAL("failed to create wp\n", fd[i] != -1);
 146        }
 147
 148        for (; i < (bp_cnt + wp_cnt); i++) {
 149                fd[i] = bp_event((void *)test_function, &attr);
 150                if (fd[i] == -1)
 151                        break;
 152        }
 153
 154        ret = i != (bp_cnt + wp_cnt);
 155
 156        while (i--)
 157                close(fd[i]);
 158
 159        return ret;
 160}
 161
 162/*
 163 * This test does following:
 164 *   - detects the number of watch/break-points,
 165 *     skip test if any is missing
 166 *   - detects PERF_EVENT_IOC_MODIFY_ATTRIBUTES ioctl,
 167 *     skip test if it's missing
 168 *   - detects if watchpoints and breakpoints share
 169 *     same slots
 170 *   - create all possible watchpoints on cpu 0
 171 *   - change one of it to breakpoint
 172 *   - in case wp and bp do not share slots,
 173 *     we create another watchpoint to ensure
 174 *     the slot accounting is correct
 175 */
 176int test__bp_accounting(struct test *test __maybe_unused, int subtest __maybe_unused)
 177{
 178        int has_ioctl = detect_ioctl();
 179        int wp_cnt = detect_cnt(false);
 180        int bp_cnt = detect_cnt(true);
 181        int share  = detect_share(wp_cnt, bp_cnt);
 182
 183        pr_debug("watchpoints count %d, breakpoints count %d, has_ioctl %d, share %d\n",
 184                 wp_cnt, bp_cnt, has_ioctl, share);
 185
 186        if (!wp_cnt || !bp_cnt || !has_ioctl)
 187                return TEST_SKIP;
 188
 189        return bp_accounting(wp_cnt, share);
 190}
 191
 192bool test__bp_account_is_supported(void)
 193{
 194        /*
 195         * PowerPC and S390 do not support creation of instruction
 196         * breakpoints using the perf_event interface.
 197         *
 198         * Just disable the test for these architectures until these
 199         * issues are resolved.
 200         */
 201#if defined(__powerpc__) || defined(__s390x__)
 202        return false;
 203#else
 204        return true;
 205#endif
 206}
 207