linux/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * perf events self profiling example test case for hw breakpoints.
   4 *
   5 * This tests perf PERF_TYPE_BREAKPOINT parameters
   6 * 1) tests all variants of the break on read/write flags
   7 * 2) tests exclude_user == 0 and 1
   8 * 3) test array matches (if DAWR is supported))
   9 * 4) test different numbers of breakpoints matches
  10 *
  11 * Configure this breakpoint, then read and write the data a number of
  12 * times. Then check the output count from perf is as expected.
  13 *
  14 * Based on:
  15 *   http://ozlabs.org/~anton/junkcode/perf_events_example1.c
  16 *
  17 * Copyright (C) 2018 Michael Neuling, IBM Corporation.
  18 */
  19
  20#include <unistd.h>
  21#include <assert.h>
  22#include <stdio.h>
  23#include <stdlib.h>
  24#include <string.h>
  25#include <sys/ioctl.h>
  26#include <elf.h>
  27#include <pthread.h>
  28#include <sys/syscall.h>
  29#include <linux/perf_event.h>
  30#include <linux/hw_breakpoint.h>
  31#include "utils.h"
  32
  33#define MAX_LOOPS 10000
  34
  35#define DAWR_LENGTH_MAX ((0x3f + 1) * 8)
  36
  37static inline int sys_perf_event_open(struct perf_event_attr *attr, pid_t pid,
  38                                      int cpu, int group_fd,
  39                                      unsigned long flags)
  40{
  41        attr->size = sizeof(*attr);
  42        return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
  43}
  44
  45static inline bool breakpoint_test(int len)
  46{
  47        struct perf_event_attr attr;
  48        int fd;
  49
  50        /* setup counters */
  51        memset(&attr, 0, sizeof(attr));
  52        attr.disabled = 1;
  53        attr.type = PERF_TYPE_BREAKPOINT;
  54        attr.bp_type = HW_BREAKPOINT_R;
  55        /* bp_addr can point anywhere but needs to be aligned */
  56        attr.bp_addr = (__u64)(&attr) & 0xfffffffffffff800;
  57        attr.bp_len = len;
  58        fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
  59        if (fd < 0)
  60                return false;
  61        close(fd);
  62        return true;
  63}
  64
  65static inline bool perf_breakpoint_supported(void)
  66{
  67        return breakpoint_test(4);
  68}
  69
  70static inline bool dawr_supported(void)
  71{
  72        return breakpoint_test(DAWR_LENGTH_MAX);
  73}
  74
  75static int runtestsingle(int readwriteflag, int exclude_user, int arraytest)
  76{
  77        int i,j;
  78        struct perf_event_attr attr;
  79        size_t res;
  80        unsigned long long breaks, needed;
  81        int readint;
  82        int readintarraybig[2*DAWR_LENGTH_MAX/sizeof(int)];
  83        int *readintalign;
  84        volatile int *ptr;
  85        int break_fd;
  86        int loop_num = MAX_LOOPS - (rand() % 100); /* provide some variability */
  87        volatile int *k;
  88
  89        /* align to 0x400 boundary as required by DAWR */
  90        readintalign = (int *)(((unsigned long)readintarraybig + 0x7ff) &
  91                               0xfffffffffffff800);
  92
  93        ptr = &readint;
  94        if (arraytest)
  95                ptr = &readintalign[0];
  96
  97        /* setup counters */
  98        memset(&attr, 0, sizeof(attr));
  99        attr.disabled = 1;
 100        attr.type = PERF_TYPE_BREAKPOINT;
 101        attr.bp_type = readwriteflag;
 102        attr.bp_addr = (__u64)ptr;
 103        attr.bp_len = sizeof(int);
 104        if (arraytest)
 105                attr.bp_len = DAWR_LENGTH_MAX;
 106        attr.exclude_user = exclude_user;
 107        break_fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
 108        if (break_fd < 0) {
 109                perror("sys_perf_event_open");
 110                exit(1);
 111        }
 112
 113        /* start counters */
 114        ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
 115
 116        /* Test a bunch of reads and writes */
 117        k = &readint;
 118        for (i = 0; i < loop_num; i++) {
 119                if (arraytest)
 120                        k = &(readintalign[i % (DAWR_LENGTH_MAX/sizeof(int))]);
 121
 122                j = *k;
 123                *k = j;
 124        }
 125
 126        /* stop counters */
 127        ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
 128
 129        /* read and check counters */
 130        res = read(break_fd, &breaks, sizeof(unsigned long long));
 131        assert(res == sizeof(unsigned long long));
 132        /* we read and write each loop, so subtract the ones we are counting */
 133        needed = 0;
 134        if (readwriteflag & HW_BREAKPOINT_R)
 135                needed += loop_num;
 136        if (readwriteflag & HW_BREAKPOINT_W)
 137                needed += loop_num;
 138        needed = needed * (1 - exclude_user);
 139        printf("TESTED: addr:0x%lx brks:% 8lld loops:% 8i rw:%i !user:%i array:%i\n",
 140               (unsigned long int)ptr, breaks, loop_num, readwriteflag, exclude_user, arraytest);
 141        if (breaks != needed) {
 142                printf("FAILED: 0x%lx brks:%lld needed:%lli %i %i %i\n\n",
 143                       (unsigned long int)ptr, breaks, needed, loop_num, readwriteflag, exclude_user);
 144                return 1;
 145        }
 146        close(break_fd);
 147
 148        return 0;
 149}
 150
 151static int runtest(void)
 152{
 153        int rwflag;
 154        int exclude_user;
 155        int ret;
 156
 157        /*
 158         * perf defines rwflag as two bits read and write and at least
 159         * one must be set.  So range 1-3.
 160         */
 161        for (rwflag = 1 ; rwflag < 4; rwflag++) {
 162                for (exclude_user = 0 ; exclude_user < 2; exclude_user++) {
 163                        ret = runtestsingle(rwflag, exclude_user, 0);
 164                        if (ret)
 165                                return ret;
 166
 167                        /* if we have the dawr, we can do an array test */
 168                        if (!dawr_supported())
 169                                continue;
 170                        ret = runtestsingle(rwflag, exclude_user, 1);
 171                        if (ret)
 172                                return ret;
 173                }
 174        }
 175        return 0;
 176}
 177
 178
 179static int perf_hwbreak(void)
 180{
 181        srand ( time(NULL) );
 182
 183        SKIP_IF(!perf_breakpoint_supported());
 184
 185        return runtest();
 186}
 187
 188int main(int argc, char *argv[], char **envp)
 189{
 190        return test_harness(perf_hwbreak, "perf_hwbreak");
 191}
 192