gator: Version 5.21.1
[firefly-linux-kernel-4.4.55.git] / tools / gator / daemon / PerfDriver.cpp
1 /**
2  * Copyright (C) ARM Limited 2013-2015. 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 "PerfDriver.h"
10
11 #include <dirent.h>
12 #include <sys/utsname.h>
13 #include <time.h>
14 #include <unistd.h>
15
16 #include "Buffer.h"
17 #include "Config.h"
18 #include "ConfigurationXML.h"
19 #include "Counter.h"
20 #include "DriverSource.h"
21 #include "DynBuf.h"
22 #include "Logging.h"
23 #include "PerfGroup.h"
24 #include "SessionData.h"
25 #include "Setup.h"
26
27 #define PERF_DEVICES "/sys/bus/event_source/devices"
28
29 #define TYPE_DERIVED ~0U
30
31 // From gator.h
32 struct gator_cpu {
33         const int cpuid;
34         // Human readable name
35         const char *const core_name;
36         // gatorfs event and Perf PMU name
37         const char *const pmnc_name;
38         const int pmnc_counters;
39 };
40
41 // From gator_main.c
42 static const struct gator_cpu gator_cpus[] = {
43         { 0x41b36, "ARM1136",      "ARM_ARM11",        3 },
44         { 0x41b56, "ARM1156",      "ARM_ARM11",        3 },
45         { 0x41b76, "ARM1176",      "ARM_ARM11",        3 },
46         { 0x41b02, "ARM11MPCore",  "ARM_ARM11MPCore",  3 },
47         { 0x41c05, "Cortex-A5",    "ARMv7_Cortex_A5",  2 },
48         { 0x41c07, "Cortex-A7",    "ARMv7_Cortex_A7",  4 },
49         { 0x41c08, "Cortex-A8",    "ARMv7_Cortex_A8",  4 },
50         { 0x41c09, "Cortex-A9",    "ARMv7_Cortex_A9",  6 },
51         { 0x41c0f, "Cortex-A15",   "ARMv7_Cortex_A15", 6 },
52         { 0x41c0d, "Cortex-A17",   "ARMv7_Cortex_A17", 6 },
53         { 0x41c0e, "Cortex-A17",   "ARMv7_Cortex_A17", 6 },
54         { 0x5100f, "Scorpion",     "Scorpion",         4 },
55         { 0x5102d, "ScorpionMP",   "ScorpionMP",       4 },
56         { 0x51049, "KraitSIM",     "Krait",            4 },
57         { 0x5104d, "Krait",        "Krait",            4 },
58         { 0x5106f, "Krait S4 Pro", "Krait",            4 },
59         { 0x41d03, "Cortex-A53",   "ARM_Cortex-A53",   6 },
60         { 0x41d07, "Cortex-A57",   "ARM_Cortex-A57",   6 },
61         { 0x41d08, "Cortex-A72",   "ARM_Cortex-A72",   6 },
62 };
63
64 static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-";
65 static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_";
66
67 struct uncore_counter {
68         // Perf PMU name
69         const char *const perfName;
70         // gatorfs event name
71         const char *const gatorName;
72         const int count;
73         const bool hasCyclesCounter;
74 };
75
76 static const struct uncore_counter uncore_counters[] = {
77         { "CCI_400",    "CCI_400",     4, true },
78         { "CCI_400-r1", "CCI_400-r1",  4, true },
79         { "CCI_500",    "CCI_500",     8, false },
80         { "ccn",        "ARM_CCN_5XX", 8, true },
81 };
82
83 class PerfCounter : public DriverCounter {
84 public:
85         PerfCounter(DriverCounter *next, const char *name, uint32_t type, uint64_t config, uint64_t sampleType, uint64_t flags) : DriverCounter(next, name), mType(type), mConfig(config), mSampleType(sampleType), mFlags(flags), mCount(0) {}
86
87         ~PerfCounter() {
88         }
89
90         uint32_t getType() const { return mType; }
91         int getCount() const { return mCount; }
92         void setCount(const int count) { mCount = count; }
93         uint64_t getConfig() const { return mConfig; }
94         void setConfig(const uint64_t config) { mConfig = config; }
95         uint64_t getSampleType() const { return mSampleType; }
96         uint64_t getFlags() const { return mFlags; }
97         virtual void read(Buffer *const, const int) {}
98
99 private:
100         const uint32_t mType;
101         uint64_t mConfig;
102         const uint64_t mSampleType;
103         const uint64_t mFlags;
104         int mCount;
105
106         // Intentionally undefined
107         PerfCounter(const PerfCounter &);
108         PerfCounter &operator=(const PerfCounter &);
109 };
110
111 class CPUFreqDriver : public PerfCounter {
112 public:
113         CPUFreqDriver(DriverCounter *next, uint64_t id) : PerfCounter(next, "Linux_power_cpu_freq", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_RAW, PERF_GROUP_LEADER | PERF_GROUP_PER_CPU) {}
114
115         void read(Buffer *const buffer, const int cpu) {
116                 char buf[64];
117
118                 snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%i/cpufreq/cpuinfo_cur_freq", cpu);
119                 int64_t freq;
120                 if (DriverSource::readInt64Driver(buf, &freq) != 0) {
121                         freq = 0;
122                 }
123                 buffer->perfCounter(cpu, getKey(), 1000*freq);
124         }
125
126 private:
127         // Intentionally undefined
128         CPUFreqDriver(const CPUFreqDriver &);
129         CPUFreqDriver &operator=(const CPUFreqDriver &);
130 };
131
132 PerfDriver::PerfDriver() : mIsSetup(false), mLegacySupport(false) {
133 }
134
135 PerfDriver::~PerfDriver() {
136 }
137
138 void PerfDriver::addCpuCounters(const char *const counterName, const int type, const int numCounters) {
139         int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1;
140         char *name = new char[len];
141         snprintf(name, len, "%s_ccnt", counterName);
142         setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU));
143
144         for (int j = 0; j < numCounters; ++j) {
145                 len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1;
146                 name = new char[len];
147                 snprintf(name, len, "%s_cnt%d", counterName, j);
148                 setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU));
149         }
150 }
151
152 void PerfDriver::addUncoreCounters(const char *const counterName, const int type, const int numCounters, const bool hasCyclesCounter) {
153         int len;
154         char *name;
155
156         if (hasCyclesCounter) {
157                 len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1;
158                 name = new char[len];
159                 snprintf(name, len, "%s_ccnt", counterName);
160                 setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, 0));
161         }
162
163         for (int j = 0; j < numCounters; ++j) {
164                 len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1;
165                 name = new char[len];
166                 snprintf(name, len, "%s_cnt%d", counterName, j);
167                 setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, 0));
168         }
169 }
170
171 bool PerfDriver::setup() {
172         // Check the kernel version
173         int release[3];
174         if (!getLinuxVersion(release)) {
175                 logg->logMessage("getLinuxVersion failed");
176                 return false;
177         }
178
179         if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 4, 0)) {
180                 logg->logMessage("Unsupported kernel version");
181                 return false;
182         }
183         mLegacySupport = KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 12, 0);
184
185         if (access(EVENTS_PATH, R_OK) != 0) {
186                 logg->logMessage(EVENTS_PATH " does not exist, is CONFIG_TRACING and CONFIG_CONTEXT_SWITCH_TRACER enabled?");
187                 return false;
188         }
189
190         // Add supported PMUs
191         bool foundCpu = false;
192         DIR *dir = opendir(PERF_DEVICES);
193         if (dir == NULL) {
194                 logg->logMessage("opendir failed");
195                 return false;
196         }
197
198         struct dirent *dirent;
199         while ((dirent = readdir(dir)) != NULL) {
200                 for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) {
201                         const struct gator_cpu *const gator_cpu = &gator_cpus[i];
202
203                         // Do the names match exactly?
204                         if (strcasecmp(gator_cpu->pmnc_name, dirent->d_name) != 0 &&
205                             // Do these names match but have the old vs new prefix?
206                             ((strncasecmp(dirent->d_name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) != 0 ||
207                               strncasecmp(gator_cpu->pmnc_name, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) != 0 ||
208                               strcasecmp(dirent->d_name + sizeof(OLD_PMU_PREFIX) - 1, gator_cpu->pmnc_name + sizeof(NEW_PMU_PREFIX) - 1) != 0))) {
209                                 continue;
210                         }
211
212                         int type;
213                         char buf[256];
214                         snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name);
215                         if (DriverSource::readIntDriver(buf, &type) != 0) {
216                                 continue;
217                         }
218
219                         foundCpu = true;
220                         logg->logMessage("Adding cpu counters for %s", gator_cpu->pmnc_name);
221                         addCpuCounters(gator_cpu->pmnc_name, type, gator_cpu->pmnc_counters);
222                 }
223
224                 for (int i = 0; i < ARRAY_LENGTH(uncore_counters); ++i) {
225                         if (strcmp(dirent->d_name, uncore_counters[i].perfName) != 0) {
226                                 continue;
227                         }
228
229                         int type;
230                         char buf[256];
231                         snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name);
232                         if (DriverSource::readIntDriver(buf, &type) != 0) {
233                                 continue;
234                         }
235
236                         logg->logMessage("Adding uncore counters for %s", uncore_counters[i].gatorName);
237                         addUncoreCounters(uncore_counters[i].gatorName, type, uncore_counters[i].count, uncore_counters[i].hasCyclesCounter);
238                 }
239         }
240         closedir(dir);
241
242         if (!foundCpu) {
243                 // If no cpu was found based on pmu names, try by cpuid
244                 for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) {
245                         if (gSessionData->mMaxCpuId != gator_cpus[i].cpuid) {
246                                 continue;
247                         }
248
249                         foundCpu = true;
250                         logg->logMessage("Adding cpu counters (based on cpuid) for %s", gator_cpus[i].pmnc_name);
251                         addCpuCounters(gator_cpus[i].pmnc_name, PERF_TYPE_RAW, gator_cpus[i].pmnc_counters);
252                 }
253         }
254
255         if (!foundCpu) {
256                 // If all else fails, use the ARM architected counters
257                 logg->logMessage("Using Other cpu");
258                 addCpuCounters("Other", PERF_TYPE_RAW, 6);
259         }
260
261         // Add supported software counters
262         long long id;
263         DynBuf printb;
264
265         id = getTracepointId("irq/softirq_exit", &printb);
266         if (id >= 0) {
267                 setCounters(new PerfCounter(getCounters(), "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU));
268         }
269
270         id = getTracepointId("irq/irq_handler_exit", &printb);
271         if (id >= 0) {
272                 setCounters(new PerfCounter(getCounters(), "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU));
273         }
274
275         id = getTracepointId(SCHED_SWITCH, &printb);
276         if (id >= 0) {
277                 setCounters(new PerfCounter(getCounters(), "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU));
278         }
279
280         id = getTracepointId(CPU_FREQUENCY, &printb);
281         if (id >= 0) {
282                 setCounters(new CPUFreqDriver(getCounters(), id));
283         }
284
285         setCounters(new PerfCounter(getCounters(), "Linux_cpu_wait_contention", TYPE_DERIVED, -1, 0, 0));
286
287         //Linux_cpu_wait_io
288
289         mIsSetup = true;
290         return true;
291 }
292
293 bool PerfDriver::summary(Buffer *const buffer) {
294         struct utsname utsname;
295         if (uname(&utsname) != 0) {
296                 logg->logMessage("uname failed");
297                 return false;
298         }
299
300         char buf[512];
301         snprintf(buf, sizeof(buf), "%s %s %s %s %s GNU/Linux", utsname.sysname, utsname.nodename, utsname.release, utsname.version, utsname.machine);
302
303         struct timespec ts;
304         if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
305                 logg->logMessage("clock_gettime failed");
306                 return false;
307         }
308         const int64_t timestamp = (int64_t)ts.tv_sec * NS_PER_S + ts.tv_nsec;
309
310         const uint64_t monotonicStarted = getTime();
311         gSessionData->mMonotonicStarted = monotonicStarted;
312         const uint64_t currTime = 0;//getTime() - gSessionData->mMonotonicStarted;
313
314         buffer->summary(currTime, timestamp, monotonicStarted, monotonicStarted, buf);
315
316         for (int i = 0; i < gSessionData->mCores; ++i) {
317                 coreName(currTime, buffer, i);
318         }
319         buffer->commit(currTime);
320
321         return true;
322 }
323
324 void PerfDriver::coreName(const uint64_t currTime, Buffer *const buffer, const int cpu) {
325         // Don't send information on a cpu we know nothing about
326         if (gSessionData->mCpuIds[cpu] == -1) {
327                 return;
328         }
329
330         int j;
331         for (j = 0; j < ARRAY_LENGTH(gator_cpus); ++j) {
332                 if (gator_cpus[j].cpuid == gSessionData->mCpuIds[cpu]) {
333                         break;
334                 }
335         }
336         if (j < ARRAY_LENGTH(gator_cpus) && gator_cpus[j].cpuid == gSessionData->mCpuIds[cpu]) {
337                 buffer->coreName(currTime, cpu, gSessionData->mCpuIds[cpu], gator_cpus[j].core_name);
338         } else {
339                 char buf[32];
340                 if (gSessionData->mCpuIds[cpu] == -1) {
341                         snprintf(buf, sizeof(buf), "Unknown");
342                 } else {
343                         snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[cpu]);
344                 }
345                 buffer->coreName(currTime, cpu, gSessionData->mCpuIds[cpu], buf);
346         }
347 }
348
349 void PerfDriver::setupCounter(Counter &counter) {
350         PerfCounter *const perfCounter = static_cast<PerfCounter *>(findCounter(counter));
351         if (perfCounter == NULL) {
352                 counter.setEnabled(false);
353                 return;
354         }
355
356         // Don't use the config from counters XML if it's not set, ex: software counters
357         if (counter.getEvent() != -1) {
358                 perfCounter->setConfig(counter.getEvent());
359         }
360         perfCounter->setCount(counter.getCount());
361         perfCounter->setEnabled(true);
362         counter.setKey(perfCounter->getKey());
363 }
364
365 bool PerfDriver::enable(const uint64_t currTime, PerfGroup *const group, Buffer *const buffer) const {
366         for (PerfCounter *counter = static_cast<PerfCounter *>(getCounters()); counter != NULL; counter = static_cast<PerfCounter *>(counter->getNext())) {
367                 if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED)) {
368                         int count = counter->getCount();
369                         uint64_t sampleType = counter->getSampleType();
370                         if (sampleType & PERF_SAMPLE_RAW) {
371                                 // If raw is enabled, every sample is needed
372                                 count = 1;
373                         }
374                         if (!group->add(currTime, buffer, counter->getKey(), counter->getType(), counter->getConfig(), count,
375                                         // use getCount instead of count as EBS counters need TID and IP but RAW tracepoints don't
376                                         (counter->getCount() > 0 ? PERF_SAMPLE_TID | PERF_SAMPLE_IP : 0) | sampleType,
377                                         counter->getFlags())) {
378                                 logg->logMessage("PerfGroup::add failed");
379                                 return false;
380                         }
381                 }
382         }
383
384         return true;
385 }
386
387 void PerfDriver::read(Buffer *const buffer, const int cpu) {
388         for (PerfCounter *counter = static_cast<PerfCounter *>(getCounters()); counter != NULL; counter = static_cast<PerfCounter *>(counter->getNext())) {
389                 if (!counter->isEnabled()) {
390                         continue;
391                 }
392                 counter->read(buffer, cpu);
393         }
394 }
395
396 long long PerfDriver::getTracepointId(const char *const name, DynBuf *const printb) {
397         if (!printb->printf(EVENTS_PATH "/%s/id", name)) {
398                 logg->logMessage("DynBuf::printf failed");
399                 return -1;
400         }
401
402         int64_t result;
403         if (DriverSource::readInt64Driver(printb->getBuf(), &result) != 0) {
404                 logg->logMessage("DriverSource::readInt64Driver failed");
405                 return -1;
406         }
407
408         return result;
409 }