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