19651SAndreas.Sandberg@ARM.com/*
29651SAndreas.Sandberg@ARM.com * Copyright (c) 2012 ARM Limited
39651SAndreas.Sandberg@ARM.com * All rights reserved
49651SAndreas.Sandberg@ARM.com *
59651SAndreas.Sandberg@ARM.com * The license below extends only to copyright in the software and shall
69651SAndreas.Sandberg@ARM.com * not be construed as granting a license to any other intellectual
79651SAndreas.Sandberg@ARM.com * property including but not limited to intellectual property relating
89651SAndreas.Sandberg@ARM.com * to a hardware implementation of the functionality of the software
99651SAndreas.Sandberg@ARM.com * licensed hereunder.  You may use the software subject to the license
109651SAndreas.Sandberg@ARM.com * terms below provided that you ensure that this notice is replicated
119651SAndreas.Sandberg@ARM.com * unmodified and in its entirety in all distributions of the software,
129651SAndreas.Sandberg@ARM.com * modified or unmodified, in source code or in binary form.
139651SAndreas.Sandberg@ARM.com *
149651SAndreas.Sandberg@ARM.com * Redistribution and use in source and binary forms, with or without
159651SAndreas.Sandberg@ARM.com * modification, are permitted provided that the following conditions are
169651SAndreas.Sandberg@ARM.com * met: redistributions of source code must retain the above copyright
179651SAndreas.Sandberg@ARM.com * notice, this list of conditions and the following disclaimer;
189651SAndreas.Sandberg@ARM.com * redistributions in binary form must reproduce the above copyright
199651SAndreas.Sandberg@ARM.com * notice, this list of conditions and the following disclaimer in the
209651SAndreas.Sandberg@ARM.com * documentation and/or other materials provided with the distribution;
219651SAndreas.Sandberg@ARM.com * neither the name of the copyright holders nor the names of its
229651SAndreas.Sandberg@ARM.com * contributors may be used to endorse or promote products derived from
239651SAndreas.Sandberg@ARM.com * this software without specific prior written permission.
249651SAndreas.Sandberg@ARM.com *
259651SAndreas.Sandberg@ARM.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
269651SAndreas.Sandberg@ARM.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
279651SAndreas.Sandberg@ARM.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
289651SAndreas.Sandberg@ARM.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
299651SAndreas.Sandberg@ARM.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
309651SAndreas.Sandberg@ARM.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
319651SAndreas.Sandberg@ARM.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
329651SAndreas.Sandberg@ARM.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
339651SAndreas.Sandberg@ARM.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
349651SAndreas.Sandberg@ARM.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
359651SAndreas.Sandberg@ARM.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
369651SAndreas.Sandberg@ARM.com *
379651SAndreas.Sandberg@ARM.com * Authors: Andreas Sandberg
389651SAndreas.Sandberg@ARM.com */
399651SAndreas.Sandberg@ARM.com
4011793Sbrandon.potter@amd.com#include <fcntl.h>
419651SAndreas.Sandberg@ARM.com#include <sys/ioctl.h>
429651SAndreas.Sandberg@ARM.com#include <sys/mman.h>
439651SAndreas.Sandberg@ARM.com#include <sys/syscall.h>
449651SAndreas.Sandberg@ARM.com#include <sys/types.h>
459651SAndreas.Sandberg@ARM.com#include <syscall.h>
469651SAndreas.Sandberg@ARM.com#include <unistd.h>
479651SAndreas.Sandberg@ARM.com
489651SAndreas.Sandberg@ARM.com#include <cassert>
499651SAndreas.Sandberg@ARM.com#include <cerrno>
509651SAndreas.Sandberg@ARM.com#include <csignal>
519651SAndreas.Sandberg@ARM.com#include <cstring>
529651SAndreas.Sandberg@ARM.com
5312334Sgabeblack@google.com#include "base/logging.hh"
549651SAndreas.Sandberg@ARM.com#include "perfevent.hh"
559651SAndreas.Sandberg@ARM.com
569651SAndreas.Sandberg@ARM.comPerfKvmCounterConfig::PerfKvmCounterConfig(uint32_t type, uint64_t config)
579651SAndreas.Sandberg@ARM.com{
589651SAndreas.Sandberg@ARM.com    memset(&attr, 0, sizeof(attr));
599651SAndreas.Sandberg@ARM.com
609651SAndreas.Sandberg@ARM.com    attr.size = PERF_ATTR_SIZE_VER0;
619651SAndreas.Sandberg@ARM.com    attr.type = type;
629651SAndreas.Sandberg@ARM.com    attr.config = config;
639651SAndreas.Sandberg@ARM.com}
649651SAndreas.Sandberg@ARM.com
659651SAndreas.Sandberg@ARM.comPerfKvmCounterConfig::~PerfKvmCounterConfig()
669651SAndreas.Sandberg@ARM.com{
679651SAndreas.Sandberg@ARM.com}
689651SAndreas.Sandberg@ARM.com
699651SAndreas.Sandberg@ARM.com
709651SAndreas.Sandberg@ARM.comPerfKvmCounter::PerfKvmCounter(PerfKvmCounterConfig &config, pid_t tid)
719651SAndreas.Sandberg@ARM.com    : fd(-1), ringBuffer(NULL), pageSize(-1)
729651SAndreas.Sandberg@ARM.com{
739651SAndreas.Sandberg@ARM.com    attach(config, tid, -1);
749651SAndreas.Sandberg@ARM.com}
759651SAndreas.Sandberg@ARM.com
769651SAndreas.Sandberg@ARM.comPerfKvmCounter::PerfKvmCounter(PerfKvmCounterConfig &config,
779651SAndreas.Sandberg@ARM.com                         pid_t tid, const PerfKvmCounter &parent)
789651SAndreas.Sandberg@ARM.com    : fd(-1), ringBuffer(NULL), pageSize(-1)
799651SAndreas.Sandberg@ARM.com{
809651SAndreas.Sandberg@ARM.com    attach(config, tid, parent);
819651SAndreas.Sandberg@ARM.com}
829651SAndreas.Sandberg@ARM.com
839651SAndreas.Sandberg@ARM.comPerfKvmCounter::PerfKvmCounter()
849651SAndreas.Sandberg@ARM.com    : fd(-1), ringBuffer(NULL), pageSize(-1)
859651SAndreas.Sandberg@ARM.com{
869651SAndreas.Sandberg@ARM.com}
879651SAndreas.Sandberg@ARM.com
889651SAndreas.Sandberg@ARM.comPerfKvmCounter::~PerfKvmCounter()
899651SAndreas.Sandberg@ARM.com{
909651SAndreas.Sandberg@ARM.com    if (attached())
919651SAndreas.Sandberg@ARM.com        detach();
929651SAndreas.Sandberg@ARM.com}
939651SAndreas.Sandberg@ARM.com
949651SAndreas.Sandberg@ARM.comvoid
959651SAndreas.Sandberg@ARM.comPerfKvmCounter::detach()
969651SAndreas.Sandberg@ARM.com{
979651SAndreas.Sandberg@ARM.com    assert(attached());
989651SAndreas.Sandberg@ARM.com
999651SAndreas.Sandberg@ARM.com    if (munmap(ringBuffer, ringNumPages * pageSize) == -1)
1009651SAndreas.Sandberg@ARM.com        warn("PerfKvmCounter: Failed to unmap ring buffer (%i)\n",
1019651SAndreas.Sandberg@ARM.com             errno);
1029651SAndreas.Sandberg@ARM.com    close(fd);
1039651SAndreas.Sandberg@ARM.com
1049651SAndreas.Sandberg@ARM.com    fd = -1;
1059651SAndreas.Sandberg@ARM.com    ringBuffer = NULL;
1069651SAndreas.Sandberg@ARM.com}
1079651SAndreas.Sandberg@ARM.com
1089651SAndreas.Sandberg@ARM.comvoid
1099651SAndreas.Sandberg@ARM.comPerfKvmCounter::start()
1109651SAndreas.Sandberg@ARM.com{
1119651SAndreas.Sandberg@ARM.com    if (ioctl(PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1)
1129651SAndreas.Sandberg@ARM.com        panic("KVM: Failed to enable performance counters (%i)\n", errno);
1139651SAndreas.Sandberg@ARM.com}
1149651SAndreas.Sandberg@ARM.com
1159651SAndreas.Sandberg@ARM.comvoid
1169651SAndreas.Sandberg@ARM.comPerfKvmCounter::stop()
1179651SAndreas.Sandberg@ARM.com{
1189651SAndreas.Sandberg@ARM.com    if (ioctl(PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1)
1199651SAndreas.Sandberg@ARM.com        panic("KVM: Failed to disable performance counters (%i)\n", errno);
1209651SAndreas.Sandberg@ARM.com}
1219651SAndreas.Sandberg@ARM.com
1229651SAndreas.Sandberg@ARM.comvoid
1239651SAndreas.Sandberg@ARM.comPerfKvmCounter::period(uint64_t period)
1249651SAndreas.Sandberg@ARM.com{
1259651SAndreas.Sandberg@ARM.com    if (ioctl(PERF_EVENT_IOC_PERIOD, &period) == -1)
1269651SAndreas.Sandberg@ARM.com        panic("KVM: Failed to set period of performance counter (%i)\n", errno);
1279651SAndreas.Sandberg@ARM.com}
1289651SAndreas.Sandberg@ARM.com
1299651SAndreas.Sandberg@ARM.comvoid
1309651SAndreas.Sandberg@ARM.comPerfKvmCounter::refresh(int refresh)
1319651SAndreas.Sandberg@ARM.com{
1329651SAndreas.Sandberg@ARM.com    if (ioctl(PERF_EVENT_IOC_REFRESH, refresh) == -1)
1339651SAndreas.Sandberg@ARM.com        panic("KVM: Failed to refresh performance counter (%i)\n", errno);
1349651SAndreas.Sandberg@ARM.com}
1359651SAndreas.Sandberg@ARM.com
1369651SAndreas.Sandberg@ARM.comuint64_t
1379651SAndreas.Sandberg@ARM.comPerfKvmCounter::read() const
1389651SAndreas.Sandberg@ARM.com{
1399651SAndreas.Sandberg@ARM.com    uint64_t value;
1409651SAndreas.Sandberg@ARM.com
1419651SAndreas.Sandberg@ARM.com    read(&value, sizeof(uint64_t));
1429651SAndreas.Sandberg@ARM.com    return value;
1439651SAndreas.Sandberg@ARM.com}
1449651SAndreas.Sandberg@ARM.com
1459651SAndreas.Sandberg@ARM.comvoid
1469651SAndreas.Sandberg@ARM.comPerfKvmCounter::enableSignals(pid_t tid, int signal)
1479651SAndreas.Sandberg@ARM.com{
1489651SAndreas.Sandberg@ARM.com    struct f_owner_ex sigowner;
1499651SAndreas.Sandberg@ARM.com
1509651SAndreas.Sandberg@ARM.com    sigowner.type = F_OWNER_TID;
1519651SAndreas.Sandberg@ARM.com    sigowner.pid = tid;
1529651SAndreas.Sandberg@ARM.com
1539651SAndreas.Sandberg@ARM.com    if (fcntl(F_SETOWN_EX, &sigowner) == -1 ||
1549651SAndreas.Sandberg@ARM.com        fcntl(F_SETSIG, signal) == -1 ||
1559651SAndreas.Sandberg@ARM.com        fcntl(F_SETFL, O_ASYNC) == -1)
1569651SAndreas.Sandberg@ARM.com        panic("PerfKvmCounter: Failed to enable signals for counter (%i)\n",
1579651SAndreas.Sandberg@ARM.com              errno);
1589651SAndreas.Sandberg@ARM.com}
1599651SAndreas.Sandberg@ARM.com
1609651SAndreas.Sandberg@ARM.comvoid
1619651SAndreas.Sandberg@ARM.comPerfKvmCounter::attach(PerfKvmCounterConfig &config,
1629651SAndreas.Sandberg@ARM.com                    pid_t tid, int group_fd)
1639651SAndreas.Sandberg@ARM.com{
1649651SAndreas.Sandberg@ARM.com    assert(!attached());
1659651SAndreas.Sandberg@ARM.com
1669651SAndreas.Sandberg@ARM.com    fd = syscall(__NR_perf_event_open,
1679651SAndreas.Sandberg@ARM.com                 &config.attr, tid,
1689651SAndreas.Sandberg@ARM.com                 -1, // CPU (-1 => Any CPU that the task happens to run on)
1699651SAndreas.Sandberg@ARM.com                 group_fd,
1709651SAndreas.Sandberg@ARM.com                 0); // Flags
1719651SAndreas.Sandberg@ARM.com    if (fd == -1)
17213787Sgambordr@oregonstate.edu    {
17313787Sgambordr@oregonstate.edu        if (errno == EACCES)
17413787Sgambordr@oregonstate.edu        {
17513787Sgambordr@oregonstate.edu            panic("PerfKvmCounter::attach recieved error EACCESS\n"
17613787Sgambordr@oregonstate.edu            "  This error may be caused by a too restrictive setting\n"
17713787Sgambordr@oregonstate.edu            "  in the file '/proc/sys/kernel/perf_event_paranoid'\n"
17813787Sgambordr@oregonstate.edu            "  The default value was changed to 2 in kernel 4.6\n"
17913787Sgambordr@oregonstate.edu            "  A value greater than 1 prevents gem5 from making\n"
18013787Sgambordr@oregonstate.edu            "  the syscall to perf_event_open");
18113787Sgambordr@oregonstate.edu        }
18213787Sgambordr@oregonstate.edu        panic("PerfKvmCounter::attach failed (%i)\n", errno);
18313787Sgambordr@oregonstate.edu    }
1849651SAndreas.Sandberg@ARM.com
1859651SAndreas.Sandberg@ARM.com    mmapPerf(1);
1869651SAndreas.Sandberg@ARM.com}
1879651SAndreas.Sandberg@ARM.com
1889651SAndreas.Sandberg@ARM.compid_t
1899651SAndreas.Sandberg@ARM.comPerfKvmCounter::gettid()
1909651SAndreas.Sandberg@ARM.com{
1919651SAndreas.Sandberg@ARM.com    return syscall(__NR_gettid);
1929651SAndreas.Sandberg@ARM.com}
1939651SAndreas.Sandberg@ARM.com
1949651SAndreas.Sandberg@ARM.comvoid
1959651SAndreas.Sandberg@ARM.comPerfKvmCounter::mmapPerf(int pages)
1969651SAndreas.Sandberg@ARM.com{
1979651SAndreas.Sandberg@ARM.com    assert(attached());
1989651SAndreas.Sandberg@ARM.com    assert(ringBuffer == NULL);
1999651SAndreas.Sandberg@ARM.com
2009651SAndreas.Sandberg@ARM.com    if (pageSize == -1) {
2019651SAndreas.Sandberg@ARM.com        pageSize = sysconf(_SC_PAGE_SIZE);
2029651SAndreas.Sandberg@ARM.com        if (pageSize == -1)
2039651SAndreas.Sandberg@ARM.com            panic("PerfKvmCounter: Failed to determine page size (%i)\n",
2049651SAndreas.Sandberg@ARM.com                  errno);
2059651SAndreas.Sandberg@ARM.com    }
2069651SAndreas.Sandberg@ARM.com
2079651SAndreas.Sandberg@ARM.com    ringNumPages = pages + 1;
2089651SAndreas.Sandberg@ARM.com    ringBuffer = (struct perf_event_mmap_page *)mmap(
2099651SAndreas.Sandberg@ARM.com        NULL, ringNumPages * 4096,
2109651SAndreas.Sandberg@ARM.com        PROT_READ | PROT_WRITE, MAP_SHARED,
2119651SAndreas.Sandberg@ARM.com        fd, 0);
2129651SAndreas.Sandberg@ARM.com    if (ringBuffer == MAP_FAILED)
2139651SAndreas.Sandberg@ARM.com        panic("PerfKvmCounter: MMAP failed (%i)\n",
2149651SAndreas.Sandberg@ARM.com              errno);
2159651SAndreas.Sandberg@ARM.com}
2169651SAndreas.Sandberg@ARM.com
2179651SAndreas.Sandberg@ARM.comint
2189651SAndreas.Sandberg@ARM.comPerfKvmCounter::fcntl(int cmd, long p1)
2199651SAndreas.Sandberg@ARM.com{
2209651SAndreas.Sandberg@ARM.com    assert(attached());
2219651SAndreas.Sandberg@ARM.com    return ::fcntl(fd, cmd, p1);
2229651SAndreas.Sandberg@ARM.com}
2239651SAndreas.Sandberg@ARM.com
2249651SAndreas.Sandberg@ARM.comint
2259651SAndreas.Sandberg@ARM.comPerfKvmCounter::ioctl(int request, long p1)
2269651SAndreas.Sandberg@ARM.com{
2279651SAndreas.Sandberg@ARM.com    assert(attached());
2289651SAndreas.Sandberg@ARM.com    return ::ioctl(fd, request, p1);
2299651SAndreas.Sandberg@ARM.com}
2309651SAndreas.Sandberg@ARM.com
2319651SAndreas.Sandberg@ARM.comvoid
2329651SAndreas.Sandberg@ARM.comPerfKvmCounter::read(void *buf, size_t size) const
2339651SAndreas.Sandberg@ARM.com{
2349651SAndreas.Sandberg@ARM.com    char *_buf = (char *)buf;
2359651SAndreas.Sandberg@ARM.com    size_t _size = size;
2369651SAndreas.Sandberg@ARM.com
2379651SAndreas.Sandberg@ARM.com    assert(attached());
2389651SAndreas.Sandberg@ARM.com
2399651SAndreas.Sandberg@ARM.com    do {
2409651SAndreas.Sandberg@ARM.com        ssize_t ret;
2419651SAndreas.Sandberg@ARM.com        ret = ::read(fd, _buf, _size);
2429651SAndreas.Sandberg@ARM.com        switch (ret) {
2439651SAndreas.Sandberg@ARM.com          case -1:
2449651SAndreas.Sandberg@ARM.com            if (errno != EAGAIN)
2459651SAndreas.Sandberg@ARM.com                panic("PerfKvmCounter::read failed (%i)\n", errno);
2469651SAndreas.Sandberg@ARM.com            break;
2479651SAndreas.Sandberg@ARM.com
2489651SAndreas.Sandberg@ARM.com          case 0:
2499651SAndreas.Sandberg@ARM.com            panic("PerfKvmCounter::read unexpected EOF.\n");
2509651SAndreas.Sandberg@ARM.com
2519651SAndreas.Sandberg@ARM.com          default:
2529651SAndreas.Sandberg@ARM.com            _size -= ret;
2539651SAndreas.Sandberg@ARM.com            _buf += ret;
2549651SAndreas.Sandberg@ARM.com            break;
2559651SAndreas.Sandberg@ARM.com        }
25611321Ssteve.reinhardt@amd.com    } while (_size);
2579651SAndreas.Sandberg@ARM.com}
258