gator: Version 5.20
[firefly-linux-kernel-4.4.55.git] / tools / gator / daemon / PerfGroup.cpp
1 /**
2  * Copyright (C) ARM Limited 2013-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 "PerfGroup.h"
10
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <string.h>
14 #include <sys/ioctl.h>
15 #include <sys/syscall.h>
16 #include <unistd.h>
17
18 #include "Buffer.h"
19 #include "Logging.h"
20 #include "Monitor.h"
21 #include "PerfBuffer.h"
22 #include "SessionData.h"
23
24 #define DEFAULT_PEA_ARGS(pea, additionalSampleType) \
25         pea.size = sizeof(pea); \
26         /* Emit time, read_format below, group leader id, and raw tracepoint info */ \
27         pea.sample_type = (gSessionData->perf.getLegacySupport() \
28                                                                                  ? PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_ID \
29                                                                                  : PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_IDENTIFIER ) | additionalSampleType; \
30         /* Emit emit value in group format */ \
31         pea.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; \
32         /* start out disabled */ \
33         pea.disabled = 1; \
34         /* have a sampling interrupt happen when we cross the wakeup_watermark boundary */ \
35         pea.watermark = 1; \
36         /* Be conservative in flush size as only one buffer set is monitored */ \
37         pea.wakeup_watermark = BUF_SIZE / 2
38
39 static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t pid, const int cpu, const int group_fd, const unsigned long flags) {
40         int fd = syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
41         if (fd < 0) {
42                 return -1;
43         }
44         int fdf = fcntl(fd, F_GETFD);
45         if ((fdf == -1) || (fcntl(fd, F_SETFD, fdf | FD_CLOEXEC) != 0)) {
46                 close(fd);
47                 return -1;
48         }
49         return fd;
50 }
51
52 PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb) {
53         memset(&mAttrs, 0, sizeof(mAttrs));
54         memset(&mPerCpu, 0, sizeof(mPerCpu));
55         memset(&mKeys, -1, sizeof(mKeys));
56         memset(&mFds, -1, sizeof(mFds));
57 }
58
59 PerfGroup::~PerfGroup() {
60         for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) {
61                 if (mFds[pos] >= 0) {
62                         close(mFds[pos]);
63                 }
64         }
65 }
66
67 bool PerfGroup::add(const uint64_t currTime, Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) {
68         int i;
69         for (i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
70                 if (mKeys[i] < 0) {
71                         break;
72                 }
73         }
74
75         if (i >= ARRAY_LENGTH(mKeys)) {
76                 logg->logMessage("%s(%s:%i): Too many counters", __FUNCTION__, __FILE__, __LINE__);
77                 return false;
78         }
79
80         DEFAULT_PEA_ARGS(mAttrs[i], sampleType);
81         mAttrs[i].type = type;
82         mAttrs[i].config = config;
83         mAttrs[i].sample_period = sample;
84         // always be on the CPU but only a group leader can be pinned
85         mAttrs[i].pinned = (i == 0 ? 1 : 0);
86         mAttrs[i].mmap = (flags & PERF_GROUP_MMAP ? 1 : 0);
87         mAttrs[i].comm = (flags & PERF_GROUP_COMM ? 1 : 0);
88         mAttrs[i].freq = (flags & PERF_GROUP_FREQ ? 1 : 0);
89         mAttrs[i].task = (flags & PERF_GROUP_TASK ? 1 : 0);
90         mAttrs[i].sample_id_all = (flags & PERF_GROUP_SAMPLE_ID_ALL ? 1 : 0);
91         mPerCpu[i] = (flags & PERF_GROUP_PER_CPU);
92
93         mKeys[i] = key;
94
95         buffer->pea(currTime, &mAttrs[i], key);
96
97         return true;
98 }
99
100 int PerfGroup::prepareCPU(const int cpu, Monitor *const monitor) {
101         logg->logMessage("%s(%s:%i): Onlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
102
103         for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
104                 if (mKeys[i] < 0) {
105                         continue;
106                 }
107
108                 if ((cpu != 0) && !mPerCpu[i]) {
109                         continue;
110                 }
111
112                 const int offset = i * gSessionData->mCores;
113                 if (mFds[cpu + offset] >= 0) {
114                         logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__);
115                         return PG_FAILURE;
116                 }
117
118                 logg->logMessage("%s(%s:%i): perf_event_open cpu: %i type: %lli config: %lli sample: %lli sample_type: 0x%llx pinned: %i mmap: %i comm: %i freq: %i task: %i sample_id_all: %i", __FUNCTION__, __FILE__, __LINE__, cpu, (long long)mAttrs[i].type, (long long)mAttrs[i].config, (long long)mAttrs[i].sample_period, (long long)mAttrs[i].sample_type, mAttrs[i].pinned, mAttrs[i].mmap, mAttrs[i].comm, mAttrs[i].freq, mAttrs[i].task, mAttrs[i].sample_id_all);
119                 mFds[cpu + offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, i == 0 ? -1 : mFds[cpu], i == 0 ? 0 : PERF_FLAG_FD_OUTPUT);
120                 if (mFds[cpu + offset] < 0) {
121                         logg->logMessage("%s(%s:%i): failed %s", __FUNCTION__, __FILE__, __LINE__, strerror(errno));
122                         if (errno == ENODEV) {
123                                 return PG_CPU_OFFLINE;
124                         }
125                         continue;
126                 }
127
128                 if (!mPb->useFd(cpu, mFds[cpu + offset])) {
129                         logg->logMessage("%s(%s:%i): PerfBuffer::useFd failed", __FUNCTION__, __FILE__, __LINE__);
130                         return PG_FAILURE;
131                 }
132
133
134                 if (!monitor->add(mFds[cpu + offset])) {
135                   logg->logMessage("%s(%s:%i): Monitor::add failed", __FUNCTION__, __FILE__, __LINE__);
136                   return PG_FAILURE;
137                 }
138         }
139
140         return PG_SUCCESS;
141 }
142
143 int PerfGroup::onlineCPU(const uint64_t currTime, const int cpu, const bool start, Buffer *const buffer) {
144         __u64 ids[ARRAY_LENGTH(mKeys)];
145         int coreKeys[ARRAY_LENGTH(mKeys)];
146         int idCount = 0;
147
148         for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
149                 const int fd = mFds[cpu + i * gSessionData->mCores];
150                 if (fd < 0) {
151                         continue;
152                 }
153
154                 coreKeys[idCount] = mKeys[i];
155                 if (!gSessionData->perf.getLegacySupport() && ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0 &&
156                                 // Workaround for running 32-bit gatord on 64-bit systems, kernel patch in the works
157                                 ioctl(fd, (PERF_EVENT_IOC_ID & ~IOCSIZE_MASK) | (8 << _IOC_SIZESHIFT), &ids[idCount]) != 0) {
158                         logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
159                         return 0;
160                 }
161                 ++idCount;
162         }
163
164         if (!gSessionData->perf.getLegacySupport()) {
165                 buffer->keys(currTime, idCount, ids, coreKeys);
166         } else {
167                 char buf[1024];
168                 ssize_t bytes = read(mFds[cpu], buf, sizeof(buf));
169                 if (bytes < 0) {
170                         logg->logMessage("read failed");
171                         return 0;
172                 }
173                 buffer->keysOld(currTime, idCount, coreKeys, bytes, buf);
174         }
175
176         if (start) {
177                 for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
178                         int offset = i * gSessionData->mCores + cpu;
179                         if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_ENABLE, 0) < 0) {
180                                 logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
181                                 return 0;
182                         }
183                 }
184         }
185
186         if (idCount == 0) {
187                 logg->logMessage("%s(%s:%i): no events came online", __FUNCTION__, __FILE__, __LINE__);
188         }
189
190         return idCount;
191 }
192
193 bool PerfGroup::offlineCPU(const int cpu) {
194         logg->logMessage("%s(%s:%i): Offlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
195
196         for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
197                 int offset = i * gSessionData->mCores + cpu;
198                 if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_DISABLE, 0) < 0) {
199                         logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
200                         return false;
201                 }
202         }
203
204         // Mark the buffer so that it will be released next time it's read
205         mPb->discard(cpu);
206
207         for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
208                 if (mKeys[i] < 0) {
209                         continue;
210                 }
211
212                 int offset = i * gSessionData->mCores + cpu;
213                 if (mFds[offset] >= 0) {
214                         close(mFds[offset]);
215                         mFds[offset] = -1;
216                 }
217         }
218
219         return true;
220 }
221
222 bool PerfGroup::start() {
223         for (int pos = 0; pos < ARRAY_LENGTH(mFds); ++pos) {
224                 if (mFds[pos] >= 0 && ioctl(mFds[pos], PERF_EVENT_IOC_ENABLE, 0) < 0) {
225                         logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
226                         goto fail;
227                 }
228         }
229
230         return true;
231
232  fail:
233         stop();
234
235         return false;
236 }
237
238 void PerfGroup::stop() {
239         for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) {
240                 if (mFds[pos] >= 0) {
241                         ioctl(mFds[pos], PERF_EVENT_IOC_DISABLE, 0);
242                 }
243         }
244 }