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