summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/PerfGroup.cpp')
-rw-r--r--daemon/PerfGroup.cpp206
1 files changed, 206 insertions, 0 deletions
diff --git a/daemon/PerfGroup.cpp b/daemon/PerfGroup.cpp
new file mode 100644
index 0000000..faf5fca
--- /dev/null
+++ b/daemon/PerfGroup.cpp
@@ -0,0 +1,206 @@
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
36static 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
40PerfGroup::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
46PerfGroup::~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
54bool 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
86bool 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
116int 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
155bool 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
184bool 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
200void 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}