Home | History | Annotate | Download | only in linux
      1 /*
      2  *
      3  * honggfuzz - architecture dependent code (LINUX/PERF)
      4  * -----------------------------------------
      5  *
      6  * Author: Robert Swiecki <swiecki (at) google.com>
      7  *
      8  * Copyright 2010-2018 by Google Inc. All Rights Reserved.
      9  *
     10  * Licensed under the Apache License, Version 2.0 (the "License"); you may
     11  * not use this file except in compliance with the License. You may obtain
     12  * a copy of the License at
     13  *
     14  * http://www.apache.org/licenses/LICENSE-2.0
     15  *
     16  * Unless required by applicable law or agreed to in writing, software
     17  * distributed under the License is distributed on an "AS IS" BASIS,
     18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
     19  * implied. See the License for the specific language governing
     20  * permissions and limitations under the License.
     21  *
     22  */
     23 
     24 #include "perf.h"
     25 
     26 #include <asm/mman.h>
     27 #include <errno.h>
     28 #include <fcntl.h>
     29 #include <inttypes.h>
     30 #include <linux/hw_breakpoint.h>
     31 #include <linux/perf_event.h>
     32 #include <linux/sysctl.h>
     33 #include <signal.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <sys/ioctl.h>
     37 #include <sys/mman.h>
     38 #include <sys/poll.h>
     39 #include <sys/ptrace.h>
     40 #include <sys/syscall.h>
     41 #include <unistd.h>
     42 
     43 #include "libhfcommon/common.h"
     44 #include "libhfcommon/files.h"
     45 #include "libhfcommon/log.h"
     46 #include "libhfcommon/util.h"
     47 #include "pt.h"
     48 
     49 #define _HF_PERF_MAP_SZ (1024 * 512)
     50 #define _HF_PERF_AUX_SZ (1024 * 1024)
     51 /* PERF_TYPE for Intel_PT/BTS -1 if none */
     52 static int32_t perfIntelPtPerfType = -1;
     53 static int32_t perfIntelBtsPerfType = -1;
     54 
     55 #if defined(PERF_ATTR_SIZE_VER5)
     56 __attribute__((hot)) static inline void arch_perfBtsCount(run_t* run) {
     57     struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->linux.perfMmapBuf;
     58     struct bts_branch {
     59         uint64_t from;
     60         uint64_t to;
     61         uint64_t misc;
     62     };
     63 
     64     uint64_t aux_head = ATOMIC_GET(pem->aux_head);
     65     struct bts_branch* br = (struct bts_branch*)run->linux.perfMmapAux;
     66     for (; br < ((struct bts_branch*)(run->linux.perfMmapAux + aux_head)); br++) {
     67         /*
     68          * Kernel sometimes reports branches from the kernel (iret), we are not interested in that
     69          * as it makes the whole concept of unique branch counting less predictable
     70          */
     71         if (run->global->linux.kernelOnly == false &&
     72             (__builtin_expect(br->from > 0xFFFFFFFF00000000, false) ||
     73                 __builtin_expect(br->to > 0xFFFFFFFF00000000, false))) {
     74             LOG_D("Adding branch %#018" PRIx64 " - %#018" PRIx64, br->from, br->to);
     75             continue;
     76         }
     77         if (br->from >= run->global->linux.dynamicCutOffAddr ||
     78             br->to >= run->global->linux.dynamicCutOffAddr) {
     79             continue;
     80         }
     81 
     82         register size_t pos = ((br->from << 12) ^ (br->to & 0xFFF));
     83         pos &= _HF_PERF_BITMAP_BITSZ_MASK;
     84         register uint8_t prev = ATOMIC_BTS(run->global->feedback.feedbackMap->bbMapPc, pos);
     85         if (!prev) {
     86             run->linux.hwCnts.newBBCnt++;
     87         }
     88     }
     89 }
     90 #endif /* defined(PERF_ATTR_SIZE_VER5) */
     91 
     92 static inline void arch_perfMmapParse(run_t* run HF_ATTR_UNUSED) {
     93 #if defined(PERF_ATTR_SIZE_VER5)
     94     struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->linux.perfMmapBuf;
     95     if (pem->aux_head == pem->aux_tail) {
     96         return;
     97     }
     98     if (pem->aux_head < pem->aux_tail) {
     99         LOG_F("The PERF AUX data has been overwritten. The AUX buffer is too small");
    100     }
    101     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
    102         arch_perfBtsCount(run);
    103     }
    104     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
    105         arch_ptAnalyze(run);
    106     }
    107 #endif /* defined(PERF_ATTR_SIZE_VER5) */
    108 }
    109 
    110 static long perf_event_open(
    111     struct perf_event_attr* hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) {
    112     return syscall(__NR_perf_event_open, hw_event, (uintptr_t)pid, (uintptr_t)cpu,
    113         (uintptr_t)group_fd, (uintptr_t)flags);
    114 }
    115 
    116 static bool arch_perfCreate(run_t* run, pid_t pid, dynFileMethod_t method, int* perfFd) {
    117     LOG_D("Enabling PERF for pid=%d method=%x", pid, method);
    118 
    119     if (*perfFd != -1) {
    120         LOG_F("The PERF FD is already initialized, possibly conflicting perf types enabled");
    121     }
    122 
    123     if ((method & _HF_DYNFILE_BTS_EDGE) && perfIntelBtsPerfType == -1) {
    124         LOG_F("Intel BTS events (new type) are not supported on this platform");
    125     }
    126     if ((method & _HF_DYNFILE_IPT_BLOCK) && perfIntelPtPerfType == -1) {
    127         LOG_F("Intel PT events are not supported on this platform");
    128     }
    129 
    130     struct perf_event_attr pe;
    131     memset(&pe, 0, sizeof(struct perf_event_attr));
    132     pe.size = sizeof(struct perf_event_attr);
    133     if (run->global->linux.kernelOnly) {
    134         pe.exclude_user = 1;
    135     } else {
    136         pe.exclude_kernel = 1;
    137     }
    138     pe.disabled = 1;
    139     if (!run->global->exe.persistent) {
    140         pe.enable_on_exec = 1;
    141     }
    142     pe.exclude_hv = 1;
    143     pe.type = PERF_TYPE_HARDWARE;
    144 
    145     switch (method) {
    146         case _HF_DYNFILE_INSTR_COUNT:
    147             LOG_D("Using: PERF_COUNT_HW_INSTRUCTIONS for pid=%d", (int)pid);
    148             pe.config = PERF_COUNT_HW_INSTRUCTIONS;
    149             pe.inherit = 1;
    150             break;
    151         case _HF_DYNFILE_BRANCH_COUNT:
    152             LOG_D("Using: PERF_COUNT_HW_BRANCH_INSTRUCTIONS for pid=%d", (int)pid);
    153             pe.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
    154             pe.inherit = 1;
    155             break;
    156         case _HF_DYNFILE_BTS_EDGE:
    157             LOG_D("Using: (Intel BTS) type=%" PRIu32 " for pid=%d", perfIntelBtsPerfType, (int)pid);
    158             pe.type = perfIntelBtsPerfType;
    159             break;
    160         case _HF_DYNFILE_IPT_BLOCK:
    161             LOG_D("Using: (Intel PT) type=%" PRIu32 " for pid=%d", perfIntelPtPerfType, (int)pid);
    162             pe.type = perfIntelPtPerfType;
    163             pe.config = RTIT_CTL_DISRETC;
    164             break;
    165         default:
    166             LOG_E("Unknown perf mode: '%d' for pid=%d", method, (int)pid);
    167             return false;
    168             break;
    169     }
    170 
    171 #if !defined(PERF_FLAG_FD_CLOEXEC)
    172 #define PERF_FLAG_FD_CLOEXEC 0
    173 #endif
    174     *perfFd = perf_event_open(&pe, pid, -1, -1, PERF_FLAG_FD_CLOEXEC);
    175     if (*perfFd == -1) {
    176         PLOG_E("perf_event_open() failed");
    177         return false;
    178     }
    179 
    180     if (method != _HF_DYNFILE_BTS_EDGE && method != _HF_DYNFILE_IPT_BLOCK) {
    181         return true;
    182     }
    183 #if defined(PERF_ATTR_SIZE_VER5)
    184     if ((run->linux.perfMmapBuf = mmap(NULL, _HF_PERF_MAP_SZ + getpagesize(),
    185              PROT_READ | PROT_WRITE, MAP_SHARED, *perfFd, 0)) == MAP_FAILED) {
    186         run->linux.perfMmapBuf = NULL;
    187         PLOG_W("mmap(mmapBuf) failed, sz=%zu, try increasing the kernel.perf_event_mlock_kb sysctl "
    188                "(up to even 300000000)",
    189             (size_t)_HF_PERF_MAP_SZ + getpagesize());
    190         close(*perfFd);
    191         *perfFd = -1;
    192         return false;
    193     }
    194 
    195     struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->linux.perfMmapBuf;
    196     pem->aux_offset = pem->data_offset + pem->data_size;
    197     pem->aux_size = _HF_PERF_AUX_SZ;
    198     if ((run->linux.perfMmapAux = mmap(
    199              NULL, pem->aux_size, PROT_READ, MAP_SHARED, *perfFd, pem->aux_offset)) == MAP_FAILED) {
    200         munmap(run->linux.perfMmapBuf, _HF_PERF_MAP_SZ + getpagesize());
    201         run->linux.perfMmapBuf = NULL;
    202         PLOG_W(
    203             "mmap(mmapAuxBuf) failed, try increasing the kernel.perf_event_mlock_kb sysctl (up to "
    204             "even 300000000)");
    205         close(*perfFd);
    206         *perfFd = -1;
    207         return false;
    208     }
    209 #else  /* defined(PERF_ATTR_SIZE_VER5) */
    210     LOG_F("Your <linux/perf_event.h> includes are too old to support Intel PT/BTS");
    211 #endif /* defined(PERF_ATTR_SIZE_VER5) */
    212 
    213     return true;
    214 }
    215 
    216 bool arch_perfOpen(run_t* run) {
    217     if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
    218         return true;
    219     }
    220 
    221     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) {
    222         if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_INSTR_COUNT, &run->linux.cpuInstrFd)) {
    223             LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_INSTR_COUNT)", (int)run->pid);
    224             goto out;
    225         }
    226     }
    227     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) {
    228         if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_BRANCH_COUNT, &run->linux.cpuBranchFd)) {
    229             LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_BRANCH_COUNT)", (int)run->pid);
    230             goto out;
    231         }
    232     }
    233     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
    234         if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_BTS_EDGE, &run->linux.cpuIptBtsFd)) {
    235             LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_BTS_EDGE)", (int)run->pid);
    236             goto out;
    237         }
    238     }
    239     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
    240         if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_IPT_BLOCK, &run->linux.cpuIptBtsFd)) {
    241             LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_IPT_BLOCK)", (int)run->pid);
    242             goto out;
    243         }
    244     }
    245 
    246     return true;
    247 
    248 out:
    249     close(run->linux.cpuInstrFd);
    250     run->linux.cpuInstrFd = -1;
    251     close(run->linux.cpuBranchFd);
    252     run->linux.cpuBranchFd = -1;
    253     close(run->linux.cpuIptBtsFd);
    254     run->linux.cpuIptBtsFd = 1;
    255 
    256     return false;
    257 }
    258 
    259 void arch_perfClose(run_t* run) {
    260     if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
    261         return;
    262     }
    263 
    264     if (run->linux.perfMmapAux != NULL) {
    265         munmap(run->linux.perfMmapAux, _HF_PERF_AUX_SZ);
    266         run->linux.perfMmapAux = NULL;
    267     }
    268     if (run->linux.perfMmapBuf != NULL) {
    269         munmap(run->linux.perfMmapBuf, _HF_PERF_MAP_SZ + getpagesize());
    270         run->linux.perfMmapBuf = NULL;
    271     }
    272 
    273     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) {
    274         close(run->linux.cpuInstrFd);
    275         run->linux.cpuInstrFd = -1;
    276     }
    277     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) {
    278         close(run->linux.cpuBranchFd);
    279         run->linux.cpuBranchFd = -1;
    280     }
    281     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
    282         close(run->linux.cpuIptBtsFd);
    283         run->linux.cpuIptBtsFd = -1;
    284     }
    285     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
    286         close(run->linux.cpuIptBtsFd);
    287         run->linux.cpuIptBtsFd = -1;
    288     }
    289 }
    290 
    291 bool arch_perfEnable(run_t* run) {
    292     if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
    293         return true;
    294     }
    295     /* It's enabled on exec in such scenario */
    296     if (!run->global->exe.persistent) {
    297         return true;
    298     }
    299 
    300     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) {
    301         ioctl(run->linux.cpuInstrFd, PERF_EVENT_IOC_ENABLE, 0);
    302     }
    303     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) {
    304         ioctl(run->linux.cpuBranchFd, PERF_EVENT_IOC_ENABLE, 0);
    305     }
    306     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
    307         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_ENABLE, 0);
    308     }
    309     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
    310         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_ENABLE, 0);
    311     }
    312 
    313     return true;
    314 }
    315 
    316 static void arch_perfMmapReset(run_t* run) {
    317     /* smp_mb() required as per /usr/include/linux/perf_event.h */
    318     wmb();
    319 
    320     struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->linux.perfMmapBuf;
    321     ATOMIC_SET(pem->data_head, 0);
    322     ATOMIC_SET(pem->data_tail, 0);
    323 #if defined(PERF_ATTR_SIZE_VER5)
    324     ATOMIC_SET(pem->aux_head, 0);
    325     ATOMIC_SET(pem->aux_tail, 0);
    326 #endif /* defined(PERF_ATTR_SIZE_VER5) */
    327 }
    328 
    329 void arch_perfAnalyze(run_t* run) {
    330     if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
    331         return;
    332     }
    333 
    334     uint64_t instrCount = 0;
    335     if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) &&
    336         run->linux.cpuInstrFd != -1) {
    337         ioctl(run->linux.cpuInstrFd, PERF_EVENT_IOC_DISABLE, 0);
    338         if (files_readFromFd(run->linux.cpuInstrFd, (uint8_t*)&instrCount, sizeof(instrCount)) !=
    339             sizeof(instrCount)) {
    340             PLOG_E("read(perfFd='%d') failed", run->linux.cpuInstrFd);
    341         }
    342         ioctl(run->linux.cpuInstrFd, PERF_EVENT_IOC_RESET, 0);
    343     }
    344 
    345     uint64_t branchCount = 0;
    346     if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) &&
    347         run->linux.cpuBranchFd != -1) {
    348         ioctl(run->linux.cpuBranchFd, PERF_EVENT_IOC_DISABLE, 0);
    349         if (files_readFromFd(run->linux.cpuBranchFd, (uint8_t*)&branchCount, sizeof(branchCount)) !=
    350             sizeof(branchCount)) {
    351             PLOG_E("read(perfFd='%d') failed", run->linux.cpuBranchFd);
    352         }
    353         ioctl(run->linux.cpuBranchFd, PERF_EVENT_IOC_RESET, 0);
    354     }
    355 
    356     if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) &&
    357         run->linux.cpuIptBtsFd != -1) {
    358         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_DISABLE, 0);
    359         arch_perfMmapParse(run);
    360         arch_perfMmapReset(run);
    361         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_RESET, 0);
    362     }
    363     if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) &&
    364         run->linux.cpuIptBtsFd != -1) {
    365         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_DISABLE, 0);
    366         arch_perfMmapParse(run);
    367         arch_perfMmapReset(run);
    368         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_RESET, 0);
    369     }
    370 
    371     run->linux.hwCnts.cpuInstrCnt = instrCount;
    372     run->linux.hwCnts.cpuBranchCnt = branchCount;
    373 }
    374 
    375 bool arch_perfInit(honggfuzz_t* hfuzz HF_ATTR_UNUSED) {
    376     static char const intel_pt_path[] = "/sys/bus/event_source/devices/intel_pt/type";
    377     static char const intel_bts_path[] = "/sys/bus/event_source/devices/intel_bts/type";
    378 
    379     if (files_exists(intel_pt_path)) {
    380         uint8_t buf[256];
    381         ssize_t sz = files_readFileToBufMax(intel_pt_path, buf, sizeof(buf) - 1);
    382         if (sz > 0) {
    383             buf[sz] = '\0';
    384             perfIntelPtPerfType = (int32_t)strtoul((char*)buf, NULL, 10);
    385             LOG_D("perfIntelPtPerfType = %" PRIu32, perfIntelPtPerfType);
    386         }
    387     }
    388 
    389     if (files_exists(intel_bts_path)) {
    390         uint8_t buf[256];
    391         ssize_t sz = files_readFileToBufMax(intel_bts_path, buf, sizeof(buf) - 1);
    392         if (sz > 0) {
    393             buf[sz] = '\0';
    394             perfIntelBtsPerfType = (int32_t)strtoul((char*)buf, NULL, 10);
    395             LOG_D("perfIntelBtsPerfType = %" PRIu32, perfIntelBtsPerfType);
    396         }
    397     }
    398 
    399     perf_ptInit();
    400 
    401     return true;
    402 }
    403