gator: Version 5.19
[firefly-linux-kernel-4.4.55.git] / tools / gator / daemon / PerfSource.cpp
1 /**
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
11 #include <errno.h>
12 #include <string.h>
13 #include <unistd.h>
14
15 #include "Child.h"
16 #include "DynBuf.h"
17 #include "Logging.h"
18 #include "PerfDriver.h"
19 #include "Proc.h"
20 #include "SessionData.h"
21
22 #define MS_PER_US 1000000
23
24 extern Child *child;
25
26 static bool sendTracepointFormat(Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b) {
27         if (!printb->printf(EVENTS_PATH "/%s/format", name)) {
28                 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
29                 return false;
30         }
31         if (!b->read(printb->getBuf())) {
32                 logg->logMessage("%s(%s:%i): DynBuf::read failed", __FUNCTION__, __FILE__, __LINE__);
33                 return false;
34         }
35         buffer->format(b->getLength(), b->getBuf());
36
37         return true;
38 }
39
40 PerfSource::PerfSource(sem_t *senderSem, sem_t *startProfile) : mSummary(0, FRAME_SUMMARY, 1024, senderSem), mBuffer(0, FRAME_PERF_ATTRS, 4*1024*1024, senderSem), mCountersBuf(), mCountersGroup(&mCountersBuf), mMonitor(), mUEvent(), mSenderSem(senderSem), mStartProfile(startProfile), mInterruptFd(-1), mIsDone(false) {
41         long l = sysconf(_SC_PAGE_SIZE);
42         if (l < 0) {
43                 logg->logError(__FILE__, __LINE__, "Unable to obtain the page size");
44                 handleException();
45         }
46         gSessionData->mPageSize = static_cast<int>(l);
47
48         l = sysconf(_SC_NPROCESSORS_CONF);
49         if (l < 0) {
50                 logg->logError(__FILE__, __LINE__, "Unable to obtain the number of cores");
51                 handleException();
52         }
53         gSessionData->mCores = static_cast<int>(l);
54 }
55
56 PerfSource::~PerfSource() {
57 }
58
59 struct PrepareParallelArgs {
60         PerfGroup *pg;
61         int cpu;
62 };
63
64 void *prepareParallel(void *arg) {
65         const PrepareParallelArgs *const args = (PrepareParallelArgs *)arg;
66         args->pg->prepareCPU(args->cpu);
67         return NULL;
68 }
69
70 bool PerfSource::prepare() {
71         DynBuf printb;
72         DynBuf b1;
73         DynBuf b2;
74         DynBuf b3;
75         long long schedSwitchId;
76
77         // Reread cpuinfo since cores may have changed since startup
78         gSessionData->readCpuInfo();
79
80         if (0
81                         || !mMonitor.init()
82                         || !mUEvent.init()
83                         || !mMonitor.add(mUEvent.getFd())
84
85                         || (schedSwitchId = PerfDriver::getTracepointId(SCHED_SWITCH, &printb)) < 0
86                         || !sendTracepointFormat(&mBuffer, SCHED_SWITCH, &printb, &b1)
87
88                         // Only want RAW but not IP on sched_switch and don't want TID on SAMPLE_ID
89                         || !mCountersGroup.add(&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)
90
91                         // Only want TID and IP but not RAW on timer
92                         || (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && !mCountersGroup.add(&mBuffer, 99/**/, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP, PERF_GROUP_PER_CPU))
93
94                         || !gSessionData->perf.enable(&mCountersGroup, &mBuffer)
95                         || 0) {
96                 logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.4 or later?", __FUNCTION__, __FILE__, __LINE__);
97                 return false;
98         }
99
100         if (!gSessionData->perf.summary(&mSummary)) {
101                 logg->logMessage("%s(%s:%i): PerfDriver::summary failed", __FUNCTION__, __FILE__, __LINE__);
102                 return false;
103         }
104
105         {
106                 // Run prepareCPU in parallel as perf_event_open can take more than 1 sec in some cases
107                 pthread_t threads[NR_CPUS];
108                 PrepareParallelArgs args[NR_CPUS];
109                 for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
110                         args[cpu].pg = &mCountersGroup;
111                         args[cpu].cpu = cpu;
112                         if (pthread_create(&threads[cpu], NULL, prepareParallel, &args[cpu]) != 0) {
113                                 logg->logMessage("%s(%s:%i): pthread_create failed", __FUNCTION__, __FILE__, __LINE__);
114                                 return false;
115                         }
116                 }
117                 for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
118                         if (pthread_join(threads[cpu], NULL) != 0) {
119                                 logg->logMessage("%s(%s:%i): pthread_join failed", __FUNCTION__, __FILE__, __LINE__);
120                                 return false;
121                         }
122                 }
123         }
124
125         int numEvents = 0;
126         for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
127                 numEvents += mCountersGroup.onlineCPU(cpu, false, &mBuffer, &mMonitor);
128         }
129         if (numEvents <= 0) {
130                 logg->logMessage("%s(%s:%i): PerfGroup::onlineCPU failed on all cores", __FUNCTION__, __FILE__, __LINE__);
131                 return false;
132         }
133
134         // Start events before reading proc to avoid race conditions
135         if (!mCountersGroup.start()) {
136                 logg->logMessage("%s(%s:%i): PerfGroup::start failed", __FUNCTION__, __FILE__, __LINE__);
137                 return false;
138         }
139
140         if (!readProc(&mBuffer, true, &printb, &b1, &b2, &b3)) {
141                 logg->logMessage("%s(%s:%i): readProc failed", __FUNCTION__, __FILE__, __LINE__);
142                 return false;
143         }
144
145         mBuffer.commit(1);
146
147         return true;
148 }
149
150 static const char CPU_DEVPATH[] = "/devices/system/cpu/cpu";
151
152 void PerfSource::run() {
153         int pipefd[2];
154
155         if (pipe(pipefd) != 0) {
156                 logg->logError(__FILE__, __LINE__, "pipe failed");
157                 handleException();
158         }
159         mInterruptFd = pipefd[1];
160
161         if (!mMonitor.add(pipefd[0])) {
162                 logg->logError(__FILE__, __LINE__, "Monitor::add failed");
163                 handleException();
164         }
165
166         int timeout = -1;
167         if (gSessionData->mLiveRate > 0) {
168                 timeout = gSessionData->mLiveRate/MS_PER_US;
169         }
170
171         sem_post(mStartProfile);
172
173         while (gSessionData->mSessionIsActive) {
174                 // +1 for uevents, +1 for pipe
175                 struct epoll_event events[NR_CPUS + 2];
176                 int ready = mMonitor.wait(events, ARRAY_LENGTH(events), timeout);
177                 if (ready < 0) {
178                         logg->logError(__FILE__, __LINE__, "Monitor::wait failed");
179                         handleException();
180                 }
181
182                 for (int i = 0; i < ready; ++i) {
183                         if (events[i].data.fd == mUEvent.getFd()) {
184                                 if (!handleUEvent()) {
185                                         logg->logError(__FILE__, __LINE__, "PerfSource::handleUEvent failed");
186                                         handleException();
187                                 }
188                                 break;
189                         }
190                 }
191
192                 // send a notification that data is ready
193                 sem_post(mSenderSem);
194
195                 // In one shot mode, stop collection once all the buffers are filled
196                 // Assume timeout == 0 in this case
197                 if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
198                         logg->logMessage("%s(%s:%i): One shot", __FUNCTION__, __FILE__, __LINE__);
199                         child->endSession();
200                 }
201         }
202
203         mCountersGroup.stop();
204         mBuffer.setDone();
205         mIsDone = true;
206
207         // send a notification that data is ready
208         sem_post(mSenderSem);
209
210         mInterruptFd = -1;
211         close(pipefd[0]);
212         close(pipefd[1]);
213 }
214
215 bool PerfSource::handleUEvent() {
216         UEventResult result;
217         if (!mUEvent.read(&result)) {
218                 logg->logMessage("%s(%s:%i): UEvent::Read failed", __FUNCTION__, __FILE__, __LINE__);
219                 return false;
220         }
221
222         if (strcmp(result.mSubsystem, "cpu") == 0) {
223                 if (strncmp(result.mDevPath, CPU_DEVPATH, sizeof(CPU_DEVPATH) - 1) != 0) {
224                         logg->logMessage("%s(%s:%i): Unexpected cpu DEVPATH format", __FUNCTION__, __FILE__, __LINE__);
225                         return false;
226                 }
227                 char *endptr;
228                 errno = 0;
229                 int cpu = strtol(result.mDevPath + sizeof(CPU_DEVPATH) - 1, &endptr, 10);
230                 if (errno != 0 || *endptr != '\0') {
231                         logg->logMessage("%s(%s:%i): strtol failed", __FUNCTION__, __FILE__, __LINE__);
232                         return false;
233                 }
234                 if (strcmp(result.mAction, "online") == 0) {
235                         // Only call onlineCPU if prepareCPU succeeded
236                         const bool result = mCountersGroup.prepareCPU(cpu) &&
237                                 mCountersGroup.onlineCPU(cpu, true, &mBuffer, &mMonitor);
238                         mBuffer.commit(1);
239                         return result;
240                 } else if (strcmp(result.mAction, "offline") == 0) {
241                         return mCountersGroup.offlineCPU(cpu);
242                 }
243         }
244
245         return true;
246 }
247
248 void PerfSource::interrupt() {
249         if (mInterruptFd >= 0) {
250                 int8_t c = 0;
251                 // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread
252                 if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) {
253                         logg->logError(__FILE__, __LINE__, "write failed");
254                         handleException();
255                 }
256         }
257 }
258
259 bool PerfSource::isDone () {
260         return mBuffer.isDone() && mIsDone && mCountersBuf.isEmpty();
261 }
262
263 void PerfSource::write (Sender *sender) {
264         if (!mSummary.isDone()) {
265                 mSummary.write(sender);
266                 gSessionData->mSentSummary = true;
267         }
268         if (!mBuffer.isDone()) {
269                 mBuffer.write(sender);
270         }
271         if (!mCountersBuf.send(sender)) {
272                 logg->logError(__FILE__, __LINE__, "PerfBuffer::send failed");
273                 handleException();
274         }
275 }