blob: cfc62e4cc77e99549cca4e345a5a48cc9c221ea8 [file] [log] [blame]
Jon Medhurst15ce78d2014-04-10 09:02:02 +01001/**
Jon Medhurstb1d07442015-05-08 12:04:18 +01002 * Copyright (C) ARM Limited 2013-2015. All rights reserved.
Jon Medhurst15ce78d2014-04-10 09:02:02 +01003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include "PerfGroup.h"
10
11#include <errno.h>
Jon Medhurst96b56152014-10-30 18:01:15 +000012#include <fcntl.h>
Jon Medhurst15ce78d2014-04-10 09:02:02 +010013#include <string.h>
14#include <sys/ioctl.h>
15#include <sys/syscall.h>
16#include <unistd.h>
17
18#include "Buffer.h"
Jon Medhurstb1d07442015-05-08 12:04:18 +010019#include "DynBuf.h"
Jon Medhurst15ce78d2014-04-10 09:02:02 +010020#include "Logging.h"
21#include "Monitor.h"
22#include "PerfBuffer.h"
23#include "SessionData.h"
24
Jon Medhurstb1d07442015-05-08 12:04:18 +010025static const int schedSwitchKey = getEventKey();
26static const int clockKey = getEventKey();
27
Jon Medhurst15ce78d2014-04-10 09:02:02 +010028#define DEFAULT_PEA_ARGS(pea, additionalSampleType) \
29 pea.size = sizeof(pea); \
30 /* Emit time, read_format below, group leader id, and raw tracepoint info */ \
Jon Medhurste31266f2014-08-04 15:47:44 +010031 pea.sample_type = (gSessionData->perf.getLegacySupport() \
Jon Medhurstb1d07442015-05-08 12:04:18 +010032 ? PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_ID \
33 : PERF_SAMPLE_IDENTIFIER ) | PERF_SAMPLE_TIME | additionalSampleType; \
Jon Medhurst15ce78d2014-04-10 09:02:02 +010034 /* Emit emit value in group format */ \
35 pea.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; \
36 /* start out disabled */ \
37 pea.disabled = 1; \
38 /* have a sampling interrupt happen when we cross the wakeup_watermark boundary */ \
39 pea.watermark = 1; \
40 /* Be conservative in flush size as only one buffer set is monitored */ \
Jon Medhurst96b56152014-10-30 18:01:15 +000041 pea.wakeup_watermark = BUF_SIZE / 2
Jon Medhurst15ce78d2014-04-10 09:02:02 +010042
43static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t pid, const int cpu, const int group_fd, const unsigned long flags) {
Jon Medhurst96b56152014-10-30 18:01:15 +000044 int fd = syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
45 if (fd < 0) {
46 return -1;
47 }
48 int fdf = fcntl(fd, F_GETFD);
49 if ((fdf == -1) || (fcntl(fd, F_SETFD, fdf | FD_CLOEXEC) != 0)) {
50 close(fd);
51 return -1;
52 }
53 return fd;
Jon Medhurst15ce78d2014-04-10 09:02:02 +010054}
55
Jon Medhurstb1d07442015-05-08 12:04:18 +010056PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb), mSchedSwitchId(-1) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +010057 memset(&mAttrs, 0, sizeof(mAttrs));
Jon Medhurstb1d07442015-05-08 12:04:18 +010058 memset(&mFlags, 0, sizeof(mFlags));
Jon Medhurst15ce78d2014-04-10 09:02:02 +010059 memset(&mKeys, -1, sizeof(mKeys));
60 memset(&mFds, -1, sizeof(mFds));
Jon Medhurstb1d07442015-05-08 12:04:18 +010061 memset(&mLeaders, -1, sizeof(mLeaders));
Jon Medhurst15ce78d2014-04-10 09:02:02 +010062}
63
64PerfGroup::~PerfGroup() {
65 for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) {
66 if (mFds[pos] >= 0) {
67 close(mFds[pos]);
68 }
69 }
70}
71
Jon Medhurstb1d07442015-05-08 12:04:18 +010072int PerfGroup::doAdd(const uint64_t currTime, Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +010073 int i;
74 for (i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
75 if (mKeys[i] < 0) {
76 break;
77 }
78 }
79
80 if (i >= ARRAY_LENGTH(mKeys)) {
Jon Medhurstb1d07442015-05-08 12:04:18 +010081 logg->logMessage("Too many counters");
82 return -1;
Jon Medhurst15ce78d2014-04-10 09:02:02 +010083 }
84
85 DEFAULT_PEA_ARGS(mAttrs[i], sampleType);
86 mAttrs[i].type = type;
87 mAttrs[i].config = config;
88 mAttrs[i].sample_period = sample;
89 // always be on the CPU but only a group leader can be pinned
Jon Medhurstb1d07442015-05-08 12:04:18 +010090 mAttrs[i].pinned = (flags & PERF_GROUP_LEADER ? 1 : 0);
Jon Medhurst15ce78d2014-04-10 09:02:02 +010091 mAttrs[i].mmap = (flags & PERF_GROUP_MMAP ? 1 : 0);
92 mAttrs[i].comm = (flags & PERF_GROUP_COMM ? 1 : 0);
93 mAttrs[i].freq = (flags & PERF_GROUP_FREQ ? 1 : 0);
94 mAttrs[i].task = (flags & PERF_GROUP_TASK ? 1 : 0);
95 mAttrs[i].sample_id_all = (flags & PERF_GROUP_SAMPLE_ID_ALL ? 1 : 0);
Jon Medhurstb1d07442015-05-08 12:04:18 +010096 mFlags[i] = flags;
Jon Medhurst15ce78d2014-04-10 09:02:02 +010097
98 mKeys[i] = key;
99
Jon Medhurstb1d07442015-05-08 12:04:18 +0100100 buffer->marshalPea(currTime, &mAttrs[i], key);
101
102 return i;
103}
104
105/* Counters from different hardware PMUs need to be in different
106 * groups. Software counters can be in the same group as the CPU and
107 * should be marked as PERF_GROUP_CPU. The big and little clusters can
108 * be in the same group as only one or the other will be available on
109 * a given CPU.
110 */
111int PerfGroup::getEffectiveType(const int type, const int flags) {
112 const int effectiveType = flags & PERF_GROUP_CPU ? (int)PERF_TYPE_HARDWARE : type;
113 if (effectiveType >= ARRAY_LENGTH(mLeaders)) {
114 logg->logError("perf type is too large, please increase the size of PerfGroup::mLeaders");
115 handleException();
116 }
117 return effectiveType;
118}
119
120bool PerfGroup::createCpuGroup(const uint64_t currTime, Buffer *const buffer) {
121 if (mSchedSwitchId < 0) {
122 DynBuf b;
123 mSchedSwitchId = PerfDriver::getTracepointId(SCHED_SWITCH, &b);
124 if (mSchedSwitchId < 0) {
125 logg->logMessage("Unable to read sched_switch id");
126 return false;
127 }
128 }
129
130 mLeaders[PERF_TYPE_HARDWARE] = doAdd(currTime, buffer, schedSwitchKey, PERF_TYPE_TRACEPOINT, mSchedSwitchId, 1, PERF_SAMPLE_READ | PERF_SAMPLE_RAW, PERF_GROUP_MMAP | PERF_GROUP_COMM | PERF_GROUP_TASK | PERF_GROUP_SAMPLE_ID_ALL | PERF_GROUP_PER_CPU | PERF_GROUP_LEADER | PERF_GROUP_CPU);
131 if (mLeaders[PERF_TYPE_HARDWARE] < 0) {
132 return false;
133 }
134
135 if (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && doAdd(currTime, buffer, clockKey, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU) < 0) {
136 return false;
137 }
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100138
139 return true;
140}
141
Jon Medhurstb1d07442015-05-08 12:04:18 +0100142bool PerfGroup::add(const uint64_t currTime, Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) {
143 const int effectiveType = getEffectiveType(type, flags);
144
145 // Does a group exist for this already?
146 if (!(flags & PERF_GROUP_LEADER) && mLeaders[effectiveType] < 0) {
147 // Create it
148 if (effectiveType == PERF_TYPE_HARDWARE) {
149 if (!createCpuGroup(currTime, buffer)) {
150 return false;
151 }
152 } else {
153 // Non-CPU PMUs are sampled every 100ms for Sample Rate: None and EBS, otherwise they would never be sampled
154 const uint64_t timeout = gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS ? 1000000000UL / gSessionData->mSampleRate : 100000000UL;
155 // PERF_SAMPLE_TID | PERF_SAMPLE_IP aren't helpful on non-CPU or 'uncore' PMUs - which CPU is the right one to sample? But removing it causes problems, remove it later.
156 mLeaders[effectiveType] = doAdd(currTime, buffer, clockKey, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, timeout, PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_READ, PERF_GROUP_LEADER);
157 if (mLeaders[effectiveType] < 0) {
158 return false;
159 }
160 }
161 }
162
163 if (!(flags & PERF_GROUP_LEADER) && effectiveType != PERF_TYPE_HARDWARE && (flags & PERF_GROUP_PER_CPU)) {
164 logg->logError("'uncore' counters are not permitted to be per-cpu");
165 handleException();
166 }
167
168 return doAdd(currTime, buffer, key, type, config, sample, sampleType, flags) >= 0;
169}
170
Jon Medhurst96b56152014-10-30 18:01:15 +0000171int PerfGroup::prepareCPU(const int cpu, Monitor *const monitor) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100172 logg->logMessage("Onlining cpu %i", cpu);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100173
174 for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
175 if (mKeys[i] < 0) {
176 continue;
177 }
178
Jon Medhurstb1d07442015-05-08 12:04:18 +0100179 if ((cpu != 0) && !(mFlags[i] & PERF_GROUP_PER_CPU)) {
Jon Medhurste31266f2014-08-04 15:47:44 +0100180 continue;
181 }
182
Jon Medhurstb1d07442015-05-08 12:04:18 +0100183 const int offset = i * gSessionData->mCores + cpu;
184 if (mFds[offset] >= 0) {
185 logg->logMessage("cpu already online or not correctly cleaned up");
Jon Medhurst96b56152014-10-30 18:01:15 +0000186 return PG_FAILURE;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100187 }
188
Jon Medhurstb1d07442015-05-08 12:04:18 +0100189 logg->logMessage("perf_event_open cpu: %i type: %i config: %lli sample: %lli sample_type: 0x%llx pinned: %lli mmap: %lli comm: %lli freq: %lli task: %lli sample_id_all: %lli", cpu, mAttrs[i].type, mAttrs[i].config, mAttrs[i].sample_period, mAttrs[i].sample_type, mAttrs[i].pinned, mAttrs[i].mmap, mAttrs[i].comm, mAttrs[i].freq, mAttrs[i].task, mAttrs[i].sample_id_all);
190 mFds[offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, mAttrs[i].pinned ? -1 : mFds[mLeaders[getEffectiveType(mAttrs[i].type, mFlags[i])] * gSessionData->mCores + cpu], mAttrs[i].pinned ? 0 : PERF_FLAG_FD_OUTPUT);
191 if (mFds[offset] < 0) {
192 logg->logMessage("failed %s", strerror(errno));
Jon Medhurst96b56152014-10-30 18:01:15 +0000193 if (errno == ENODEV) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100194 // The core is offline
Jon Medhurst96b56152014-10-30 18:01:15 +0000195 return PG_CPU_OFFLINE;
196 }
Jon Medhurstb1d07442015-05-08 12:04:18 +0100197#ifndef USE_STRICTER_CHECK
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100198 continue;
Jon Medhurstb1d07442015-05-08 12:04:18 +0100199#else
200 if (errno == ENOENT) {
201 // This event doesn't apply to this CPU but should apply to a different one, ex bL
202 continue;
203 }
204 logg->logMessage("perf_event_open failed");
205 return PG_FAILURE;
206#endif
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100207 }
208
Jon Medhurstb1d07442015-05-08 12:04:18 +0100209 if (!mPb->useFd(cpu, mFds[offset])) {
210 logg->logMessage("PerfBuffer::useFd failed");
Jon Medhurst96b56152014-10-30 18:01:15 +0000211 return PG_FAILURE;
212 }
213
214
Jon Medhurstb1d07442015-05-08 12:04:18 +0100215 if (!monitor->add(mFds[offset])) {
216 logg->logMessage("Monitor::add failed");
217 return PG_FAILURE;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100218 }
219 }
220
Jon Medhurst96b56152014-10-30 18:01:15 +0000221 return PG_SUCCESS;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100222}
223
Jon Medhurstb1d07442015-05-08 12:04:18 +0100224static bool readAndSend(const uint64_t currTime, Buffer *const buffer, const int fd, const int keyCount, const int *const keys) {
225 char buf[1024];
226 ssize_t bytes = read(fd, buf, sizeof(buf));
227 if (bytes < 0) {
228 logg->logMessage("read failed");
229 return false;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100230 }
Jon Medhurstb1d07442015-05-08 12:04:18 +0100231 buffer->marshalKeysOld(currTime, keyCount, keys, bytes, buf);
232
233 return true;
234}
235
236int PerfGroup::onlineCPU(const uint64_t currTime, const int cpu, const bool enable, Buffer *const buffer) {
237 bool addedEvents = false;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100238
Jon Medhurste31266f2014-08-04 15:47:44 +0100239 if (!gSessionData->perf.getLegacySupport()) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100240 int idCount = 0;
241 int coreKeys[ARRAY_LENGTH(mKeys)];
242 __u64 ids[ARRAY_LENGTH(mKeys)];
243
244 for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
245 const int fd = mFds[cpu + i * gSessionData->mCores];
246 if (fd < 0) {
247 continue;
248 }
249
250 coreKeys[idCount] = mKeys[i];
251 if (ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0 &&
252 // Workaround for running 32-bit gatord on 64-bit systems, kernel patch in the works
253 ioctl(fd, (PERF_EVENT_IOC_ID & ~IOCSIZE_MASK) | (8 << _IOC_SIZESHIFT), &ids[idCount]) != 0) {
254 logg->logMessage("ioctl failed");
255 return 0;
256 }
257 ++idCount;
258 addedEvents = true;
Jon Medhurste31266f2014-08-04 15:47:44 +0100259 }
Jon Medhurstb1d07442015-05-08 12:04:18 +0100260
261 buffer->marshalKeys(currTime, idCount, ids, coreKeys);
262 } else {
263 int idCounts[ARRAY_LENGTH(mLeaders)] = { 0 };
264 int coreKeys[ARRAY_LENGTH(mLeaders)][ARRAY_LENGTH(mKeys)];
265 for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
266 const int fd = mFds[cpu + i * gSessionData->mCores];
267 if (fd < 0) {
268 continue;
269 }
270
271 const int effectiveType = getEffectiveType(mAttrs[i].type, mFlags[i]);
272 if (mAttrs[i].pinned && mLeaders[effectiveType] != i) {
273 if (!readAndSend(currTime, buffer, fd, 1, mKeys + i)) {
274 return 0;
275 }
276 } else {
277 coreKeys[effectiveType][idCounts[effectiveType]] = mKeys[i];
278 ++idCounts[effectiveType];
279 addedEvents = true;
280 }
281 }
282
283 for (int i = 0; i < ARRAY_LENGTH(mLeaders); ++i) {
284 if (idCounts[i] > 0 && !readAndSend(currTime, buffer, mFds[mLeaders[i] * gSessionData->mCores + cpu], idCounts[i], coreKeys[i])) {
285 return 0;
286 }
287 }
Jon Medhurste31266f2014-08-04 15:47:44 +0100288 }
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100289
Jon Medhurstb1d07442015-05-08 12:04:18 +0100290 if (enable) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100291 for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
292 int offset = i * gSessionData->mCores + cpu;
Jon Medhurst96b56152014-10-30 18:01:15 +0000293 if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_ENABLE, 0) < 0) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100294 logg->logMessage("ioctl failed");
Jon Medhurst96b56152014-10-30 18:01:15 +0000295 return 0;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100296 }
297 }
298 }
299
Jon Medhurstb1d07442015-05-08 12:04:18 +0100300 if (!addedEvents) {
301 logg->logMessage("no events came online");
Jon Medhurst96b56152014-10-30 18:01:15 +0000302 }
303
Jon Medhurstb1d07442015-05-08 12:04:18 +0100304 return 1;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100305}
306
307bool PerfGroup::offlineCPU(const int cpu) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100308 logg->logMessage("Offlining cpu %i", cpu);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100309
Jon Medhurstb1d07442015-05-08 12:04:18 +0100310 for (int i = ARRAY_LENGTH(mKeys) - 1; i >= 0; --i) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100311 int offset = i * gSessionData->mCores + cpu;
Jon Medhurst96b56152014-10-30 18:01:15 +0000312 if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_DISABLE, 0) < 0) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100313 logg->logMessage("ioctl failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100314 return false;
315 }
316 }
317
318 // Mark the buffer so that it will be released next time it's read
319 mPb->discard(cpu);
320
Jon Medhurstb1d07442015-05-08 12:04:18 +0100321 for (int i = ARRAY_LENGTH(mKeys) - 1; i >= 0; --i) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100322 if (mKeys[i] < 0) {
323 continue;
324 }
325
326 int offset = i * gSessionData->mCores + cpu;
327 if (mFds[offset] >= 0) {
328 close(mFds[offset]);
329 mFds[offset] = -1;
330 }
331 }
332
333 return true;
334}
335
336bool PerfGroup::start() {
337 for (int pos = 0; pos < ARRAY_LENGTH(mFds); ++pos) {
Jon Medhurst96b56152014-10-30 18:01:15 +0000338 if (mFds[pos] >= 0 && ioctl(mFds[pos], PERF_EVENT_IOC_ENABLE, 0) < 0) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100339 logg->logMessage("ioctl failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100340 goto fail;
341 }
342 }
343
344 return true;
345
346 fail:
347 stop();
348
349 return false;
350}
351
352void PerfGroup::stop() {
353 for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) {
354 if (mFds[pos] >= 0) {
Jon Medhurst96b56152014-10-30 18:01:15 +0000355 ioctl(mFds[pos], PERF_EVENT_IOC_DISABLE, 0);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100356 }
357 }
358}