blob: 193b7789a290bb1583db307568ec94a2043599ed [file] [log] [blame]
Jon Medhurst15ce78d2014-04-10 09:02:02 +01001/**
2 * Copyright (C) ARM Limited 2010-2014. All rights reserved.
3 *
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 "PerfSource.h"
10
Jon Medhurst96b56152014-10-30 18:01:15 +000011#include <dirent.h>
Jon Medhurst15ce78d2014-04-10 09:02:02 +010012#include <errno.h>
Jon Medhurst96b56152014-10-30 18:01:15 +000013#include <signal.h>
Jon Medhurst15ce78d2014-04-10 09:02:02 +010014#include <string.h>
Jon Medhurst96b56152014-10-30 18:01:15 +000015#include <sys/prctl.h>
16#include <sys/resource.h>
17#include <sys/syscall.h>
18#include <sys/types.h>
Jon Medhurst15ce78d2014-04-10 09:02:02 +010019#include <unistd.h>
20
21#include "Child.h"
22#include "DynBuf.h"
23#include "Logging.h"
24#include "PerfDriver.h"
25#include "Proc.h"
26#include "SessionData.h"
27
Jon Medhurst96b56152014-10-30 18:01:15 +000028#ifndef SCHED_RESET_ON_FORK
29#define SCHED_RESET_ON_FORK 0x40000000
30#endif
Jon Medhurst15ce78d2014-04-10 09:02:02 +010031
32extern Child *child;
33
Jon Medhurst96b56152014-10-30 18:01:15 +000034static bool sendTracepointFormat(const uint64_t currTime, Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +010035 if (!printb->printf(EVENTS_PATH "/%s/format", name)) {
36 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
37 return false;
38 }
39 if (!b->read(printb->getBuf())) {
40 logg->logMessage("%s(%s:%i): DynBuf::read failed", __FUNCTION__, __FILE__, __LINE__);
41 return false;
42 }
Jon Medhurst96b56152014-10-30 18:01:15 +000043 buffer->format(currTime, b->getLength(), b->getBuf());
Jon Medhurst15ce78d2014-04-10 09:02:02 +010044
45 return true;
46}
47
Jon Medhurst96b56152014-10-30 18:01:15 +000048static void *syncFunc(void *arg)
49{
50 struct timespec ts;
51 int64_t nextTime = gSessionData->mMonotonicStarted;
52 int err;
53 (void)arg;
54
55 prctl(PR_SET_NAME, (unsigned long)&"gatord-sync", 0, 0, 0);
56
57 // Mask all signals so that this thread will not be woken up
58 {
59 sigset_t set;
60 if (sigfillset(&set) != 0) {
61 logg->logError(__FILE__, __LINE__, "sigfillset failed");
62 handleException();
63 }
64 if ((err = pthread_sigmask(SIG_SETMASK, &set, NULL)) != 0) {
65 logg->logError(__FILE__, __LINE__, "pthread_sigmask failed");
66 handleException();
67 }
68 }
69
70 for (;;) {
71 if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) {
72 logg->logError(__FILE__, __LINE__, "clock_gettime failed");
73 handleException();
74 }
75 const int64_t currTime = ts.tv_sec * NS_PER_S + ts.tv_nsec;
76
77 // Wake up once a second
78 nextTime += NS_PER_S;
79
80 // Always sleep more than 1 ms, hopefully things will line up better next time
81 const int64_t sleepTime = max(nextTime - currTime, (int64_t)(NS_PER_MS + 1));
82 ts.tv_sec = sleepTime/NS_PER_S;
83 ts.tv_nsec = sleepTime % NS_PER_S;
84
85 err = nanosleep(&ts, NULL);
86 if (err != 0) {
87 fprintf(stderr, "clock_nanosleep failed: %s\n", strerror(err));
88 return NULL;
89 }
90 }
91
92 return NULL;
93}
94
95static long getMaxCoreNum() {
96 DIR *dir = opendir("/sys/devices/system/cpu");
97 if (dir == NULL) {
98 logg->logError(__FILE__, __LINE__, "Unable to determine the number of cores on the target, opendir failed");
99 handleException();
100 }
101
102 long maxCoreNum = -1;
103 struct dirent *dirent;
104 while ((dirent = readdir(dir)) != NULL) {
105 if (strncmp(dirent->d_name, "cpu", 3) == 0) {
106 char *endptr;
107 errno = 0;
108 long coreNum = strtol(dirent->d_name + 3, &endptr, 10);
109 if ((errno == 0) && (*endptr == '\0') && (coreNum >= maxCoreNum)) {
110 maxCoreNum = coreNum + 1;
111 }
112 }
113 }
114 closedir(dir);
115
116 if (maxCoreNum < 1) {
117 logg->logError(__FILE__, __LINE__, "Unable to determine the number of cores on the target, no cpu# directories found");
118 handleException();
119 }
120
121 if (maxCoreNum >= NR_CPUS) {
122 logg->logError(__FILE__, __LINE__, "Too many cores on the target, please increase NR_CPUS in Config.h");
123 handleException();
124 }
125
126 return maxCoreNum;
127}
128
129PerfSource::PerfSource(sem_t *senderSem, sem_t *startProfile) : mSummary(0, FRAME_SUMMARY, 1024, senderSem), mBuffer(0, FRAME_PERF_ATTRS, 1024*1024, senderSem), mCountersBuf(), mCountersGroup(&mCountersBuf), mIdleGroup(&mCountersBuf), mMonitor(), mUEvent(), mSenderSem(senderSem), mStartProfile(startProfile), mInterruptFd(-1), mIsDone(false) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100130 long l = sysconf(_SC_PAGE_SIZE);
131 if (l < 0) {
132 logg->logError(__FILE__, __LINE__, "Unable to obtain the page size");
133 handleException();
134 }
135 gSessionData->mPageSize = static_cast<int>(l);
Jon Medhurst96b56152014-10-30 18:01:15 +0000136 gSessionData->mCores = static_cast<int>(getMaxCoreNum());
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100137}
138
139PerfSource::~PerfSource() {
140}
141
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100142bool PerfSource::prepare() {
143 DynBuf printb;
144 DynBuf b1;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100145 long long schedSwitchId;
Jon Medhurst96b56152014-10-30 18:01:15 +0000146 long long cpuIdleId;
147
148 const uint64_t currTime = getTime();
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100149
Jon Medhurste31266f2014-08-04 15:47:44 +0100150 // Reread cpuinfo since cores may have changed since startup
151 gSessionData->readCpuInfo();
152
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100153 if (0
154 || !mMonitor.init()
155 || !mUEvent.init()
156 || !mMonitor.add(mUEvent.getFd())
157
158 || (schedSwitchId = PerfDriver::getTracepointId(SCHED_SWITCH, &printb)) < 0
Jon Medhurst96b56152014-10-30 18:01:15 +0000159 || !sendTracepointFormat(currTime, &mBuffer, SCHED_SWITCH, &printb, &b1)
160
161 || (cpuIdleId = PerfDriver::getTracepointId(CPU_IDLE, &printb)) < 0
162 || !sendTracepointFormat(currTime, &mBuffer, CPU_IDLE, &printb, &b1)
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100163
164 // Only want RAW but not IP on sched_switch and don't want TID on SAMPLE_ID
Jon Medhurst96b56152014-10-30 18:01:15 +0000165 || !mCountersGroup.add(currTime, &mBuffer, 100/**/, PERF_TYPE_TRACEPOINT, schedSwitchId, 1, PERF_SAMPLE_RAW, PERF_GROUP_MMAP | PERF_GROUP_COMM | PERF_GROUP_TASK | PERF_GROUP_SAMPLE_ID_ALL | PERF_GROUP_PER_CPU)
166 || !mIdleGroup.add(currTime, &mBuffer, 101/**/, PERF_TYPE_TRACEPOINT, cpuIdleId, 1, PERF_SAMPLE_RAW, PERF_GROUP_PER_CPU)
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100167
168 // Only want TID and IP but not RAW on timer
Jon Medhurst96b56152014-10-30 18:01:15 +0000169 || (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && !mCountersGroup.add(currTime, &mBuffer, 102/**/, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP, PERF_GROUP_PER_CPU))
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100170
Jon Medhurst96b56152014-10-30 18:01:15 +0000171 || !gSessionData->perf.enable(currTime, &mCountersGroup, &mBuffer)
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100172 || 0) {
Jon Medhurste31266f2014-08-04 15:47:44 +0100173 logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.4 or later?", __FUNCTION__, __FILE__, __LINE__);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100174 return false;
175 }
176
Jon Medhurst96b56152014-10-30 18:01:15 +0000177 for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
178 const int result = mCountersGroup.prepareCPU(cpu, &mMonitor);
179 if ((result != PG_SUCCESS) && (result != PG_CPU_OFFLINE)) {
180 logg->logError(__FILE__, __LINE__, "PerfGroup::prepareCPU on mCountersGroup failed");
181 handleException();
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100182 }
Jon Medhurst96b56152014-10-30 18:01:15 +0000183 }
184 for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
185 const int result = mIdleGroup.prepareCPU(cpu, &mMonitor);
186 if ((result != PG_SUCCESS) && (result != PG_CPU_OFFLINE)) {
187 logg->logError(__FILE__, __LINE__, "PerfGroup::prepareCPU on mIdleGroup failed");
188 handleException();
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100189 }
190 }
191
192 int numEvents = 0;
193 for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
Jon Medhurst96b56152014-10-30 18:01:15 +0000194 numEvents += mCountersGroup.onlineCPU(currTime, cpu, false, &mBuffer);
195 }
196 for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
197 numEvents += mIdleGroup.onlineCPU(currTime, cpu, false, &mBuffer);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100198 }
199 if (numEvents <= 0) {
200 logg->logMessage("%s(%s:%i): PerfGroup::onlineCPU failed on all cores", __FUNCTION__, __FILE__, __LINE__);
201 return false;
202 }
203
Jon Medhurst96b56152014-10-30 18:01:15 +0000204 // Send the summary right before the start so that the monotonic delta is close to the start time
205 if (!gSessionData->perf.summary(&mSummary)) {
206 logg->logError(__FILE__, __LINE__, "PerfDriver::summary failed", __FUNCTION__, __FILE__, __LINE__);
207 handleException();
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100208 }
209
Jon Medhurst96b56152014-10-30 18:01:15 +0000210 // Start the timer thread to used to sync perf and monotonic raw times
211 pthread_t syncThread;
212 if (pthread_create(&syncThread, NULL, syncFunc, NULL)) {
213 logg->logError(__FILE__, __LINE__, "pthread_create failed", __FUNCTION__, __FILE__, __LINE__);
214 handleException();
215 }
216 struct sched_param param;
217 param.sched_priority = sched_get_priority_max(SCHED_FIFO);
218 if (pthread_setschedparam(syncThread, SCHED_FIFO | SCHED_RESET_ON_FORK, &param) != 0) {
219 logg->logError(__FILE__, __LINE__, "pthread_setschedparam failed");
220 handleException();
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100221 }
222
Jon Medhurst96b56152014-10-30 18:01:15 +0000223 mBuffer.commit(currTime);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100224
225 return true;
226}
227
Jon Medhurst96b56152014-10-30 18:01:15 +0000228struct ProcThreadArgs {
229 Buffer *mBuffer;
230 uint64_t mCurrTime;
231 bool mIsDone;
232};
233
234void *procFunc(void *arg) {
235 DynBuf printb;
236 DynBuf b;
237 const ProcThreadArgs *const args = (ProcThreadArgs *)arg;
238
239 prctl(PR_SET_NAME, (unsigned long)&"gatord-proc", 0, 0, 0);
240
241 // Gator runs at a high priority, reset the priority to the default
242 if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), 0) == -1) {
243 logg->logError(__FILE__, __LINE__, "setpriority failed");
244 handleException();
245 }
246
247 if (!readProcMaps(args->mCurrTime, args->mBuffer, &printb, &b)) {
248 logg->logError(__FILE__, __LINE__, "readProcMaps failed");
249 handleException();
250 }
251 args->mBuffer->commit(args->mCurrTime);
252
253 if (!readKallsyms(args->mCurrTime, args->mBuffer, &args->mIsDone)) {
254 logg->logError(__FILE__, __LINE__, "readKallsyms failed");
255 handleException();
256 }
257 args->mBuffer->commit(args->mCurrTime);
258
259 return NULL;
260}
261
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100262static const char CPU_DEVPATH[] = "/devices/system/cpu/cpu";
263
264void PerfSource::run() {
265 int pipefd[2];
Jon Medhurst96b56152014-10-30 18:01:15 +0000266 pthread_t procThread;
267 ProcThreadArgs procThreadArgs;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100268
Jon Medhurst96b56152014-10-30 18:01:15 +0000269 {
270 DynBuf printb;
271 DynBuf b1;
272 DynBuf b2;
273
274 const uint64_t currTime = getTime();
275
276 // Start events before reading proc to avoid race conditions
277 if (!mCountersGroup.start() || !mIdleGroup.start()) {
278 logg->logError(__FILE__, __LINE__, "PerfGroup::start failed", __FUNCTION__, __FILE__, __LINE__);
279 handleException();
280 }
281
282 if (!readProcComms(currTime, &mBuffer, &printb, &b1, &b2)) {
283 logg->logError(__FILE__, __LINE__, "readProcComms failed");
284 handleException();
285 }
286 mBuffer.commit(currTime);
287
288 // Postpone reading kallsyms as on android adb gets too backed up and data is lost
289 procThreadArgs.mBuffer = &mBuffer;
290 procThreadArgs.mCurrTime = currTime;
291 procThreadArgs.mIsDone = false;
292 if (pthread_create(&procThread, NULL, procFunc, &procThreadArgs)) {
293 logg->logError(__FILE__, __LINE__, "pthread_create failed", __FUNCTION__, __FILE__, __LINE__);
294 handleException();
295 }
296 }
297
298 if (pipe_cloexec(pipefd) != 0) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100299 logg->logError(__FILE__, __LINE__, "pipe failed");
300 handleException();
301 }
302 mInterruptFd = pipefd[1];
303
304 if (!mMonitor.add(pipefd[0])) {
305 logg->logError(__FILE__, __LINE__, "Monitor::add failed");
306 handleException();
307 }
308
309 int timeout = -1;
310 if (gSessionData->mLiveRate > 0) {
Jon Medhurst96b56152014-10-30 18:01:15 +0000311 timeout = gSessionData->mLiveRate/NS_PER_MS;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100312 }
313
314 sem_post(mStartProfile);
315
316 while (gSessionData->mSessionIsActive) {
317 // +1 for uevents, +1 for pipe
318 struct epoll_event events[NR_CPUS + 2];
319 int ready = mMonitor.wait(events, ARRAY_LENGTH(events), timeout);
320 if (ready < 0) {
321 logg->logError(__FILE__, __LINE__, "Monitor::wait failed");
322 handleException();
323 }
Jon Medhurst96b56152014-10-30 18:01:15 +0000324 const uint64_t currTime = getTime();
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100325
326 for (int i = 0; i < ready; ++i) {
327 if (events[i].data.fd == mUEvent.getFd()) {
Jon Medhurst96b56152014-10-30 18:01:15 +0000328 if (!handleUEvent(currTime)) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100329 logg->logError(__FILE__, __LINE__, "PerfSource::handleUEvent failed");
330 handleException();
331 }
332 break;
333 }
334 }
335
336 // send a notification that data is ready
337 sem_post(mSenderSem);
338
339 // In one shot mode, stop collection once all the buffers are filled
340 // Assume timeout == 0 in this case
341 if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
342 logg->logMessage("%s(%s:%i): One shot", __FUNCTION__, __FILE__, __LINE__);
343 child->endSession();
344 }
345 }
346
Jon Medhurst96b56152014-10-30 18:01:15 +0000347 procThreadArgs.mIsDone = true;
348 pthread_join(procThread, NULL);
349 mIdleGroup.stop();
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100350 mCountersGroup.stop();
351 mBuffer.setDone();
352 mIsDone = true;
353
354 // send a notification that data is ready
355 sem_post(mSenderSem);
356
357 mInterruptFd = -1;
358 close(pipefd[0]);
359 close(pipefd[1]);
360}
361
Jon Medhurst96b56152014-10-30 18:01:15 +0000362bool PerfSource::handleUEvent(const uint64_t currTime) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100363 UEventResult result;
364 if (!mUEvent.read(&result)) {
365 logg->logMessage("%s(%s:%i): UEvent::Read failed", __FUNCTION__, __FILE__, __LINE__);
366 return false;
367 }
368
369 if (strcmp(result.mSubsystem, "cpu") == 0) {
370 if (strncmp(result.mDevPath, CPU_DEVPATH, sizeof(CPU_DEVPATH) - 1) != 0) {
371 logg->logMessage("%s(%s:%i): Unexpected cpu DEVPATH format", __FUNCTION__, __FILE__, __LINE__);
372 return false;
373 }
374 char *endptr;
375 errno = 0;
376 int cpu = strtol(result.mDevPath + sizeof(CPU_DEVPATH) - 1, &endptr, 10);
377 if (errno != 0 || *endptr != '\0') {
378 logg->logMessage("%s(%s:%i): strtol failed", __FUNCTION__, __FILE__, __LINE__);
379 return false;
380 }
Jon Medhurst96b56152014-10-30 18:01:15 +0000381
382 if (cpu >= gSessionData->mCores) {
383 logg->logError(__FILE__, __LINE__, "Only %i cores are expected but core %i reports %s", gSessionData->mCores, cpu, result.mAction);
384 handleException();
385 }
386
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100387 if (strcmp(result.mAction, "online") == 0) {
Jon Medhurst96b56152014-10-30 18:01:15 +0000388 mBuffer.onlineCPU(currTime, currTime - gSessionData->mMonotonicStarted, cpu);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100389 // Only call onlineCPU if prepareCPU succeeded
Jon Medhurst96b56152014-10-30 18:01:15 +0000390 bool result = false;
391 int err = mCountersGroup.prepareCPU(cpu, &mMonitor);
392 if (err == PG_CPU_OFFLINE) {
393 result = true;
394 } else if (err == PG_SUCCESS) {
395 if (mCountersGroup.onlineCPU(currTime, cpu, true, &mBuffer)) {
396 err = mIdleGroup.prepareCPU(cpu, &mMonitor);
397 if (err == PG_CPU_OFFLINE) {
398 result = true;
399 } else if (err == PG_SUCCESS) {
400 if (mIdleGroup.onlineCPU(currTime, cpu, true, &mBuffer)) {
401 result = true;
402 }
403 }
404 }
405 }
406 mBuffer.commit(currTime);
407
408 gSessionData->readCpuInfo();
409 gSessionData->perf.coreName(currTime, &mSummary, cpu);
410 mSummary.commit(currTime);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100411 return result;
412 } else if (strcmp(result.mAction, "offline") == 0) {
Jon Medhurst96b56152014-10-30 18:01:15 +0000413 const bool result = mCountersGroup.offlineCPU(cpu) && mIdleGroup.offlineCPU(cpu);
414 mBuffer.offlineCPU(currTime, currTime - gSessionData->mMonotonicStarted, cpu);
415 return result;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100416 }
417 }
418
419 return true;
420}
421
422void PerfSource::interrupt() {
423 if (mInterruptFd >= 0) {
424 int8_t c = 0;
425 // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread
426 if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) {
427 logg->logError(__FILE__, __LINE__, "write failed");
428 handleException();
429 }
430 }
431}
432
433bool PerfSource::isDone () {
434 return mBuffer.isDone() && mIsDone && mCountersBuf.isEmpty();
435}
436
437void PerfSource::write (Sender *sender) {
438 if (!mSummary.isDone()) {
439 mSummary.write(sender);
Jon Medhurste31266f2014-08-04 15:47:44 +0100440 gSessionData->mSentSummary = true;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100441 }
442 if (!mBuffer.isDone()) {
443 mBuffer.write(sender);
444 }
445 if (!mCountersBuf.send(sender)) {
446 logg->logError(__FILE__, __LINE__, "PerfBuffer::send failed");
447 handleException();
448 }
449}