b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 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 | } |