1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "Profiler.h"
18 
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 
23 #include <algorithm>
24 #include <iostream>
25 
26 #if defined(__linux__)
27 
28 #include <sys/syscall.h>
29 
30 #ifdef __ARM_ARCH
31     enum ARMv8PmuPerfTypes{
32         // Common micro-architecture events
33         ARMV8_PMUV3_PERFCTR_L1_ICACHE_REFILL    = 0x01,
34         ARMV8_PMUV3_PERFCTR_L1_ICACHE_ACCESS    = 0x14,
35         ARMV8_PMUV3_PERFCTR_L2_CACHE_ACCESS     = 0x16,
36         ARMV8_PMUV3_PERFCTR_L2_CACHE_REFILL     = 0x17,
37         ARMV8_PMUV3_PERFCTR_L2_CACHE_WB         = 0x18,
38     };
39 #endif
40 
perf_event_open(struct perf_event_attr * hw_event,pid_t pid,int cpu,int group_fd,unsigned long flags)41 static int perf_event_open(struct perf_event_attr* hw_event, pid_t pid,
42         int cpu, int group_fd, unsigned long flags) {
43     return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
44 }
45 
46 #endif // __linux__
47 
48 namespace utils {
49 
get()50 Profiler& Profiler::get() noexcept {
51     static Profiler sProfiler;
52     return sProfiler;
53 }
54 
Profiler()55 Profiler::Profiler() noexcept {
56     std::uninitialized_fill(mCountersFd.begin(), mCountersFd.end(), -1);
57     Profiler::resetEvents(EV_CPU_CYCLES | EV_L1D_RATES | EV_BPU_RATES);
58 }
59 
~Profiler()60 Profiler::~Profiler() noexcept {
61     for (int fd : mCountersFd) {
62         if (fd >= 0) {
63             close(fd);
64         }
65     }
66 }
67 
resetEvents(uint32_t eventMask)68 uint32_t Profiler::resetEvents(uint32_t eventMask) noexcept {
69     // close all counters
70     for (int& fd : mCountersFd) {
71         if (fd >= 0) {
72             close(fd);
73             fd = -1;
74         }
75     }
76     mEnabledEvents = 0;
77 
78 #if defined(__linux__)
79 
80     struct perf_event_attr pe;
81     memset(&pe, 0, sizeof(struct perf_event_attr));
82     pe.type = PERF_TYPE_HARDWARE;
83     pe.size = sizeof(struct perf_event_attr);
84     pe.config = PERF_COUNT_HW_INSTRUCTIONS;
85     pe.disabled = 1;
86     pe.exclude_kernel = 1;
87     pe.exclude_hv = 1;
88     pe.read_format = PERF_FORMAT_GROUP |
89                      PERF_FORMAT_ID |
90                      PERF_FORMAT_TOTAL_TIME_ENABLED |
91                      PERF_FORMAT_TOTAL_TIME_RUNNING;
92 
93     uint8_t count = 0;
94     int fd = perf_event_open(&pe, 0, -1, -1, 0);
95     if (fd >= 0) {
96         const int groupFd = fd;
97         mIds[INSTRUCTIONS] = count++;
98         mCountersFd[INSTRUCTIONS] = fd;
99 
100         pe.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
101 
102         if (eventMask & EV_CPU_CYCLES) {
103             pe.type = PERF_TYPE_HARDWARE;
104             pe.config = PERF_COUNT_HW_CPU_CYCLES;
105             mCountersFd[CPU_CYCLES] = perf_event_open(&pe, 0, -1, groupFd, 0);
106             if (mCountersFd[CPU_CYCLES] > 0) {
107                 mIds[CPU_CYCLES] = count++;
108                 mEnabledEvents |= EV_CPU_CYCLES;
109             }
110         }
111 
112         if (eventMask & EV_L1D_REFS) {
113             pe.type = PERF_TYPE_HARDWARE;
114             pe.config = PERF_COUNT_HW_CACHE_REFERENCES;
115             mCountersFd[DCACHE_REFS] = perf_event_open(&pe, 0, -1, groupFd, 0);
116             if (mCountersFd[DCACHE_REFS] > 0) {
117                 mIds[DCACHE_REFS] = count++;
118                 mEnabledEvents |= EV_L1D_REFS;
119             }
120         }
121 
122         if (eventMask & EV_L1D_MISSES) {
123             pe.type = PERF_TYPE_HARDWARE;
124             pe.config = PERF_COUNT_HW_CACHE_MISSES;
125             mCountersFd[DCACHE_MISSES] = perf_event_open(&pe, 0, -1, groupFd, 0);
126             if (mCountersFd[DCACHE_MISSES] > 0) {
127                 mIds[DCACHE_MISSES] = count++;
128                 mEnabledEvents |= EV_L1D_MISSES;
129             }
130         }
131 
132         if (eventMask & EV_BPU_REFS) {
133             pe.type = PERF_TYPE_HARDWARE;
134             pe.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
135             mCountersFd[BRANCHES] = perf_event_open(&pe, 0, -1, groupFd, 0);
136             if (mCountersFd[BRANCHES] > 0) {
137                 mIds[BRANCHES] = count++;
138                 mEnabledEvents |= EV_BPU_REFS;
139             }
140         }
141 
142         if (eventMask & EV_BPU_MISSES) {
143             pe.type = PERF_TYPE_HARDWARE;
144             pe.config = PERF_COUNT_HW_BRANCH_MISSES;
145             mCountersFd[BRANCH_MISSES] = perf_event_open(&pe, 0, -1, groupFd, 0);
146             if (mCountersFd[BRANCH_MISSES] > 0) {
147                 mIds[BRANCH_MISSES] = count++;
148                 mEnabledEvents |= EV_BPU_MISSES;
149             }
150         }
151 
152 #ifdef __ARM_ARCH
153         if (eventMask & EV_L1I_REFS) {
154             pe.type = PERF_TYPE_RAW;
155             pe.config = ARMV8_PMUV3_PERFCTR_L1_ICACHE_ACCESS;
156             mCountersFd[ICACHE_REFS] = perf_event_open(&pe, 0, -1, groupFd, 0);
157             if (mCountersFd[ICACHE_REFS] > 0) {
158                 mIds[ICACHE_REFS] = count++;
159                 mEnabledEvents |= EV_L1I_REFS;
160             }
161         }
162 
163         if (eventMask & EV_L1I_MISSES) {
164             pe.type = PERF_TYPE_RAW;
165             pe.config = ARMV8_PMUV3_PERFCTR_L1_ICACHE_REFILL;
166             mCountersFd[ICACHE_MISSES] = perf_event_open(&pe, 0, -1, groupFd, 0);
167             if (mCountersFd[ICACHE_MISSES] > 0) {
168                 mIds[ICACHE_MISSES] = count++;
169                 mEnabledEvents |= EV_L1I_MISSES;
170             }
171         }
172 #else
173         if (eventMask & EV_L1I_REFS) {
174             pe.type = PERF_TYPE_HW_CACHE;
175             pe.config = PERF_COUNT_HW_CACHE_L1I |
176                 (PERF_COUNT_HW_CACHE_OP_READ<<8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS<<16);
177             mCountersFd[ICACHE_REFS] = perf_event_open(&pe, 0, -1, groupFd, 0);
178             if (mCountersFd[ICACHE_REFS] > 0) {
179                 mIds[ICACHE_REFS] = count++;
180                 mEnabledEvents |= EV_L1I_REFS;
181             }
182         }
183 
184         if (eventMask & EV_L1I_MISSES) {
185             pe.type = PERF_TYPE_HW_CACHE;
186             pe.config = PERF_COUNT_HW_CACHE_L1I |
187                 (PERF_COUNT_HW_CACHE_OP_READ<<8) | (PERF_COUNT_HW_CACHE_RESULT_MISS<<16);
188             mCountersFd[ICACHE_MISSES] = perf_event_open(&pe, 0, -1, groupFd, 0);
189             if (mCountersFd[ICACHE_MISSES] > 0) {
190                 mIds[ICACHE_MISSES] = count++;
191                 mEnabledEvents |= EV_L1I_MISSES;
192             }
193         }
194 #endif
195     }
196 #endif // __linux__
197     return mEnabledEvents;
198 }
199 
200 } // namespace utils
201