blob: ecfaa66832bd783995086ea8fdb91a2ab9de9fc6 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/**
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
24extern Child *child;
25
26static 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
40PerfSource::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
56PerfSource::~PerfSource() {
57}
58
59struct PrepareParallelArgs {
60 PerfGroup *pg;
61 int cpu;
62};
63
64void *prepareParallel(void *arg) {
65 const PrepareParallelArgs *const args = (PrepareParallelArgs *)arg;
66 args->pg->prepareCPU(args->cpu);
67 return NULL;
68}
69
70bool 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
150static const char CPU_DEVPATH[] = "/devices/system/cpu/cpu";
151
152void 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
215bool 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
248void 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
259bool PerfSource::isDone () {
260 return mBuffer.isDone() && mIsDone && mCountersBuf.isEmpty();
261}
262
263void 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}