gator: Version 5.18
[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 <string.h>
13 #include <sys/ioctl.h>
14 #include <sys/syscall.h>
15 #include <unistd.h>
16
17 #include "Buffer.h"
18 #include "Logging.h"
19 #include "Monitor.h"
20 #include "PerfBuffer.h"
21 #include "SessionData.h"
22
23 #define DEFAULT_PEA_ARGS(pea, additionalSampleType) \
24         pea.size = sizeof(pea); \
25         /* Emit time, read_format below, group leader id, and raw tracepoint info */ \
26         pea.sample_type = PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_IDENTIFIER | additionalSampleType; \
27         /* Emit emit value in group format */ \
28         pea.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; \
29         /* start out disabled */ \
30         pea.disabled = 1; \
31         /* have a sampling interrupt happen when we cross the wakeup_watermark boundary */ \
32         pea.watermark = 1; \
33         /* Be conservative in flush size as only one buffer set is monitored */ \
34         pea.wakeup_watermark = 3 * BUF_SIZE / 4
35
36 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) {
37         return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
38 }
39
40 PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb) {
41         memset(&mAttrs, 0, sizeof(mAttrs));
42         memset(&mKeys, -1, sizeof(mKeys));
43         memset(&mFds, -1, sizeof(mFds));
44 }
45
46 PerfGroup::~PerfGroup() {
47         for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) {
48                 if (mFds[pos] >= 0) {
49                         close(mFds[pos]);
50                 }
51         }
52 }
53
54 bool PerfGroup::add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) {
55         int i;
56         for (i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
57                 if (mKeys[i] < 0) {
58                         break;
59                 }
60         }
61
62         if (i >= ARRAY_LENGTH(mKeys)) {
63                 logg->logMessage("%s(%s:%i): Too many counters", __FUNCTION__, __FILE__, __LINE__);
64                 return false;
65         }
66
67         DEFAULT_PEA_ARGS(mAttrs[i], sampleType);
68         mAttrs[i].type = type;
69         mAttrs[i].config = config;
70         mAttrs[i].sample_period = sample;
71         // always be on the CPU but only a group leader can be pinned
72         mAttrs[i].pinned = (i == 0 ? 1 : 0);
73         mAttrs[i].mmap = (flags & PERF_GROUP_MMAP ? 1 : 0);
74         mAttrs[i].comm = (flags & PERF_GROUP_COMM ? 1 : 0);
75         mAttrs[i].freq = (flags & PERF_GROUP_FREQ ? 1 : 0);
76         mAttrs[i].task = (flags & PERF_GROUP_TASK ? 1 : 0);
77         mAttrs[i].sample_id_all = (flags & PERF_GROUP_SAMPLE_ID_ALL ? 1 : 0);
78
79         mKeys[i] = key;
80
81         buffer->pea(&mAttrs[i], key);
82
83         return true;
84 }
85
86 bool PerfGroup::prepareCPU(const int cpu) {
87         logg->logMessage("%s(%s:%i): Onlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
88
89         for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
90                 if (mKeys[i] < 0) {
91                         continue;
92                 }
93
94                 const int offset = i * gSessionData->mCores;
95                 if (mFds[cpu + offset] >= 0) {
96                         logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__);
97                         return false;
98                 }
99
100                 logg->logMessage("%s(%s:%i): perf_event_open cpu: %i type: %lli config: %lli sample: %lli sample_type: %lli", __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);
101                 mFds[cpu + offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, i == 0 ? -1 : mFds[cpu], i == 0 ? 0 : PERF_FLAG_FD_OUTPUT);
102                 if (mFds[cpu + offset] < 0) {
103                         logg->logMessage("%s(%s:%i): failed %s", __FUNCTION__, __FILE__, __LINE__, strerror(errno));
104                         continue;
105                 }
106
107                 if (!mPb->useFd(cpu, mFds[cpu + offset], mFds[cpu])) {
108                         logg->logMessage("%s(%s:%i): PerfBuffer::useFd failed", __FUNCTION__, __FILE__, __LINE__);
109                         return false;
110                 }
111         }
112
113         return true;
114 }
115
116 int PerfGroup::onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor) {
117         __u64 ids[ARRAY_LENGTH(mKeys)];
118         int coreKeys[ARRAY_LENGTH(mKeys)];
119         int idCount = 0;
120
121         for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
122                 const int fd = mFds[cpu + i * gSessionData->mCores];
123                 if (fd < 0) {
124                         continue;
125                 }
126
127                 coreKeys[idCount] = mKeys[i];
128                 if (ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0) {
129                         logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
130                         return false;
131                 }
132                 ++idCount;
133         }
134
135         if (!monitor->add(mFds[cpu])) {
136                 logg->logMessage("%s(%s:%i): Monitor::add failed", __FUNCTION__, __FILE__, __LINE__);
137                 return false;
138         }
139
140         buffer->keys(idCount, ids, coreKeys);
141
142         if (start) {
143                 for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
144                         int offset = i * gSessionData->mCores + cpu;
145                         if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_ENABLE) < 0) {
146                                 logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
147                                 return false;
148                         }
149                 }
150         }
151
152         return idCount;
153 }
154
155 bool PerfGroup::offlineCPU(const int cpu) {
156         logg->logMessage("%s(%s:%i): Offlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
157
158         for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
159                 int offset = i * gSessionData->mCores + cpu;
160                 if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_DISABLE) < 0) {
161                         logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
162                         return false;
163                 }
164         }
165
166         // Mark the buffer so that it will be released next time it's read
167         mPb->discard(cpu);
168
169         for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
170                 if (mKeys[i] < 0) {
171                         continue;
172                 }
173
174                 int offset = i * gSessionData->mCores + cpu;
175                 if (mFds[offset] >= 0) {
176                         close(mFds[offset]);
177                         mFds[offset] = -1;
178                 }
179         }
180
181         return true;
182 }
183
184 bool PerfGroup::start() {
185         for (int pos = 0; pos < ARRAY_LENGTH(mFds); ++pos) {
186                 if (mFds[pos] >= 0 && ioctl(mFds[pos], PERF_EVENT_IOC_ENABLE) < 0) {
187                         logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
188                         goto fail;
189                 }
190         }
191
192         return true;
193
194  fail:
195         stop();
196
197         return false;
198 }
199
200 void PerfGroup::stop() {
201         for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) {
202                 if (mFds[pos] >= 0) {
203                         ioctl(mFds[pos], PERF_EVENT_IOC_DISABLE);
204                 }
205         }
206 }