1/* 2 * Copyright (c) 2012 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Andreas Sandberg 38 */ 39 40#include <fcntl.h> 41#include <sys/ioctl.h> 42#include <sys/mman.h> 43#include <sys/syscall.h> 44#include <sys/types.h> 45#include <syscall.h> 46#include <unistd.h> 47 48#include <cassert> 49#include <cerrno> 50#include <csignal> 51#include <cstring> 52 53#include "base/logging.hh" 54#include "perfevent.hh" 55 56PerfKvmCounterConfig::PerfKvmCounterConfig(uint32_t type, uint64_t config) 57{ 58 memset(&attr, 0, sizeof(attr)); 59 60 attr.size = PERF_ATTR_SIZE_VER0; 61 attr.type = type; 62 attr.config = config; 63} 64 65PerfKvmCounterConfig::~PerfKvmCounterConfig() 66{ 67} 68 69 70PerfKvmCounter::PerfKvmCounter(PerfKvmCounterConfig &config, pid_t tid) 71 : fd(-1), ringBuffer(NULL), pageSize(-1) 72{ 73 attach(config, tid, -1); 74} 75 76PerfKvmCounter::PerfKvmCounter(PerfKvmCounterConfig &config, 77 pid_t tid, const PerfKvmCounter &parent) 78 : fd(-1), ringBuffer(NULL), pageSize(-1) 79{ 80 attach(config, tid, parent); 81} 82 83PerfKvmCounter::PerfKvmCounter() 84 : fd(-1), ringBuffer(NULL), pageSize(-1) 85{ 86} 87 88PerfKvmCounter::~PerfKvmCounter() 89{ 90 if (attached()) 91 detach(); 92} 93 94void 95PerfKvmCounter::detach() 96{ 97 assert(attached()); 98 99 if (munmap(ringBuffer, ringNumPages * pageSize) == -1) 100 warn("PerfKvmCounter: Failed to unmap ring buffer (%i)\n", 101 errno); 102 close(fd); 103 104 fd = -1; 105 ringBuffer = NULL; 106} 107 108void 109PerfKvmCounter::start() 110{ 111 if (ioctl(PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) 112 panic("KVM: Failed to enable performance counters (%i)\n", errno); 113} 114 115void 116PerfKvmCounter::stop() 117{ 118 if (ioctl(PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) 119 panic("KVM: Failed to disable performance counters (%i)\n", errno); 120} 121 122void 123PerfKvmCounter::period(uint64_t period) 124{ 125 if (ioctl(PERF_EVENT_IOC_PERIOD, &period) == -1) 126 panic("KVM: Failed to set period of performance counter (%i)\n", errno); 127} 128 129void 130PerfKvmCounter::refresh(int refresh) 131{ 132 if (ioctl(PERF_EVENT_IOC_REFRESH, refresh) == -1) 133 panic("KVM: Failed to refresh performance counter (%i)\n", errno); 134} 135 136uint64_t 137PerfKvmCounter::read() const 138{ 139 uint64_t value; 140 141 read(&value, sizeof(uint64_t)); 142 return value; 143} 144 145void 146PerfKvmCounter::enableSignals(pid_t tid, int signal) 147{ 148 struct f_owner_ex sigowner; 149 150 sigowner.type = F_OWNER_TID; 151 sigowner.pid = tid; 152 153 if (fcntl(F_SETOWN_EX, &sigowner) == -1 || 154 fcntl(F_SETSIG, signal) == -1 || 155 fcntl(F_SETFL, O_ASYNC) == -1) 156 panic("PerfKvmCounter: Failed to enable signals for counter (%i)\n", 157 errno); 158} 159 160void 161PerfKvmCounter::attach(PerfKvmCounterConfig &config, 162 pid_t tid, int group_fd) 163{ 164 assert(!attached()); 165 166 fd = syscall(__NR_perf_event_open, 167 &config.attr, tid, 168 -1, // CPU (-1 => Any CPU that the task happens to run on) 169 group_fd, 170 0); // Flags 171 if (fd == -1) 172 { 173 if (errno == EACCES) 174 { 175 panic("PerfKvmCounter::attach recieved error EACCESS\n" 176 " This error may be caused by a too restrictive setting\n" 177 " in the file '/proc/sys/kernel/perf_event_paranoid'\n" 178 " The default value was changed to 2 in kernel 4.6\n" 179 " A value greater than 1 prevents gem5 from making\n" 180 " the syscall to perf_event_open"); 181 } 182 panic("PerfKvmCounter::attach failed (%i)\n", errno); 183 } 184 185 mmapPerf(1); 186} 187 188pid_t 189PerfKvmCounter::gettid() 190{ 191 return syscall(__NR_gettid); 192} 193 194void 195PerfKvmCounter::mmapPerf(int pages) 196{ 197 assert(attached()); 198 assert(ringBuffer == NULL); 199 200 if (pageSize == -1) { 201 pageSize = sysconf(_SC_PAGE_SIZE); 202 if (pageSize == -1) 203 panic("PerfKvmCounter: Failed to determine page size (%i)\n", 204 errno); 205 } 206 207 ringNumPages = pages + 1; 208 ringBuffer = (struct perf_event_mmap_page *)mmap( 209 NULL, ringNumPages * 4096, 210 PROT_READ | PROT_WRITE, MAP_SHARED, 211 fd, 0); 212 if (ringBuffer == MAP_FAILED) 213 panic("PerfKvmCounter: MMAP failed (%i)\n", 214 errno); 215} 216 217int 218PerfKvmCounter::fcntl(int cmd, long p1) 219{ 220 assert(attached()); 221 return ::fcntl(fd, cmd, p1); 222} 223 224int 225PerfKvmCounter::ioctl(int request, long p1) 226{ 227 assert(attached()); 228 return ::ioctl(fd, request, p1); 229} 230 231void 232PerfKvmCounter::read(void *buf, size_t size) const 233{ 234 char *_buf = (char *)buf; 235 size_t _size = size; 236 237 assert(attached()); 238 239 do { 240 ssize_t ret; 241 ret = ::read(fd, _buf, _size); 242 switch (ret) { 243 case -1: 244 if (errno != EAGAIN) 245 panic("PerfKvmCounter::read failed (%i)\n", errno); 246 break; 247 248 case 0: 249 panic("PerfKvmCounter::read unexpected EOF.\n"); 250 251 default: 252 _size -= ret; 253 _buf += ret; 254 break; 255 } 256 } while (_size); 257} 258