diff options
author | Jon Medhurst | 2014-04-09 06:39:14 -0500 |
---|---|---|
committer | Jon Medhurst | 2014-04-09 06:40:23 -0500 |
commit | 0f66ada81c8f90a7b2e4f7570032e33aaedfb32f (patch) | |
tree | c686b9fe25f272834766eba38b51cc7a97c69786 /daemon | |
parent | 33bef9ed7feca41e7cd6de8bf5d80052669278d3 (diff) | |
download | arm-ds5-gator-0f66ada81c8f90a7b2e4f7570032e33aaedfb32f.tar.gz arm-ds5-gator-0f66ada81c8f90a7b2e4f7570032e33aaedfb32f.tar.xz arm-ds5-gator-0f66ada81c8f90a7b2e4f7570032e33aaedfb32f.zip |
gator: Version 5.18DS-5.18
Signed-off-by: Jon Medhurst <tixy@linaro.org>
Diffstat (limited to 'daemon')
78 files changed, 3804 insertions, 599 deletions
diff --git a/daemon/Android.mk b/daemon/Android.mk index a042971..045d028 100644 --- a/daemon/Android.mk +++ b/daemon/Android.mk | |||
@@ -1,7 +1,7 @@ | |||
1 | LOCAL_PATH := $(call my-dir) | 1 | LOCAL_PATH := $(call my-dir) |
2 | include $(CLEAR_VARS) | 2 | include $(CLEAR_VARS) |
3 | 3 | ||
4 | XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h configuration_xml.h) | 4 | XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h defaults_xml.h) |
5 | 5 | ||
6 | LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors | 6 | LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors |
7 | 7 | ||
@@ -9,22 +9,33 @@ LOCAL_SRC_FILES := \ | |||
9 | Buffer.cpp \ | 9 | Buffer.cpp \ |
10 | CapturedXML.cpp \ | 10 | CapturedXML.cpp \ |
11 | Child.cpp \ | 11 | Child.cpp \ |
12 | Collector.cpp \ | ||
13 | ConfigurationXML.cpp \ | 12 | ConfigurationXML.cpp \ |
14 | Driver.cpp \ | 13 | Driver.cpp \ |
14 | DriverSource.cpp \ | ||
15 | DynBuf.cpp \ | ||
15 | EventsXML.cpp \ | 16 | EventsXML.cpp \ |
17 | ExternalSource.cpp \ | ||
16 | Fifo.cpp \ | 18 | Fifo.cpp \ |
17 | Hwmon.cpp \ | 19 | Hwmon.cpp \ |
18 | KMod.cpp \ | 20 | KMod.cpp \ |
19 | LocalCapture.cpp \ | 21 | LocalCapture.cpp \ |
20 | Logging.cpp \ | 22 | Logging.cpp \ |
21 | main.cpp \ | 23 | main.cpp \ |
24 | Monitor.cpp \ | ||
22 | OlySocket.cpp \ | 25 | OlySocket.cpp \ |
23 | OlyUtility.cpp \ | 26 | OlyUtility.cpp \ |
27 | PerfBuffer.cpp \ | ||
28 | PerfDriver.cpp \ | ||
29 | PerfGroup.cpp \ | ||
30 | PerfSource.cpp \ | ||
31 | Proc.cpp \ | ||
24 | Sender.cpp \ | 32 | Sender.cpp \ |
25 | SessionData.cpp \ | 33 | SessionData.cpp \ |
26 | SessionXML.cpp \ | 34 | SessionXML.cpp \ |
35 | Source.cpp \ | ||
27 | StreamlineSetup.cpp \ | 36 | StreamlineSetup.cpp \ |
37 | UEvent.cpp \ | ||
38 | UserSpaceSource.cpp \ | ||
28 | libsensors/access.c \ | 39 | libsensors/access.c \ |
29 | libsensors/conf-lex.c \ | 40 | libsensors/conf-lex.c \ |
30 | libsensors/conf-parse.c \ | 41 | libsensors/conf-parse.c \ |
diff --git a/daemon/Buffer.cpp b/daemon/Buffer.cpp index 090a715..93557da 100644 --- a/daemon/Buffer.cpp +++ b/daemon/Buffer.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -12,33 +12,60 @@ | |||
12 | #include "Sender.h" | 12 | #include "Sender.h" |
13 | #include "SessionData.h" | 13 | #include "SessionData.h" |
14 | 14 | ||
15 | #define mask (size - 1) | 15 | #define mask (mSize - 1) |
16 | 16 | ||
17 | Buffer::Buffer (const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : core(core), buftype(buftype), size(size), readPos(0), writePos(0), commitPos(0), available(true), done(false), buf(new char[size]), commitTime(gSessionData->mLiveRate), readerSem(readerSem) { | 17 | enum { |
18 | if ((size & mask) != 0) { | 18 | CODE_PEA = 1, |
19 | CODE_KEYS = 2, | ||
20 | CODE_FORMAT = 3, | ||
21 | CODE_MAPS = 4, | ||
22 | CODE_COMM = 5, | ||
23 | }; | ||
24 | |||
25 | // Summary Frame Messages | ||
26 | enum { | ||
27 | MESSAGE_SUMMARY = 1, | ||
28 | MESSAGE_CORE_NAME = 3, | ||
29 | }; | ||
30 | |||
31 | // From gator_marshaling.c | ||
32 | #define NEWLINE_CANARY \ | ||
33 | /* Unix */ \ | ||
34 | "1\n" \ | ||
35 | /* Windows */ \ | ||
36 | "2\r\n" \ | ||
37 | /* Mac OS */ \ | ||
38 | "3\r" \ | ||
39 | /* RISC OS */ \ | ||
40 | "4\n\r" \ | ||
41 | /* Add another character so the length isn't 0x0a bytes */ \ | ||
42 | "5" | ||
43 | |||
44 | Buffer::Buffer(const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : mCore(core), mBufType(buftype), mSize(size), mReadPos(0), mWritePos(0), mCommitPos(0), mAvailable(true), mIsDone(false), mBuf(new char[mSize]), mCommitTime(gSessionData->mLiveRate), mReaderSem(readerSem) { | ||
45 | if ((mSize & mask) != 0) { | ||
19 | logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2"); | 46 | logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2"); |
20 | handleException(); | 47 | handleException(); |
21 | } | 48 | } |
22 | frame(); | 49 | frame(); |
23 | } | 50 | } |
24 | 51 | ||
25 | Buffer::~Buffer () { | 52 | Buffer::~Buffer() { |
26 | delete [] buf; | 53 | delete [] mBuf; |
27 | } | 54 | } |
28 | 55 | ||
29 | void Buffer::write (Sender * const sender) { | 56 | void Buffer::write(Sender *const sender) { |
30 | if (!commitReady()) { | 57 | if (!commitReady()) { |
31 | return; | 58 | return; |
32 | } | 59 | } |
33 | 60 | ||
34 | // determine the size of two halves | 61 | // determine the size of two halves |
35 | int length1 = commitPos - readPos; | 62 | int length1 = mCommitPos - mReadPos; |
36 | char * buffer1 = buf + readPos; | 63 | char *buffer1 = mBuf + mReadPos; |
37 | int length2 = 0; | 64 | int length2 = 0; |
38 | char * buffer2 = buf; | 65 | char *buffer2 = mBuf; |
39 | if (length1 < 0) { | 66 | if (length1 < 0) { |
40 | length1 = size - readPos; | 67 | length1 = mSize - mReadPos; |
41 | length2 = commitPos; | 68 | length2 = mCommitPos; |
42 | } | 69 | } |
43 | 70 | ||
44 | logg->logMessage("Sending data length1: %i length2: %i", length1, length2); | 71 | logg->logMessage("Sending data length1: %i length2: %i", length1, length2); |
@@ -53,22 +80,22 @@ void Buffer::write (Sender * const sender) { | |||
53 | sender->writeData(buffer2, length2, RESPONSE_APC_DATA); | 80 | sender->writeData(buffer2, length2, RESPONSE_APC_DATA); |
54 | } | 81 | } |
55 | 82 | ||
56 | readPos = commitPos; | 83 | mReadPos = mCommitPos; |
57 | } | 84 | } |
58 | 85 | ||
59 | bool Buffer::commitReady () const { | 86 | bool Buffer::commitReady() const { |
60 | return commitPos != readPos; | 87 | return mCommitPos != mReadPos; |
61 | } | 88 | } |
62 | 89 | ||
63 | int Buffer::bytesAvailable () const { | 90 | int Buffer::bytesAvailable() const { |
64 | int filled = writePos - readPos; | 91 | int filled = mWritePos - mReadPos; |
65 | if (filled < 0) { | 92 | if (filled < 0) { |
66 | filled += size; | 93 | filled += mSize; |
67 | } | 94 | } |
68 | 95 | ||
69 | int remaining = size - filled; | 96 | int remaining = mSize - filled; |
70 | 97 | ||
71 | if (available) { | 98 | if (mAvailable) { |
72 | // Give some extra room; also allows space to insert the overflow error packet | 99 | // Give some extra room; also allows space to insert the overflow error packet |
73 | remaining -= 200; | 100 | remaining -= 200; |
74 | } else { | 101 | } else { |
@@ -79,58 +106,68 @@ int Buffer::bytesAvailable () const { | |||
79 | return remaining; | 106 | return remaining; |
80 | } | 107 | } |
81 | 108 | ||
82 | bool Buffer::checkSpace (const int bytes) { | 109 | bool Buffer::checkSpace(const int bytes) { |
83 | const int remaining = bytesAvailable(); | 110 | const int remaining = bytesAvailable(); |
84 | 111 | ||
85 | if (remaining < bytes) { | 112 | if (remaining < bytes) { |
86 | available = false; | 113 | mAvailable = false; |
87 | } else { | 114 | } else { |
88 | available = true; | 115 | mAvailable = true; |
89 | } | 116 | } |
90 | 117 | ||
91 | return available; | 118 | return mAvailable; |
119 | } | ||
120 | |||
121 | int Buffer::contiguousSpaceAvailable() const { | ||
122 | int remaining = bytesAvailable(); | ||
123 | int contiguous = mSize - mWritePos; | ||
124 | if (remaining < contiguous) { | ||
125 | return remaining; | ||
126 | } else { | ||
127 | return contiguous; | ||
128 | } | ||
92 | } | 129 | } |
93 | 130 | ||
94 | void Buffer::commit (const uint64_t time) { | 131 | void Buffer::commit(const uint64_t time) { |
95 | // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload | 132 | // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload |
96 | const int typeLength = gSessionData->mLocalCapture ? 0 : 1; | 133 | const int typeLength = gSessionData->mLocalCapture ? 0 : 1; |
97 | int length = writePos - commitPos; | 134 | int length = mWritePos - mCommitPos; |
98 | if (length < 0) { | 135 | if (length < 0) { |
99 | length += size; | 136 | length += mSize; |
100 | } | 137 | } |
101 | length = length - typeLength - sizeof(int32_t); | 138 | length = length - typeLength - sizeof(int32_t); |
102 | for (size_t byte = 0; byte < sizeof(int32_t); byte++) { | 139 | for (size_t byte = 0; byte < sizeof(int32_t); byte++) { |
103 | buf[(commitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; | 140 | mBuf[(mCommitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; |
104 | } | 141 | } |
105 | 142 | ||
106 | logg->logMessage("Committing data readPos: %i writePos: %i commitPos: %i", readPos, writePos, commitPos); | 143 | logg->logMessage("Committing data mReadPos: %i mWritePos: %i mCommitPos: %i", mReadPos, mWritePos, mCommitPos); |
107 | commitPos = writePos; | 144 | mCommitPos = mWritePos; |
108 | 145 | ||
109 | if (gSessionData->mLiveRate > 0) { | 146 | if (gSessionData->mLiveRate > 0) { |
110 | while (time > commitTime) { | 147 | while (time > mCommitTime) { |
111 | commitTime += gSessionData->mLiveRate; | 148 | mCommitTime += gSessionData->mLiveRate; |
112 | } | 149 | } |
113 | } | 150 | } |
114 | 151 | ||
115 | if (!done) { | 152 | if (!mIsDone) { |
116 | frame(); | 153 | frame(); |
117 | } | 154 | } |
118 | 155 | ||
119 | // send a notification that data is ready | 156 | // send a notification that data is ready |
120 | sem_post(readerSem); | 157 | sem_post(mReaderSem); |
121 | } | 158 | } |
122 | 159 | ||
123 | void Buffer::check (const uint64_t time) { | 160 | void Buffer::check(const uint64_t time) { |
124 | int filled = writePos - commitPos; | 161 | int filled = mWritePos - mCommitPos; |
125 | if (filled < 0) { | 162 | if (filled < 0) { |
126 | filled += size; | 163 | filled += mSize; |
127 | } | 164 | } |
128 | if (filled >= ((size * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= commitTime)) { | 165 | if (filled >= ((mSize * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= mCommitTime)) { |
129 | commit(time); | 166 | commit(time); |
130 | } | 167 | } |
131 | } | 168 | } |
132 | 169 | ||
133 | void Buffer::packInt (int32_t x) { | 170 | void Buffer::packInt(int32_t x) { |
134 | int packedBytes = 0; | 171 | int packedBytes = 0; |
135 | int more = true; | 172 | int more = true; |
136 | while (more) { | 173 | while (more) { |
@@ -144,14 +181,14 @@ void Buffer::packInt (int32_t x) { | |||
144 | b |= 0x80; | 181 | b |= 0x80; |
145 | } | 182 | } |
146 | 183 | ||
147 | buf[(writePos + packedBytes) & mask] = b; | 184 | mBuf[(mWritePos + packedBytes) & mask] = b; |
148 | packedBytes++; | 185 | packedBytes++; |
149 | } | 186 | } |
150 | 187 | ||
151 | writePos = (writePos + packedBytes) & mask; | 188 | mWritePos = (mWritePos + packedBytes) & mask; |
152 | } | 189 | } |
153 | 190 | ||
154 | void Buffer::packInt64 (int64_t x) { | 191 | void Buffer::packInt64(int64_t x) { |
155 | int packedBytes = 0; | 192 | int packedBytes = 0; |
156 | int more = true; | 193 | int more = true; |
157 | while (more) { | 194 | while (more) { |
@@ -165,24 +202,61 @@ void Buffer::packInt64 (int64_t x) { | |||
165 | b |= 0x80; | 202 | b |= 0x80; |
166 | } | 203 | } |
167 | 204 | ||
168 | buf[(writePos + packedBytes) & mask] = b; | 205 | mBuf[(mWritePos + packedBytes) & mask] = b; |
169 | packedBytes++; | 206 | packedBytes++; |
170 | } | 207 | } |
171 | 208 | ||
172 | writePos = (writePos + packedBytes) & mask; | 209 | mWritePos = (mWritePos + packedBytes) & mask; |
210 | } | ||
211 | |||
212 | void Buffer::writeBytes(const void *const data, size_t count) { | ||
213 | size_t i; | ||
214 | for (i = 0; i < count; ++i) { | ||
215 | mBuf[(mWritePos + i) & mask] = static_cast<const char *>(data)[i]; | ||
216 | } | ||
217 | |||
218 | mWritePos = (mWritePos + i) & mask; | ||
173 | } | 219 | } |
174 | 220 | ||
175 | void Buffer::frame () { | 221 | void Buffer::writeString(const char *const str) { |
222 | const int len = strlen(str); | ||
223 | packInt(len); | ||
224 | writeBytes(str, len); | ||
225 | } | ||
226 | |||
227 | void Buffer::frame() { | ||
176 | if (!gSessionData->mLocalCapture) { | 228 | if (!gSessionData->mLocalCapture) { |
177 | packInt(RESPONSE_APC_DATA); | 229 | packInt(RESPONSE_APC_DATA); |
178 | } | 230 | } |
179 | // Reserve space for the length | 231 | // Reserve space for the length |
180 | writePos += sizeof(int32_t); | 232 | mWritePos += sizeof(int32_t); |
181 | packInt(buftype); | 233 | packInt(mBufType); |
182 | packInt(core); | 234 | packInt(mCore); |
183 | } | 235 | } |
184 | 236 | ||
185 | bool Buffer::eventHeader (const uint64_t curr_time) { | 237 | void Buffer::summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname) { |
238 | packInt(MESSAGE_SUMMARY); | ||
239 | writeString(NEWLINE_CANARY); | ||
240 | packInt64(timestamp); | ||
241 | packInt64(uptime); | ||
242 | packInt64(monotonicDelta); | ||
243 | writeString("uname"); | ||
244 | writeString(uname); | ||
245 | writeString(""); | ||
246 | check(1); | ||
247 | } | ||
248 | |||
249 | void Buffer::coreName(const int core, const int cpuid, const char *const name) { | ||
250 | if (checkSpace(3 * MAXSIZE_PACK32 + 0x100)) { | ||
251 | packInt(MESSAGE_CORE_NAME); | ||
252 | packInt(core); | ||
253 | packInt(cpuid); | ||
254 | writeString(name); | ||
255 | } | ||
256 | check(1); | ||
257 | } | ||
258 | |||
259 | bool Buffer::eventHeader(const uint64_t curr_time) { | ||
186 | bool retval = false; | 260 | bool retval = false; |
187 | if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) { | 261 | if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) { |
188 | packInt(0); // key of zero indicates a timestamp | 262 | packInt(0); // key of zero indicates a timestamp |
@@ -193,9 +267,9 @@ bool Buffer::eventHeader (const uint64_t curr_time) { | |||
193 | return retval; | 267 | return retval; |
194 | } | 268 | } |
195 | 269 | ||
196 | bool Buffer::eventTid (const int tid) { | 270 | bool Buffer::eventTid(const int tid) { |
197 | bool retval = false; | 271 | bool retval = false; |
198 | if (checkSpace(2*MAXSIZE_PACK32)) { | 272 | if (checkSpace(2 * MAXSIZE_PACK32)) { |
199 | packInt(1); // key of 1 indicates a tid | 273 | packInt(1); // key of 1 indicates a tid |
200 | packInt(tid); | 274 | packInt(tid); |
201 | retval = true; | 275 | retval = true; |
@@ -204,25 +278,94 @@ bool Buffer::eventTid (const int tid) { | |||
204 | return retval; | 278 | return retval; |
205 | } | 279 | } |
206 | 280 | ||
207 | void Buffer::event (const int32_t key, const int32_t value) { | 281 | void Buffer::event(const int32_t key, const int32_t value) { |
208 | if (checkSpace(2 * MAXSIZE_PACK32)) { | 282 | if (checkSpace(2 * MAXSIZE_PACK32)) { |
209 | packInt(key); | 283 | packInt(key); |
210 | packInt(value); | 284 | packInt(value); |
211 | } | 285 | } |
212 | } | 286 | } |
213 | 287 | ||
214 | void Buffer::event64 (const int64_t key, const int64_t value) { | 288 | void Buffer::event64(const int64_t key, const int64_t value) { |
215 | if (checkSpace(2 * MAXSIZE_PACK64)) { | 289 | if (checkSpace(2 * MAXSIZE_PACK64)) { |
216 | packInt64(key); | 290 | packInt64(key); |
217 | packInt64(value); | 291 | packInt64(value); |
218 | } | 292 | } |
219 | } | 293 | } |
220 | 294 | ||
221 | void Buffer::setDone () { | 295 | void Buffer::pea(const struct perf_event_attr *const pea, int key) { |
222 | done = true; | 296 | if (checkSpace(2 * MAXSIZE_PACK32 + pea->size)) { |
297 | packInt(CODE_PEA); | ||
298 | writeBytes(pea, pea->size); | ||
299 | packInt(key); | ||
300 | } else { | ||
301 | logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); | ||
302 | handleException(); | ||
303 | } | ||
304 | // Don't know the real perf time so use 1 as it will work for now | ||
305 | check(1); | ||
306 | } | ||
307 | |||
308 | void Buffer::keys(const int count, const __u64 *const ids, const int *const keys) { | ||
309 | if (checkSpace(2 * MAXSIZE_PACK32 + count * (MAXSIZE_PACK32 + MAXSIZE_PACK64))) { | ||
310 | packInt(CODE_KEYS); | ||
311 | packInt(count); | ||
312 | for (int i = 0; i < count; ++i) { | ||
313 | packInt64(ids[i]); | ||
314 | packInt(keys[i]); | ||
315 | } | ||
316 | } else { | ||
317 | logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); | ||
318 | handleException(); | ||
319 | } | ||
320 | check(1); | ||
321 | } | ||
322 | |||
323 | void Buffer::format(const int length, const char *const format) { | ||
324 | if (checkSpace(MAXSIZE_PACK32 + length + 1)) { | ||
325 | packInt(CODE_FORMAT); | ||
326 | writeBytes(format, length + 1); | ||
327 | } else { | ||
328 | logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); | ||
329 | handleException(); | ||
330 | } | ||
331 | check(1); | ||
332 | } | ||
333 | |||
334 | void Buffer::maps(const int pid, const int tid, const char *const maps) { | ||
335 | const int mapsLen = strlen(maps) + 1; | ||
336 | if (checkSpace(3 * MAXSIZE_PACK32 + mapsLen)) { | ||
337 | packInt(CODE_MAPS); | ||
338 | packInt(pid); | ||
339 | packInt(tid); | ||
340 | writeBytes(maps, mapsLen); | ||
341 | } else { | ||
342 | logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); | ||
343 | handleException(); | ||
344 | } | ||
345 | check(1); | ||
346 | } | ||
347 | |||
348 | void Buffer::comm(const int pid, const int tid, const char *const image, const char *const comm) { | ||
349 | const int imageLen = strlen(image) + 1; | ||
350 | const int commLen = strlen(comm) + 1; | ||
351 | if (checkSpace(3 * MAXSIZE_PACK32 + imageLen + commLen)) { | ||
352 | packInt(CODE_COMM); | ||
353 | packInt(pid); | ||
354 | packInt(tid); | ||
355 | writeBytes(image, imageLen); | ||
356 | writeBytes(comm, commLen); | ||
357 | } else { | ||
358 | logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); | ||
359 | handleException(); | ||
360 | } | ||
361 | check(1); | ||
362 | } | ||
363 | |||
364 | void Buffer::setDone() { | ||
365 | mIsDone = true; | ||
223 | commit(0); | 366 | commit(0); |
224 | } | 367 | } |
225 | 368 | ||
226 | bool Buffer::isDone () const { | 369 | bool Buffer::isDone() const { |
227 | return done && readPos == commitPos && commitPos == writePos; | 370 | return mIsDone && mReadPos == mCommitPos && mCommitPos == mWritePos; |
228 | } | 371 | } |
diff --git a/daemon/Buffer.h b/daemon/Buffer.h index b3c8d78..5023777 100644 --- a/daemon/Buffer.h +++ b/daemon/Buffer.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -9,54 +9,89 @@ | |||
9 | #ifndef BUFFER_H | 9 | #ifndef BUFFER_H |
10 | #define BUFFER_H | 10 | #define BUFFER_H |
11 | 11 | ||
12 | #include <stddef.h> | ||
13 | #include <stdint.h> | 12 | #include <stdint.h> |
14 | #include <semaphore.h> | 13 | #include <semaphore.h> |
15 | 14 | ||
15 | #include "k/perf_event.h" | ||
16 | |||
16 | class Sender; | 17 | class Sender; |
17 | 18 | ||
19 | enum { | ||
20 | FRAME_SUMMARY = 1, | ||
21 | FRAME_BLOCK_COUNTER = 5, | ||
22 | FRAME_EXTERNAL = 10, | ||
23 | FRAME_PERF_ATTRS = 11, | ||
24 | FRAME_PERF = 12, | ||
25 | }; | ||
26 | |||
18 | class Buffer { | 27 | class Buffer { |
19 | public: | 28 | public: |
20 | static const size_t MAXSIZE_PACK32 = 5; | 29 | static const size_t MAXSIZE_PACK32 = 5; |
21 | static const size_t MAXSIZE_PACK64 = 10; | 30 | static const size_t MAXSIZE_PACK64 = 10; |
22 | 31 | ||
23 | Buffer (int32_t core, int32_t buftype, const int size, sem_t *const readerSem); | 32 | Buffer(int32_t core, int32_t buftype, const int size, sem_t *const readerSem); |
24 | ~Buffer (); | 33 | ~Buffer(); |
34 | |||
35 | void write(Sender *sender); | ||
36 | |||
37 | int bytesAvailable() const; | ||
38 | int contiguousSpaceAvailable() const; | ||
39 | void commit(const uint64_t time); | ||
40 | void check(const uint64_t time); | ||
25 | 41 | ||
26 | void write (Sender * sender); | 42 | void frame(); |
27 | 43 | ||
28 | int bytesAvailable () const; | 44 | // Summary messages |
29 | void commit (const uint64_t time); | 45 | void summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname); |
30 | void check (const uint64_t time); | 46 | void coreName(const int core, const int cpuid, const char *const name); |
31 | 47 | ||
32 | void frame (); | 48 | // Block Counter messages |
49 | bool eventHeader(uint64_t curr_time); | ||
50 | bool eventTid(int tid); | ||
51 | void event(int32_t key, int32_t value); | ||
52 | void event64(int64_t key, int64_t value); | ||
33 | 53 | ||
34 | bool eventHeader (uint64_t curr_time); | 54 | // Perf Attrs messages |
35 | bool eventTid (int tid); | 55 | void pea(const struct perf_event_attr *const pea, int key); |
36 | void event (int32_t key, int32_t value); | 56 | void keys(const int count, const __u64 *const ids, const int *const keys); |
37 | void event64 (int64_t key, int64_t value); | 57 | void format(const int length, const char *const format); |
58 | void maps(const int pid, const int tid, const char *const maps); | ||
59 | void comm(const int pid, const int tid, const char *const image, const char *const comm); | ||
38 | 60 | ||
39 | void setDone (); | 61 | void setDone(); |
40 | bool isDone () const; | 62 | bool isDone() const; |
63 | |||
64 | // Prefer a new member to using these functions if possible | ||
65 | char *getWritePos() { return mBuf + mWritePos; } | ||
66 | void advanceWrite(int bytes) { mWritePos = (mWritePos + bytes) & /*mask*/(mSize - 1); } | ||
67 | |||
68 | static void writeLEInt(unsigned char *buf, int v) { | ||
69 | buf[0] = (v >> 0) & 0xFF; | ||
70 | buf[1] = (v >> 8) & 0xFF; | ||
71 | buf[2] = (v >> 16) & 0xFF; | ||
72 | buf[3] = (v >> 24) & 0xFF; | ||
73 | } | ||
41 | 74 | ||
42 | private: | 75 | private: |
43 | bool commitReady () const; | 76 | bool commitReady() const; |
44 | bool checkSpace (int bytes); | 77 | bool checkSpace(int bytes); |
45 | 78 | ||
46 | void packInt (int32_t x); | 79 | void packInt(int32_t x); |
47 | void packInt64 (int64_t x); | 80 | void packInt64(int64_t x); |
48 | 81 | void writeBytes(const void *const data, size_t count); | |
49 | const int32_t core; | 82 | void writeString(const char *const str); |
50 | const int32_t buftype; | 83 | |
51 | const int size; | 84 | const int32_t mCore; |
52 | int readPos; | 85 | const int32_t mBufType; |
53 | int writePos; | 86 | const int mSize; |
54 | int commitPos; | 87 | int mReadPos; |
55 | bool available; | 88 | int mWritePos; |
56 | bool done; | 89 | int mCommitPos; |
57 | char *const buf; | 90 | bool mAvailable; |
58 | uint64_t commitTime; | 91 | bool mIsDone; |
59 | sem_t *const readerSem; | 92 | char *const mBuf; |
93 | uint64_t mCommitTime; | ||
94 | sem_t *const mReaderSem; | ||
60 | 95 | ||
61 | // Intentionally unimplemented | 96 | // Intentionally unimplemented |
62 | Buffer(const Buffer &); | 97 | Buffer(const Buffer &); |
diff --git a/daemon/CapturedXML.cpp b/daemon/CapturedXML.cpp index 30c4c44..cf79b72 100644 --- a/daemon/CapturedXML.cpp +++ b/daemon/CapturedXML.cpp | |||
@@ -1,16 +1,18 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "CapturedXML.h" | ||
10 | |||
9 | #include <stdlib.h> | 11 | #include <stdlib.h> |
10 | #include <string.h> | 12 | #include <string.h> |
11 | #include <dirent.h> | 13 | #include <dirent.h> |
14 | |||
12 | #include "SessionData.h" | 15 | #include "SessionData.h" |
13 | #include "CapturedXML.h" | ||
14 | #include "Logging.h" | 16 | #include "Logging.h" |
15 | #include "OlyUtility.h" | 17 | #include "OlyUtility.h" |
16 | 18 | ||
@@ -30,6 +32,9 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { | |||
30 | 32 | ||
31 | captured = mxmlNewElement(xml, "captured"); | 33 | captured = mxmlNewElement(xml, "captured"); |
32 | mxmlElementSetAttr(captured, "version", "1"); | 34 | mxmlElementSetAttr(captured, "version", "1"); |
35 | if (gSessionData->perf.isSetup()) { | ||
36 | mxmlElementSetAttr(captured, "type", "Perf"); | ||
37 | } | ||
33 | mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); | 38 | mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); |
34 | if (includeTime) { // Send the following only after the capture is complete | 39 | if (includeTime) { // Send the following only after the capture is complete |
35 | if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010) | 40 | if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010) |
@@ -41,7 +46,7 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { | |||
41 | mxmlElementSetAttr(target, "name", gSessionData->mCoreName); | 46 | mxmlElementSetAttr(target, "name", gSessionData->mCoreName); |
42 | mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); | 47 | mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); |
43 | mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); | 48 | mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); |
44 | mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mCpuId); | 49 | mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mMaxCpuId); |
45 | 50 | ||
46 | if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) { | 51 | if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) { |
47 | mxmlElementSetAttr(target, "supports_live", "yes"); | 52 | mxmlElementSetAttr(target, "supports_live", "yes"); |
diff --git a/daemon/CapturedXML.h b/daemon/CapturedXML.h index b0482f5..efc1e52 100644 --- a/daemon/CapturedXML.h +++ b/daemon/CapturedXML.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/Child.cpp b/daemon/Child.cpp index 9ee2ef8..ca33561 100644 --- a/daemon/Child.cpp +++ b/daemon/Child.cpp | |||
@@ -1,38 +1,39 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "Child.h" | ||
10 | |||
9 | #include <stdlib.h> | 11 | #include <stdlib.h> |
10 | #include <string.h> | 12 | #include <string.h> |
11 | #include <signal.h> | 13 | #include <signal.h> |
12 | #include <unistd.h> | 14 | #include <unistd.h> |
13 | #include <sys/prctl.h> | 15 | #include <sys/prctl.h> |
16 | |||
14 | #include "Logging.h" | 17 | #include "Logging.h" |
15 | #include "CapturedXML.h" | 18 | #include "CapturedXML.h" |
16 | #include "SessionData.h" | 19 | #include "SessionData.h" |
17 | #include "Child.h" | ||
18 | #include "LocalCapture.h" | 20 | #include "LocalCapture.h" |
19 | #include "Collector.h" | ||
20 | #include "Sender.h" | 21 | #include "Sender.h" |
21 | #include "OlyUtility.h" | 22 | #include "OlyUtility.h" |
23 | #include "OlySocket.h" | ||
22 | #include "StreamlineSetup.h" | 24 | #include "StreamlineSetup.h" |
23 | #include "ConfigurationXML.h" | 25 | #include "ConfigurationXML.h" |
24 | #include "Driver.h" | 26 | #include "Driver.h" |
25 | #include "Fifo.h" | 27 | #include "PerfSource.h" |
26 | #include "Buffer.h" | 28 | #include "DriverSource.h" |
27 | 29 | #include "UserSpaceSource.h" | |
28 | #define NS_PER_S ((uint64_t)1000000000) | 30 | #include "ExternalSource.h" |
29 | #define NS_PER_US 1000 | ||
30 | 31 | ||
31 | static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads | 32 | static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads |
32 | static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads | 33 | static Source *primarySource = NULL; |
33 | static Buffer* buffer = NULL; | 34 | static Source *userSpaceSource = NULL; |
35 | static Source *externalSource = NULL; | ||
34 | static Sender* sender = NULL; // Shared by Child.cpp and spawned threads | 36 | static Sender* sender = NULL; // Shared by Child.cpp and spawned threads |
35 | static Collector* collector = NULL; | ||
36 | Child* child = NULL; // shared by Child.cpp and main.cpp | 37 | Child* child = NULL; // shared by Child.cpp and main.cpp |
37 | 38 | ||
38 | extern void cleanUp(); | 39 | extern void cleanUp(); |
@@ -78,7 +79,7 @@ static void child_handler(int signum) { | |||
78 | } | 79 | } |
79 | beenHere = true; | 80 | beenHere = true; |
80 | logg->logMessage("Gator is shutting down."); | 81 | logg->logMessage("Gator is shutting down."); |
81 | if (signum == SIGALRM || !collector) { | 82 | if (signum == SIGALRM || !primarySource) { |
82 | exit(1); | 83 | exit(1); |
83 | } else { | 84 | } else { |
84 | child->endSession(); | 85 | child->endSession(); |
@@ -139,77 +140,22 @@ static void *stopThread(void *) { | |||
139 | return 0; | 140 | return 0; |
140 | } | 141 | } |
141 | 142 | ||
142 | static void *countersThread(void *) { | ||
143 | prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); | ||
144 | |||
145 | gSessionData->hwmon.start(); | ||
146 | |||
147 | int64_t monotonic_started = 0; | ||
148 | while (monotonic_started <= 0) { | ||
149 | usleep(10); | ||
150 | |||
151 | if (Collector::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) { | ||
152 | logg->logError(__FILE__, __LINE__, "Error reading gator driver start time"); | ||
153 | handleException(); | ||
154 | } | ||
155 | } | ||
156 | |||
157 | uint64_t next_time = 0; | ||
158 | while (gSessionData->mSessionIsActive) { | ||
159 | struct timespec ts; | ||
160 | #ifndef CLOCK_MONOTONIC_RAW | ||
161 | // Android doesn't have this defined but it was added in Linux 2.6.28 | ||
162 | #define CLOCK_MONOTONIC_RAW 4 | ||
163 | #endif | ||
164 | if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { | ||
165 | logg->logError(__FILE__, __LINE__, "Failed to get uptime"); | ||
166 | handleException(); | ||
167 | } | ||
168 | const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started; | ||
169 | // Sample ten times a second ignoring gSessionData->mSampleRate | ||
170 | next_time += NS_PER_S/10;//gSessionData->mSampleRate; | ||
171 | if (next_time < curr_time) { | ||
172 | logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time); | ||
173 | next_time = curr_time; | ||
174 | } | ||
175 | |||
176 | if (buffer->eventHeader(curr_time)) { | ||
177 | gSessionData->hwmon.read(buffer); | ||
178 | // Only check after writing all counters so that time and corresponding counters appear in the same frame | ||
179 | buffer->check(curr_time); | ||
180 | } | ||
181 | |||
182 | if (buffer->bytesAvailable() <= 0) { | ||
183 | logg->logMessage("One shot (counters)"); | ||
184 | child->endSession(); | ||
185 | } | ||
186 | |||
187 | usleep((next_time - curr_time)/NS_PER_US); | ||
188 | } | ||
189 | |||
190 | buffer->setDone(); | ||
191 | |||
192 | return NULL; | ||
193 | } | ||
194 | |||
195 | static void *senderThread(void *) { | 143 | static void *senderThread(void *) { |
196 | int length = 1; | ||
197 | char* data; | ||
198 | char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; | 144 | char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; |
199 | 145 | ||
200 | sem_post(&senderThreadStarted); | 146 | sem_post(&senderThreadStarted); |
201 | prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); | 147 | prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); |
202 | sem_wait(&haltPipeline); | 148 | sem_wait(&haltPipeline); |
203 | 149 | ||
204 | while (length > 0 || !buffer->isDone()) { | 150 | while (!primarySource->isDone() || (userSpaceSource != NULL && !userSpaceSource->isDone()) || (externalSource != NULL && !externalSource->isDone())) { |
205 | sem_wait(&senderSem); | 151 | sem_wait(&senderSem); |
206 | data = collectorFifo->read(&length); | 152 | |
207 | if (data != NULL) { | 153 | primarySource->write(sender); |
208 | sender->writeData(data, length, RESPONSE_APC_DATA); | 154 | if (userSpaceSource != NULL) { |
209 | collectorFifo->release(); | 155 | userSpaceSource->write(sender); |
210 | } | 156 | } |
211 | if (!buffer->isDone()) { | 157 | if (externalSource != NULL) { |
212 | buffer->write(sender); | 158 | externalSource->write(sender); |
213 | } | 159 | } |
214 | } | 160 | } |
215 | 161 | ||
@@ -255,15 +201,13 @@ void Child::initialization() { | |||
255 | 201 | ||
256 | void Child::endSession() { | 202 | void Child::endSession() { |
257 | gSessionData->mSessionIsActive = false; | 203 | gSessionData->mSessionIsActive = false; |
258 | collector->stop(); | 204 | primarySource->interrupt(); |
259 | sem_post(&haltPipeline); | 205 | sem_post(&haltPipeline); |
260 | } | 206 | } |
261 | 207 | ||
262 | void Child::run() { | 208 | void Child::run() { |
263 | char* collectBuffer; | ||
264 | int bytesCollected = 0; | ||
265 | LocalCapture* localCapture = NULL; | 209 | LocalCapture* localCapture = NULL; |
266 | pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID; | 210 | pthread_t durationThreadID, stopThreadID, senderThreadID; |
267 | 211 | ||
268 | prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0); | 212 | prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0); |
269 | 213 | ||
@@ -282,7 +226,11 @@ void Child::run() { | |||
282 | { ConfigurationXML configuration; } | 226 | { ConfigurationXML configuration; } |
283 | 227 | ||
284 | // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated | 228 | // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated |
285 | collector = new Collector(); | 229 | if (!gSessionData->perf.isSetup()) { |
230 | primarySource = new DriverSource(&senderSem, &startProfile); | ||
231 | } else { | ||
232 | primarySource = new PerfSource(&senderSem, &startProfile); | ||
233 | } | ||
286 | 234 | ||
287 | // Initialize all drivers | 235 | // Initialize all drivers |
288 | for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { | 236 | for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { |
@@ -317,15 +265,11 @@ void Child::run() { | |||
317 | free(xmlString); | 265 | free(xmlString); |
318 | } | 266 | } |
319 | 267 | ||
320 | // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length | 268 | // Must be after session XML is parsed |
321 | logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize()); | 269 | if (!primarySource->prepare()) { |
322 | collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); | 270 | logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); |
323 | 271 | handleException(); | |
324 | // Get the initial pointer to the collect buffer | 272 | } |
325 | collectBuffer = collectorFifo->start(); | ||
326 | |||
327 | // Create a new Block Counter Buffer | ||
328 | buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); | ||
329 | 273 | ||
330 | // Sender thread shall be halted until it is signaled for one shot mode | 274 | // Sender thread shall be halted until it is signaled for one shot mode |
331 | sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); | 275 | sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); |
@@ -340,14 +284,21 @@ void Child::run() { | |||
340 | thread_creation_success = false; | 284 | thread_creation_success = false; |
341 | } | 285 | } |
342 | 286 | ||
343 | bool startcountersThread = gSessionData->hwmon.countersEnabled(); | 287 | if (gSessionData->hwmon.countersEnabled()) { |
344 | if (startcountersThread) { | 288 | userSpaceSource = new UserSpaceSource(&senderSem); |
345 | if (pthread_create(&countersThreadID, NULL, countersThread, this)) { | 289 | if (!userSpaceSource->prepare()) { |
346 | thread_creation_success = false; | 290 | logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); |
291 | handleException(); | ||
347 | } | 292 | } |
348 | } else { | 293 | userSpaceSource->start(); |
349 | // Let senderThread know there is no buffer data to send | 294 | } |
350 | buffer->setDone(); | 295 | if (access("/tmp/gator", F_OK) == 0) { |
296 | externalSource = new ExternalSource(&senderSem); | ||
297 | if (!externalSource->prepare()) { | ||
298 | logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); | ||
299 | handleException(); | ||
300 | } | ||
301 | externalSource->start(); | ||
351 | } | 302 | } |
352 | 303 | ||
353 | if (!thread_creation_success) { | 304 | if (!thread_creation_success) { |
@@ -359,28 +310,13 @@ void Child::run() { | |||
359 | sem_wait(&senderThreadStarted); | 310 | sem_wait(&senderThreadStarted); |
360 | 311 | ||
361 | // Start profiling | 312 | // Start profiling |
362 | logg->logMessage("********** Profiling started **********"); | 313 | primarySource->run(); |
363 | collector->start(); | ||
364 | sem_post(&startProfile); | ||
365 | |||
366 | // Collect Data | ||
367 | do { | ||
368 | // This command will stall until data is received from the driver | ||
369 | bytesCollected = collector->collect(collectBuffer); | ||
370 | |||
371 | // In one shot mode, stop collection once all the buffers are filled | ||
372 | if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { | ||
373 | if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) { | ||
374 | logg->logMessage("One shot"); | ||
375 | endSession(); | ||
376 | } | ||
377 | } | ||
378 | collectBuffer = collectorFifo->write(bytesCollected); | ||
379 | } while (bytesCollected > 0); | ||
380 | logg->logMessage("Exit collect data loop"); | ||
381 | 314 | ||
382 | if (startcountersThread) { | 315 | if (externalSource != NULL) { |
383 | pthread_join(countersThreadID, NULL); | 316 | externalSource->join(); |
317 | } | ||
318 | if (userSpaceSource != NULL) { | ||
319 | userSpaceSource->join(); | ||
384 | } | 320 | } |
385 | 321 | ||
386 | // Wait for the other threads to exit | 322 | // Wait for the other threads to exit |
@@ -401,9 +337,9 @@ void Child::run() { | |||
401 | 337 | ||
402 | logg->logMessage("Profiling ended."); | 338 | logg->logMessage("Profiling ended."); |
403 | 339 | ||
404 | delete buffer; | 340 | delete externalSource; |
405 | delete collectorFifo; | 341 | delete userSpaceSource; |
342 | delete primarySource; | ||
406 | delete sender; | 343 | delete sender; |
407 | delete collector; | ||
408 | delete localCapture; | 344 | delete localCapture; |
409 | } | 345 | } |
diff --git a/daemon/Child.h b/daemon/Child.h index 0330e9d..9e206d7 100644 --- a/daemon/Child.h +++ b/daemon/Child.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -9,8 +9,6 @@ | |||
9 | #ifndef __CHILD_H__ | 9 | #ifndef __CHILD_H__ |
10 | #define __CHILD_H__ | 10 | #define __CHILD_H__ |
11 | 11 | ||
12 | #include <pthread.h> | ||
13 | |||
14 | class OlySocket; | 12 | class OlySocket; |
15 | 13 | ||
16 | class Child { | 14 | class Child { |
diff --git a/daemon/Collector.h b/daemon/Collector.h deleted file mode 100644 index c5e9eac..0000000 --- a/daemon/Collector.h +++ /dev/null | |||
@@ -1,38 +0,0 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2010-2013. 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 | #ifndef __COLLECTOR_H__ | ||
10 | #define __COLLECTOR_H__ | ||
11 | |||
12 | #include <stdio.h> | ||
13 | |||
14 | class Collector { | ||
15 | public: | ||
16 | Collector(); | ||
17 | ~Collector(); | ||
18 | void start(); | ||
19 | void stop(); | ||
20 | int collect(char* buffer); | ||
21 | int getBufferSize() {return mBufferSize;} | ||
22 | |||
23 | static int readIntDriver(const char* path, int* value); | ||
24 | static int readInt64Driver(const char* path, int64_t* value); | ||
25 | static int writeDriver(const char* path, int value); | ||
26 | static int writeDriver(const char* path, int64_t value); | ||
27 | static int writeDriver(const char* path, const char* data); | ||
28 | static int writeReadDriver(const char* path, int* value); | ||
29 | static int writeReadDriver(const char* path, int64_t* value); | ||
30 | |||
31 | private: | ||
32 | int mBufferSize; | ||
33 | int mBufferFD; | ||
34 | |||
35 | void checkVersion(); | ||
36 | }; | ||
37 | |||
38 | #endif //__COLLECTOR_H__ | ||
diff --git a/daemon/Config.h b/daemon/Config.h new file mode 100644 index 0000000..6f5e2aa --- /dev/null +++ b/daemon/Config.h | |||
@@ -0,0 +1,17 @@ | |||
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 | #ifndef CONFIG_H | ||
10 | #define CONFIG_H | ||
11 | |||
12 | #define ARRAY_LENGTH(A) static_cast<int>(sizeof(A)/sizeof((A)[0])) | ||
13 | |||
14 | #define MAX_PERFORMANCE_COUNTERS 50 | ||
15 | #define NR_CPUS 16 | ||
16 | |||
17 | #endif // CONFIG_H | ||
diff --git a/daemon/ConfigurationXML.cpp b/daemon/ConfigurationXML.cpp index 2a5252a..fd479f2 100644 --- a/daemon/ConfigurationXML.cpp +++ b/daemon/ConfigurationXML.cpp | |||
@@ -1,15 +1,17 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "ConfigurationXML.h" | ||
10 | |||
9 | #include <string.h> | 11 | #include <string.h> |
10 | #include <stdlib.h> | 12 | #include <stdlib.h> |
11 | #include <dirent.h> | 13 | #include <dirent.h> |
12 | #include "ConfigurationXML.h" | 14 | |
13 | #include "Driver.h" | 15 | #include "Driver.h" |
14 | #include "Logging.h" | 16 | #include "Logging.h" |
15 | #include "OlyUtility.h" | 17 | #include "OlyUtility.h" |
@@ -67,6 +69,7 @@ int ConfigurationXML::parse(const char* configurationXML) { | |||
67 | 69 | ||
68 | // clear counter overflow | 70 | // clear counter overflow |
69 | gSessionData->mCounterOverflow = 0; | 71 | gSessionData->mCounterOverflow = 0; |
72 | gSessionData->mIsEBS = false; | ||
70 | mIndex = 0; | 73 | mIndex = 0; |
71 | 74 | ||
72 | // disable all counters prior to parsing the configuration xml | 75 | // disable all counters prior to parsing the configuration xml |
@@ -155,6 +158,9 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { | |||
155 | if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER)); | 158 | if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER)); |
156 | if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16)); | 159 | if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16)); |
157 | if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10)); | 160 | if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10)); |
161 | if (counter.getCount() > 0) { | ||
162 | gSessionData->mIsEBS = true; | ||
163 | } | ||
158 | counter.setEnabled(true); | 164 | counter.setEnabled(true); |
159 | 165 | ||
160 | // Associate a driver with each counter | 166 | // Associate a driver with each counter |
@@ -181,9 +187,9 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { | |||
181 | } | 187 | } |
182 | 188 | ||
183 | void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) { | 189 | void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) { |
184 | #include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len | 190 | #include "defaults_xml.h" // defines and initializes char defaults_xml[] and int defaults_xml_len |
185 | xml = (const char *)configuration_xml; | 191 | xml = (const char *)defaults_xml; |
186 | len = configuration_xml_len; | 192 | len = defaults_xml_len; |
187 | } | 193 | } |
188 | 194 | ||
189 | void ConfigurationXML::getPath(char* path) { | 195 | void ConfigurationXML::getPath(char* path) { |
diff --git a/daemon/ConfigurationXML.h b/daemon/ConfigurationXML.h index 5650f48..efa415e 100644 --- a/daemon/ConfigurationXML.h +++ b/daemon/ConfigurationXML.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/Counter.h b/daemon/Counter.h index 231a85d..6891745 100644 --- a/daemon/Counter.h +++ b/daemon/Counter.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -25,7 +25,7 @@ public: | |||
25 | void clear () { | 25 | void clear () { |
26 | mType[0] = '\0'; | 26 | mType[0] = '\0'; |
27 | mEnabled = false; | 27 | mEnabled = false; |
28 | mEvent = 0; | 28 | mEvent = -1; |
29 | mCount = 0; | 29 | mCount = 0; |
30 | mKey = 0; | 30 | mKey = 0; |
31 | mDriver = NULL; | 31 | mDriver = NULL; |
diff --git a/daemon/Driver.cpp b/daemon/Driver.cpp index c262467..09e0401 100644 --- a/daemon/Driver.cpp +++ b/daemon/Driver.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/Driver.h b/daemon/Driver.h index f3a932f..e5ed7b6 100644 --- a/daemon/Driver.h +++ b/daemon/Driver.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -27,7 +27,7 @@ public: | |||
27 | virtual void setupCounter(Counter &counter) = 0; | 27 | virtual void setupCounter(Counter &counter) = 0; |
28 | 28 | ||
29 | // Emits available counters | 29 | // Emits available counters |
30 | virtual void writeCounters(mxml_node_t *root) const = 0; | 30 | virtual int writeCounters(mxml_node_t *root) const = 0; |
31 | // Emits possible dynamically generated events/counters | 31 | // Emits possible dynamically generated events/counters |
32 | virtual void writeEvents(mxml_node_t *) const {} | 32 | virtual void writeEvents(mxml_node_t *) const {} |
33 | 33 | ||
diff --git a/daemon/Collector.cpp b/daemon/DriverSource.cpp index bf73534..f78ec6b 100644 --- a/daemon/Collector.cpp +++ b/daemon/DriverSource.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -8,23 +8,47 @@ | |||
8 | 8 | ||
9 | #define __STDC_FORMAT_MACROS | 9 | #define __STDC_FORMAT_MACROS |
10 | 10 | ||
11 | #include "DriverSource.h" | ||
12 | |||
11 | #include <fcntl.h> | 13 | #include <fcntl.h> |
12 | #include <unistd.h> | ||
13 | #include <string.h> | ||
14 | #include <stdlib.h> | ||
15 | #include <errno.h> | ||
16 | #include <sys/time.h> | ||
17 | #include <inttypes.h> | 14 | #include <inttypes.h> |
18 | #include "Collector.h" | 15 | #include <unistd.h> |
19 | #include "SessionData.h" | 16 | |
17 | #include "Child.h" | ||
18 | #include "Fifo.h" | ||
20 | #include "Logging.h" | 19 | #include "Logging.h" |
21 | #include "Sender.h" | 20 | #include "Sender.h" |
21 | #include "SessionData.h" | ||
22 | |||
23 | extern Child *child; | ||
22 | 24 | ||
23 | // Driver initialization independent of session settings | 25 | DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) { |
24 | Collector::Collector() { | 26 | int driver_version = 0; |
25 | mBufferFD = 0; | ||
26 | 27 | ||
27 | checkVersion(); | 28 | if (readIntDriver("/dev/gator/version", &driver_version) == -1) { |
29 | logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); | ||
30 | handleException(); | ||
31 | } | ||
32 | |||
33 | // Verify the driver version matches the daemon version | ||
34 | if (driver_version != PROTOCOL_VERSION) { | ||
35 | if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { | ||
36 | // One of the mismatched versions is development version | ||
37 | logg->logError(__FILE__, __LINE__, | ||
38 | "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" | ||
39 | ">> The following must be synchronized from engineering repository:\n" | ||
40 | ">> * gator driver\n" | ||
41 | ">> * gator daemon\n" | ||
42 | ">> * Streamline", driver_version, PROTOCOL_VERSION); | ||
43 | handleException(); | ||
44 | } else { | ||
45 | // Release version mismatch | ||
46 | logg->logError(__FILE__, __LINE__, | ||
47 | "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" | ||
48 | ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); | ||
49 | handleException(); | ||
50 | } | ||
51 | } | ||
28 | 52 | ||
29 | int enable = -1; | 53 | int enable = -1; |
30 | if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { | 54 | if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { |
@@ -37,14 +61,15 @@ Collector::Collector() { | |||
37 | gSessionData->mCores = 1; | 61 | gSessionData->mCores = 1; |
38 | } | 62 | } |
39 | 63 | ||
40 | mBufferSize = 0; | ||
41 | if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { | 64 | if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { |
42 | logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); | 65 | logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); |
43 | handleException(); | 66 | handleException(); |
44 | } | 67 | } |
45 | } | 68 | } |
46 | 69 | ||
47 | Collector::~Collector() { | 70 | DriverSource::~DriverSource() { |
71 | delete mFifo; | ||
72 | |||
48 | // Write zero for safety, as a zero should have already been written | 73 | // Write zero for safety, as a zero should have already been written |
49 | writeDriver("/dev/gator/enable", "0"); | 74 | writeDriver("/dev/gator/enable", "0"); |
50 | 75 | ||
@@ -54,36 +79,21 @@ Collector::~Collector() { | |||
54 | } | 79 | } |
55 | } | 80 | } |
56 | 81 | ||
57 | void Collector::checkVersion() { | 82 | bool DriverSource::prepare() { |
58 | int driver_version = 0; | 83 | // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length |
59 | 84 | logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, mBufferSize); | |
60 | if (readIntDriver("/dev/gator/version", &driver_version) == -1) { | 85 | mFifo = new Fifo(mBufferSize + 5, gSessionData->mTotalBufferSize*1024*1024, mSenderSem); |
61 | logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); | ||
62 | handleException(); | ||
63 | } | ||
64 | 86 | ||
65 | // Verify the driver version matches the daemon version | 87 | return true; |
66 | if (driver_version != PROTOCOL_VERSION) { | ||
67 | if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { | ||
68 | // One of the mismatched versions is development version | ||
69 | logg->logError(__FILE__, __LINE__, | ||
70 | "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" | ||
71 | ">> The following must be synchronized from engineering repository:\n" | ||
72 | ">> * gator driver\n" | ||
73 | ">> * gator daemon\n" | ||
74 | ">> * Streamline", driver_version, PROTOCOL_VERSION); | ||
75 | handleException(); | ||
76 | } else { | ||
77 | // Release version mismatch | ||
78 | logg->logError(__FILE__, __LINE__, | ||
79 | "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" | ||
80 | ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); | ||
81 | handleException(); | ||
82 | } | ||
83 | } | ||
84 | } | 88 | } |
85 | 89 | ||
86 | void Collector::start() { | 90 | void DriverSource::run() { |
91 | // Get the initial pointer to the collect buffer | ||
92 | char *collectBuffer = mFifo->start(); | ||
93 | int bytesCollected = 0; | ||
94 | |||
95 | logg->logMessage("********** Profiling started **********"); | ||
96 | |||
87 | // Set the maximum backtrace depth | 97 | // Set the maximum backtrace depth |
88 | if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { | 98 | if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { |
89 | logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); | 99 | logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); |
@@ -125,79 +135,112 @@ void Collector::start() { | |||
125 | } | 135 | } |
126 | 136 | ||
127 | lseek(mBufferFD, 0, SEEK_SET); | 137 | lseek(mBufferFD, 0, SEEK_SET); |
138 | |||
139 | sem_post(mStartProfile); | ||
140 | |||
141 | // Collect Data | ||
142 | do { | ||
143 | // This command will stall until data is received from the driver | ||
144 | // Calls event_buffer_read in the driver | ||
145 | errno = 0; | ||
146 | bytesCollected = read(mBufferFD, collectBuffer, mBufferSize); | ||
147 | |||
148 | // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data | ||
149 | if (bytesCollected == -1 && errno == EINTR) { | ||
150 | bytesCollected = read(mBufferFD, collectBuffer, mBufferSize); | ||
151 | } | ||
152 | |||
153 | // return the total bytes written | ||
154 | logg->logMessage("Driver read of %d bytes", bytesCollected); | ||
155 | |||
156 | // In one shot mode, stop collection once all the buffers are filled | ||
157 | if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { | ||
158 | if (bytesCollected == -1 || mFifo->willFill(bytesCollected)) { | ||
159 | logg->logMessage("One shot"); | ||
160 | child->endSession(); | ||
161 | } | ||
162 | } | ||
163 | collectBuffer = mFifo->write(bytesCollected); | ||
164 | } while (bytesCollected > 0); | ||
165 | |||
166 | logg->logMessage("Exit collect data loop"); | ||
128 | } | 167 | } |
129 | 168 | ||
130 | // These commands should cause the read() function in collect() to return | 169 | void DriverSource::interrupt() { |
131 | void Collector::stop() { | 170 | // This command should cause the read() function in collect() to return and stop the driver from profiling |
132 | // This will stop the driver from profiling | ||
133 | if (writeDriver("/dev/gator/enable", "0") != 0) { | 171 | if (writeDriver("/dev/gator/enable", "0") != 0) { |
134 | logg->logMessage("Stopping kernel failed"); | 172 | logg->logMessage("Stopping kernel failed"); |
135 | } | 173 | } |
136 | } | 174 | } |
137 | 175 | ||
138 | int Collector::collect(char* buffer) { | 176 | bool DriverSource::isDone() { |
139 | // Calls event_buffer_read in the driver | 177 | return mLength <= 0; |
140 | int bytesRead; | 178 | } |
141 | |||
142 | errno = 0; | ||
143 | bytesRead = read(mBufferFD, buffer, mBufferSize); | ||
144 | 179 | ||
145 | // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data | 180 | void DriverSource::write(Sender *sender) { |
146 | if (bytesRead == -1 && errno == EINTR) { | 181 | char *data = mFifo->read(&mLength); |
147 | bytesRead = read(mBufferFD, buffer, mBufferSize); | 182 | if (data != NULL) { |
183 | sender->writeData(data, mLength, RESPONSE_APC_DATA); | ||
184 | mFifo->release(); | ||
148 | } | 185 | } |
149 | |||
150 | // return the total bytes written | ||
151 | logg->logMessage("Driver read of %d bytes", bytesRead); | ||
152 | return bytesRead; | ||
153 | } | 186 | } |
154 | 187 | ||
155 | int Collector::readIntDriver(const char* fullpath, int* value) { | 188 | int DriverSource::readIntDriver(const char *fullpath, int *value) { |
156 | FILE* file = fopen(fullpath, "r"); | 189 | char data[40]; // Sufficiently large to hold any integer |
157 | if (file == NULL) { | 190 | const int fd = open(fullpath, O_RDONLY); |
191 | if (fd < 0) { | ||
192 | return -1; | ||
193 | } | ||
194 | |||
195 | const ssize_t bytes = read(fd, data, sizeof(data) - 1); | ||
196 | close(fd); | ||
197 | if (bytes < 0) { | ||
158 | return -1; | 198 | return -1; |
159 | } | 199 | } |
160 | if (fscanf(file, "%u", value) != 1) { | 200 | data[bytes] = '\0'; |
161 | fclose(file); | 201 | |
202 | char *endptr; | ||
203 | errno = 0; | ||
204 | *value = strtol(data, &endptr, 10); | ||
205 | if (errno != 0 || *endptr != '\n') { | ||
162 | logg->logMessage("Invalid value in file %s", fullpath); | 206 | logg->logMessage("Invalid value in file %s", fullpath); |
163 | return -1; | 207 | return -1; |
164 | } | 208 | } |
165 | fclose(file); | 209 | |
166 | return 0; | 210 | return 0; |
167 | } | 211 | } |
168 | 212 | ||
169 | int Collector::readInt64Driver(const char* fullpath, int64_t* value) { | 213 | int DriverSource::readInt64Driver(const char *fullpath, int64_t *value) { |
170 | FILE* file = fopen(fullpath, "r"); | 214 | char data[40]; // Sufficiently large to hold any integer |
171 | if (file == NULL) { | 215 | const int fd = open(fullpath, O_RDONLY); |
216 | if (fd < 0) { | ||
172 | return -1; | 217 | return -1; |
173 | } | 218 | } |
174 | if (fscanf(file, "%" SCNi64, value) != 1) { | 219 | |
175 | fclose(file); | 220 | const ssize_t bytes = read(fd, data, sizeof(data) - 1); |
176 | logg->logMessage("Invalid value in file %s", fullpath); | 221 | close(fd); |
222 | if (bytes < 0) { | ||
177 | return -1; | 223 | return -1; |
178 | } | 224 | } |
179 | fclose(file); | 225 | data[bytes] = '\0'; |
180 | return 0; | ||
181 | } | ||
182 | 226 | ||
183 | int Collector::writeDriver(const char* path, int value) { | 227 | char *endptr; |
184 | char data[40]; // Sufficiently large to hold any integer | 228 | errno = 0; |
185 | snprintf(data, sizeof(data), "%d", value); | 229 | *value = strtoll(data, &endptr, 10); |
186 | return writeDriver(path, data); | 230 | if (errno != 0 || *endptr != '\n') { |
187 | } | 231 | logg->logMessage("Invalid value in file %s", fullpath); |
232 | return -1; | ||
233 | } | ||
188 | 234 | ||
189 | int Collector::writeDriver(const char* path, int64_t value) { | 235 | return 0; |
190 | char data[40]; // Sufficiently large to hold any integer | ||
191 | snprintf(data, sizeof(data), "%" PRIi64, value); | ||
192 | return writeDriver(path, data); | ||
193 | } | 236 | } |
194 | 237 | ||
195 | int Collector::writeDriver(const char* fullpath, const char* data) { | 238 | int DriverSource::writeDriver(const char *fullpath, const char *data) { |
196 | int fd = open(fullpath, O_WRONLY); | 239 | int fd = open(fullpath, O_WRONLY); |
197 | if (fd < 0) { | 240 | if (fd < 0) { |
198 | return -1; | 241 | return -1; |
199 | } | 242 | } |
200 | if (write(fd, data, strlen(data)) < 0) { | 243 | if (::write(fd, data, strlen(data)) < 0) { |
201 | close(fd); | 244 | close(fd); |
202 | logg->logMessage("Opened but could not write to %s", fullpath); | 245 | logg->logMessage("Opened but could not write to %s", fullpath); |
203 | return -1; | 246 | return -1; |
@@ -206,14 +249,26 @@ int Collector::writeDriver(const char* fullpath, const char* data) { | |||
206 | return 0; | 249 | return 0; |
207 | } | 250 | } |
208 | 251 | ||
209 | int Collector::writeReadDriver(const char* path, int* value) { | 252 | int DriverSource::writeDriver(const char *path, int value) { |
253 | char data[40]; // Sufficiently large to hold any integer | ||
254 | snprintf(data, sizeof(data), "%d", value); | ||
255 | return writeDriver(path, data); | ||
256 | } | ||
257 | |||
258 | int DriverSource::writeDriver(const char *path, int64_t value) { | ||
259 | char data[40]; // Sufficiently large to hold any integer | ||
260 | snprintf(data, sizeof(data), "%" PRIi64, value); | ||
261 | return writeDriver(path, data); | ||
262 | } | ||
263 | |||
264 | int DriverSource::writeReadDriver(const char *path, int *value) { | ||
210 | if (writeDriver(path, *value) || readIntDriver(path, value)) { | 265 | if (writeDriver(path, *value) || readIntDriver(path, value)) { |
211 | return -1; | 266 | return -1; |
212 | } | 267 | } |
213 | return 0; | 268 | return 0; |
214 | } | 269 | } |
215 | 270 | ||
216 | int Collector::writeReadDriver(const char* path, int64_t* value) { | 271 | int DriverSource::writeReadDriver(const char *path, int64_t *value) { |
217 | if (writeDriver(path, *value) || readInt64Driver(path, value)) { | 272 | if (writeDriver(path, *value) || readInt64Driver(path, value)) { |
218 | return -1; | 273 | return -1; |
219 | } | 274 | } |
diff --git a/daemon/DriverSource.h b/daemon/DriverSource.h new file mode 100644 index 0000000..dcf1078 --- /dev/null +++ b/daemon/DriverSource.h | |||
@@ -0,0 +1,52 @@ | |||
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 | #ifndef DRIVERSOURCE_H | ||
10 | #define DRIVERSOURCE_H | ||
11 | |||
12 | #include <semaphore.h> | ||
13 | #include <stdint.h> | ||
14 | |||
15 | #include "Source.h" | ||
16 | |||
17 | class Fifo; | ||
18 | |||
19 | class DriverSource : public Source { | ||
20 | public: | ||
21 | DriverSource(sem_t *senderSem, sem_t *startProfile); | ||
22 | ~DriverSource(); | ||
23 | |||
24 | bool prepare(); | ||
25 | void run(); | ||
26 | void interrupt(); | ||
27 | |||
28 | bool isDone(); | ||
29 | void write(Sender *sender); | ||
30 | |||
31 | static int readIntDriver(const char *fullpath, int *value); | ||
32 | static int readInt64Driver(const char *fullpath, int64_t *value); | ||
33 | static int writeDriver(const char *fullpath, const char *data); | ||
34 | static int writeDriver(const char *path, int value); | ||
35 | static int writeDriver(const char *path, int64_t value); | ||
36 | static int writeReadDriver(const char *path, int *value); | ||
37 | static int writeReadDriver(const char *path, int64_t *value); | ||
38 | |||
39 | private: | ||
40 | Fifo *mFifo; | ||
41 | sem_t *const mSenderSem; | ||
42 | sem_t *const mStartProfile; | ||
43 | int mBufferSize; | ||
44 | int mBufferFD; | ||
45 | int mLength; | ||
46 | |||
47 | // Intentionally unimplemented | ||
48 | DriverSource(const DriverSource &); | ||
49 | DriverSource &operator=(const DriverSource &); | ||
50 | }; | ||
51 | |||
52 | #endif // DRIVERSOURCE_H | ||
diff --git a/daemon/DynBuf.cpp b/daemon/DynBuf.cpp new file mode 100644 index 0000000..6f92b33 --- /dev/null +++ b/daemon/DynBuf.cpp | |||
@@ -0,0 +1,139 @@ | |||
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 "DynBuf.h" | ||
10 | |||
11 | #include <errno.h> | ||
12 | #include <fcntl.h> | ||
13 | #include <stdarg.h> | ||
14 | #include <stdio.h> | ||
15 | #include <unistd.h> | ||
16 | |||
17 | #include "Logging.h" | ||
18 | |||
19 | // Pick an aggressive size as buffer is primarily used for disk IO | ||
20 | #define MIN_BUFFER_FREE (1 << 12) | ||
21 | |||
22 | int DynBuf::resize(const size_t minCapacity) { | ||
23 | size_t scaledCapacity = 2 * capacity; | ||
24 | if (scaledCapacity < minCapacity) { | ||
25 | scaledCapacity = minCapacity; | ||
26 | } | ||
27 | if (scaledCapacity < 2 * MIN_BUFFER_FREE) { | ||
28 | scaledCapacity = 2 * MIN_BUFFER_FREE; | ||
29 | } | ||
30 | capacity = scaledCapacity; | ||
31 | |||
32 | buf = static_cast<char *>(realloc(buf, capacity)); | ||
33 | if (buf == NULL) { | ||
34 | return -errno; | ||
35 | } | ||
36 | |||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | bool DynBuf::read(const char *const path) { | ||
41 | int result = false; | ||
42 | |||
43 | const int fd = open(path, O_RDONLY); | ||
44 | if (fd < 0) { | ||
45 | logg->logMessage("%s(%s:%i): open failed", __FUNCTION__, __FILE__, __LINE__); | ||
46 | return false; | ||
47 | } | ||
48 | |||
49 | length = 0; | ||
50 | |||
51 | for (;;) { | ||
52 | const size_t minCapacity = length + MIN_BUFFER_FREE + 1; | ||
53 | if (capacity < minCapacity) { | ||
54 | if (resize(minCapacity) != 0) { | ||
55 | logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); | ||
56 | goto fail; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | const ssize_t bytes = ::read(fd, buf + length, capacity - length - 1); | ||
61 | if (bytes < 0) { | ||
62 | logg->logMessage("%s(%s:%i): read failed", __FUNCTION__, __FILE__, __LINE__); | ||
63 | goto fail; | ||
64 | } else if (bytes == 0) { | ||
65 | break; | ||
66 | } | ||
67 | length += bytes; | ||
68 | } | ||
69 | |||
70 | buf[length] = '\0'; | ||
71 | result = true; | ||
72 | |||
73 | fail: | ||
74 | close(fd); | ||
75 | |||
76 | return result; | ||
77 | } | ||
78 | |||
79 | int DynBuf::readlink(const char *const path) { | ||
80 | ssize_t bytes = MIN_BUFFER_FREE; | ||
81 | |||
82 | for (;;) { | ||
83 | if (static_cast<size_t>(bytes) >= capacity) { | ||
84 | const int err = resize(2 * bytes); | ||
85 | if (err != 0) { | ||
86 | return err; | ||
87 | } | ||
88 | } | ||
89 | bytes = ::readlink(path, buf, capacity); | ||
90 | if (bytes < 0) { | ||
91 | return -errno; | ||
92 | } else if (static_cast<size_t>(bytes) < capacity) { | ||
93 | break; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | length = bytes; | ||
98 | buf[bytes] = '\0'; | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | bool DynBuf::printf(const char *format, ...) { | ||
104 | va_list ap; | ||
105 | |||
106 | if (capacity <= 0) { | ||
107 | if (resize(2 * MIN_BUFFER_FREE) != 0) { | ||
108 | logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); | ||
109 | return false; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | va_start(ap, format); | ||
114 | int bytes = vsnprintf(buf, capacity, format, ap); | ||
115 | va_end(ap); | ||
116 | if (bytes < 0) { | ||
117 | logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); | ||
118 | return false; | ||
119 | } | ||
120 | |||
121 | if (static_cast<size_t>(bytes) > capacity) { | ||
122 | if (resize(bytes + 1) != 0) { | ||
123 | logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); | ||
124 | return false; | ||
125 | } | ||
126 | |||
127 | va_start(ap, format); | ||
128 | bytes = vsnprintf(buf, capacity, format, ap); | ||
129 | va_end(ap); | ||
130 | if (bytes < 0) { | ||
131 | logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); | ||
132 | return false; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | length = bytes; | ||
137 | |||
138 | return true; | ||
139 | } | ||
diff --git a/daemon/DynBuf.h b/daemon/DynBuf.h new file mode 100644 index 0000000..2f4554a --- /dev/null +++ b/daemon/DynBuf.h | |||
@@ -0,0 +1,52 @@ | |||
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 | #ifndef DYNBUF_H | ||
10 | #define DYNBUF_H | ||
11 | |||
12 | #include <stdlib.h> | ||
13 | |||
14 | class DynBuf { | ||
15 | public: | ||
16 | DynBuf() : capacity(0), length(0), buf(NULL) {} | ||
17 | ~DynBuf() { | ||
18 | reset(); | ||
19 | } | ||
20 | |||
21 | inline void reset() { | ||
22 | capacity = 0; | ||
23 | length = 0; | ||
24 | if (buf != NULL) { | ||
25 | free(buf); | ||
26 | buf = NULL; | ||
27 | } | ||
28 | } | ||
29 | |||
30 | bool read(const char *const path); | ||
31 | // On error instead of printing the error and returning false, this returns -errno | ||
32 | int readlink(const char *const path); | ||
33 | __attribute__ ((format(printf, 2, 3))) | ||
34 | bool printf(const char *format, ...); | ||
35 | |||
36 | size_t getLength() const { return length; } | ||
37 | const char *getBuf() const { return buf; } | ||
38 | char *getBuf() { return buf; } | ||
39 | |||
40 | private: | ||
41 | int resize(const size_t minCapacity); | ||
42 | |||
43 | size_t capacity; | ||
44 | size_t length; | ||
45 | char *buf; | ||
46 | |||
47 | // Intentionally undefined | ||
48 | DynBuf(const DynBuf &); | ||
49 | DynBuf &operator=(const DynBuf &); | ||
50 | }; | ||
51 | |||
52 | #endif // DYNBUF_H | ||
diff --git a/daemon/EventsXML.cpp b/daemon/EventsXML.cpp index 2a80482..a07a046 100644 --- a/daemon/EventsXML.cpp +++ b/daemon/EventsXML.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -35,7 +35,7 @@ char* EventsXML::getXML() { | |||
35 | fclose(fl); | 35 | fclose(fl); |
36 | } else { | 36 | } else { |
37 | logg->logMessage("Unable to locate events.xml, using default"); | 37 | logg->logMessage("Unable to locate events.xml, using default"); |
38 | xml = mxmlLoadString(NULL, (char *)events_xml, MXML_NO_CALLBACK); | 38 | xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK); |
39 | } | 39 | } |
40 | 40 | ||
41 | // Add dynamic events from the drivers | 41 | // Add dynamic events from the drivers |
diff --git a/daemon/EventsXML.h b/daemon/EventsXML.h index 8e693ef..6cd1560 100644 --- a/daemon/EventsXML.h +++ b/daemon/EventsXML.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/ExternalSource.cpp b/daemon/ExternalSource.cpp new file mode 100644 index 0000000..fe5824b --- /dev/null +++ b/daemon/ExternalSource.cpp | |||
@@ -0,0 +1,56 @@ | |||
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 "ExternalSource.h" | ||
10 | |||
11 | #include <sys/prctl.h> | ||
12 | |||
13 | #include "Logging.h" | ||
14 | #include "OlySocket.h" | ||
15 | #include "SessionData.h" | ||
16 | |||
17 | ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 1024, senderSem), mSock("/tmp/gator") { | ||
18 | } | ||
19 | |||
20 | ExternalSource::~ExternalSource() { | ||
21 | } | ||
22 | |||
23 | bool ExternalSource::prepare() { | ||
24 | return true; | ||
25 | } | ||
26 | |||
27 | void ExternalSource::run() { | ||
28 | prctl(PR_SET_NAME, (unsigned long)&"gatord-uds", 0, 0, 0); | ||
29 | |||
30 | while (gSessionData->mSessionIsActive) { | ||
31 | // Will be aborted when the socket is closed at the end of the capture | ||
32 | int length = mSock.receive(mBuffer.getWritePos(), mBuffer.contiguousSpaceAvailable()); | ||
33 | if (length <= 0) { | ||
34 | break; | ||
35 | } | ||
36 | |||
37 | mBuffer.advanceWrite(length); | ||
38 | mBuffer.check(0); | ||
39 | } | ||
40 | |||
41 | mBuffer.setDone(); | ||
42 | } | ||
43 | |||
44 | void ExternalSource::interrupt() { | ||
45 | // Do nothing | ||
46 | } | ||
47 | |||
48 | bool ExternalSource::isDone() { | ||
49 | return mBuffer.isDone(); | ||
50 | } | ||
51 | |||
52 | void ExternalSource::write(Sender *sender) { | ||
53 | if (!mBuffer.isDone()) { | ||
54 | mBuffer.write(sender); | ||
55 | } | ||
56 | } | ||
diff --git a/daemon/ExternalSource.h b/daemon/ExternalSource.h new file mode 100644 index 0000000..2052bdf --- /dev/null +++ b/daemon/ExternalSource.h | |||
@@ -0,0 +1,40 @@ | |||
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 | #ifndef EXTERNALSOURCE_H | ||
10 | #define EXTERNALSOURCE_H | ||
11 | |||
12 | #include <semaphore.h> | ||
13 | |||
14 | #include "Buffer.h" | ||
15 | #include "OlySocket.h" | ||
16 | #include "Source.h" | ||
17 | |||
18 | // Unix domain socket counters from external sources like graphics drivers | ||
19 | class ExternalSource : public Source { | ||
20 | public: | ||
21 | ExternalSource(sem_t *senderSem); | ||
22 | ~ExternalSource(); | ||
23 | |||
24 | bool prepare(); | ||
25 | void run(); | ||
26 | void interrupt(); | ||
27 | |||
28 | bool isDone(); | ||
29 | void write(Sender *sender); | ||
30 | |||
31 | private: | ||
32 | Buffer mBuffer; | ||
33 | OlySocket mSock; | ||
34 | |||
35 | // Intentionally unimplemented | ||
36 | ExternalSource(const ExternalSource &); | ||
37 | ExternalSource &operator=(const ExternalSource &); | ||
38 | }; | ||
39 | |||
40 | #endif // EXTERNALSOURCE_H | ||
diff --git a/daemon/Fifo.cpp b/daemon/Fifo.cpp index 250a4d0..f672e92 100644 --- a/daemon/Fifo.cpp +++ b/daemon/Fifo.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/Fifo.h b/daemon/Fifo.h index d25cd68..7dd7426 100644 --- a/daemon/Fifo.h +++ b/daemon/Fifo.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -12,7 +12,7 @@ | |||
12 | #ifdef WIN32 | 12 | #ifdef WIN32 |
13 | #include <windows.h> | 13 | #include <windows.h> |
14 | #define sem_t HANDLE | 14 | #define sem_t HANDLE |
15 | #define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, INFINITE, NULL)) == NULL) | 15 | #define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, LONG_MAX, NULL)) == NULL) |
16 | #define sem_wait(sem) WaitForSingleObject(*(sem), INFINITE) | 16 | #define sem_wait(sem) WaitForSingleObject(*(sem), INFINITE) |
17 | #define sem_post(sem) ReleaseSemaphore(*(sem), 1, NULL) | 17 | #define sem_post(sem) ReleaseSemaphore(*(sem), 1, NULL) |
18 | #define sem_destroy(sem) CloseHandle(*(sem)) | 18 | #define sem_destroy(sem) CloseHandle(*(sem)) |
diff --git a/daemon/Hwmon.cpp b/daemon/Hwmon.cpp index 1d7c0da..778f307 100644 --- a/daemon/Hwmon.cpp +++ b/daemon/Hwmon.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -17,7 +17,7 @@ | |||
17 | 17 | ||
18 | class HwmonCounter { | 18 | class HwmonCounter { |
19 | public: | 19 | public: |
20 | HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature); | 20 | HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature); |
21 | ~HwmonCounter(); | 21 | ~HwmonCounter(); |
22 | 22 | ||
23 | HwmonCounter *getNext() const { return next; } | 23 | HwmonCounter *getNext() const { return next; } |
@@ -69,7 +69,7 @@ private: | |||
69 | HwmonCounter &operator=(const HwmonCounter &); | 69 | HwmonCounter &operator=(const HwmonCounter &); |
70 | }; | 70 | }; |
71 | 71 | ||
72 | HwmonCounter::HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(key), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) { | 72 | HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(getEventKey()), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) { |
73 | 73 | ||
74 | int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1; | 74 | int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1; |
75 | char *chip_name = new char[len]; | 75 | char *chip_name = new char[len]; |
@@ -205,6 +205,23 @@ bool HwmonCounter::canRead() { | |||
205 | } | 205 | } |
206 | 206 | ||
207 | Hwmon::Hwmon() : counters(NULL) { | 207 | Hwmon::Hwmon() : counters(NULL) { |
208 | } | ||
209 | |||
210 | Hwmon::~Hwmon() { | ||
211 | while (counters != NULL) { | ||
212 | HwmonCounter * counter = counters; | ||
213 | counters = counter->getNext(); | ||
214 | delete counter; | ||
215 | } | ||
216 | sensors_cleanup(); | ||
217 | } | ||
218 | |||
219 | void Hwmon::setup() { | ||
220 | // hwmon does not currently work with perf | ||
221 | if (gSessionData->perf.isSetup()) { | ||
222 | return; | ||
223 | } | ||
224 | |||
208 | int err = sensors_init(NULL); | 225 | int err = sensors_init(NULL); |
209 | if (err) { | 226 | if (err) { |
210 | logg->logMessage("Failed to initialize libsensors! (%d)", err); | 227 | logg->logMessage("Failed to initialize libsensors! (%d)", err); |
@@ -218,20 +235,11 @@ Hwmon::Hwmon() : counters(NULL) { | |||
218 | int feature_nr = 0; | 235 | int feature_nr = 0; |
219 | const sensors_feature *feature; | 236 | const sensors_feature *feature; |
220 | while ((feature = sensors_get_features(chip, &feature_nr))) { | 237 | while ((feature = sensors_get_features(chip, &feature_nr))) { |
221 | counters = new HwmonCounter(counters, getEventKey(), chip, feature); | 238 | counters = new HwmonCounter(counters, chip, feature); |
222 | } | 239 | } |
223 | } | 240 | } |
224 | } | 241 | } |
225 | 242 | ||
226 | Hwmon::~Hwmon() { | ||
227 | while (counters != NULL) { | ||
228 | HwmonCounter * counter = counters; | ||
229 | counters = counter->getNext(); | ||
230 | delete counter; | ||
231 | } | ||
232 | sensors_cleanup(); | ||
233 | } | ||
234 | |||
235 | HwmonCounter *Hwmon::findCounter(const Counter &counter) const { | 243 | HwmonCounter *Hwmon::findCounter(const Counter &counter) const { |
236 | for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) { | 244 | for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) { |
237 | if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) { | 245 | if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) { |
@@ -271,14 +279,18 @@ void Hwmon::setupCounter(Counter &counter) { | |||
271 | counter.setKey(hwmonCounter->getKey()); | 279 | counter.setKey(hwmonCounter->getKey()); |
272 | } | 280 | } |
273 | 281 | ||
274 | void Hwmon::writeCounters(mxml_node_t *root) const { | 282 | int Hwmon::writeCounters(mxml_node_t *root) const { |
283 | int count = 0; | ||
275 | for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { | 284 | for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { |
276 | if (!counter->canRead()) { | 285 | if (!counter->canRead()) { |
277 | continue; | 286 | continue; |
278 | } | 287 | } |
279 | mxml_node_t *node = mxmlNewElement(root, "counter"); | 288 | mxml_node_t *node = mxmlNewElement(root, "counter"); |
280 | mxmlElementSetAttr(node, "name", counter->getName()); | 289 | mxmlElementSetAttr(node, "name", counter->getName()); |
290 | ++count; | ||
281 | } | 291 | } |
292 | |||
293 | return count; | ||
282 | } | 294 | } |
283 | 295 | ||
284 | void Hwmon::writeEvents(mxml_node_t *root) const { | 296 | void Hwmon::writeEvents(mxml_node_t *root) const { |
diff --git a/daemon/Hwmon.h b/daemon/Hwmon.h index 46bb42e..a22a360 100644 --- a/daemon/Hwmon.h +++ b/daemon/Hwmon.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -19,12 +19,14 @@ public: | |||
19 | Hwmon(); | 19 | Hwmon(); |
20 | ~Hwmon(); | 20 | ~Hwmon(); |
21 | 21 | ||
22 | void setup(); | ||
23 | |||
22 | bool claimCounter(const Counter &counter) const; | 24 | bool claimCounter(const Counter &counter) const; |
23 | bool countersEnabled() const; | 25 | bool countersEnabled() const; |
24 | void resetCounters(); | 26 | void resetCounters(); |
25 | void setupCounter(Counter &counter); | 27 | void setupCounter(Counter &counter); |
26 | 28 | ||
27 | void writeCounters(mxml_node_t *root) const; | 29 | int writeCounters(mxml_node_t *root) const; |
28 | void writeEvents(mxml_node_t *root) const; | 30 | void writeEvents(mxml_node_t *root) const; |
29 | 31 | ||
30 | void start(); | 32 | void start(); |
diff --git a/daemon/KMod.cpp b/daemon/KMod.cpp index 559297f..9300002 100644 --- a/daemon/KMod.cpp +++ b/daemon/KMod.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -12,9 +12,9 @@ | |||
12 | #include <dirent.h> | 12 | #include <dirent.h> |
13 | #include <unistd.h> | 13 | #include <unistd.h> |
14 | 14 | ||
15 | #include "Collector.h" | ||
16 | #include "ConfigurationXML.h" | 15 | #include "ConfigurationXML.h" |
17 | #include "Counter.h" | 16 | #include "Counter.h" |
17 | #include "DriverSource.h" | ||
18 | #include "Logging.h" | 18 | #include "Logging.h" |
19 | 19 | ||
20 | // Claim all the counters in /dev/gator/events | 20 | // Claim all the counters in /dev/gator/events |
@@ -38,9 +38,9 @@ void KMod::resetCounters() { | |||
38 | continue; | 38 | continue; |
39 | snprintf(base, sizeof(base), "/dev/gator/events/%s", ent->d_name); | 39 | snprintf(base, sizeof(base), "/dev/gator/events/%s", ent->d_name); |
40 | snprintf(text, sizeof(text), "%s/enabled", base); | 40 | snprintf(text, sizeof(text), "%s/enabled", base); |
41 | Collector::writeDriver(text, 0); | 41 | DriverSource::writeDriver(text, 0); |
42 | snprintf(text, sizeof(text), "%s/count", base); | 42 | snprintf(text, sizeof(text), "%s/count", base); |
43 | Collector::writeDriver(text, 0); | 43 | DriverSource::writeDriver(text, 0); |
44 | } | 44 | } |
45 | closedir(dir); | 45 | closedir(dir); |
46 | } | 46 | } |
@@ -53,22 +53,22 @@ void KMod::setupCounter(Counter &counter) { | |||
53 | 53 | ||
54 | snprintf(text, sizeof(text), "%s/enabled", base); | 54 | snprintf(text, sizeof(text), "%s/enabled", base); |
55 | int enabled = true; | 55 | int enabled = true; |
56 | if (Collector::writeReadDriver(text, &enabled) || !enabled) { | 56 | if (DriverSource::writeReadDriver(text, &enabled) || !enabled) { |
57 | counter.setEnabled(false); | 57 | counter.setEnabled(false); |
58 | return; | 58 | return; |
59 | } | 59 | } |
60 | 60 | ||
61 | snprintf(text, sizeof(text), "%s/key", base); | 61 | snprintf(text, sizeof(text), "%s/key", base); |
62 | int key = 0; | 62 | int key = 0; |
63 | Collector::readIntDriver(text, &key); | 63 | DriverSource::readIntDriver(text, &key); |
64 | counter.setKey(key); | 64 | counter.setKey(key); |
65 | 65 | ||
66 | snprintf(text, sizeof(text), "%s/event", base); | 66 | snprintf(text, sizeof(text), "%s/event", base); |
67 | Collector::writeDriver(text, counter.getEvent()); | 67 | DriverSource::writeDriver(text, counter.getEvent()); |
68 | snprintf(text, sizeof(text), "%s/count", base); | 68 | snprintf(text, sizeof(text), "%s/count", base); |
69 | if (access(text, F_OK) == 0) { | 69 | if (access(text, F_OK) == 0) { |
70 | int count = counter.getCount(); | 70 | int count = counter.getCount(); |
71 | if (Collector::writeReadDriver(text, &count) && counter.getCount() > 0) { | 71 | if (DriverSource::writeReadDriver(text, &count) && counter.getCount() > 0) { |
72 | logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount()); | 72 | logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount()); |
73 | handleException(); | 73 | handleException(); |
74 | } | 74 | } |
@@ -80,23 +80,26 @@ void KMod::setupCounter(Counter &counter) { | |||
80 | } | 80 | } |
81 | } | 81 | } |
82 | 82 | ||
83 | void KMod::writeCounters(mxml_node_t *root) const { | 83 | int KMod::writeCounters(mxml_node_t *root) const { |
84 | struct dirent *ent; | 84 | struct dirent *ent; |
85 | mxml_node_t *counter; | 85 | mxml_node_t *counter; |
86 | 86 | ||
87 | // counters.xml is simply a file listing of /dev/gator/events | 87 | // counters.xml is simply a file listing of /dev/gator/events |
88 | DIR* dir = opendir("/dev/gator/events"); | 88 | DIR* dir = opendir("/dev/gator/events"); |
89 | if (dir == NULL) { | 89 | if (dir == NULL) { |
90 | logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events"); | 90 | return 0; |
91 | handleException(); | ||
92 | } | 91 | } |
93 | 92 | ||
93 | int count = 0; | ||
94 | while ((ent = readdir(dir)) != NULL) { | 94 | while ((ent = readdir(dir)) != NULL) { |
95 | // skip hidden files, current dir, and parent dir | 95 | // skip hidden files, current dir, and parent dir |
96 | if (ent->d_name[0] == '.') | 96 | if (ent->d_name[0] == '.') |
97 | continue; | 97 | continue; |
98 | counter = mxmlNewElement(root, "counter"); | 98 | counter = mxmlNewElement(root, "counter"); |
99 | mxmlElementSetAttr(counter, "name", ent->d_name); | 99 | mxmlElementSetAttr(counter, "name", ent->d_name); |
100 | ++count; | ||
100 | } | 101 | } |
101 | closedir(dir); | 102 | closedir(dir); |
103 | |||
104 | return count; | ||
102 | } | 105 | } |
diff --git a/daemon/KMod.h b/daemon/KMod.h index 7974262..fb7fc8a 100644 --- a/daemon/KMod.h +++ b/daemon/KMod.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -21,7 +21,7 @@ public: | |||
21 | void resetCounters(); | 21 | void resetCounters(); |
22 | void setupCounter(Counter &counter); | 22 | void setupCounter(Counter &counter); |
23 | 23 | ||
24 | void writeCounters(mxml_node_t *root) const; | 24 | int writeCounters(mxml_node_t *root) const; |
25 | }; | 25 | }; |
26 | 26 | ||
27 | #endif // KMOD_H | 27 | #endif // KMOD_H |
diff --git a/daemon/LocalCapture.cpp b/daemon/LocalCapture.cpp index 3235a34..d2a4b79 100644 --- a/daemon/LocalCapture.cpp +++ b/daemon/LocalCapture.cpp | |||
@@ -1,18 +1,20 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "LocalCapture.h" | ||
10 | |||
9 | #include <sys/stat.h> | 11 | #include <sys/stat.h> |
10 | #include <sys/types.h> | 12 | #include <sys/types.h> |
11 | #include <dirent.h> | 13 | #include <dirent.h> |
12 | #include <string.h> | 14 | #include <string.h> |
13 | #include <stdlib.h> | 15 | #include <stdlib.h> |
14 | #include <unistd.h> | 16 | #include <unistd.h> |
15 | #include "LocalCapture.h" | 17 | |
16 | #include "SessionData.h" | 18 | #include "SessionData.h" |
17 | #include "Logging.h" | 19 | #include "Logging.h" |
18 | #include "OlyUtility.h" | 20 | #include "OlyUtility.h" |
diff --git a/daemon/LocalCapture.h b/daemon/LocalCapture.h index 8042d6a..aadecce 100644 --- a/daemon/LocalCapture.h +++ b/daemon/LocalCapture.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/Logging.cpp b/daemon/Logging.cpp index 5fd45b5..b8d3178 100644 --- a/daemon/Logging.cpp +++ b/daemon/Logging.cpp | |||
@@ -1,11 +1,13 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "Logging.h" | ||
10 | |||
9 | #include <stdio.h> | 11 | #include <stdio.h> |
10 | #include <stdlib.h> | 12 | #include <stdlib.h> |
11 | #include <stdarg.h> | 13 | #include <stdarg.h> |
@@ -23,8 +25,6 @@ | |||
23 | #define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex) | 25 | #define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex) |
24 | #endif | 26 | #endif |
25 | 27 | ||
26 | #include "Logging.h" | ||
27 | |||
28 | // Global thread-safe logging | 28 | // Global thread-safe logging |
29 | Logging* logg = NULL; | 29 | Logging* logg = NULL; |
30 | 30 | ||
diff --git a/daemon/Logging.h b/daemon/Logging.h index 8f960de..6ae3280 100644 --- a/daemon/Logging.h +++ b/daemon/Logging.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -9,14 +9,7 @@ | |||
9 | #ifndef __LOGGING_H__ | 9 | #ifndef __LOGGING_H__ |
10 | #define __LOGGING_H__ | 10 | #define __LOGGING_H__ |
11 | 11 | ||
12 | #include <stdio.h> | ||
13 | #include <string.h> | ||
14 | #include <limits.h> | ||
15 | #ifdef WIN32 | ||
16 | #include <windows.h> | ||
17 | #else | ||
18 | #include <pthread.h> | 12 | #include <pthread.h> |
19 | #endif | ||
20 | 13 | ||
21 | #define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord" | 14 | #define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord" |
22 | 15 | ||
@@ -33,11 +26,7 @@ private: | |||
33 | char mErrBuf[4096]; // Arbitrarily large buffer to hold a string | 26 | char mErrBuf[4096]; // Arbitrarily large buffer to hold a string |
34 | char mLogBuf[4096]; // Arbitrarily large buffer to hold a string | 27 | char mLogBuf[4096]; // Arbitrarily large buffer to hold a string |
35 | bool mDebug; | 28 | bool mDebug; |
36 | #ifdef WIN32 | ||
37 | HANDLE mLoggingMutex; | ||
38 | #else | ||
39 | pthread_mutex_t mLoggingMutex; | 29 | pthread_mutex_t mLoggingMutex; |
40 | #endif | ||
41 | }; | 30 | }; |
42 | 31 | ||
43 | extern Logging* logg; | 32 | extern Logging* logg; |
diff --git a/daemon/Monitor.cpp b/daemon/Monitor.cpp new file mode 100644 index 0000000..90d5c47 --- /dev/null +++ b/daemon/Monitor.cpp | |||
@@ -0,0 +1,61 @@ | |||
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 "Monitor.h" | ||
10 | |||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <unistd.h> | ||
14 | |||
15 | #include "Logging.h" | ||
16 | |||
17 | Monitor::Monitor() : mFd(-1) { | ||
18 | } | ||
19 | |||
20 | Monitor::~Monitor() { | ||
21 | if (mFd >= -1) { | ||
22 | close(mFd); | ||
23 | } | ||
24 | } | ||
25 | |||
26 | bool Monitor::init() { | ||
27 | mFd = epoll_create(16); | ||
28 | if (mFd < 0) { | ||
29 | logg->logMessage("%s(%s:%i): epoll_create1 failed", __FUNCTION__, __FILE__, __LINE__); | ||
30 | return false; | ||
31 | } | ||
32 | |||
33 | return true; | ||
34 | } | ||
35 | |||
36 | bool Monitor::add(const int fd) { | ||
37 | struct epoll_event event; | ||
38 | memset(&event, 0, sizeof(event)); | ||
39 | event.data.fd = fd; | ||
40 | event.events = EPOLLIN; | ||
41 | if (epoll_ctl(mFd, EPOLL_CTL_ADD, fd, &event) != 0) { | ||
42 | logg->logMessage("%s(%s:%i): epoll_ctl failed", __FUNCTION__, __FILE__, __LINE__); | ||
43 | return false; | ||
44 | } | ||
45 | |||
46 | return true; | ||
47 | } | ||
48 | |||
49 | int Monitor::wait(struct epoll_event *const events, int maxevents, int timeout) { | ||
50 | int result = epoll_wait(mFd, events, maxevents, timeout); | ||
51 | if (result < 0) { | ||
52 | // Ignore if the call was interrupted as this will happen when SIGINT is received | ||
53 | if (errno == EINTR) { | ||
54 | result = 0; | ||
55 | } else { | ||
56 | logg->logMessage("%s(%s:%i): epoll_wait failed", __FUNCTION__, __FILE__, __LINE__); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | return result; | ||
61 | } | ||
diff --git a/daemon/Monitor.h b/daemon/Monitor.h new file mode 100644 index 0000000..6e268b6 --- /dev/null +++ b/daemon/Monitor.h | |||
@@ -0,0 +1,32 @@ | |||
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 | #ifndef MONITOR_H | ||
10 | #define MONITOR_H | ||
11 | |||
12 | #include <sys/epoll.h> | ||
13 | |||
14 | class Monitor { | ||
15 | public: | ||
16 | Monitor(); | ||
17 | ~Monitor(); | ||
18 | |||
19 | bool init(); | ||
20 | bool add(const int fd); | ||
21 | int wait(struct epoll_event *const events, int maxevents, int timeout); | ||
22 | |||
23 | private: | ||
24 | |||
25 | int mFd; | ||
26 | |||
27 | // Intentionally unimplemented | ||
28 | Monitor(const Monitor &); | ||
29 | Monitor &operator=(const Monitor &); | ||
30 | }; | ||
31 | |||
32 | #endif // MONITOR_H | ||
diff --git a/daemon/OlySocket.cpp b/daemon/OlySocket.cpp index ab5c3c2..26e4768 100644 --- a/daemon/OlySocket.cpp +++ b/daemon/OlySocket.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -15,6 +15,7 @@ | |||
15 | #else | 15 | #else |
16 | #include <netinet/in.h> | 16 | #include <netinet/in.h> |
17 | #include <sys/socket.h> | 17 | #include <sys/socket.h> |
18 | #include <sys/un.h> | ||
18 | #include <unistd.h> | 19 | #include <unistd.h> |
19 | #include <netdb.h> | 20 | #include <netdb.h> |
20 | #endif | 21 | #endif |
@@ -30,7 +31,7 @@ | |||
30 | #define SHUTDOWN_RX_TX SHUT_RDWR | 31 | #define SHUTDOWN_RX_TX SHUT_RDWR |
31 | #endif | 32 | #endif |
32 | 33 | ||
33 | OlySocket::OlySocket(int port, bool multiple) { | 34 | OlyServerSocket::OlyServerSocket(int port) { |
34 | #ifdef WIN32 | 35 | #ifdef WIN32 |
35 | WSADATA wsaData; | 36 | WSADATA wsaData; |
36 | if (WSAStartup(0x0202, &wsaData) != 0) { | 37 | if (WSAStartup(0x0202, &wsaData) != 0) { |
@@ -39,24 +40,82 @@ OlySocket::OlySocket(int port, bool multiple) { | |||
39 | } | 40 | } |
40 | #endif | 41 | #endif |
41 | 42 | ||
42 | if (multiple) { | 43 | createServerSocket(port); |
43 | createServerSocket(port); | ||
44 | } else { | ||
45 | createSingleServerConnection(port); | ||
46 | } | ||
47 | } | 44 | } |
48 | 45 | ||
49 | OlySocket::OlySocket(int port, char* host) { | 46 | OlySocket::OlySocket(int port, const char* host) { |
50 | mFDServer = 0; | ||
51 | createClientSocket(host, port); | 47 | createClientSocket(host, port); |
52 | } | 48 | } |
53 | 49 | ||
50 | OlySocket::OlySocket(int socketID) : mSocketID(socketID) { | ||
51 | } | ||
52 | |||
53 | #ifndef WIN32 | ||
54 | |||
55 | OlyServerSocket::OlyServerSocket(const char* path) { | ||
56 | // Create socket | ||
57 | mFDServer = socket(PF_UNIX, SOCK_STREAM, 0); | ||
58 | if (mFDServer < 0) { | ||
59 | logg->logError(__FILE__, __LINE__, "Error creating server socket"); | ||
60 | handleException(); | ||
61 | } | ||
62 | |||
63 | unlink(path); | ||
64 | |||
65 | // Create sockaddr_in structure, ensuring non-populated fields are zero | ||
66 | struct sockaddr_un sockaddr; | ||
67 | memset((void*)&sockaddr, 0, sizeof(sockaddr)); | ||
68 | sockaddr.sun_family = AF_UNIX; | ||
69 | strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); | ||
70 | sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; | ||
71 | |||
72 | // Bind the socket to an address | ||
73 | if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { | ||
74 | logg->logError(__FILE__, __LINE__, "Binding of server socket failed."); | ||
75 | handleException(); | ||
76 | } | ||
77 | |||
78 | // Listen for connections on this socket | ||
79 | if (listen(mFDServer, 1) < 0) { | ||
80 | logg->logError(__FILE__, __LINE__, "Listening of server socket failed"); | ||
81 | handleException(); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | OlySocket::OlySocket(const char* path) { | ||
86 | mSocketID = socket(PF_UNIX, SOCK_STREAM, 0); | ||
87 | if (mSocketID < 0) { | ||
88 | return; | ||
89 | } | ||
90 | |||
91 | // Create sockaddr_in structure, ensuring non-populated fields are zero | ||
92 | struct sockaddr_un sockaddr; | ||
93 | memset((void*)&sockaddr, 0, sizeof(sockaddr)); | ||
94 | sockaddr.sun_family = AF_UNIX; | ||
95 | strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); | ||
96 | sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; | ||
97 | |||
98 | if (connect(mSocketID, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { | ||
99 | close(mSocketID); | ||
100 | mSocketID = -1; | ||
101 | return; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | #endif | ||
106 | |||
54 | OlySocket::~OlySocket() { | 107 | OlySocket::~OlySocket() { |
55 | if (mSocketID > 0) { | 108 | if (mSocketID > 0) { |
56 | CLOSE_SOCKET(mSocketID); | 109 | CLOSE_SOCKET(mSocketID); |
57 | } | 110 | } |
58 | } | 111 | } |
59 | 112 | ||
113 | OlyServerSocket::~OlyServerSocket() { | ||
114 | if (mFDServer > 0) { | ||
115 | CLOSE_SOCKET(mFDServer); | ||
116 | } | ||
117 | } | ||
118 | |||
60 | void OlySocket::shutdownConnection() { | 119 | void OlySocket::shutdownConnection() { |
61 | // Shutdown is primarily used to unblock other threads that are blocking on send/receive functions | 120 | // Shutdown is primarily used to unblock other threads that are blocking on send/receive functions |
62 | shutdown(mSocketID, SHUTDOWN_RX_TX); | 121 | shutdown(mSocketID, SHUTDOWN_RX_TX); |
@@ -70,7 +129,7 @@ void OlySocket::closeSocket() { | |||
70 | } | 129 | } |
71 | } | 130 | } |
72 | 131 | ||
73 | void OlySocket::closeServerSocket() { | 132 | void OlyServerSocket::closeServerSocket() { |
74 | if (CLOSE_SOCKET(mFDServer) != 0) { | 133 | if (CLOSE_SOCKET(mFDServer) != 0) { |
75 | logg->logError(__FILE__, __LINE__, "Failed to close server socket."); | 134 | logg->logError(__FILE__, __LINE__, "Failed to close server socket."); |
76 | handleException(); | 135 | handleException(); |
@@ -78,7 +137,7 @@ void OlySocket::closeServerSocket() { | |||
78 | mFDServer = 0; | 137 | mFDServer = 0; |
79 | } | 138 | } |
80 | 139 | ||
81 | void OlySocket::createClientSocket(char* hostname, int portno) { | 140 | void OlySocket::createClientSocket(const char* hostname, int portno) { |
82 | #ifdef WIN32 | 141 | #ifdef WIN32 |
83 | // TODO: Implement for Windows | 142 | // TODO: Implement for Windows |
84 | #else | 143 | #else |
@@ -119,14 +178,7 @@ void OlySocket::createClientSocket(char* hostname, int portno) { | |||
119 | #endif | 178 | #endif |
120 | } | 179 | } |
121 | 180 | ||
122 | void OlySocket::createSingleServerConnection(int port) { | 181 | void OlyServerSocket::createServerSocket(int port) { |
123 | createServerSocket(port); | ||
124 | |||
125 | mSocketID = acceptConnection(); | ||
126 | closeServerSocket(); | ||
127 | } | ||
128 | |||
129 | void OlySocket::createServerSocket(int port) { | ||
130 | int family = AF_INET6; | 182 | int family = AF_INET6; |
131 | 183 | ||
132 | // Create socket | 184 | // Create socket |
@@ -169,22 +221,23 @@ void OlySocket::createServerSocket(int port) { | |||
169 | 221 | ||
170 | // mSocketID is always set to the most recently accepted connection | 222 | // mSocketID is always set to the most recently accepted connection |
171 | // The user of this class should maintain the different socket connections, e.g. by forking the process | 223 | // The user of this class should maintain the different socket connections, e.g. by forking the process |
172 | int OlySocket::acceptConnection() { | 224 | int OlyServerSocket::acceptConnection() { |
225 | int socketID; | ||
173 | if (mFDServer <= 0) { | 226 | if (mFDServer <= 0) { |
174 | logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket"); | 227 | logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket"); |
175 | handleException(); | 228 | handleException(); |
176 | } | 229 | } |
177 | 230 | ||
178 | // Accept a connection, note that this call blocks until a client connects | 231 | // Accept a connection, note that this call blocks until a client connects |
179 | mSocketID = accept(mFDServer, NULL, NULL); | 232 | socketID = accept(mFDServer, NULL, NULL); |
180 | if (mSocketID < 0) { | 233 | if (socketID < 0) { |
181 | logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); | 234 | logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); |
182 | handleException(); | 235 | handleException(); |
183 | } | 236 | } |
184 | return mSocketID; | 237 | return socketID; |
185 | } | 238 | } |
186 | 239 | ||
187 | void OlySocket::send(char* buffer, int size) { | 240 | void OlySocket::send(const char* buffer, int size) { |
188 | if (size <= 0 || buffer == NULL) { | 241 | if (size <= 0 || buffer == NULL) { |
189 | return; | 242 | return; |
190 | } | 243 | } |
diff --git a/daemon/OlySocket.h b/daemon/OlySocket.h index 5bab7d1..eab786b 100644 --- a/daemon/OlySocket.h +++ b/daemon/OlySocket.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -9,27 +9,44 @@ | |||
9 | #ifndef __OLY_SOCKET_H__ | 9 | #ifndef __OLY_SOCKET_H__ |
10 | #define __OLY_SOCKET_H__ | 10 | #define __OLY_SOCKET_H__ |
11 | 11 | ||
12 | #include <string.h> | ||
13 | |||
14 | class OlySocket { | 12 | class OlySocket { |
15 | public: | 13 | public: |
16 | OlySocket(int port, bool multipleConnections = false); | 14 | OlySocket(int port, const char* hostname); |
17 | OlySocket(int port, char* hostname); | 15 | OlySocket(int socketID); |
16 | #ifndef WIN32 | ||
17 | OlySocket(const char* path); | ||
18 | #endif | ||
18 | ~OlySocket(); | 19 | ~OlySocket(); |
19 | int acceptConnection(); | 20 | |
20 | void closeSocket(); | 21 | void closeSocket(); |
21 | void closeServerSocket(); | ||
22 | void shutdownConnection(); | 22 | void shutdownConnection(); |
23 | void send(char* buffer, int size); | 23 | void send(const char* buffer, int size); |
24 | void sendString(const char* string) {send((char*)string, strlen(string));} | ||
25 | int receive(char* buffer, int size); | 24 | int receive(char* buffer, int size); |
26 | int receiveNBytes(char* buffer, int size); | 25 | int receiveNBytes(char* buffer, int size); |
27 | int receiveString(char* buffer, int size); | 26 | int receiveString(char* buffer, int size); |
28 | int getSocketID() {return mSocketID;} | 27 | |
28 | bool isValid() const { return mSocketID >= 0; } | ||
29 | |||
30 | private: | ||
31 | int mSocketID; | ||
32 | |||
33 | void createClientSocket(const char* hostname, int port); | ||
34 | }; | ||
35 | |||
36 | class OlyServerSocket { | ||
37 | public: | ||
38 | OlyServerSocket(int port); | ||
39 | #ifndef WIN32 | ||
40 | OlyServerSocket(const char* path); | ||
41 | #endif | ||
42 | ~OlyServerSocket(); | ||
43 | |||
44 | int acceptConnection(); | ||
45 | void closeServerSocket(); | ||
46 | |||
29 | private: | 47 | private: |
30 | int mSocketID, mFDServer; | 48 | int mFDServer; |
31 | void createClientSocket(char* hostname, int port); | 49 | |
32 | void createSingleServerConnection(int port); | ||
33 | void createServerSocket(int port); | 50 | void createServerSocket(int port); |
34 | }; | 51 | }; |
35 | 52 | ||
diff --git a/daemon/OlyUtility.cpp b/daemon/OlyUtility.cpp index 0b22d6e..45340a2 100644 --- a/daemon/OlyUtility.cpp +++ b/daemon/OlyUtility.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/OlyUtility.h b/daemon/OlyUtility.h index abab0a5..1d26beb 100644 --- a/daemon/OlyUtility.h +++ b/daemon/OlyUtility.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/PerfBuffer.cpp b/daemon/PerfBuffer.cpp new file mode 100644 index 0000000..5fad583 --- /dev/null +++ b/daemon/PerfBuffer.cpp | |||
@@ -0,0 +1,139 @@ | |||
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 "PerfBuffer.h" | ||
10 | |||
11 | #include <sys/ioctl.h> | ||
12 | #include <sys/mman.h> | ||
13 | |||
14 | #include "Buffer.h" | ||
15 | #include "Logging.h" | ||
16 | #include "Sender.h" | ||
17 | #include "SessionData.h" | ||
18 | |||
19 | PerfBuffer::PerfBuffer() { | ||
20 | for (int cpu = 0; cpu < ARRAY_LENGTH(mBuf); ++cpu) { | ||
21 | mBuf[cpu] = MAP_FAILED; | ||
22 | mDiscard[cpu] = false; | ||
23 | } | ||
24 | } | ||
25 | |||
26 | PerfBuffer::~PerfBuffer() { | ||
27 | for (int cpu = ARRAY_LENGTH(mBuf) - 1; cpu >= 0; --cpu) { | ||
28 | if (mBuf[cpu] != MAP_FAILED) { | ||
29 | munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | |||
34 | bool PerfBuffer::useFd(const int cpu, const int fd, const int groupFd) { | ||
35 | if (fd == groupFd) { | ||
36 | if (mBuf[cpu] != MAP_FAILED) { | ||
37 | logg->logMessage("%s(%s:%i): cpu %i already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__, cpu); | ||
38 | return false; | ||
39 | } | ||
40 | |||
41 | // The buffer isn't mapped yet | ||
42 | mBuf[cpu] = mmap(NULL, gSessionData->mPageSize + BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | ||
43 | if (mBuf[cpu] == MAP_FAILED) { | ||
44 | logg->logMessage("%s(%s:%i): mmap failed", __FUNCTION__, __FILE__, __LINE__); | ||
45 | return false; | ||
46 | } | ||
47 | |||
48 | // Check the version | ||
49 | struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]); | ||
50 | if (pemp->compat_version != 0) { | ||
51 | logg->logMessage("%s(%s:%i): Incompatible perf_event_mmap_page compat_version", __FUNCTION__, __FILE__, __LINE__); | ||
52 | return false; | ||
53 | } | ||
54 | } else { | ||
55 | if (mBuf[cpu] == MAP_FAILED) { | ||
56 | logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); | ||
57 | return false; | ||
58 | } | ||
59 | |||
60 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, groupFd) < 0) { | ||
61 | logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); | ||
62 | return false; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | return true; | ||
67 | } | ||
68 | |||
69 | void PerfBuffer::discard(const int cpu) { | ||
70 | if (mBuf[cpu] != MAP_FAILED) { | ||
71 | mDiscard[cpu] = true; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | bool PerfBuffer::isEmpty() { | ||
76 | for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { | ||
77 | if (mBuf[cpu] != MAP_FAILED) { | ||
78 | // Take a snapshot of the positions | ||
79 | struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]); | ||
80 | const __u64 head = pemp->data_head; | ||
81 | const __u64 tail = pemp->data_tail; | ||
82 | |||
83 | if (head != tail) { | ||
84 | return false; | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | return true; | ||
90 | } | ||
91 | |||
92 | bool PerfBuffer::send(Sender *const sender) { | ||
93 | for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { | ||
94 | if (mBuf[cpu] == MAP_FAILED) { | ||
95 | continue; | ||
96 | } | ||
97 | |||
98 | // Take a snapshot of the positions | ||
99 | struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]); | ||
100 | const __u64 head = pemp->data_head; | ||
101 | const __u64 tail = pemp->data_tail; | ||
102 | |||
103 | if (head > tail) { | ||
104 | const uint8_t *const b = static_cast<uint8_t *>(mBuf[cpu]) + gSessionData->mPageSize; | ||
105 | const int offset = gSessionData->mLocalCapture ? 1 : 0; | ||
106 | unsigned char header[7]; | ||
107 | header[0] = RESPONSE_APC_DATA; | ||
108 | Buffer::writeLEInt(header + 1, head - tail + sizeof(header) - 5); | ||
109 | // Should use real packing functions | ||
110 | header[5] = FRAME_PERF; | ||
111 | header[6] = cpu; | ||
112 | |||
113 | // Write header | ||
114 | sender->writeData(reinterpret_cast<const char *>(&header) + offset, sizeof(header) - offset, RESPONSE_APC_DATA); | ||
115 | |||
116 | // Write data | ||
117 | if ((head & ~BUF_MASK) == (tail & ~BUF_MASK)) { | ||
118 | // Not wrapped | ||
119 | sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), head - tail, RESPONSE_APC_DATA); | ||
120 | } else { | ||
121 | // Wrapped | ||
122 | sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), BUF_SIZE - (tail & BUF_MASK), RESPONSE_APC_DATA); | ||
123 | sender->writeData(reinterpret_cast<const char *>(b), head & BUF_MASK, RESPONSE_APC_DATA); | ||
124 | } | ||
125 | |||
126 | // Update tail with the data read | ||
127 | pemp->data_tail = head; | ||
128 | } | ||
129 | |||
130 | if (mDiscard[cpu]) { | ||
131 | munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); | ||
132 | mBuf[cpu] = MAP_FAILED; | ||
133 | mDiscard[cpu] = false; | ||
134 | logg->logMessage("%s(%s:%i): Unmaped cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | return true; | ||
139 | } | ||
diff --git a/daemon/PerfBuffer.h b/daemon/PerfBuffer.h new file mode 100644 index 0000000..278a3b9 --- /dev/null +++ b/daemon/PerfBuffer.h | |||
@@ -0,0 +1,39 @@ | |||
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 | #ifndef PERF_BUFFER | ||
10 | #define PERF_BUFFER | ||
11 | |||
12 | #include "Config.h" | ||
13 | |||
14 | #define BUF_SIZE (gSessionData->mTotalBufferSize * 1024 * 1024) | ||
15 | #define BUF_MASK (BUF_SIZE - 1) | ||
16 | |||
17 | class Sender; | ||
18 | |||
19 | class PerfBuffer { | ||
20 | public: | ||
21 | PerfBuffer(); | ||
22 | ~PerfBuffer(); | ||
23 | |||
24 | bool useFd(const int cpu, const int fd, const int groupFd); | ||
25 | void discard(const int cpu); | ||
26 | bool isEmpty(); | ||
27 | bool send(Sender *const sender); | ||
28 | |||
29 | private: | ||
30 | void *mBuf[NR_CPUS]; | ||
31 | // After the buffer is flushed it should be unmaped | ||
32 | bool mDiscard[NR_CPUS]; | ||
33 | |||
34 | // Intentionally undefined | ||
35 | PerfBuffer(const PerfBuffer &); | ||
36 | PerfBuffer &operator=(const PerfBuffer &); | ||
37 | }; | ||
38 | |||
39 | #endif // PERF_BUFFER | ||
diff --git a/daemon/PerfDriver.cpp b/daemon/PerfDriver.cpp new file mode 100644 index 0000000..8e25c22 --- /dev/null +++ b/daemon/PerfDriver.cpp | |||
@@ -0,0 +1,355 @@ | |||
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 "PerfDriver.h" | ||
10 | |||
11 | #include <dirent.h> | ||
12 | #include <sys/utsname.h> | ||
13 | #include <time.h> | ||
14 | |||
15 | #include "Buffer.h" | ||
16 | #include "Config.h" | ||
17 | #include "ConfigurationXML.h" | ||
18 | #include "Counter.h" | ||
19 | #include "DriverSource.h" | ||
20 | #include "DynBuf.h" | ||
21 | #include "Logging.h" | ||
22 | #include "PerfGroup.h" | ||
23 | #include "SessionData.h" | ||
24 | |||
25 | #define PERF_DEVICES "/sys/bus/event_source/devices" | ||
26 | |||
27 | #define TYPE_DERIVED ~0U | ||
28 | |||
29 | // From gator.h | ||
30 | struct gator_cpu { | ||
31 | const int cpuid; | ||
32 | // Human readable name | ||
33 | const char core_name[32]; | ||
34 | // gatorfs event and Perf PMU name | ||
35 | const char *const pmnc_name; | ||
36 | const int pmnc_counters; | ||
37 | }; | ||
38 | |||
39 | // From gator_main.c | ||
40 | static const struct gator_cpu gator_cpus[] = { | ||
41 | { 0xb36, "ARM1136", "ARM_ARM11", 3 }, | ||
42 | { 0xb56, "ARM1156", "ARM_ARM11", 3 }, | ||
43 | { 0xb76, "ARM1176", "ARM_ARM11", 3 }, | ||
44 | { 0xb02, "ARM11MPCore", "ARM_ARM11MPCore", 3 }, | ||
45 | { 0xc05, "Cortex-A5", "ARMv7_Cortex_A5", 2 }, | ||
46 | { 0xc07, "Cortex-A7", "ARMv7_Cortex_A7", 4 }, | ||
47 | { 0xc08, "Cortex-A8", "ARMv7_Cortex_A8", 4 }, | ||
48 | { 0xc09, "Cortex-A9", "ARMv7_Cortex_A9", 6 }, | ||
49 | { 0xc0d, "Cortex-A12", "ARMv7_Cortex_A12", 6 }, | ||
50 | { 0xc0f, "Cortex-A15", "ARMv7_Cortex_A15", 6 }, | ||
51 | { 0xc0e, "Cortex-A17", "ARMv7_Cortex_A17", 6 }, | ||
52 | { 0x00f, "Scorpion", "Scorpion", 4 }, | ||
53 | { 0x02d, "ScorpionMP", "ScorpionMP", 4 }, | ||
54 | { 0x049, "KraitSIM", "Krait", 4 }, | ||
55 | { 0x04d, "Krait", "Krait", 4 }, | ||
56 | { 0x06f, "Krait S4 Pro", "Krait", 4 }, | ||
57 | { 0xd03, "Cortex-A53", "ARM_Cortex-A53", 6 }, | ||
58 | { 0xd07, "Cortex-A57", "ARM_Cortex-A57", 6 }, | ||
59 | { 0xd0f, "AArch64", "ARM_AArch64", 6 }, | ||
60 | }; | ||
61 | |||
62 | static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-"; | ||
63 | static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_"; | ||
64 | |||
65 | class PerfCounter { | ||
66 | public: | ||
67 | PerfCounter(PerfCounter *next, const char *name, uint32_t type, uint64_t config) : mNext(next), mName(name), mType(type), mCount(0), mKey(getEventKey()), mConfig(config), mEnabled(false) {} | ||
68 | ~PerfCounter() { | ||
69 | delete [] mName; | ||
70 | } | ||
71 | |||
72 | PerfCounter *getNext() const { return mNext; } | ||
73 | const char *getName() const { return mName; } | ||
74 | uint32_t getType() const { return mType; } | ||
75 | int getCount() const { return mCount; } | ||
76 | void setCount(const int count) { mCount = count; } | ||
77 | int getKey() const { return mKey; } | ||
78 | uint64_t getConfig() const { return mConfig; } | ||
79 | void setConfig(const uint64_t config) { mConfig = config; } | ||
80 | bool isEnabled() const { return mEnabled; } | ||
81 | void setEnabled(const bool enabled) { mEnabled = enabled; } | ||
82 | |||
83 | private: | ||
84 | PerfCounter *const mNext; | ||
85 | const char *const mName; | ||
86 | const uint32_t mType; | ||
87 | int mCount; | ||
88 | const int mKey; | ||
89 | uint64_t mConfig; | ||
90 | bool mEnabled; | ||
91 | }; | ||
92 | |||
93 | PerfDriver::PerfDriver() : mCounters(NULL), mIsSetup(false) { | ||
94 | } | ||
95 | |||
96 | PerfDriver::~PerfDriver() { | ||
97 | while (mCounters != NULL) { | ||
98 | PerfCounter *counter = mCounters; | ||
99 | mCounters = counter->getNext(); | ||
100 | delete counter; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | void PerfDriver::addCpuCounters(const char *const counterName, const int type, const int numCounters) { | ||
105 | int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1; | ||
106 | char *name = new char[len]; | ||
107 | snprintf(name, len, "%s_ccnt", counterName); | ||
108 | mCounters = new PerfCounter(mCounters, name, type, -1); | ||
109 | |||
110 | for (int j = 0; j < numCounters; ++j) { | ||
111 | len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1; | ||
112 | name = new char[len]; | ||
113 | snprintf(name, len, "%s_cnt%d", counterName, j); | ||
114 | mCounters = new PerfCounter(mCounters, name, type, -1); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | // From include/generated/uapi/linux/version.h | ||
119 | #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) | ||
120 | |||
121 | bool PerfDriver::setup() { | ||
122 | // Check the kernel version | ||
123 | struct utsname utsname; | ||
124 | if (uname(&utsname) != 0) { | ||
125 | logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); | ||
126 | return false; | ||
127 | } | ||
128 | |||
129 | int release[3] = { 0, 0, 0 }; | ||
130 | int part = 0; | ||
131 | char *ch = utsname.release; | ||
132 | while (*ch >= '0' && *ch <= '9' && part < ARRAY_LENGTH(release)) { | ||
133 | release[part] = 10*release[part] + *ch - '0'; | ||
134 | |||
135 | ++ch; | ||
136 | if (*ch == '.') { | ||
137 | ++part; | ||
138 | ++ch; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 12, 0)) { | ||
143 | logg->logMessage("%s(%s:%i): Unsupported kernel version", __FUNCTION__, __FILE__, __LINE__); | ||
144 | return false; | ||
145 | } | ||
146 | |||
147 | // Add supported PMUs | ||
148 | bool foundCpu = false; | ||
149 | DIR *dir = opendir(PERF_DEVICES); | ||
150 | if (dir == NULL) { | ||
151 | logg->logMessage("%s(%s:%i): opendif failed", __FUNCTION__, __FILE__, __LINE__); | ||
152 | return false; | ||
153 | } | ||
154 | |||
155 | struct dirent *dirent; | ||
156 | while ((dirent = readdir(dir)) != NULL) { | ||
157 | for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { | ||
158 | // Do the names match exactly? | ||
159 | if (strcmp(dirent->d_name, gator_cpus[i].pmnc_name) != 0 && | ||
160 | // Do these names match but have the old vs new prefix? | ||
161 | (strncmp(dirent->d_name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) != 0 || | ||
162 | strncmp(gator_cpus[i].pmnc_name, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) != 0 || | ||
163 | strcmp(dirent->d_name + sizeof(OLD_PMU_PREFIX) - 1, gator_cpus[i].pmnc_name + sizeof(NEW_PMU_PREFIX) - 1) != 0)) { | ||
164 | continue; | ||
165 | } | ||
166 | |||
167 | int type; | ||
168 | char buf[256]; | ||
169 | snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name); | ||
170 | if (DriverSource::readIntDriver(buf, &type) != 0) { | ||
171 | continue; | ||
172 | } | ||
173 | |||
174 | foundCpu = true; | ||
175 | addCpuCounters(gator_cpus[i].pmnc_name, type, gator_cpus[i].pmnc_counters); | ||
176 | } | ||
177 | } | ||
178 | closedir(dir); | ||
179 | |||
180 | if (!foundCpu) { | ||
181 | // If no cpu was found based on pmu names, try by cpuid | ||
182 | for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { | ||
183 | if (gSessionData->mMaxCpuId != gator_cpus[i].cpuid) { | ||
184 | continue; | ||
185 | } | ||
186 | |||
187 | foundCpu = true; | ||
188 | addCpuCounters(gator_cpus[i].pmnc_name, PERF_TYPE_RAW, gator_cpus[i].pmnc_counters); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | if (!foundCpu) { | ||
194 | // If all else fails, use the perf architected counters | ||
195 | // 9 because that's how many are in events-Perf-Hardware.xml - assume they can all be enabled at once | ||
196 | addCpuCounters("Perf_Hardware", PERF_TYPE_HARDWARE, 9); | ||
197 | } | ||
198 | */ | ||
199 | |||
200 | // Add supported software counters | ||
201 | long long id; | ||
202 | DynBuf printb; | ||
203 | |||
204 | id = getTracepointId("irq/softirq_exit", &printb); | ||
205 | if (id >= 0) { | ||
206 | mCounters = new PerfCounter(mCounters, "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id); | ||
207 | } | ||
208 | |||
209 | id = getTracepointId("irq/irq_handler_exit", &printb); | ||
210 | if (id >= 0) { | ||
211 | mCounters = new PerfCounter(mCounters, "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id); | ||
212 | } | ||
213 | |||
214 | //Linux_block_rq_wr | ||
215 | //Linux_block_rq_rd | ||
216 | //Linux_net_rx | ||
217 | //Linux_net_tx | ||
218 | |||
219 | id = getTracepointId(SCHED_SWITCH, &printb); | ||
220 | if (id >= 0) { | ||
221 | mCounters = new PerfCounter(mCounters, "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id); | ||
222 | } | ||
223 | |||
224 | //Linux_meminfo_memused | ||
225 | //Linux_meminfo_memfree | ||
226 | //Linux_meminfo_bufferram | ||
227 | //Linux_power_cpu_freq | ||
228 | //Linux_power_cpu_idle | ||
229 | |||
230 | mCounters = new PerfCounter(mCounters, "Linux_cpu_wait_contention", TYPE_DERIVED, -1); | ||
231 | |||
232 | //Linux_cpu_wait_io | ||
233 | |||
234 | mIsSetup = true; | ||
235 | return true; | ||
236 | } | ||
237 | |||
238 | bool PerfDriver::summary(Buffer *const buffer) { | ||
239 | struct utsname utsname; | ||
240 | if (uname(&utsname) != 0) { | ||
241 | logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); | ||
242 | return false; | ||
243 | } | ||
244 | |||
245 | char buf[512]; | ||
246 | snprintf(buf, sizeof(buf), "%s %s %s %s %s GNU/Linux", utsname.sysname, utsname.nodename, utsname.release, utsname.version, utsname.machine); | ||
247 | |||
248 | struct timespec ts; | ||
249 | if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { | ||
250 | logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__); | ||
251 | return false; | ||
252 | } | ||
253 | const int64_t timestamp = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec; | ||
254 | |||
255 | if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { | ||
256 | logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__); | ||
257 | return false; | ||
258 | } | ||
259 | const int64_t uptime = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec; | ||
260 | |||
261 | buffer->summary(timestamp, uptime, 0, buf); | ||
262 | |||
263 | for (int i = 0; i < gSessionData->mCores; ++i) { | ||
264 | int j; | ||
265 | for (j = 0; j < ARRAY_LENGTH(gator_cpus); ++j) { | ||
266 | if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { | ||
267 | break; | ||
268 | } | ||
269 | } | ||
270 | if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { | ||
271 | buffer->coreName(i, gSessionData->mCpuIds[i], gator_cpus[j].core_name); | ||
272 | } else { | ||
273 | snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[i]); | ||
274 | buffer->coreName(i, gSessionData->mCpuIds[i], buf); | ||
275 | } | ||
276 | } | ||
277 | buffer->commit(1); | ||
278 | |||
279 | return true; | ||
280 | } | ||
281 | |||
282 | PerfCounter *PerfDriver::findCounter(const Counter &counter) const { | ||
283 | for (PerfCounter * perfCounter = mCounters; perfCounter != NULL; perfCounter = perfCounter->getNext()) { | ||
284 | if (strcmp(perfCounter->getName(), counter.getType()) == 0) { | ||
285 | return perfCounter; | ||
286 | } | ||
287 | } | ||
288 | |||
289 | return NULL; | ||
290 | } | ||
291 | |||
292 | bool PerfDriver::claimCounter(const Counter &counter) const { | ||
293 | return findCounter(counter) != NULL; | ||
294 | } | ||
295 | |||
296 | void PerfDriver::resetCounters() { | ||
297 | for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { | ||
298 | counter->setEnabled(false); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | void PerfDriver::setupCounter(Counter &counter) { | ||
303 | PerfCounter *const perfCounter = findCounter(counter); | ||
304 | if (perfCounter == NULL) { | ||
305 | counter.setEnabled(false); | ||
306 | return; | ||
307 | } | ||
308 | |||
309 | // Don't use the config from counters XML if it's not set, ex: software counters | ||
310 | if (counter.getEvent() != -1) { | ||
311 | perfCounter->setConfig(counter.getEvent()); | ||
312 | } | ||
313 | perfCounter->setCount(counter.getCount()); | ||
314 | perfCounter->setEnabled(true); | ||
315 | counter.setKey(perfCounter->getKey()); | ||
316 | } | ||
317 | |||
318 | int PerfDriver::writeCounters(mxml_node_t *root) const { | ||
319 | int count = 0; | ||
320 | for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { | ||
321 | mxml_node_t *node = mxmlNewElement(root, "counter"); | ||
322 | mxmlElementSetAttr(node, "name", counter->getName()); | ||
323 | ++count; | ||
324 | } | ||
325 | |||
326 | return count; | ||
327 | } | ||
328 | |||
329 | bool PerfDriver::enable(PerfGroup *group, Buffer *const buffer) const { | ||
330 | for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { | ||
331 | if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED)) { | ||
332 | if (!group->add(buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), 0, 0)) { | ||
333 | logg->logMessage("%s(%s:%i): PerfGroup::add failed", __FUNCTION__, __FILE__, __LINE__); | ||
334 | return false; | ||
335 | } | ||
336 | } | ||
337 | } | ||
338 | |||
339 | return true; | ||
340 | } | ||
341 | |||
342 | long long PerfDriver::getTracepointId(const char *const name, DynBuf *const printb) { | ||
343 | if (!printb->printf(EVENTS_PATH "/%s/id", name)) { | ||
344 | logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); | ||
345 | return -1; | ||
346 | } | ||
347 | |||
348 | int64_t result; | ||
349 | if (DriverSource::readInt64Driver(printb->getBuf(), &result) != 0) { | ||
350 | logg->logMessage("%s(%s:%i): DriverSource::readInt64Driver failed", __FUNCTION__, __FILE__, __LINE__); | ||
351 | return -1; | ||
352 | } | ||
353 | |||
354 | return result; | ||
355 | } | ||
diff --git a/daemon/PerfDriver.h b/daemon/PerfDriver.h new file mode 100644 index 0000000..3181b74 --- /dev/null +++ b/daemon/PerfDriver.h | |||
@@ -0,0 +1,56 @@ | |||
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 | #ifndef PERFDRIVER_H | ||
10 | #define PERFDRIVER_H | ||
11 | |||
12 | #include "Driver.h" | ||
13 | |||
14 | // If debugfs is not mounted at /sys/kernel/debug, update DEBUGFS_PATH | ||
15 | #define DEBUGFS_PATH "/sys/kernel/debug" | ||
16 | #define EVENTS_PATH DEBUGFS_PATH "/tracing/events" | ||
17 | |||
18 | #define SCHED_SWITCH "sched/sched_switch" | ||
19 | |||
20 | class Buffer; | ||
21 | class DynBuf; | ||
22 | class PerfCounter; | ||
23 | class PerfGroup; | ||
24 | |||
25 | class PerfDriver : public Driver { | ||
26 | public: | ||
27 | PerfDriver(); | ||
28 | ~PerfDriver(); | ||
29 | |||
30 | bool setup(); | ||
31 | bool summary(Buffer *const buffer); | ||
32 | bool isSetup() const { return mIsSetup; } | ||
33 | |||
34 | bool claimCounter(const Counter &counter) const; | ||
35 | void resetCounters(); | ||
36 | void setupCounter(Counter &counter); | ||
37 | |||
38 | int writeCounters(mxml_node_t *root) const; | ||
39 | |||
40 | bool enable(PerfGroup *group, Buffer *const buffer) const; | ||
41 | |||
42 | static long long getTracepointId(const char *const name, DynBuf *const printb); | ||
43 | |||
44 | private: | ||
45 | PerfCounter *findCounter(const Counter &counter) const; | ||
46 | void addCpuCounters(const char *const counterName, const int type, const int numCounters); | ||
47 | |||
48 | PerfCounter *mCounters; | ||
49 | bool mIsSetup; | ||
50 | |||
51 | // Intentionally undefined | ||
52 | PerfDriver(const PerfDriver &); | ||
53 | PerfDriver &operator=(const PerfDriver &); | ||
54 | }; | ||
55 | |||
56 | #endif // PERFDRIVER_H | ||
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 | |||
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 | } | ||
diff --git a/daemon/PerfGroup.h b/daemon/PerfGroup.h new file mode 100644 index 0000000..af496d4 --- /dev/null +++ b/daemon/PerfGroup.h | |||
@@ -0,0 +1,55 @@ | |||
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 | #ifndef PERF_GROUP | ||
10 | #define PERF_GROUP | ||
11 | |||
12 | // Use a snapshot of perf_event.h as it may be more recent than what is on the target and if not newer features won't be supported anyways | ||
13 | #include "k/perf_event.h" | ||
14 | |||
15 | #include "Config.h" | ||
16 | |||
17 | class Buffer; | ||
18 | class Monitor; | ||
19 | class PerfBuffer; | ||
20 | |||
21 | enum PerfGroupFlags { | ||
22 | PERF_GROUP_MMAP = 1 << 0, | ||
23 | PERF_GROUP_COMM = 1 << 1, | ||
24 | PERF_GROUP_FREQ = 1 << 2, | ||
25 | PERF_GROUP_TASK = 1 << 3, | ||
26 | PERF_GROUP_SAMPLE_ID_ALL = 1 << 4, | ||
27 | }; | ||
28 | |||
29 | class PerfGroup { | ||
30 | public: | ||
31 | PerfGroup(PerfBuffer *const pb); | ||
32 | ~PerfGroup(); | ||
33 | |||
34 | bool add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags); | ||
35 | // Safe to call concurrently | ||
36 | bool prepareCPU(const int cpu); | ||
37 | // Not safe to call concurrently. Returns the number of events enabled | ||
38 | int onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor); | ||
39 | bool offlineCPU(int cpu); | ||
40 | bool start(); | ||
41 | void stop(); | ||
42 | |||
43 | private: | ||
44 | // +1 for the group leader | ||
45 | struct perf_event_attr mAttrs[MAX_PERFORMANCE_COUNTERS + 1]; | ||
46 | int mKeys[MAX_PERFORMANCE_COUNTERS + 1]; | ||
47 | int mFds[NR_CPUS * (MAX_PERFORMANCE_COUNTERS + 1)]; | ||
48 | PerfBuffer *const mPb; | ||
49 | |||
50 | // Intentionally undefined | ||
51 | PerfGroup(const PerfGroup &); | ||
52 | PerfGroup &operator=(const PerfGroup &); | ||
53 | }; | ||
54 | |||
55 | #endif // PERF_GROUP | ||
diff --git a/daemon/PerfSource.cpp b/daemon/PerfSource.cpp new file mode 100644 index 0000000..1f1cb19 --- /dev/null +++ b/daemon/PerfSource.cpp | |||
@@ -0,0 +1,271 @@ | |||
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, 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 | if (0 | ||
78 | || !mMonitor.init() | ||
79 | || !mUEvent.init() | ||
80 | || !mMonitor.add(mUEvent.getFd()) | ||
81 | |||
82 | || (schedSwitchId = PerfDriver::getTracepointId(SCHED_SWITCH, &printb)) < 0 | ||
83 | || !sendTracepointFormat(&mBuffer, SCHED_SWITCH, &printb, &b1) | ||
84 | |||
85 | // Only want RAW but not IP on sched_switch and don't want TID on SAMPLE_ID | ||
86 | || !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) | ||
87 | |||
88 | // Only want TID and IP but not RAW on timer | ||
89 | || (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, 0)) | ||
90 | |||
91 | || !gSessionData->perf.enable(&mCountersGroup, &mBuffer) | ||
92 | || 0) { | ||
93 | logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.12 or later?", __FUNCTION__, __FILE__, __LINE__); | ||
94 | return false; | ||
95 | } | ||
96 | |||
97 | if (!gSessionData->perf.summary(&mSummary)) { | ||
98 | logg->logMessage("%s(%s:%i): PerfDriver::summary failed", __FUNCTION__, __FILE__, __LINE__); | ||
99 | return false; | ||
100 | } | ||
101 | |||
102 | { | ||
103 | // Run prepareCPU in parallel as perf_event_open can take more than 1 sec in some cases | ||
104 | pthread_t threads[NR_CPUS]; | ||
105 | PrepareParallelArgs args[NR_CPUS]; | ||
106 | for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { | ||
107 | args[cpu].pg = &mCountersGroup; | ||
108 | args[cpu].cpu = cpu; | ||
109 | if (pthread_create(&threads[cpu], NULL, prepareParallel, &args[cpu]) != 0) { | ||
110 | logg->logMessage("%s(%s:%i): pthread_create failed", __FUNCTION__, __FILE__, __LINE__); | ||
111 | return false; | ||
112 | } | ||
113 | } | ||
114 | for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { | ||
115 | if (pthread_join(threads[cpu], NULL) != 0) { | ||
116 | logg->logMessage("%s(%s:%i): pthread_join failed", __FUNCTION__, __FILE__, __LINE__); | ||
117 | return false; | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | |||
122 | int numEvents = 0; | ||
123 | for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { | ||
124 | numEvents += mCountersGroup.onlineCPU(cpu, false, &mBuffer, &mMonitor); | ||
125 | } | ||
126 | if (numEvents <= 0) { | ||
127 | logg->logMessage("%s(%s:%i): PerfGroup::onlineCPU failed on all cores", __FUNCTION__, __FILE__, __LINE__); | ||
128 | return false; | ||
129 | } | ||
130 | |||
131 | // Start events before reading proc to avoid race conditions | ||
132 | if (!mCountersGroup.start()) { | ||
133 | logg->logMessage("%s(%s:%i): PerfGroup::start failed", __FUNCTION__, __FILE__, __LINE__); | ||
134 | return false; | ||
135 | } | ||
136 | |||
137 | if (!readProc(&mBuffer, &printb, &b1, &b2, &b3)) { | ||
138 | logg->logMessage("%s(%s:%i): readProc failed", __FUNCTION__, __FILE__, __LINE__); | ||
139 | return false; | ||
140 | } | ||
141 | |||
142 | mBuffer.commit(1); | ||
143 | |||
144 | return true; | ||
145 | } | ||
146 | |||
147 | static const char CPU_DEVPATH[] = "/devices/system/cpu/cpu"; | ||
148 | |||
149 | void PerfSource::run() { | ||
150 | int pipefd[2]; | ||
151 | |||
152 | if (pipe(pipefd) != 0) { | ||
153 | logg->logError(__FILE__, __LINE__, "pipe failed"); | ||
154 | handleException(); | ||
155 | } | ||
156 | mInterruptFd = pipefd[1]; | ||
157 | |||
158 | if (!mMonitor.add(pipefd[0])) { | ||
159 | logg->logError(__FILE__, __LINE__, "Monitor::add failed"); | ||
160 | handleException(); | ||
161 | } | ||
162 | |||
163 | int timeout = -1; | ||
164 | if (gSessionData->mLiveRate > 0) { | ||
165 | timeout = gSessionData->mLiveRate/MS_PER_US; | ||
166 | } | ||
167 | |||
168 | sem_post(mStartProfile); | ||
169 | |||
170 | while (gSessionData->mSessionIsActive) { | ||
171 | // +1 for uevents, +1 for pipe | ||
172 | struct epoll_event events[NR_CPUS + 2]; | ||
173 | int ready = mMonitor.wait(events, ARRAY_LENGTH(events), timeout); | ||
174 | if (ready < 0) { | ||
175 | logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); | ||
176 | handleException(); | ||
177 | } | ||
178 | |||
179 | for (int i = 0; i < ready; ++i) { | ||
180 | if (events[i].data.fd == mUEvent.getFd()) { | ||
181 | if (!handleUEvent()) { | ||
182 | logg->logError(__FILE__, __LINE__, "PerfSource::handleUEvent failed"); | ||
183 | handleException(); | ||
184 | } | ||
185 | break; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | // send a notification that data is ready | ||
190 | sem_post(mSenderSem); | ||
191 | |||
192 | // In one shot mode, stop collection once all the buffers are filled | ||
193 | // Assume timeout == 0 in this case | ||
194 | if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { | ||
195 | logg->logMessage("%s(%s:%i): One shot", __FUNCTION__, __FILE__, __LINE__); | ||
196 | child->endSession(); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | mCountersGroup.stop(); | ||
201 | mBuffer.setDone(); | ||
202 | mIsDone = true; | ||
203 | |||
204 | // send a notification that data is ready | ||
205 | sem_post(mSenderSem); | ||
206 | |||
207 | mInterruptFd = -1; | ||
208 | close(pipefd[0]); | ||
209 | close(pipefd[1]); | ||
210 | } | ||
211 | |||
212 | bool PerfSource::handleUEvent() { | ||
213 | UEventResult result; | ||
214 | if (!mUEvent.read(&result)) { | ||
215 | logg->logMessage("%s(%s:%i): UEvent::Read failed", __FUNCTION__, __FILE__, __LINE__); | ||
216 | return false; | ||
217 | } | ||
218 | |||
219 | if (strcmp(result.mSubsystem, "cpu") == 0) { | ||
220 | if (strncmp(result.mDevPath, CPU_DEVPATH, sizeof(CPU_DEVPATH) - 1) != 0) { | ||
221 | logg->logMessage("%s(%s:%i): Unexpected cpu DEVPATH format", __FUNCTION__, __FILE__, __LINE__); | ||
222 | return false; | ||
223 | } | ||
224 | char *endptr; | ||
225 | errno = 0; | ||
226 | int cpu = strtol(result.mDevPath + sizeof(CPU_DEVPATH) - 1, &endptr, 10); | ||
227 | if (errno != 0 || *endptr != '\0') { | ||
228 | logg->logMessage("%s(%s:%i): strtol failed", __FUNCTION__, __FILE__, __LINE__); | ||
229 | return false; | ||
230 | } | ||
231 | if (strcmp(result.mAction, "online") == 0) { | ||
232 | // Only call onlineCPU if prepareCPU succeeded | ||
233 | const bool result = mCountersGroup.prepareCPU(cpu) && | ||
234 | mCountersGroup.onlineCPU(cpu, true, &mBuffer, &mMonitor); | ||
235 | mBuffer.commit(1); | ||
236 | return result; | ||
237 | } else if (strcmp(result.mAction, "offline") == 0) { | ||
238 | return mCountersGroup.offlineCPU(cpu); | ||
239 | } | ||
240 | } | ||
241 | |||
242 | return true; | ||
243 | } | ||
244 | |||
245 | void PerfSource::interrupt() { | ||
246 | if (mInterruptFd >= 0) { | ||
247 | int8_t c = 0; | ||
248 | // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread | ||
249 | if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) { | ||
250 | logg->logError(__FILE__, __LINE__, "write failed"); | ||
251 | handleException(); | ||
252 | } | ||
253 | } | ||
254 | } | ||
255 | |||
256 | bool PerfSource::isDone () { | ||
257 | return mBuffer.isDone() && mIsDone && mCountersBuf.isEmpty(); | ||
258 | } | ||
259 | |||
260 | void PerfSource::write (Sender *sender) { | ||
261 | if (!mSummary.isDone()) { | ||
262 | mSummary.write(sender); | ||
263 | } | ||
264 | if (!mBuffer.isDone()) { | ||
265 | mBuffer.write(sender); | ||
266 | } | ||
267 | if (!mCountersBuf.send(sender)) { | ||
268 | logg->logError(__FILE__, __LINE__, "PerfBuffer::send failed"); | ||
269 | handleException(); | ||
270 | } | ||
271 | } | ||
diff --git a/daemon/PerfSource.h b/daemon/PerfSource.h new file mode 100644 index 0000000..3f471c8 --- /dev/null +++ b/daemon/PerfSource.h | |||
@@ -0,0 +1,54 @@ | |||
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 | #ifndef PERFSOURCE_H | ||
10 | #define PERFSOURCE_H | ||
11 | |||
12 | #include <semaphore.h> | ||
13 | |||
14 | #include "Buffer.h" | ||
15 | #include "Monitor.h" | ||
16 | #include "PerfBuffer.h" | ||
17 | #include "PerfGroup.h" | ||
18 | #include "Source.h" | ||
19 | #include "UEvent.h" | ||
20 | |||
21 | class Sender; | ||
22 | |||
23 | class PerfSource : public Source { | ||
24 | public: | ||
25 | PerfSource(sem_t *senderSem, sem_t *startProfile); | ||
26 | ~PerfSource(); | ||
27 | |||
28 | bool prepare(); | ||
29 | void run(); | ||
30 | void interrupt(); | ||
31 | |||
32 | bool isDone(); | ||
33 | void write(Sender *sender); | ||
34 | |||
35 | private: | ||
36 | bool handleUEvent(); | ||
37 | |||
38 | Buffer mSummary; | ||
39 | Buffer mBuffer; | ||
40 | PerfBuffer mCountersBuf; | ||
41 | PerfGroup mCountersGroup; | ||
42 | Monitor mMonitor; | ||
43 | UEvent mUEvent; | ||
44 | sem_t *const mSenderSem; | ||
45 | sem_t *const mStartProfile; | ||
46 | int mInterruptFd; | ||
47 | bool mIsDone; | ||
48 | |||
49 | // Intentionally undefined | ||
50 | PerfSource(const PerfSource &); | ||
51 | PerfSource &operator=(const PerfSource &); | ||
52 | }; | ||
53 | |||
54 | #endif // PERFSOURCE_H | ||
diff --git a/daemon/Proc.cpp b/daemon/Proc.cpp new file mode 100644 index 0000000..e0b9e22 --- /dev/null +++ b/daemon/Proc.cpp | |||
@@ -0,0 +1,179 @@ | |||
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 "Proc.h" | ||
10 | |||
11 | #include <dirent.h> | ||
12 | #include <errno.h> | ||
13 | #include <stdio.h> | ||
14 | #include <stdlib.h> | ||
15 | #include <string.h> | ||
16 | |||
17 | #include "Buffer.h" | ||
18 | #include "DynBuf.h" | ||
19 | #include "Logging.h" | ||
20 | |||
21 | struct ProcStat { | ||
22 | // From linux-dev/include/linux/sched.h | ||
23 | #define TASK_COMM_LEN 16 | ||
24 | // TASK_COMM_LEN may grow, so be ready for it to get larger | ||
25 | char comm[2*TASK_COMM_LEN]; | ||
26 | long numThreads; | ||
27 | }; | ||
28 | |||
29 | static bool readProcStat(ProcStat *const ps, const char *const pathname, DynBuf *const b) { | ||
30 | if (!b->read(pathname)) { | ||
31 | logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__); | ||
32 | // This is not a fatal error - the thread just doesn't exist any more | ||
33 | return true; | ||
34 | } | ||
35 | |||
36 | char *comm = strchr(b->getBuf(), '('); | ||
37 | if (comm == NULL) { | ||
38 | logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__); | ||
39 | return false; | ||
40 | } | ||
41 | ++comm; | ||
42 | char *const str = strrchr(comm, ')'); | ||
43 | if (str == NULL) { | ||
44 | logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__); | ||
45 | return false; | ||
46 | } | ||
47 | *str = '\0'; | ||
48 | strncpy(ps->comm, comm, sizeof(ps->comm) - 1); | ||
49 | ps->comm[sizeof(ps->comm) - 1] = '\0'; | ||
50 | |||
51 | const int count = sscanf(str + 2, " %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %ld", &ps->numThreads); | ||
52 | if (count != 1) { | ||
53 | logg->logMessage("%s(%s:%i): sscanf failed", __FUNCTION__, __FILE__, __LINE__); | ||
54 | return false; | ||
55 | } | ||
56 | |||
57 | return true; | ||
58 | } | ||
59 | |||
60 | static bool readProcTask(Buffer *const buffer, const int pid, const char *const image, DynBuf *const printb, DynBuf *const b) { | ||
61 | bool result = false; | ||
62 | |||
63 | if (!b->printf("/proc/%i/task", pid)) { | ||
64 | logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); | ||
65 | return result; | ||
66 | } | ||
67 | DIR *task = opendir(b->getBuf()); | ||
68 | if (task == NULL) { | ||
69 | logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); | ||
70 | return result; | ||
71 | } | ||
72 | |||
73 | struct dirent *dirent; | ||
74 | while ((dirent = readdir(task)) != NULL) { | ||
75 | char *endptr; | ||
76 | const int tid = strtol(dirent->d_name, &endptr, 10); | ||
77 | if (*endptr != '\0') { | ||
78 | // Ignore task items that are not integers like ., etc... | ||
79 | continue; | ||
80 | } | ||
81 | |||
82 | if (!printb->printf("/proc/%i/task/%i/stat", pid, tid)) { | ||
83 | logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); | ||
84 | goto fail; | ||
85 | } | ||
86 | ProcStat ps; | ||
87 | if (!readProcStat(&ps, printb->getBuf(), b)) { | ||
88 | logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__); | ||
89 | goto fail; | ||
90 | } | ||
91 | |||
92 | buffer->comm(pid, tid, image, ps.comm); | ||
93 | } | ||
94 | |||
95 | result = true; | ||
96 | |||
97 | fail: | ||
98 | closedir(task); | ||
99 | |||
100 | return result; | ||
101 | } | ||
102 | |||
103 | bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3) { | ||
104 | bool result = false; | ||
105 | |||
106 | DIR *proc = opendir("/proc"); | ||
107 | if (proc == NULL) { | ||
108 | logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); | ||
109 | return result; | ||
110 | } | ||
111 | |||
112 | struct dirent *dirent; | ||
113 | while ((dirent = readdir(proc)) != NULL) { | ||
114 | char *endptr; | ||
115 | const int pid = strtol(dirent->d_name, &endptr, 10); | ||
116 | if (*endptr != '\0') { | ||
117 | // Ignore proc items that are not integers like ., cpuinfo, etc... | ||
118 | continue; | ||
119 | } | ||
120 | |||
121 | if (!printb->printf("/proc/%i/stat", pid)) { | ||
122 | logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); | ||
123 | goto fail; | ||
124 | } | ||
125 | ProcStat ps; | ||
126 | if (!readProcStat(&ps, printb->getBuf(), b1)) { | ||
127 | logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__); | ||
128 | goto fail; | ||
129 | } | ||
130 | |||
131 | if (!printb->printf("/proc/%i/exe", pid)) { | ||
132 | logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); | ||
133 | goto fail; | ||
134 | } | ||
135 | const int err = b1->readlink(printb->getBuf()); | ||
136 | const char *image; | ||
137 | if (err == 0) { | ||
138 | image = strrchr(b1->getBuf(), '/'); | ||
139 | if (image == NULL) { | ||
140 | image = b1->getBuf(); | ||
141 | } else { | ||
142 | ++image; | ||
143 | } | ||
144 | } else if (err == -ENOENT) { | ||
145 | // readlink /proc/[pid]/exe returns ENOENT for kernel threads | ||
146 | image = "\0"; | ||
147 | } else { | ||
148 | logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__); | ||
149 | goto fail; | ||
150 | } | ||
151 | |||
152 | if (!printb->printf("/proc/%i/maps", pid)) { | ||
153 | logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); | ||
154 | goto fail; | ||
155 | } | ||
156 | if (!b2->read(printb->getBuf())) { | ||
157 | logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__); | ||
158 | // This is not a fatal error - the process just doesn't exist any more | ||
159 | continue; | ||
160 | } | ||
161 | |||
162 | buffer->maps(pid, pid, b2->getBuf()); | ||
163 | if (ps.numThreads <= 1) { | ||
164 | buffer->comm(pid, pid, image, ps.comm); | ||
165 | } else { | ||
166 | if (!readProcTask(buffer, pid, image, printb, b3)) { | ||
167 | logg->logMessage("%s(%s:%i): readProcTask failed", __FUNCTION__, __FILE__, __LINE__); | ||
168 | goto fail; | ||
169 | } | ||
170 | } | ||
171 | } | ||
172 | |||
173 | result = true; | ||
174 | |||
175 | fail: | ||
176 | closedir(proc); | ||
177 | |||
178 | return result; | ||
179 | } | ||
diff --git a/daemon/Proc.h b/daemon/Proc.h new file mode 100644 index 0000000..057b610 --- /dev/null +++ b/daemon/Proc.h | |||
@@ -0,0 +1,17 @@ | |||
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 | #ifndef PROC_H | ||
10 | #define PROC_H | ||
11 | |||
12 | class Buffer; | ||
13 | class DynBuf; | ||
14 | |||
15 | bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3); | ||
16 | |||
17 | #endif // PROC_H | ||
diff --git a/daemon/Sender.cpp b/daemon/Sender.cpp index 8eb348f..3a981a6 100644 --- a/daemon/Sender.cpp +++ b/daemon/Sender.cpp | |||
@@ -1,19 +1,18 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <string.h> | 9 | #include "Sender.h" |
10 | #include <sys/socket.h> | 10 | |
11 | #include <netinet/in.h> | ||
12 | #include <sys/types.h> | ||
13 | #include <arpa/inet.h> | ||
14 | #include <stdlib.h> | 11 | #include <stdlib.h> |
12 | #include <string.h> | ||
15 | #include <unistd.h> | 13 | #include <unistd.h> |
16 | #include "Sender.h" | 14 | |
15 | #include "Buffer.h" | ||
17 | #include "Logging.h" | 16 | #include "Logging.h" |
18 | #include "OlySocket.h" | 17 | #include "OlySocket.h" |
19 | #include "SessionData.h" | 18 | #include "SessionData.h" |
@@ -49,9 +48,12 @@ Sender::Sender(OlySocket* socket) { | |||
49 | } | 48 | } |
50 | 49 | ||
51 | Sender::~Sender() { | 50 | Sender::~Sender() { |
52 | delete mDataSocket; | 51 | // Just close it as the client socket is on the stack |
53 | mDataSocket = NULL; | 52 | if (mDataSocket != NULL) { |
54 | if (mDataFile) { | 53 | mDataSocket->closeSocket(); |
54 | mDataSocket = NULL; | ||
55 | } | ||
56 | if (mDataFile != NULL) { | ||
55 | fclose(mDataFile); | 57 | fclose(mDataFile); |
56 | } | 58 | } |
57 | } | 59 | } |
@@ -95,10 +97,7 @@ void Sender::writeData(const char* data, int length, int type) { | |||
95 | // type and length already added by the Collector for apc data | 97 | // type and length already added by the Collector for apc data |
96 | unsigned char header[5]; | 98 | unsigned char header[5]; |
97 | header[0] = type; | 99 | header[0] = type; |
98 | header[1] = (length >> 0) & 0xff; | 100 | Buffer::writeLEInt(header + 1, length); |
99 | header[2] = (length >> 8) & 0xff; | ||
100 | header[3] = (length >> 16) & 0xff; | ||
101 | header[4] = (length >> 24) & 0xff; | ||
102 | mDataSocket->send((char*)&header, sizeof(header)); | 101 | mDataSocket->send((char*)&header, sizeof(header)); |
103 | } | 102 | } |
104 | 103 | ||
@@ -106,7 +105,7 @@ void Sender::writeData(const char* data, int length, int type) { | |||
106 | const int chunkSize = 100*1000 * alarmDuration / 8; | 105 | const int chunkSize = 100*1000 * alarmDuration / 8; |
107 | int pos = 0; | 106 | int pos = 0; |
108 | while (true) { | 107 | while (true) { |
109 | mDataSocket->send((char*)data + pos, min(length - pos, chunkSize)); | 108 | mDataSocket->send((const char*)data + pos, min(length - pos, chunkSize)); |
110 | pos += chunkSize; | 109 | pos += chunkSize; |
111 | if (pos >= length) { | 110 | if (pos >= length) { |
112 | break; | 111 | break; |
diff --git a/daemon/Sender.h b/daemon/Sender.h index b388f03..4c359db 100644 --- a/daemon/Sender.h +++ b/daemon/Sender.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/SessionData.cpp b/daemon/SessionData.cpp index cf84407..c169299 100644 --- a/daemon/SessionData.cpp +++ b/daemon/SessionData.cpp | |||
@@ -1,13 +1,15 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <string.h> | ||
10 | #include "SessionData.h" | 9 | #include "SessionData.h" |
10 | |||
11 | #include <string.h> | ||
12 | |||
11 | #include "SessionXML.h" | 13 | #include "SessionXML.h" |
12 | #include "Logging.h" | 14 | #include "Logging.h" |
13 | 15 | ||
@@ -38,6 +40,7 @@ void SessionData::initialize() { | |||
38 | mTotalBufferSize = 0; | 40 | mTotalBufferSize = 0; |
39 | // sysconf(_SC_NPROCESSORS_CONF) is unreliable on 2.6 Android, get the value from the kernel module | 41 | // sysconf(_SC_NPROCESSORS_CONF) is unreliable on 2.6 Android, get the value from the kernel module |
40 | mCores = 1; | 42 | mCores = 1; |
43 | mPageSize = 0; | ||
41 | } | 44 | } |
42 | 45 | ||
43 | void SessionData::parseSessionXML(char* xmlString) { | 46 | void SessionData::parseSessionXML(char* xmlString) { |
@@ -88,7 +91,8 @@ void SessionData::parseSessionXML(char* xmlString) { | |||
88 | void SessionData::readCpuInfo() { | 91 | void SessionData::readCpuInfo() { |
89 | char temp[256]; // arbitrarily large amount | 92 | char temp[256]; // arbitrarily large amount |
90 | strcpy(mCoreName, "unknown"); | 93 | strcpy(mCoreName, "unknown"); |
91 | mCpuId = -1; | 94 | memset(&mCpuIds, -1, sizeof(mCpuIds)); |
95 | mMaxCpuId = -1; | ||
92 | 96 | ||
93 | FILE* f = fopen("/proc/cpuinfo", "r"); | 97 | FILE* f = fopen("/proc/cpuinfo", "r"); |
94 | if (f == NULL) { | 98 | if (f == NULL) { |
@@ -98,15 +102,16 @@ void SessionData::readCpuInfo() { | |||
98 | } | 102 | } |
99 | 103 | ||
100 | bool foundCoreName = false; | 104 | bool foundCoreName = false; |
101 | bool foundCpuId = false; | 105 | int processor = 0; |
102 | while (fgets(temp, sizeof(temp), f) && (!foundCoreName || !foundCpuId)) { | 106 | while (fgets(temp, sizeof(temp), f)) { |
103 | if (strlen(temp) > 0) { | 107 | if (strlen(temp) > 0) { |
104 | temp[strlen(temp) - 1] = 0; // Replace the line feed with a null | 108 | temp[strlen(temp) - 1] = 0; // Replace the line feed with a null |
105 | } | 109 | } |
106 | 110 | ||
107 | const bool foundHardware = strstr(temp, "Hardware") != 0; | 111 | const bool foundHardware = strstr(temp, "Hardware") != 0; |
108 | const bool foundCPUPart = strstr(temp, "CPU part") != 0; | 112 | const bool foundCPUPart = strstr(temp, "CPU part") != 0; |
109 | if (foundHardware || foundCPUPart) { | 113 | const bool foundProcessor = strstr(temp, "processor") != 0; |
114 | if (foundHardware || foundCPUPart || foundProcessor) { | ||
110 | char* position = strchr(temp, ':'); | 115 | char* position = strchr(temp, ':'); |
111 | if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) { | 116 | if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) { |
112 | logg->logMessage("Unknown format of /proc/cpuinfo\n" | 117 | logg->logMessage("Unknown format of /proc/cpuinfo\n" |
@@ -122,11 +127,15 @@ void SessionData::readCpuInfo() { | |||
122 | } | 127 | } |
123 | 128 | ||
124 | if (foundCPUPart) { | 129 | if (foundCPUPart) { |
125 | int cpuId = strtol(position, NULL, 16); | 130 | mCpuIds[processor] = strtol(position, NULL, 0); |
126 | if (cpuId > mCpuId) { | 131 | // If this does not have the full topology in /proc/cpuinfo, mCpuIds[0] may not have the 1 CPU part emitted - this guarantees it's in mMaxCpuId |
127 | mCpuId = cpuId; | 132 | if (mCpuIds[processor] > mMaxCpuId) { |
133 | mMaxCpuId = mCpuIds[processor]; | ||
128 | } | 134 | } |
129 | foundCpuId = true; | 135 | } |
136 | |||
137 | if (foundProcessor) { | ||
138 | processor = strtol(position, NULL, 0); | ||
130 | } | 139 | } |
131 | } | 140 | } |
132 | } | 141 | } |
diff --git a/daemon/SessionData.h b/daemon/SessionData.h index c834251..ea34240 100644 --- a/daemon/SessionData.h +++ b/daemon/SessionData.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -11,12 +11,12 @@ | |||
11 | 11 | ||
12 | #include <stdint.h> | 12 | #include <stdint.h> |
13 | 13 | ||
14 | #include "Config.h" | ||
14 | #include "Counter.h" | 15 | #include "Counter.h" |
15 | #include "Hwmon.h" | 16 | #include "Hwmon.h" |
17 | #include "PerfDriver.h" | ||
16 | 18 | ||
17 | #define MAX_PERFORMANCE_COUNTERS 50 | 19 | #define PROTOCOL_VERSION 18 |
18 | |||
19 | #define PROTOCOL_VERSION 17 | ||
20 | #define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions | 20 | #define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions |
21 | 21 | ||
22 | struct ImageLinkList { | 22 | struct ImageLinkList { |
@@ -34,6 +34,7 @@ public: | |||
34 | void parseSessionXML(char* xmlString); | 34 | void parseSessionXML(char* xmlString); |
35 | 35 | ||
36 | Hwmon hwmon; | 36 | Hwmon hwmon; |
37 | PerfDriver perf; | ||
37 | 38 | ||
38 | char mCoreName[MAX_STRING_LEN]; | 39 | char mCoreName[MAX_STRING_LEN]; |
39 | struct ImageLinkList *mImages; | 40 | struct ImageLinkList *mImages; |
@@ -47,6 +48,7 @@ public: | |||
47 | bool mSessionIsActive; | 48 | bool mSessionIsActive; |
48 | bool mLocalCapture; | 49 | bool mLocalCapture; |
49 | bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled | 50 | bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled |
51 | bool mIsEBS; | ||
50 | 52 | ||
51 | int mBacktraceDepth; | 53 | int mBacktraceDepth; |
52 | int mTotalBufferSize; // number of MB to use for the entire collection buffer | 54 | int mTotalBufferSize; // number of MB to use for the entire collection buffer |
@@ -54,7 +56,9 @@ public: | |||
54 | int64_t mLiveRate; | 56 | int64_t mLiveRate; |
55 | int mDuration; | 57 | int mDuration; |
56 | int mCores; | 58 | int mCores; |
57 | int mCpuId; | 59 | int mPageSize; |
60 | int mCpuIds[NR_CPUS]; | ||
61 | int mMaxCpuId; | ||
58 | 62 | ||
59 | // PMU Counters | 63 | // PMU Counters |
60 | int mCounterOverflow; | 64 | int mCounterOverflow; |
diff --git a/daemon/SessionXML.cpp b/daemon/SessionXML.cpp index 0a0a027..55b2f92 100644 --- a/daemon/SessionXML.cpp +++ b/daemon/SessionXML.cpp | |||
@@ -1,15 +1,17 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "SessionXML.h" | ||
10 | |||
9 | #include <string.h> | 11 | #include <string.h> |
10 | #include <stdlib.h> | 12 | #include <stdlib.h> |
11 | #include <limits.h> | 13 | #include <limits.h> |
12 | #include "SessionXML.h" | 14 | |
13 | #include "Logging.h" | 15 | #include "Logging.h" |
14 | #include "OlyUtility.h" | 16 | #include "OlyUtility.h" |
15 | #include "SessionData.h" | 17 | #include "SessionData.h" |
@@ -25,7 +27,7 @@ static const char* ATTR_DURATION = "duration"; | |||
25 | static const char* ATTR_PATH = "path"; | 27 | static const char* ATTR_PATH = "path"; |
26 | static const char* ATTR_LIVE_RATE = "live_rate"; | 28 | static const char* ATTR_LIVE_RATE = "live_rate"; |
27 | 29 | ||
28 | SessionXML::SessionXML(const char* str) { | 30 | SessionXML::SessionXML(const char *str) { |
29 | parameters.buffer_mode[0] = 0; | 31 | parameters.buffer_mode[0] = 0; |
30 | parameters.sample_rate[0] = 0; | 32 | parameters.sample_rate[0] = 0; |
31 | parameters.duration = 0; | 33 | parameters.duration = 0; |
@@ -33,13 +35,13 @@ SessionXML::SessionXML(const char* str) { | |||
33 | parameters.live_rate = 0; | 35 | parameters.live_rate = 0; |
34 | parameters.images = NULL; | 36 | parameters.images = NULL; |
35 | mPath = 0; | 37 | mPath = 0; |
36 | mSessionXML = (char*)str; | 38 | mSessionXML = (const char *)str; |
37 | logg->logMessage(mSessionXML); | 39 | logg->logMessage(mSessionXML); |
38 | } | 40 | } |
39 | 41 | ||
40 | SessionXML::~SessionXML() { | 42 | SessionXML::~SessionXML() { |
41 | if (mPath != 0) { | 43 | if (mPath != 0) { |
42 | free(mSessionXML); | 44 | free((char *)mSessionXML); |
43 | } | 45 | } |
44 | } | 46 | } |
45 | 47 | ||
diff --git a/daemon/SessionXML.h b/daemon/SessionXML.h index 0fb03bd..e146094 100644 --- a/daemon/SessionXML.h +++ b/daemon/SessionXML.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -24,13 +24,13 @@ struct ConfigParameters { | |||
24 | 24 | ||
25 | class SessionXML { | 25 | class SessionXML { |
26 | public: | 26 | public: |
27 | SessionXML(const char* str); | 27 | SessionXML(const char *str); |
28 | ~SessionXML(); | 28 | ~SessionXML(); |
29 | void parse(); | 29 | void parse(); |
30 | ConfigParameters parameters; | 30 | ConfigParameters parameters; |
31 | private: | 31 | private: |
32 | char* mSessionXML; | 32 | const char *mSessionXML; |
33 | char* mPath; | 33 | const char *mPath; |
34 | void sessionTag(mxml_node_t *tree, mxml_node_t *node); | 34 | void sessionTag(mxml_node_t *tree, mxml_node_t *node); |
35 | void sessionImage(mxml_node_t *node); | 35 | void sessionImage(mxml_node_t *node); |
36 | 36 | ||
diff --git a/daemon/Source.cpp b/daemon/Source.cpp new file mode 100644 index 0000000..60cf704 --- /dev/null +++ b/daemon/Source.cpp | |||
@@ -0,0 +1,33 @@ | |||
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 "Source.h" | ||
10 | |||
11 | #include "Logging.h" | ||
12 | |||
13 | Source::Source() : mThreadID() { | ||
14 | } | ||
15 | |||
16 | Source::~Source() { | ||
17 | } | ||
18 | |||
19 | void Source::start() { | ||
20 | if (pthread_create(&mThreadID, NULL, runStatic, this)) { | ||
21 | logg->logError(__FILE__, __LINE__, "Failed to create source thread"); | ||
22 | handleException(); | ||
23 | } | ||
24 | } | ||
25 | |||
26 | void Source::join() { | ||
27 | pthread_join(mThreadID, NULL); | ||
28 | } | ||
29 | |||
30 | void *Source::runStatic(void *arg) { | ||
31 | static_cast<Source *>(arg)->run(); | ||
32 | return NULL; | ||
33 | } | ||
diff --git a/daemon/Source.h b/daemon/Source.h new file mode 100644 index 0000000..56ac3d6 --- /dev/null +++ b/daemon/Source.h | |||
@@ -0,0 +1,40 @@ | |||
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 | #ifndef SOURCE_H | ||
10 | #define SOURCE_H | ||
11 | |||
12 | #include <pthread.h> | ||
13 | |||
14 | class Sender; | ||
15 | |||
16 | class Source { | ||
17 | public: | ||
18 | Source(); | ||
19 | virtual ~Source(); | ||
20 | |||
21 | virtual bool prepare() = 0; | ||
22 | void start(); | ||
23 | virtual void run() = 0; | ||
24 | virtual void interrupt() = 0; | ||
25 | void join(); | ||
26 | |||
27 | virtual bool isDone() = 0; | ||
28 | virtual void write(Sender *sender) = 0; | ||
29 | |||
30 | private: | ||
31 | static void *runStatic(void *arg); | ||
32 | |||
33 | pthread_t mThreadID; | ||
34 | |||
35 | // Intentionally undefined | ||
36 | Source(const Source &); | ||
37 | Source &operator=(const Source &); | ||
38 | }; | ||
39 | |||
40 | #endif // SOURCE_H | ||
diff --git a/daemon/StreamlineSetup.cpp b/daemon/StreamlineSetup.cpp index 2faada2..caa665e 100644 --- a/daemon/StreamlineSetup.cpp +++ b/daemon/StreamlineSetup.cpp | |||
@@ -1,26 +1,23 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2011-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2011-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <string.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <unistd.h> | ||
12 | #include <arpa/inet.h> | ||
13 | #include <sys/socket.h> | ||
14 | #include <netinet/in.h> | ||
15 | #include "Sender.h" | ||
16 | #include "Logging.h" | ||
17 | #include "OlyUtility.h" | ||
18 | #include "SessionData.h" | ||
19 | #include "CapturedXML.h" | ||
20 | #include "StreamlineSetup.h" | 9 | #include "StreamlineSetup.h" |
10 | |||
11 | #include "Buffer.h" | ||
12 | #include "CapturedXML.h" | ||
21 | #include "ConfigurationXML.h" | 13 | #include "ConfigurationXML.h" |
22 | #include "Driver.h" | 14 | #include "Driver.h" |
23 | #include "EventsXML.h" | 15 | #include "EventsXML.h" |
16 | #include "Logging.h" | ||
17 | #include "OlySocket.h" | ||
18 | #include "OlyUtility.h" | ||
19 | #include "Sender.h" | ||
20 | #include "SessionData.h" | ||
24 | 21 | ||
25 | static const char* TAG_SESSION = "session"; | 22 | static const char* TAG_SESSION = "session"; |
26 | static const char* TAG_REQUEST = "request"; | 23 | static const char* TAG_REQUEST = "request"; |
@@ -198,12 +195,9 @@ void StreamlineSetup::handleDeliver(char* xml) { | |||
198 | void StreamlineSetup::sendData(const char* data, uint32_t length, char type) { | 195 | void StreamlineSetup::sendData(const char* data, uint32_t length, char type) { |
199 | unsigned char header[5]; | 196 | unsigned char header[5]; |
200 | header[0] = type; | 197 | header[0] = type; |
201 | header[1] = (length >> 0) & 0xff; | 198 | Buffer::writeLEInt(header + 1, length); |
202 | header[2] = (length >> 8) & 0xff; | ||
203 | header[3] = (length >> 16) & 0xff; | ||
204 | header[4] = (length >> 24) & 0xff; | ||
205 | mSocket->send((char*)&header, sizeof(header)); | 199 | mSocket->send((char*)&header, sizeof(header)); |
206 | mSocket->send((char*)data, length); | 200 | mSocket->send((const char*)data, length); |
207 | } | 201 | } |
208 | 202 | ||
209 | void StreamlineSetup::sendEvents() { | 203 | void StreamlineSetup::sendEvents() { |
@@ -241,8 +235,14 @@ void StreamlineSetup::sendCounters() { | |||
241 | 235 | ||
242 | xml = mxmlNewXML("1.0"); | 236 | xml = mxmlNewXML("1.0"); |
243 | counters = mxmlNewElement(xml, "counters"); | 237 | counters = mxmlNewElement(xml, "counters"); |
238 | int count = 0; | ||
244 | for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { | 239 | for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { |
245 | driver->writeCounters(counters); | 240 | count += driver->writeCounters(counters); |
241 | } | ||
242 | |||
243 | if (count == 0) { | ||
244 | logg->logError(__FILE__, __LINE__, "No counters found, this could be because /dev/gator/events can not be read or because perf is not working correctly"); | ||
245 | handleException(); | ||
246 | } | 246 | } |
247 | 247 | ||
248 | char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); | 248 | char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); |
diff --git a/daemon/StreamlineSetup.h b/daemon/StreamlineSetup.h index d6d9a6e..74bb197 100644 --- a/daemon/StreamlineSetup.h +++ b/daemon/StreamlineSetup.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -9,7 +9,10 @@ | |||
9 | #ifndef __STREAMLINE_SETUP_H__ | 9 | #ifndef __STREAMLINE_SETUP_H__ |
10 | #define __STREAMLINE_SETUP_H__ | 10 | #define __STREAMLINE_SETUP_H__ |
11 | 11 | ||
12 | #include "OlySocket.h" | 12 | #include <stdint.h> |
13 | #include <string.h> | ||
14 | |||
15 | class OlySocket; | ||
13 | 16 | ||
14 | // Commands from Streamline | 17 | // Commands from Streamline |
15 | enum { | 18 | enum { |
diff --git a/daemon/UEvent.cpp b/daemon/UEvent.cpp new file mode 100644 index 0000000..282e965 --- /dev/null +++ b/daemon/UEvent.cpp | |||
@@ -0,0 +1,75 @@ | |||
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 "UEvent.h" | ||
10 | |||
11 | #include <linux/netlink.h> | ||
12 | #include <string.h> | ||
13 | #include <sys/socket.h> | ||
14 | #include <unistd.h> | ||
15 | |||
16 | #include "Logging.h" | ||
17 | |||
18 | static const char EMPTY[] = ""; | ||
19 | static const char ACTION[] = "ACTION="; | ||
20 | static const char DEVPATH[] = "DEVPATH="; | ||
21 | static const char SUBSYSTEM[] = "SUBSYSTEM="; | ||
22 | |||
23 | UEvent::UEvent() : mFd(-1) { | ||
24 | } | ||
25 | |||
26 | UEvent::~UEvent() { | ||
27 | if (mFd >= 0) { | ||
28 | close(mFd); | ||
29 | } | ||
30 | } | ||
31 | |||
32 | bool UEvent::init() { | ||
33 | mFd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT); | ||
34 | if (mFd < 0) { | ||
35 | logg->logMessage("%s(%s:%i): socket failed", __FUNCTION__, __FILE__, __LINE__); | ||
36 | return false; | ||
37 | } | ||
38 | |||
39 | struct sockaddr_nl sockaddr; | ||
40 | memset(&sockaddr, 0, sizeof(sockaddr)); | ||
41 | sockaddr.nl_family = AF_NETLINK; | ||
42 | sockaddr.nl_groups = 1; // bitmask: (1 << 0) == kernel events, (1 << 1) == udev events | ||
43 | sockaddr.nl_pid = 0; | ||
44 | if (bind(mFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) { | ||
45 | logg->logMessage("%s(%s:%i): bind failed", __FUNCTION__, __FILE__, __LINE__); | ||
46 | return false; | ||
47 | } | ||
48 | |||
49 | return true; | ||
50 | } | ||
51 | |||
52 | bool UEvent::read(UEventResult *const result) { | ||
53 | ssize_t bytes = recv(mFd, result->mBuf, sizeof(result->mBuf), 0); | ||
54 | if (bytes <= 0) { | ||
55 | logg->logMessage("%s(%s:%i): recv failed", __FUNCTION__, __FILE__, __LINE__); | ||
56 | return false; | ||
57 | } | ||
58 | |||
59 | result->mAction = EMPTY; | ||
60 | result->mDevPath = EMPTY; | ||
61 | result->mSubsystem = EMPTY; | ||
62 | |||
63 | for (int pos = 0; pos < bytes; pos += strlen(result->mBuf + pos) + 1) { | ||
64 | char *const str = result->mBuf + pos; | ||
65 | if (strncmp(str, ACTION, sizeof(ACTION) - 1) == 0) { | ||
66 | result->mAction = str + sizeof(ACTION) - 1; | ||
67 | } else if (strncmp(str, DEVPATH, sizeof(DEVPATH) - 1) == 0) { | ||
68 | result->mDevPath = str + sizeof(DEVPATH) - 1; | ||
69 | } else if (strncmp(str, SUBSYSTEM, sizeof(SUBSYSTEM) - 1) == 0) { | ||
70 | result->mSubsystem = str + sizeof(SUBSYSTEM) - 1; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | return true; | ||
75 | } | ||
diff --git a/daemon/UEvent.h b/daemon/UEvent.h new file mode 100644 index 0000000..2f7ef2c --- /dev/null +++ b/daemon/UEvent.h | |||
@@ -0,0 +1,36 @@ | |||
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 | #ifndef UEVENT_H | ||
10 | #define UEVENT_H | ||
11 | |||
12 | struct UEventResult { | ||
13 | const char *mAction; | ||
14 | const char *mDevPath; | ||
15 | const char *mSubsystem; | ||
16 | char mBuf[1<<13]; | ||
17 | }; | ||
18 | |||
19 | class UEvent { | ||
20 | public: | ||
21 | UEvent(); | ||
22 | ~UEvent(); | ||
23 | |||
24 | bool init(); | ||
25 | bool read(UEventResult *const result); | ||
26 | int getFd() const { return mFd; } | ||
27 | |||
28 | private: | ||
29 | int mFd; | ||
30 | |||
31 | // Intentionally undefined | ||
32 | UEvent(const UEvent &); | ||
33 | UEvent &operator=(const UEvent &); | ||
34 | }; | ||
35 | |||
36 | #endif // UEVENT_H | ||
diff --git a/daemon/UserSpaceSource.cpp b/daemon/UserSpaceSource.cpp new file mode 100644 index 0000000..debe696 --- /dev/null +++ b/daemon/UserSpaceSource.cpp | |||
@@ -0,0 +1,97 @@ | |||
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 "UserSpaceSource.h" | ||
10 | |||
11 | #include <sys/prctl.h> | ||
12 | #include <unistd.h> | ||
13 | |||
14 | #include "Child.h" | ||
15 | #include "DriverSource.h" | ||
16 | #include "Logging.h" | ||
17 | #include "SessionData.h" | ||
18 | |||
19 | #define NS_PER_S ((uint64_t)1000000000) | ||
20 | #define NS_PER_US 1000 | ||
21 | |||
22 | extern Child *child; | ||
23 | |||
24 | UserSpaceSource::UserSpaceSource(sem_t *senderSem) : mBuffer(0, FRAME_BLOCK_COUNTER, gSessionData->mTotalBufferSize*1024*1024, senderSem) { | ||
25 | } | ||
26 | |||
27 | UserSpaceSource::~UserSpaceSource() { | ||
28 | } | ||
29 | |||
30 | bool UserSpaceSource::prepare() { | ||
31 | return true; | ||
32 | } | ||
33 | |||
34 | void UserSpaceSource::run() { | ||
35 | prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); | ||
36 | |||
37 | gSessionData->hwmon.start(); | ||
38 | |||
39 | int64_t monotonic_started = 0; | ||
40 | while (monotonic_started <= 0) { | ||
41 | usleep(10); | ||
42 | |||
43 | if (DriverSource::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) { | ||
44 | logg->logError(__FILE__, __LINE__, "Error reading gator driver start time"); | ||
45 | handleException(); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | uint64_t next_time = 0; | ||
50 | while (gSessionData->mSessionIsActive) { | ||
51 | struct timespec ts; | ||
52 | #ifndef CLOCK_MONOTONIC_RAW | ||
53 | // Android doesn't have this defined but it was added in Linux 2.6.28 | ||
54 | #define CLOCK_MONOTONIC_RAW 4 | ||
55 | #endif | ||
56 | if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { | ||
57 | logg->logError(__FILE__, __LINE__, "Failed to get uptime"); | ||
58 | handleException(); | ||
59 | } | ||
60 | const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started; | ||
61 | // Sample ten times a second ignoring gSessionData->mSampleRate | ||
62 | next_time += NS_PER_S/10;//gSessionData->mSampleRate; | ||
63 | if (next_time < curr_time) { | ||
64 | logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time); | ||
65 | next_time = curr_time; | ||
66 | } | ||
67 | |||
68 | if (mBuffer.eventHeader(curr_time)) { | ||
69 | gSessionData->hwmon.read(&mBuffer); | ||
70 | // Only check after writing all counters so that time and corresponding counters appear in the same frame | ||
71 | mBuffer.check(curr_time); | ||
72 | } | ||
73 | |||
74 | if (mBuffer.bytesAvailable() <= 0) { | ||
75 | logg->logMessage("One shot (counters)"); | ||
76 | child->endSession(); | ||
77 | } | ||
78 | |||
79 | usleep((next_time - curr_time)/NS_PER_US); | ||
80 | } | ||
81 | |||
82 | mBuffer.setDone(); | ||
83 | } | ||
84 | |||
85 | void UserSpaceSource::interrupt() { | ||
86 | // Do nothing | ||
87 | } | ||
88 | |||
89 | bool UserSpaceSource::isDone() { | ||
90 | return mBuffer.isDone(); | ||
91 | } | ||
92 | |||
93 | void UserSpaceSource::write(Sender *sender) { | ||
94 | if (!mBuffer.isDone()) { | ||
95 | mBuffer.write(sender); | ||
96 | } | ||
97 | } | ||
diff --git a/daemon/UserSpaceSource.h b/daemon/UserSpaceSource.h new file mode 100644 index 0000000..fb5889d --- /dev/null +++ b/daemon/UserSpaceSource.h | |||
@@ -0,0 +1,38 @@ | |||
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 | #ifndef USERSPACESOURCE_H | ||
10 | #define USERSPACESOURCE_H | ||
11 | |||
12 | #include <semaphore.h> | ||
13 | |||
14 | #include "Buffer.h" | ||
15 | #include "Source.h" | ||
16 | |||
17 | // User space counters - currently just hwmon | ||
18 | class UserSpaceSource : public Source { | ||
19 | public: | ||
20 | UserSpaceSource(sem_t *senderSem); | ||
21 | ~UserSpaceSource(); | ||
22 | |||
23 | bool prepare(); | ||
24 | void run(); | ||
25 | void interrupt(); | ||
26 | |||
27 | bool isDone(); | ||
28 | void write(Sender *sender); | ||
29 | |||
30 | private: | ||
31 | Buffer mBuffer; | ||
32 | |||
33 | // Intentionally unimplemented | ||
34 | UserSpaceSource(const UserSpaceSource &); | ||
35 | UserSpaceSource &operator=(const UserSpaceSource &); | ||
36 | }; | ||
37 | |||
38 | #endif // USERSPACESOURCE_H | ||
diff --git a/daemon/common.mk b/daemon/common.mk index 031d169..d9dc146 100644 --- a/daemon/common.mk +++ b/daemon/common.mk | |||
@@ -25,7 +25,7 @@ include $(wildcard *.d) | |||
25 | include $(wildcard mxml/*.d) | 25 | include $(wildcard mxml/*.d) |
26 | 26 | ||
27 | EventsXML.cpp: events_xml.h | 27 | EventsXML.cpp: events_xml.h |
28 | ConfigurationXML.cpp: configuration_xml.h | 28 | ConfigurationXML.cpp: defaults_xml.h |
29 | 29 | ||
30 | # Don't regenerate conf-lex.c or conf-parse.c | 30 | # Don't regenerate conf-lex.c or conf-parse.c |
31 | libsensors/conf-lex.c: ; | 31 | libsensors/conf-lex.c: ; |
@@ -47,4 +47,4 @@ escape: escape.c | |||
47 | gcc $^ -o $@ | 47 | gcc $^ -o $@ |
48 | 48 | ||
49 | clean: | 49 | clean: |
50 | rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h configuration_xml.h | 50 | rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h defaults_xml.h |
diff --git a/daemon/configuration.xml b/daemon/defaults.xml index b44c00a..5bf096c 100644 --- a/daemon/configuration.xml +++ b/daemon/defaults.xml | |||
@@ -6,29 +6,34 @@ | |||
6 | <configuration counter="ARM_ARM11MPCore_ccnt" event="0xff"/> | 6 | <configuration counter="ARM_ARM11MPCore_ccnt" event="0xff"/> |
7 | <configuration counter="ARM_ARM11MPCore_cnt0" event="0x08"/> | 7 | <configuration counter="ARM_ARM11MPCore_cnt0" event="0x08"/> |
8 | <configuration counter="ARM_ARM11MPCore_cnt1" event="0x0b"/> | 8 | <configuration counter="ARM_ARM11MPCore_cnt1" event="0x0b"/> |
9 | <configuration counter="ARM_Cortex-A5_ccnt" event="0xff"/> | 9 | <configuration counter="ARMv7_Cortex_A5_ccnt" event="0xff"/> |
10 | <configuration counter="ARM_Cortex-A5_cnt0" event="0x8"/> | 10 | <configuration counter="ARMv7_Cortex_A5_cnt0" event="0x8"/> |
11 | <configuration counter="ARM_Cortex-A5_cnt1" event="0x1"/> | 11 | <configuration counter="ARMv7_Cortex_A5_cnt1" event="0x1"/> |
12 | <configuration counter="ARM_Cortex-A7_ccnt" event="0xff"/> | 12 | <configuration counter="ARMv7_Cortex_A7_ccnt" event="0xff"/> |
13 | <configuration counter="ARM_Cortex-A7_cnt0" event="0x08"/> | 13 | <configuration counter="ARMv7_Cortex_A7_cnt0" event="0x08"/> |
14 | <configuration counter="ARM_Cortex-A7_cnt1" event="0x10"/> | 14 | <configuration counter="ARMv7_Cortex_A7_cnt1" event="0x10"/> |
15 | <configuration counter="ARM_Cortex-A7_cnt2" event="0x16"/> | 15 | <configuration counter="ARMv7_Cortex_A7_cnt2" event="0x16"/> |
16 | <configuration counter="ARM_Cortex-A8_ccnt" event="0xff"/> | 16 | <configuration counter="ARMv7_Cortex_A8_ccnt" event="0xff"/> |
17 | <configuration counter="ARM_Cortex-A8_cnt0" event="0x8"/> | 17 | <configuration counter="ARMv7_Cortex_A8_cnt0" event="0x8"/> |
18 | <configuration counter="ARM_Cortex-A8_cnt1" event="0x44"/> | 18 | <configuration counter="ARMv7_Cortex_A8_cnt1" event="0x44"/> |
19 | <configuration counter="ARM_Cortex-A8_cnt2" event="0x43"/> | 19 | <configuration counter="ARMv7_Cortex_A8_cnt2" event="0x43"/> |
20 | <configuration counter="ARM_Cortex-A8_cnt3" event="0x10"/> | 20 | <configuration counter="ARMv7_Cortex_A8_cnt3" event="0x10"/> |
21 | <configuration counter="ARM_Cortex-A9_ccnt" event="0xff"/> | 21 | <configuration counter="ARMv7_Cortex_A9_ccnt" event="0xff"/> |
22 | <configuration counter="ARM_Cortex-A9_cnt0" event="0x68"/> | 22 | <configuration counter="ARMv7_Cortex_A9_cnt0" event="0x68"/> |
23 | <configuration counter="ARM_Cortex-A9_cnt1" event="0x06"/> | 23 | <configuration counter="ARMv7_Cortex_A9_cnt1" event="0x06"/> |
24 | <configuration counter="ARM_Cortex-A9_cnt2" event="0x07"/> | 24 | <configuration counter="ARMv7_Cortex_A9_cnt2" event="0x07"/> |
25 | <configuration counter="ARM_Cortex-A9_cnt3" event="0x03"/> | 25 | <configuration counter="ARMv7_Cortex_A9_cnt3" event="0x03"/> |
26 | <configuration counter="ARM_Cortex-A9_cnt4" event="0x04"/> | 26 | <configuration counter="ARMv7_Cortex_A9_cnt4" event="0x04"/> |
27 | <configuration counter="ARM_Cortex-A15_ccnt" event="0xff"/> | 27 | <configuration counter="ARMv7_Cortex_A12_ccnt" event="0xff"/> |
28 | <configuration counter="ARM_Cortex-A15_cnt0" event="0x8"/> | 28 | <configuration counter="ARMv7_Cortex_A12_cnt0" event="0x08"/> |
29 | <configuration counter="ARM_Cortex-A15_cnt1" event="0x16"/> | 29 | <configuration counter="ARMv7_Cortex_A12_cnt1" event="0x16"/> |
30 | <configuration counter="ARM_Cortex-A15_cnt2" event="0x10"/> | 30 | <configuration counter="ARMv7_Cortex_A12_cnt2" event="0x10"/> |
31 | <configuration counter="ARM_Cortex-A15_cnt3" event="0x19"/> | 31 | <configuration counter="ARMv7_Cortex_A12_cnt3" event="0x19"/> |
32 | <configuration counter="ARMv7_Cortex_A15_ccnt" event="0xff"/> | ||
33 | <configuration counter="ARMv7_Cortex_A15_cnt0" event="0x8"/> | ||
34 | <configuration counter="ARMv7_Cortex_A15_cnt1" event="0x16"/> | ||
35 | <configuration counter="ARMv7_Cortex_A15_cnt2" event="0x10"/> | ||
36 | <configuration counter="ARMv7_Cortex_A15_cnt3" event="0x19"/> | ||
32 | <configuration counter="ARM_Cortex-A53_ccnt" event="0x11"/> | 37 | <configuration counter="ARM_Cortex-A53_ccnt" event="0x11"/> |
33 | <configuration counter="ARM_Cortex-A53_cnt0" event="0x8"/> | 38 | <configuration counter="ARM_Cortex-A53_cnt0" event="0x8"/> |
34 | <configuration counter="ARM_Cortex-A53_cnt1" event="0x16"/> | 39 | <configuration counter="ARM_Cortex-A53_cnt1" event="0x16"/> |
diff --git a/daemon/escape.c b/daemon/escape.c index 3eec1f8..c54aa1c 100644 --- a/daemon/escape.c +++ b/daemon/escape.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/events-Cortex-A12.xml b/daemon/events-Cortex-A12.xml index 20a4772..9c04354 100644 --- a/daemon/events-Cortex-A12.xml +++ b/daemon/events-Cortex-A12.xml | |||
@@ -1,6 +1,6 @@ | |||
1 | <counter_set name="ARM_Cortex-A12_cnt" count="6"/> | 1 | <counter_set name="ARMv7_Cortex_A12_cnt" count="6"/> |
2 | <category name="Cortex-A12" counter_set="ARM_Cortex-A12_cnt" per_cpu="yes" supports_event_based_sampling="yes"> | 2 | <category name="Cortex-A12" counter_set="ARMv7_Cortex_A12_cnt" per_cpu="yes" supports_event_based_sampling="yes"> |
3 | <event counter="ARM_Cortex-A12_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> | 3 | <event counter="ARMv7_Cortex_A12_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> |
4 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> | 4 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> |
5 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> | 5 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> |
6 | <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/> | 6 | <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/> |
diff --git a/daemon/events-Cortex-A15.xml b/daemon/events-Cortex-A15.xml index faa8b1c..f50e55d 100644 --- a/daemon/events-Cortex-A15.xml +++ b/daemon/events-Cortex-A15.xml | |||
@@ -1,6 +1,6 @@ | |||
1 | <counter_set name="ARM_Cortex-A15_cnt" count="6"/> | 1 | <counter_set name="ARMv7_Cortex_A15_cnt" count="6"/> |
2 | <category name="Cortex-A15" counter_set="ARM_Cortex-A15_cnt" per_cpu="yes" supports_event_based_sampling="yes"> | 2 | <category name="Cortex-A15" counter_set="ARMv7_Cortex_A15_cnt" per_cpu="yes" supports_event_based_sampling="yes"> |
3 | <event counter="ARM_Cortex-A15_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> | 3 | <event counter="ARMv7_Cortex_A15_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> |
4 | <event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/> | 4 | <event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/> |
5 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> | 5 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> |
6 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> | 6 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> |
diff --git a/daemon/events-Cortex-A5.xml b/daemon/events-Cortex-A5.xml index a5b1546..d67581d 100644 --- a/daemon/events-Cortex-A5.xml +++ b/daemon/events-Cortex-A5.xml | |||
@@ -1,6 +1,6 @@ | |||
1 | <counter_set name="ARM_Cortex-A5_cnt" count="2"/> | 1 | <counter_set name="ARMv7_Cortex_A5_cnt" count="2"/> |
2 | <category name="Cortex-A5" counter_set="ARM_Cortex-A5_cnt" per_cpu="yes" supports_event_based_sampling="yes"> | 2 | <category name="Cortex-A5" counter_set="ARMv7_Cortex_A5_cnt" per_cpu="yes" supports_event_based_sampling="yes"> |
3 | <event counter="ARM_Cortex-A5_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> | 3 | <event counter="ARMv7_Cortex_A5_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> |
4 | <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/> | 4 | <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/> |
5 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> | 5 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> |
6 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> | 6 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> |
diff --git a/daemon/events-Cortex-A7.xml b/daemon/events-Cortex-A7.xml index 54d7264..6e078b3 100644 --- a/daemon/events-Cortex-A7.xml +++ b/daemon/events-Cortex-A7.xml | |||
@@ -1,6 +1,6 @@ | |||
1 | <counter_set name="ARM_Cortex-A7_cnt" count="4"/> | 1 | <counter_set name="ARMv7_Cortex_A7_cnt" count="4"/> |
2 | <category name="Cortex-A7" counter_set="ARM_Cortex-A7_cnt" per_cpu="yes" supports_event_based_sampling="yes"> | 2 | <category name="Cortex-A7" counter_set="ARMv7_Cortex_A7_cnt" per_cpu="yes" supports_event_based_sampling="yes"> |
3 | <event counter="ARM_Cortex-A7_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> | 3 | <event counter="ARMv7_Cortex_A7_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> |
4 | <event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/> | 4 | <event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/> |
5 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> | 5 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> |
6 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> | 6 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> |
diff --git a/daemon/events-Cortex-A8.xml b/daemon/events-Cortex-A8.xml index f251823..a69e25a 100644 --- a/daemon/events-Cortex-A8.xml +++ b/daemon/events-Cortex-A8.xml | |||
@@ -1,6 +1,6 @@ | |||
1 | <counter_set name="ARM_Cortex-A8_cnt" count="4"/> | 1 | <counter_set name="ARMv7_Cortex_A8_cnt" count="4"/> |
2 | <category name="Cortex-A8" counter_set="ARM_Cortex-A8_cnt" per_cpu="yes" supports_event_based_sampling="yes"> | 2 | <category name="Cortex-A8" counter_set="ARMv7_Cortex_A8_cnt" per_cpu="yes" supports_event_based_sampling="yes"> |
3 | <event counter="ARM_Cortex-A8_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> | 3 | <event counter="ARMv7_Cortex_A8_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> |
4 | <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/> | 4 | <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/> |
5 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> | 5 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> |
6 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> | 6 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> |
diff --git a/daemon/events-Cortex-A9.xml b/daemon/events-Cortex-A9.xml index 75f09c8..3e7f828 100644 --- a/daemon/events-Cortex-A9.xml +++ b/daemon/events-Cortex-A9.xml | |||
@@ -1,6 +1,6 @@ | |||
1 | <counter_set name="ARM_Cortex-A9_cnt" count="6"/> | 1 | <counter_set name="ARMv7_Cortex_A9_cnt" count="6"/> |
2 | <category name="Cortex-A9" counter_set="ARM_Cortex-A9_cnt" per_cpu="yes" supports_event_based_sampling="yes"> | 2 | <category name="Cortex-A9" counter_set="ARMv7_Cortex_A9_cnt" per_cpu="yes" supports_event_based_sampling="yes"> |
3 | <event counter="ARM_Cortex-A9_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> | 3 | <event counter="ARMv7_Cortex_A9_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> |
4 | <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/> | 4 | <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/> |
5 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> | 5 | <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> |
6 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> | 6 | <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> |
diff --git a/daemon/events-Linux.xml b/daemon/events-Linux.xml index 31a90a1..4d677e1 100644 --- a/daemon/events-Linux.xml +++ b/daemon/events-Linux.xml | |||
@@ -6,12 +6,12 @@ | |||
6 | <event counter="Linux_net_rx" title="Network" name="Receive" units="B" description="Receive network traffic, including effect from Streamline"/> | 6 | <event counter="Linux_net_rx" title="Network" name="Receive" units="B" description="Receive network traffic, including effect from Streamline"/> |
7 | <event counter="Linux_net_tx" title="Network" name="Transmit" units="B" description="Transmit network traffic, including effect from Streamline"/> | 7 | <event counter="Linux_net_tx" title="Network" name="Transmit" units="B" description="Transmit network traffic, including effect from Streamline"/> |
8 | <event counter="Linux_sched_switch" title="Scheduler" name="Switch" per_cpu="yes" description="Context switch events"/> | 8 | <event counter="Linux_sched_switch" title="Scheduler" name="Switch" per_cpu="yes" description="Context switch events"/> |
9 | <event counter="Linux_meminfo_memused" title="Memory" name="Used" display="maximum" units="B" proc="yes" description="Total used memory size. Note: a process' used memory includes shared memory that may be counted more than once (equivalent to RES from top). Kernel threads are not filterable."/> | 9 | <event counter="Linux_meminfo_memused" title="Memory" name="Used" class="absolute" units="B" proc="yes" description="Total used memory size. Note: a process' used memory includes shared memory that may be counted more than once (equivalent to RES from top). Kernel threads are not filterable."/> |
10 | <event counter="Linux_meminfo_memfree" title="Memory" name="Free" display="minimum" units="B" description="Available memory size"/> | 10 | <event counter="Linux_meminfo_memfree" title="Memory" name="Free" class="absolute" display="minimum" units="B" description="Available memory size"/> |
11 | <event counter="Linux_meminfo_bufferram" title="Memory" name="Buffer" display="maximum" units="B" description="Memory used by OS disk buffers"/> | 11 | <event counter="Linux_meminfo_bufferram" title="Memory" name="Buffer" class="absolute" units="B" description="Memory used by OS disk buffers"/> |
12 | <event counter="Linux_power_cpu_freq" title="Clock" name="Frequency" per_cpu="yes" display="maximum" units="Hz" series_composition="overlay" average_cores="yes" description="Frequency setting of the CPU"/> | 12 | <event counter="Linux_power_cpu_freq" title="Clock" name="Frequency" per_cpu="yes" class="absolute" units="Hz" series_composition="overlay" average_cores="yes" description="Frequency setting of the CPU"/> |
13 | <event counter="Linux_power_cpu_idle" title="Idle" name="State" per_cpu="yes" display="maximum" description="CPU Idle State + 1, set the Sample Rate to None to prevent the hrtimer from interrupting the system"/> | 13 | <event counter="Linux_power_cpu_idle" title="Idle" name="State" per_cpu="yes" class="absolute" description="CPU Idle State + 1, set the Sample Rate to None to prevent the hrtimer from interrupting the system"/> |
14 | <event counter="Linux_cpu_wait_contention" title="CPU Contention" name="Wait" per_cpu="no" display="average" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on contended resource"/> | 14 | <event counter="Linux_cpu_wait_contention" title="CPU Contention" name="Wait" per_cpu="no" class="activity" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on contended resource"/> |
15 | <event counter="Linux_cpu_wait_io" title="CPU I/O" name="Wait" per_cpu="no" display="average" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on I/O resource"/> | 15 | <event counter="Linux_cpu_wait_io" title="CPU I/O" name="Wait" per_cpu="no" class="activity" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on I/O resource"/> |
16 | </category> | 16 | </category> |
17 | 17 | ||
diff --git a/daemon/events-Mali-4xx.xml b/daemon/events-Mali-4xx.xml index 8772ce4..5a71386 100644 --- a/daemon/events-Mali-4xx.xml +++ b/daemon/events-Mali-4xx.xml | |||
@@ -207,7 +207,7 @@ | |||
207 | <event event="0x0400" option_set="fs" title="ARM Mali-4xx" name="Filmstrip" description="Scaled framebuffer"/> | 207 | <event event="0x0400" option_set="fs" title="ARM Mali-4xx" name="Filmstrip" description="Scaled framebuffer"/> |
208 | </category> | 208 | </category> |
209 | <category name="ARM_Mali-4xx_Voltage" per_cpu="no"> | 209 | <category name="ARM_Mali-4xx_Voltage" per_cpu="no"> |
210 | <event counter="ARM_Mali-4xx_Voltage" title="Mali GPU Voltage" name="Voltage" display="average" average_selection="yes" units="mV" description="GPU core voltage."/> | 210 | <event counter="ARM_Mali-4xx_Voltage" title="Mali GPU Voltage" name="Voltage" class="absolute" display="average" average_selection="yes" units="mV" description="GPU core voltage."/> |
211 | </category> | 211 | </category> |
212 | <category name="ARM_Mali-4xx_Frequency" per_cpu="no"> | 212 | <category name="ARM_Mali-4xx_Frequency" per_cpu="no"> |
213 | <event counter="ARM_Mali-4xx_Frequency" title="Mali GPU Frequency" name="Frequency" display="average" average_selection="yes" units="MHz" description="GPU core frequency."/> | 213 | <event counter="ARM_Mali-4xx_Frequency" title="Mali GPU Frequency" name="Frequency" display="average" average_selection="yes" units="MHz" description="GPU core frequency."/> |
diff --git a/daemon/events-Mali-T6xx.xml b/daemon/events-Mali-T6xx.xml index 2465238..ec9ca00 100644 --- a/daemon/events-Mali-T6xx.xml +++ b/daemon/events-Mali-T6xx.xml | |||
@@ -4,14 +4,14 @@ | |||
4 | </category> | 4 | </category> |
5 | 5 | ||
6 | <category name="Mali-T6xx-PMShader" per_cpu="no"> | 6 | <category name="Mali-T6xx-PMShader" per_cpu="no"> |
7 | <event counter="ARM_Mali-T6xx_PM_SHADER_0" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 0" description="Mali PM Shader: PM Shader Core 0."/> | 7 | <event counter="ARM_Mali-T6xx_PM_SHADER_0" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 0" description="Mali PM Shader: PM Shader Core 0."/> |
8 | <event counter="ARM_Mali-T6xx_PM_SHADER_1" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 1" description="Mali PM Shader: PM Shader Core 1."/> | 8 | <event counter="ARM_Mali-T6xx_PM_SHADER_1" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 1" description="Mali PM Shader: PM Shader Core 1."/> |
9 | <event counter="ARM_Mali-T6xx_PM_SHADER_2" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 2" description="Mali PM Shader: PM Shader Core 2."/> | 9 | <event counter="ARM_Mali-T6xx_PM_SHADER_2" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 2" description="Mali PM Shader: PM Shader Core 2."/> |
10 | <event counter="ARM_Mali-T6xx_PM_SHADER_3" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 3" description="Mali PM Shader: PM Shader Core 3."/> | 10 | <event counter="ARM_Mali-T6xx_PM_SHADER_3" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 3" description="Mali PM Shader: PM Shader Core 3."/> |
11 | <event counter="ARM_Mali-T6xx_PM_SHADER_4" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 4" description="Mali PM Shader: PM Shader Core 4."/> | 11 | <event counter="ARM_Mali-T6xx_PM_SHADER_4" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 4" description="Mali PM Shader: PM Shader Core 4."/> |
12 | <event counter="ARM_Mali-T6xx_PM_SHADER_5" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 5" description="Mali PM Shader: PM Shader Core 5."/> | 12 | <event counter="ARM_Mali-T6xx_PM_SHADER_5" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 5" description="Mali PM Shader: PM Shader Core 5."/> |
13 | <event counter="ARM_Mali-T6xx_PM_SHADER_6" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 6" description="Mali PM Shader: PM Shader Core 6."/> | 13 | <event counter="ARM_Mali-T6xx_PM_SHADER_6" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 6" description="Mali PM Shader: PM Shader Core 6."/> |
14 | <event counter="ARM_Mali-T6xx_PM_SHADER_7" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 7" description="Mali PM Shader: PM Shader Core 7."/> | 14 | <event counter="ARM_Mali-T6xx_PM_SHADER_7" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 7" description="Mali PM Shader: PM Shader Core 7."/> |
15 | </category> | 15 | </category> |
16 | 16 | ||
17 | <category name="Mali-T6xx-PMTiler" per_cpu="no"> | 17 | <category name="Mali-T6xx-PMTiler" per_cpu="no"> |
diff --git a/daemon/events-Perf-Hardware.xml b/daemon/events-Perf-Hardware.xml new file mode 100644 index 0000000..423696f --- /dev/null +++ b/daemon/events-Perf-Hardware.xml | |||
@@ -0,0 +1,12 @@ | |||
1 | <counter_set name="Perf_Hardware_cnt" count="6"/> | ||
2 | <category name="Perf Hardware" counter_set="Perf_Hardware_cnt" per_cpu="yes" supports_event_based_sampling="yes"> | ||
3 | <event counter="Perf_Hardware_ccnt" event="0" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> | ||
4 | <event event="1" title="Instruction" name="Executed" description="Instruction executed"/> | ||
5 | <event event="2" title="Cache" name="References" description="Cache References"/> | ||
6 | <event event="3" title="Cache" name="Misses" description="Cache Misses"/> | ||
7 | <event event="4" title="Branch" name="Instructions" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/> | ||
8 | <event event="5" title="Branch" name="Misses" description="Branch mispredicted or not predicted"/> | ||
9 | <event event="6" title="Bus" name="Cycles" description="Bus Cycles"/> | ||
10 | <event event="7" title="Instruction" name="Stalled Frontend" description="Stalled Frontend Cycles"/> | ||
11 | <event event="8" title="Instruction" name="Stalled Backend" description="Stalled Backend Cycles"/> | ||
12 | </category> | ||
diff --git a/daemon/k/perf_event.3.12.h b/daemon/k/perf_event.3.12.h new file mode 100644 index 0000000..e886c48 --- /dev/null +++ b/daemon/k/perf_event.3.12.h | |||
@@ -0,0 +1,792 @@ | |||
1 | /* | ||
2 | * Performance events: | ||
3 | * | ||
4 | * Copyright (C) 2008-2009, Thomas Gleixner <tglx@linutronix.de> | ||
5 | * Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar | ||
6 | * Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra | ||
7 | * | ||
8 | * Data type definitions, declarations, prototypes. | ||
9 | * | ||
10 | * Started by: Thomas Gleixner and Ingo Molnar | ||
11 | * | ||
12 | * For licencing details see kernel-base/COPYING | ||
13 | */ | ||
14 | #ifndef _LINUX_PERF_EVENT_H | ||
15 | #define _LINUX_PERF_EVENT_H | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | #include <linux/ioctl.h> | ||
19 | #include <asm/byteorder.h> | ||
20 | |||
21 | /* | ||
22 | * User-space ABI bits: | ||
23 | */ | ||
24 | |||
25 | /* | ||
26 | * attr.type | ||
27 | */ | ||
28 | enum perf_type_id { | ||
29 | PERF_TYPE_HARDWARE = 0, | ||
30 | PERF_TYPE_SOFTWARE = 1, | ||
31 | PERF_TYPE_TRACEPOINT = 2, | ||
32 | PERF_TYPE_HW_CACHE = 3, | ||
33 | PERF_TYPE_RAW = 4, | ||
34 | PERF_TYPE_BREAKPOINT = 5, | ||
35 | |||
36 | PERF_TYPE_MAX, /* non-ABI */ | ||
37 | }; | ||
38 | |||
39 | /* | ||
40 | * Generalized performance event event_id types, used by the | ||
41 | * attr.event_id parameter of the sys_perf_event_open() | ||
42 | * syscall: | ||
43 | */ | ||
44 | enum perf_hw_id { | ||
45 | /* | ||
46 | * Common hardware events, generalized by the kernel: | ||
47 | */ | ||
48 | PERF_COUNT_HW_CPU_CYCLES = 0, | ||
49 | PERF_COUNT_HW_INSTRUCTIONS = 1, | ||
50 | PERF_COUNT_HW_CACHE_REFERENCES = 2, | ||
51 | PERF_COUNT_HW_CACHE_MISSES = 3, | ||
52 | PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, | ||
53 | PERF_COUNT_HW_BRANCH_MISSES = 5, | ||
54 | PERF_COUNT_HW_BUS_CYCLES = 6, | ||
55 | PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, | ||
56 | PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, | ||
57 | PERF_COUNT_HW_REF_CPU_CYCLES = 9, | ||
58 | |||
59 | PERF_COUNT_HW_MAX, /* non-ABI */ | ||
60 | }; | ||
61 | |||
62 | /* | ||
63 | * Generalized hardware cache events: | ||
64 | * | ||
65 | * { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x | ||
66 | * { read, write, prefetch } x | ||
67 | * { accesses, misses } | ||
68 | */ | ||
69 | enum perf_hw_cache_id { | ||
70 | PERF_COUNT_HW_CACHE_L1D = 0, | ||
71 | PERF_COUNT_HW_CACHE_L1I = 1, | ||
72 | PERF_COUNT_HW_CACHE_LL = 2, | ||
73 | PERF_COUNT_HW_CACHE_DTLB = 3, | ||
74 | PERF_COUNT_HW_CACHE_ITLB = 4, | ||
75 | PERF_COUNT_HW_CACHE_BPU = 5, | ||
76 | PERF_COUNT_HW_CACHE_NODE = 6, | ||
77 | |||
78 | PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ | ||
79 | }; | ||
80 | |||
81 | enum perf_hw_cache_op_id { | ||
82 | PERF_COUNT_HW_CACHE_OP_READ = 0, | ||
83 | PERF_COUNT_HW_CACHE_OP_WRITE = 1, | ||
84 | PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, | ||
85 | |||
86 | PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ | ||
87 | }; | ||
88 | |||
89 | enum perf_hw_cache_op_result_id { | ||
90 | PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, | ||
91 | PERF_COUNT_HW_CACHE_RESULT_MISS = 1, | ||
92 | |||
93 | PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ | ||
94 | }; | ||
95 | |||
96 | /* | ||
97 | * Special "software" events provided by the kernel, even if the hardware | ||
98 | * does not support performance events. These events measure various | ||
99 | * physical and sw events of the kernel (and allow the profiling of them as | ||
100 | * well): | ||
101 | */ | ||
102 | enum perf_sw_ids { | ||
103 | PERF_COUNT_SW_CPU_CLOCK = 0, | ||
104 | PERF_COUNT_SW_TASK_CLOCK = 1, | ||
105 | PERF_COUNT_SW_PAGE_FAULTS = 2, | ||
106 | PERF_COUNT_SW_CONTEXT_SWITCHES = 3, | ||
107 | PERF_COUNT_SW_CPU_MIGRATIONS = 4, | ||
108 | PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, | ||
109 | PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, | ||
110 | PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, | ||
111 | PERF_COUNT_SW_EMULATION_FAULTS = 8, | ||
112 | PERF_COUNT_SW_DUMMY = 9, | ||
113 | |||
114 | PERF_COUNT_SW_MAX, /* non-ABI */ | ||
115 | }; | ||
116 | |||
117 | /* | ||
118 | * Bits that can be set in attr.sample_type to request information | ||
119 | * in the overflow packets. | ||
120 | */ | ||
121 | enum perf_event_sample_format { | ||
122 | PERF_SAMPLE_IP = 1U << 0, | ||
123 | PERF_SAMPLE_TID = 1U << 1, | ||
124 | PERF_SAMPLE_TIME = 1U << 2, | ||
125 | PERF_SAMPLE_ADDR = 1U << 3, | ||
126 | PERF_SAMPLE_READ = 1U << 4, | ||
127 | PERF_SAMPLE_CALLCHAIN = 1U << 5, | ||
128 | PERF_SAMPLE_ID = 1U << 6, | ||
129 | PERF_SAMPLE_CPU = 1U << 7, | ||
130 | PERF_SAMPLE_PERIOD = 1U << 8, | ||
131 | PERF_SAMPLE_STREAM_ID = 1U << 9, | ||
132 | PERF_SAMPLE_RAW = 1U << 10, | ||
133 | PERF_SAMPLE_BRANCH_STACK = 1U << 11, | ||
134 | PERF_SAMPLE_REGS_USER = 1U << 12, | ||
135 | PERF_SAMPLE_STACK_USER = 1U << 13, | ||
136 | PERF_SAMPLE_WEIGHT = 1U << 14, | ||
137 | PERF_SAMPLE_DATA_SRC = 1U << 15, | ||
138 | PERF_SAMPLE_IDENTIFIER = 1U << 16, | ||
139 | |||
140 | PERF_SAMPLE_MAX = 1U << 17, /* non-ABI */ | ||
141 | }; | ||
142 | |||
143 | /* | ||
144 | * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set | ||
145 | * | ||
146 | * If the user does not pass priv level information via branch_sample_type, | ||
147 | * the kernel uses the event's priv level. Branch and event priv levels do | ||
148 | * not have to match. Branch priv level is checked for permissions. | ||
149 | * | ||
150 | * The branch types can be combined, however BRANCH_ANY covers all types | ||
151 | * of branches and therefore it supersedes all the other types. | ||
152 | */ | ||
153 | enum perf_branch_sample_type { | ||
154 | PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */ | ||
155 | PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */ | ||
156 | PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */ | ||
157 | |||
158 | PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */ | ||
159 | PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */ | ||
160 | PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */ | ||
161 | PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */ | ||
162 | PERF_SAMPLE_BRANCH_ABORT_TX = 1U << 7, /* transaction aborts */ | ||
163 | PERF_SAMPLE_BRANCH_IN_TX = 1U << 8, /* in transaction */ | ||
164 | PERF_SAMPLE_BRANCH_NO_TX = 1U << 9, /* not in transaction */ | ||
165 | |||
166 | PERF_SAMPLE_BRANCH_MAX = 1U << 10, /* non-ABI */ | ||
167 | }; | ||
168 | |||
169 | #define PERF_SAMPLE_BRANCH_PLM_ALL \ | ||
170 | (PERF_SAMPLE_BRANCH_USER|\ | ||
171 | PERF_SAMPLE_BRANCH_KERNEL|\ | ||
172 | PERF_SAMPLE_BRANCH_HV) | ||
173 | |||
174 | /* | ||
175 | * Values to determine ABI of the registers dump. | ||
176 | */ | ||
177 | enum perf_sample_regs_abi { | ||
178 | PERF_SAMPLE_REGS_ABI_NONE = 0, | ||
179 | PERF_SAMPLE_REGS_ABI_32 = 1, | ||
180 | PERF_SAMPLE_REGS_ABI_64 = 2, | ||
181 | }; | ||
182 | |||
183 | /* | ||
184 | * The format of the data returned by read() on a perf event fd, | ||
185 | * as specified by attr.read_format: | ||
186 | * | ||
187 | * struct read_format { | ||
188 | * { u64 value; | ||
189 | * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED | ||
190 | * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING | ||
191 | * { u64 id; } && PERF_FORMAT_ID | ||
192 | * } && !PERF_FORMAT_GROUP | ||
193 | * | ||
194 | * { u64 nr; | ||
195 | * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED | ||
196 | * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING | ||
197 | * { u64 value; | ||
198 | * { u64 id; } && PERF_FORMAT_ID | ||
199 | * } cntr[nr]; | ||
200 | * } && PERF_FORMAT_GROUP | ||
201 | * }; | ||
202 | */ | ||
203 | enum perf_event_read_format { | ||
204 | PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0, | ||
205 | PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1, | ||
206 | PERF_FORMAT_ID = 1U << 2, | ||
207 | PERF_FORMAT_GROUP = 1U << 3, | ||
208 | |||
209 | PERF_FORMAT_MAX = 1U << 4, /* non-ABI */ | ||
210 | }; | ||
211 | |||
212 | #define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ | ||
213 | #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ | ||
214 | #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ | ||
215 | #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ | ||
216 | /* add: sample_stack_user */ | ||
217 | |||
218 | /* | ||
219 | * Hardware event_id to monitor via a performance monitoring event: | ||
220 | */ | ||
221 | struct perf_event_attr { | ||
222 | |||
223 | /* | ||
224 | * Major type: hardware/software/tracepoint/etc. | ||
225 | */ | ||
226 | __u32 type; | ||
227 | |||
228 | /* | ||
229 | * Size of the attr structure, for fwd/bwd compat. | ||
230 | */ | ||
231 | __u32 size; | ||
232 | |||
233 | /* | ||
234 | * Type specific configuration information. | ||
235 | */ | ||
236 | __u64 config; | ||
237 | |||
238 | union { | ||
239 | __u64 sample_period; | ||
240 | __u64 sample_freq; | ||
241 | }; | ||
242 | |||
243 | __u64 sample_type; | ||
244 | __u64 read_format; | ||
245 | |||
246 | __u64 disabled : 1, /* off by default */ | ||
247 | inherit : 1, /* children inherit it */ | ||
248 | pinned : 1, /* must always be on PMU */ | ||
249 | exclusive : 1, /* only group on PMU */ | ||
250 | exclude_user : 1, /* don't count user */ | ||
251 | exclude_kernel : 1, /* ditto kernel */ | ||
252 | exclude_hv : 1, /* ditto hypervisor */ | ||
253 | exclude_idle : 1, /* don't count when idle */ | ||
254 | mmap : 1, /* include mmap data */ | ||
255 | comm : 1, /* include comm data */ | ||
256 | freq : 1, /* use freq, not period */ | ||
257 | inherit_stat : 1, /* per task counts */ | ||
258 | enable_on_exec : 1, /* next exec enables */ | ||
259 | task : 1, /* trace fork/exit */ | ||
260 | watermark : 1, /* wakeup_watermark */ | ||
261 | /* | ||
262 | * precise_ip: | ||
263 | * | ||
264 | * 0 - SAMPLE_IP can have arbitrary skid | ||
265 | * 1 - SAMPLE_IP must have constant skid | ||
266 | * 2 - SAMPLE_IP requested to have 0 skid | ||
267 | * 3 - SAMPLE_IP must have 0 skid | ||
268 | * | ||
269 | * See also PERF_RECORD_MISC_EXACT_IP | ||
270 | */ | ||
271 | precise_ip : 2, /* skid constraint */ | ||
272 | mmap_data : 1, /* non-exec mmap data */ | ||
273 | sample_id_all : 1, /* sample_type all events */ | ||
274 | |||
275 | exclude_host : 1, /* don't count in host */ | ||
276 | exclude_guest : 1, /* don't count in guest */ | ||
277 | |||
278 | exclude_callchain_kernel : 1, /* exclude kernel callchains */ | ||
279 | exclude_callchain_user : 1, /* exclude user callchains */ | ||
280 | mmap2 : 1, /* include mmap with inode data */ | ||
281 | |||
282 | __reserved_1 : 40; | ||
283 | |||
284 | union { | ||
285 | __u32 wakeup_events; /* wakeup every n events */ | ||
286 | __u32 wakeup_watermark; /* bytes before wakeup */ | ||
287 | }; | ||
288 | |||
289 | __u32 bp_type; | ||
290 | union { | ||
291 | __u64 bp_addr; | ||
292 | __u64 config1; /* extension of config */ | ||
293 | }; | ||
294 | union { | ||
295 | __u64 bp_len; | ||
296 | __u64 config2; /* extension of config1 */ | ||
297 | }; | ||
298 | __u64 branch_sample_type; /* enum perf_branch_sample_type */ | ||
299 | |||
300 | /* | ||
301 | * Defines set of user regs to dump on samples. | ||
302 | * See asm/perf_regs.h for details. | ||
303 | */ | ||
304 | __u64 sample_regs_user; | ||
305 | |||
306 | /* | ||
307 | * Defines size of the user stack to dump on samples. | ||
308 | */ | ||
309 | __u32 sample_stack_user; | ||
310 | |||
311 | /* Align to u64. */ | ||
312 | __u32 __reserved_2; | ||
313 | }; | ||
314 | |||
315 | #define perf_flags(attr) (*(&(attr)->read_format + 1)) | ||
316 | |||
317 | /* | ||
318 | * Ioctls that can be done on a perf event fd: | ||
319 | */ | ||
320 | #define PERF_EVENT_IOC_ENABLE _IO ('$', 0) | ||
321 | #define PERF_EVENT_IOC_DISABLE _IO ('$', 1) | ||
322 | #define PERF_EVENT_IOC_REFRESH _IO ('$', 2) | ||
323 | #define PERF_EVENT_IOC_RESET _IO ('$', 3) | ||
324 | #define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64) | ||
325 | #define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) | ||
326 | #define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) | ||
327 | #define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) | ||
328 | |||
329 | enum perf_event_ioc_flags { | ||
330 | PERF_IOC_FLAG_GROUP = 1U << 0, | ||
331 | }; | ||
332 | |||
333 | /* | ||
334 | * Structure of the page that can be mapped via mmap | ||
335 | */ | ||
336 | struct perf_event_mmap_page { | ||
337 | __u32 version; /* version number of this structure */ | ||
338 | __u32 compat_version; /* lowest version this is compat with */ | ||
339 | |||
340 | /* | ||
341 | * Bits needed to read the hw events in user-space. | ||
342 | * | ||
343 | * u32 seq, time_mult, time_shift, idx, width; | ||
344 | * u64 count, enabled, running; | ||
345 | * u64 cyc, time_offset; | ||
346 | * s64 pmc = 0; | ||
347 | * | ||
348 | * do { | ||
349 | * seq = pc->lock; | ||
350 | * barrier() | ||
351 | * | ||
352 | * enabled = pc->time_enabled; | ||
353 | * running = pc->time_running; | ||
354 | * | ||
355 | * if (pc->cap_usr_time && enabled != running) { | ||
356 | * cyc = rdtsc(); | ||
357 | * time_offset = pc->time_offset; | ||
358 | * time_mult = pc->time_mult; | ||
359 | * time_shift = pc->time_shift; | ||
360 | * } | ||
361 | * | ||
362 | * idx = pc->index; | ||
363 | * count = pc->offset; | ||
364 | * if (pc->cap_usr_rdpmc && idx) { | ||
365 | * width = pc->pmc_width; | ||
366 | * pmc = rdpmc(idx - 1); | ||
367 | * } | ||
368 | * | ||
369 | * barrier(); | ||
370 | * } while (pc->lock != seq); | ||
371 | * | ||
372 | * NOTE: for obvious reason this only works on self-monitoring | ||
373 | * processes. | ||
374 | */ | ||
375 | __u32 lock; /* seqlock for synchronization */ | ||
376 | __u32 index; /* hardware event identifier */ | ||
377 | __s64 offset; /* add to hardware event value */ | ||
378 | __u64 time_enabled; /* time event active */ | ||
379 | __u64 time_running; /* time event on cpu */ | ||
380 | union { | ||
381 | __u64 capabilities; | ||
382 | struct { | ||
383 | __u64 cap_bit0 : 1, /* Always 0, deprecated, see commit 860f085b74e9 */ | ||
384 | cap_bit0_is_deprecated : 1, /* Always 1, signals that bit 0 is zero */ | ||
385 | |||
386 | cap_user_rdpmc : 1, /* The RDPMC instruction can be used to read counts */ | ||
387 | cap_user_time : 1, /* The time_* fields are used */ | ||
388 | cap_user_time_zero : 1, /* The time_zero field is used */ | ||
389 | cap_____res : 59; | ||
390 | }; | ||
391 | }; | ||
392 | |||
393 | /* | ||
394 | * If cap_usr_rdpmc this field provides the bit-width of the value | ||
395 | * read using the rdpmc() or equivalent instruction. This can be used | ||
396 | * to sign extend the result like: | ||
397 | * | ||
398 | * pmc <<= 64 - width; | ||
399 | * pmc >>= 64 - width; // signed shift right | ||
400 | * count += pmc; | ||
401 | */ | ||
402 | __u16 pmc_width; | ||
403 | |||
404 | /* | ||
405 | * If cap_usr_time the below fields can be used to compute the time | ||
406 | * delta since time_enabled (in ns) using rdtsc or similar. | ||
407 | * | ||
408 | * u64 quot, rem; | ||
409 | * u64 delta; | ||
410 | * | ||
411 | * quot = (cyc >> time_shift); | ||
412 | * rem = cyc & ((1 << time_shift) - 1); | ||
413 | * delta = time_offset + quot * time_mult + | ||
414 | * ((rem * time_mult) >> time_shift); | ||
415 | * | ||
416 | * Where time_offset,time_mult,time_shift and cyc are read in the | ||
417 | * seqcount loop described above. This delta can then be added to | ||
418 | * enabled and possible running (if idx), improving the scaling: | ||
419 | * | ||
420 | * enabled += delta; | ||
421 | * if (idx) | ||
422 | * running += delta; | ||
423 | * | ||
424 | * quot = count / running; | ||
425 | * rem = count % running; | ||
426 | * count = quot * enabled + (rem * enabled) / running; | ||
427 | */ | ||
428 | __u16 time_shift; | ||
429 | __u32 time_mult; | ||
430 | __u64 time_offset; | ||
431 | /* | ||
432 | * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated | ||
433 | * from sample timestamps. | ||
434 | * | ||
435 | * time = timestamp - time_zero; | ||
436 | * quot = time / time_mult; | ||
437 | * rem = time % time_mult; | ||
438 | * cyc = (quot << time_shift) + (rem << time_shift) / time_mult; | ||
439 | * | ||
440 | * And vice versa: | ||
441 | * | ||
442 | * quot = cyc >> time_shift; | ||
443 | * rem = cyc & ((1 << time_shift) - 1); | ||
444 | * timestamp = time_zero + quot * time_mult + | ||
445 | * ((rem * time_mult) >> time_shift); | ||
446 | */ | ||
447 | __u64 time_zero; | ||
448 | __u32 size; /* Header size up to __reserved[] fields. */ | ||
449 | |||
450 | /* | ||
451 | * Hole for extension of the self monitor capabilities | ||
452 | */ | ||
453 | |||
454 | __u8 __reserved[118*8+4]; /* align to 1k. */ | ||
455 | |||
456 | /* | ||
457 | * Control data for the mmap() data buffer. | ||
458 | * | ||
459 | * User-space reading the @data_head value should issue an smp_rmb(), | ||
460 | * after reading this value. | ||
461 | * | ||
462 | * When the mapping is PROT_WRITE the @data_tail value should be | ||
463 | * written by userspace to reflect the last read data, after issueing | ||
464 | * an smp_mb() to separate the data read from the ->data_tail store. | ||
465 | * In this case the kernel will not over-write unread data. | ||
466 | * | ||
467 | * See perf_output_put_handle() for the data ordering. | ||
468 | */ | ||
469 | __u64 data_head; /* head in the data section */ | ||
470 | __u64 data_tail; /* user-space written tail */ | ||
471 | }; | ||
472 | |||
473 | #define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0) | ||
474 | #define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) | ||
475 | #define PERF_RECORD_MISC_KERNEL (1 << 0) | ||
476 | #define PERF_RECORD_MISC_USER (2 << 0) | ||
477 | #define PERF_RECORD_MISC_HYPERVISOR (3 << 0) | ||
478 | #define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) | ||
479 | #define PERF_RECORD_MISC_GUEST_USER (5 << 0) | ||
480 | |||
481 | #define PERF_RECORD_MISC_MMAP_DATA (1 << 13) | ||
482 | /* | ||
483 | * Indicates that the content of PERF_SAMPLE_IP points to | ||
484 | * the actual instruction that triggered the event. See also | ||
485 | * perf_event_attr::precise_ip. | ||
486 | */ | ||
487 | #define PERF_RECORD_MISC_EXACT_IP (1 << 14) | ||
488 | /* | ||
489 | * Reserve the last bit to indicate some extended misc field | ||
490 | */ | ||
491 | #define PERF_RECORD_MISC_EXT_RESERVED (1 << 15) | ||
492 | |||
493 | struct perf_event_header { | ||
494 | __u32 type; | ||
495 | __u16 misc; | ||
496 | __u16 size; | ||
497 | }; | ||
498 | |||
499 | enum perf_event_type { | ||
500 | |||
501 | /* | ||
502 | * If perf_event_attr.sample_id_all is set then all event types will | ||
503 | * have the sample_type selected fields related to where/when | ||
504 | * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU, | ||
505 | * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed | ||
506 | * just after the perf_event_header and the fields already present for | ||
507 | * the existing fields, i.e. at the end of the payload. That way a newer | ||
508 | * perf.data file will be supported by older perf tools, with these new | ||
509 | * optional fields being ignored. | ||
510 | * | ||
511 | * struct sample_id { | ||
512 | * { u32 pid, tid; } && PERF_SAMPLE_TID | ||
513 | * { u64 time; } && PERF_SAMPLE_TIME | ||
514 | * { u64 id; } && PERF_SAMPLE_ID | ||
515 | * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID | ||
516 | * { u32 cpu, res; } && PERF_SAMPLE_CPU | ||
517 | * { u64 id; } && PERF_SAMPLE_IDENTIFIER | ||
518 | * } && perf_event_attr::sample_id_all | ||
519 | * | ||
520 | * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The | ||
521 | * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed | ||
522 | * relative to header.size. | ||
523 | */ | ||
524 | |||
525 | /* | ||
526 | * The MMAP events record the PROT_EXEC mappings so that we can | ||
527 | * correlate userspace IPs to code. They have the following structure: | ||
528 | * | ||
529 | * struct { | ||
530 | * struct perf_event_header header; | ||
531 | * | ||
532 | * u32 pid, tid; | ||
533 | * u64 addr; | ||
534 | * u64 len; | ||
535 | * u64 pgoff; | ||
536 | * char filename[]; | ||
537 | * struct sample_id sample_id; | ||
538 | * }; | ||
539 | */ | ||
540 | PERF_RECORD_MMAP = 1, | ||
541 | |||
542 | /* | ||
543 | * struct { | ||
544 | * struct perf_event_header header; | ||
545 | * u64 id; | ||
546 | * u64 lost; | ||
547 | * struct sample_id sample_id; | ||
548 | * }; | ||
549 | */ | ||
550 | PERF_RECORD_LOST = 2, | ||
551 | |||
552 | /* | ||
553 | * struct { | ||
554 | * struct perf_event_header header; | ||
555 | * | ||
556 | * u32 pid, tid; | ||
557 | * char comm[]; | ||
558 | * struct sample_id sample_id; | ||
559 | * }; | ||
560 | */ | ||
561 | PERF_RECORD_COMM = 3, | ||
562 | |||
563 | /* | ||
564 | * struct { | ||
565 | * struct perf_event_header header; | ||
566 | * u32 pid, ppid; | ||
567 | * u32 tid, ptid; | ||
568 | * u64 time; | ||
569 | * struct sample_id sample_id; | ||
570 | * }; | ||
571 | */ | ||
572 | PERF_RECORD_EXIT = 4, | ||
573 | |||
574 | /* | ||
575 | * struct { | ||
576 | * struct perf_event_header header; | ||
577 | * u64 time; | ||
578 | * u64 id; | ||
579 | * u64 stream_id; | ||
580 | * struct sample_id sample_id; | ||
581 | * }; | ||
582 | */ | ||
583 | PERF_RECORD_THROTTLE = 5, | ||
584 | PERF_RECORD_UNTHROTTLE = 6, | ||
585 | |||
586 | /* | ||
587 | * struct { | ||
588 | * struct perf_event_header header; | ||
589 | * u32 pid, ppid; | ||
590 | * u32 tid, ptid; | ||
591 | * u64 time; | ||
592 | * struct sample_id sample_id; | ||
593 | * }; | ||
594 | */ | ||
595 | PERF_RECORD_FORK = 7, | ||
596 | |||
597 | /* | ||
598 | * struct { | ||
599 | * struct perf_event_header header; | ||
600 | * u32 pid, tid; | ||
601 | * | ||
602 | * struct read_format values; | ||
603 | * struct sample_id sample_id; | ||
604 | * }; | ||
605 | */ | ||
606 | PERF_RECORD_READ = 8, | ||
607 | |||
608 | /* | ||
609 | * struct { | ||
610 | * struct perf_event_header header; | ||
611 | * | ||
612 | * # | ||
613 | * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. | ||
614 | * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position | ||
615 | * # is fixed relative to header. | ||
616 | * # | ||
617 | * | ||
618 | * { u64 id; } && PERF_SAMPLE_IDENTIFIER | ||
619 | * { u64 ip; } && PERF_SAMPLE_IP | ||
620 | * { u32 pid, tid; } && PERF_SAMPLE_TID | ||
621 | * { u64 time; } && PERF_SAMPLE_TIME | ||
622 | * { u64 addr; } && PERF_SAMPLE_ADDR | ||
623 | * { u64 id; } && PERF_SAMPLE_ID | ||
624 | * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID | ||
625 | * { u32 cpu, res; } && PERF_SAMPLE_CPU | ||
626 | * { u64 period; } && PERF_SAMPLE_PERIOD | ||
627 | * | ||
628 | * { struct read_format values; } && PERF_SAMPLE_READ | ||
629 | * | ||
630 | * { u64 nr, | ||
631 | * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN | ||
632 | * | ||
633 | * # | ||
634 | * # The RAW record below is opaque data wrt the ABI | ||
635 | * # | ||
636 | * # That is, the ABI doesn't make any promises wrt to | ||
637 | * # the stability of its content, it may vary depending | ||
638 | * # on event, hardware, kernel version and phase of | ||
639 | * # the moon. | ||
640 | * # | ||
641 | * # In other words, PERF_SAMPLE_RAW contents are not an ABI. | ||
642 | * # | ||
643 | * | ||
644 | * { u32 size; | ||
645 | * char data[size];}&& PERF_SAMPLE_RAW | ||
646 | * | ||
647 | * { u64 nr; | ||
648 | * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK | ||
649 | * | ||
650 | * { u64 abi; # enum perf_sample_regs_abi | ||
651 | * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER | ||
652 | * | ||
653 | * { u64 size; | ||
654 | * char data[size]; | ||
655 | * u64 dyn_size; } && PERF_SAMPLE_STACK_USER | ||
656 | * | ||
657 | * { u64 weight; } && PERF_SAMPLE_WEIGHT | ||
658 | * { u64 data_src; } && PERF_SAMPLE_DATA_SRC | ||
659 | * }; | ||
660 | */ | ||
661 | PERF_RECORD_SAMPLE = 9, | ||
662 | |||
663 | /* | ||
664 | * The MMAP2 records are an augmented version of MMAP, they add | ||
665 | * maj, min, ino numbers to be used to uniquely identify each mapping | ||
666 | * | ||
667 | * struct { | ||
668 | * struct perf_event_header header; | ||
669 | * | ||
670 | * u32 pid, tid; | ||
671 | * u64 addr; | ||
672 | * u64 len; | ||
673 | * u64 pgoff; | ||
674 | * u32 maj; | ||
675 | * u32 min; | ||
676 | * u64 ino; | ||
677 | * u64 ino_generation; | ||
678 | * char filename[]; | ||
679 | * struct sample_id sample_id; | ||
680 | * }; | ||
681 | */ | ||
682 | PERF_RECORD_MMAP2 = 10, | ||
683 | |||
684 | PERF_RECORD_MAX, /* non-ABI */ | ||
685 | }; | ||
686 | |||
687 | #define PERF_MAX_STACK_DEPTH 127 | ||
688 | |||
689 | enum perf_callchain_context { | ||
690 | PERF_CONTEXT_HV = (__u64)-32, | ||
691 | PERF_CONTEXT_KERNEL = (__u64)-128, | ||
692 | PERF_CONTEXT_USER = (__u64)-512, | ||
693 | |||
694 | PERF_CONTEXT_GUEST = (__u64)-2048, | ||
695 | PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176, | ||
696 | PERF_CONTEXT_GUEST_USER = (__u64)-2560, | ||
697 | |||
698 | PERF_CONTEXT_MAX = (__u64)-4095, | ||
699 | }; | ||
700 | |||
701 | #define PERF_FLAG_FD_NO_GROUP (1U << 0) | ||
702 | #define PERF_FLAG_FD_OUTPUT (1U << 1) | ||
703 | #define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ | ||
704 | |||
705 | union perf_mem_data_src { | ||
706 | __u64 val; | ||
707 | struct { | ||
708 | __u64 mem_op:5, /* type of opcode */ | ||
709 | mem_lvl:14, /* memory hierarchy level */ | ||
710 | mem_snoop:5, /* snoop mode */ | ||
711 | mem_lock:2, /* lock instr */ | ||
712 | mem_dtlb:7, /* tlb access */ | ||
713 | mem_rsvd:31; | ||
714 | }; | ||
715 | }; | ||
716 | |||
717 | /* type of opcode (load/store/prefetch,code) */ | ||
718 | #define PERF_MEM_OP_NA 0x01 /* not available */ | ||
719 | #define PERF_MEM_OP_LOAD 0x02 /* load instruction */ | ||
720 | #define PERF_MEM_OP_STORE 0x04 /* store instruction */ | ||
721 | #define PERF_MEM_OP_PFETCH 0x08 /* prefetch */ | ||
722 | #define PERF_MEM_OP_EXEC 0x10 /* code (execution) */ | ||
723 | #define PERF_MEM_OP_SHIFT 0 | ||
724 | |||
725 | /* memory hierarchy (memory level, hit or miss) */ | ||
726 | #define PERF_MEM_LVL_NA 0x01 /* not available */ | ||
727 | #define PERF_MEM_LVL_HIT 0x02 /* hit level */ | ||
728 | #define PERF_MEM_LVL_MISS 0x04 /* miss level */ | ||
729 | #define PERF_MEM_LVL_L1 0x08 /* L1 */ | ||
730 | #define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */ | ||
731 | #define PERF_MEM_LVL_L2 0x20 /* L2 */ | ||
732 | #define PERF_MEM_LVL_L3 0x40 /* L3 */ | ||
733 | #define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */ | ||
734 | #define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */ | ||
735 | #define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */ | ||
736 | #define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */ | ||
737 | #define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */ | ||
738 | #define PERF_MEM_LVL_IO 0x1000 /* I/O memory */ | ||
739 | #define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */ | ||
740 | #define PERF_MEM_LVL_SHIFT 5 | ||
741 | |||
742 | /* snoop mode */ | ||
743 | #define PERF_MEM_SNOOP_NA 0x01 /* not available */ | ||
744 | #define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */ | ||
745 | #define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */ | ||
746 | #define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */ | ||
747 | #define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */ | ||
748 | #define PERF_MEM_SNOOP_SHIFT 19 | ||
749 | |||
750 | /* locked instruction */ | ||
751 | #define PERF_MEM_LOCK_NA 0x01 /* not available */ | ||
752 | #define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */ | ||
753 | #define PERF_MEM_LOCK_SHIFT 24 | ||
754 | |||
755 | /* TLB access */ | ||
756 | #define PERF_MEM_TLB_NA 0x01 /* not available */ | ||
757 | #define PERF_MEM_TLB_HIT 0x02 /* hit level */ | ||
758 | #define PERF_MEM_TLB_MISS 0x04 /* miss level */ | ||
759 | #define PERF_MEM_TLB_L1 0x08 /* L1 */ | ||
760 | #define PERF_MEM_TLB_L2 0x10 /* L2 */ | ||
761 | #define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/ | ||
762 | #define PERF_MEM_TLB_OS 0x40 /* OS fault handler */ | ||
763 | #define PERF_MEM_TLB_SHIFT 26 | ||
764 | |||
765 | #define PERF_MEM_S(a, s) \ | ||
766 | (((u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT) | ||
767 | |||
768 | /* | ||
769 | * single taken branch record layout: | ||
770 | * | ||
771 | * from: source instruction (may not always be a branch insn) | ||
772 | * to: branch target | ||
773 | * mispred: branch target was mispredicted | ||
774 | * predicted: branch target was predicted | ||
775 | * | ||
776 | * support for mispred, predicted is optional. In case it | ||
777 | * is not supported mispred = predicted = 0. | ||
778 | * | ||
779 | * in_tx: running in a hardware transaction | ||
780 | * abort: aborting a hardware transaction | ||
781 | */ | ||
782 | struct perf_branch_entry { | ||
783 | __u64 from; | ||
784 | __u64 to; | ||
785 | __u64 mispred:1, /* target mispredicted */ | ||
786 | predicted:1,/* target predicted */ | ||
787 | in_tx:1, /* in transaction */ | ||
788 | abort:1, /* transaction abort */ | ||
789 | reserved:60; | ||
790 | }; | ||
791 | |||
792 | #endif /* _LINUX_PERF_EVENT_H */ | ||
diff --git a/daemon/k/perf_event.h b/daemon/k/perf_event.h new file mode 120000 index 0000000..e5dff8c --- /dev/null +++ b/daemon/k/perf_event.h | |||
@@ -0,0 +1 @@ | |||
perf_event.3.12.h \ No newline at end of file | |||
diff --git a/daemon/main.cpp b/daemon/main.cpp index bfd36b9..1275aef 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp | |||
@@ -1,32 +1,30 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 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 | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <stdlib.h> | 9 | #include <arpa/inet.h> |
10 | #include <signal.h> | ||
11 | #include <sys/wait.h> | ||
12 | #include <unistd.h> | ||
13 | #include <sys/syscall.h> | ||
14 | #include <sys/prctl.h> | ||
15 | #include <sys/types.h> | ||
16 | #include <sys/stat.h> | ||
17 | #include <sys/mount.h> | ||
18 | #include <fcntl.h> | 10 | #include <fcntl.h> |
11 | #include <pthread.h> | ||
19 | #include <sys/mman.h> | 12 | #include <sys/mman.h> |
20 | #include <sys/time.h> | 13 | #include <sys/mount.h> |
14 | #include <sys/prctl.h> | ||
21 | #include <sys/resource.h> | 15 | #include <sys/resource.h> |
22 | #include <arpa/inet.h> | ||
23 | #include <sys/socket.h> | 16 | #include <sys/socket.h> |
17 | #include <sys/stat.h> | ||
18 | #include <sys/syscall.h> | ||
19 | #include <sys/wait.h> | ||
20 | #include <unistd.h> | ||
21 | |||
24 | #include "Child.h" | 22 | #include "Child.h" |
25 | #include "SessionData.h" | 23 | #include "KMod.h" |
26 | #include "OlySocket.h" | ||
27 | #include "Logging.h" | 24 | #include "Logging.h" |
25 | #include "OlySocket.h" | ||
28 | #include "OlyUtility.h" | 26 | #include "OlyUtility.h" |
29 | #include "KMod.h" | 27 | #include "SessionData.h" |
30 | 28 | ||
31 | #define DEBUG false | 29 | #define DEBUG false |
32 | 30 | ||
@@ -34,7 +32,7 @@ extern Child* child; | |||
34 | static int shutdownFilesystem(); | 32 | static int shutdownFilesystem(); |
35 | static pthread_mutex_t numSessions_mutex; | 33 | static pthread_mutex_t numSessions_mutex; |
36 | static int numSessions = 0; | 34 | static int numSessions = 0; |
37 | static OlySocket* sock = NULL; | 35 | static OlyServerSocket* sock = NULL; |
38 | static bool driverRunningAtStart = false; | 36 | static bool driverRunningAtStart = false; |
39 | static bool driverMountedAtStart = false; | 37 | static bool driverMountedAtStart = false; |
40 | 38 | ||
@@ -157,6 +155,7 @@ typedef struct { | |||
157 | static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 }; | 155 | static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 }; |
158 | 156 | ||
159 | static void* answerThread(void* pVoid) { | 157 | static void* answerThread(void* pVoid) { |
158 | prctl(PR_SET_NAME, (unsigned long)&"gatord-discover", 0, 0, 0); | ||
160 | const struct cmdline_t * const cmdline = (struct cmdline_t *)pVoid; | 159 | const struct cmdline_t * const cmdline = (struct cmdline_t *)pVoid; |
161 | RVIConfigureInfo dstAns; | 160 | RVIConfigureInfo dstAns; |
162 | int req = udpPort(UDP_REQ_PORT); | 161 | int req = udpPort(UDP_REQ_PORT); |
@@ -231,16 +230,7 @@ static bool init_module (const char * const location) { | |||
231 | return ret; | 230 | return ret; |
232 | } | 231 | } |
233 | 232 | ||
234 | static int setupFilesystem(char* module) { | 233 | static bool setupFilesystem(char* module) { |
235 | int retval; | ||
236 | |||
237 | // Verify root permissions | ||
238 | uid_t euid = geteuid(); | ||
239 | if (euid) { | ||
240 | logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges"); | ||
241 | handleException(); | ||
242 | } | ||
243 | |||
244 | if (module) { | 234 | if (module) { |
245 | // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running | 235 | // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running |
246 | shutdownFilesystem(); | 236 | shutdownFilesystem(); |
@@ -252,7 +242,7 @@ static int setupFilesystem(char* module) { | |||
252 | } | 242 | } |
253 | } | 243 | } |
254 | 244 | ||
255 | retval = mountGatorFS(); | 245 | const int retval = mountGatorFS(); |
256 | if (retval == 1) { | 246 | if (retval == 1) { |
257 | logg->logMessage("Driver already running at startup"); | 247 | logg->logMessage("Driver already running at startup"); |
258 | driverRunningAtStart = true; | 248 | driverRunningAtStart = true; |
@@ -274,8 +264,8 @@ static int setupFilesystem(char* module) { | |||
274 | } | 264 | } |
275 | 265 | ||
276 | if (access(location, F_OK) == -1) { | 266 | if (access(location, F_OK) == -1) { |
277 | logg->logError(__FILE__, __LINE__, "Unable to locate gator.ko driver:\n >>> gator.ko should be co-located with gatord in the same directory\n >>> OR insmod gator.ko prior to launching gatord\n >>> OR specify the location of gator.ko on the command line"); | 267 | // The gator kernel is not already loaded and unable to locate gator.ko |
278 | handleException(); | 268 | return false; |
279 | } | 269 | } |
280 | 270 | ||
281 | // Load driver | 271 | // Load driver |
@@ -296,7 +286,7 @@ static int setupFilesystem(char* module) { | |||
296 | } | 286 | } |
297 | } | 287 | } |
298 | 288 | ||
299 | return 0; | 289 | return true; |
300 | } | 290 | } |
301 | 291 | ||
302 | static int shutdownFilesystem() { | 292 | static int shutdownFilesystem() { |
@@ -418,8 +408,28 @@ int main(int argc, char** argv) { | |||
418 | // Parse the command line parameters | 408 | // Parse the command line parameters |
419 | struct cmdline_t cmdline = parseCommandLine(argc, argv); | 409 | struct cmdline_t cmdline = parseCommandLine(argc, argv); |
420 | 410 | ||
411 | // Verify root permissions | ||
412 | uid_t euid = geteuid(); | ||
413 | if (euid) { | ||
414 | logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges"); | ||
415 | handleException(); | ||
416 | } | ||
417 | |||
421 | // Call before setting up the SIGCHLD handler, as system() spawns child processes | 418 | // Call before setting up the SIGCHLD handler, as system() spawns child processes |
422 | setupFilesystem(cmdline.module); | 419 | if (!setupFilesystem(cmdline.module)) { |
420 | logg->logMessage("Unable to setup gatorfs, trying perf"); | ||
421 | if (!gSessionData->perf.setup()) { | ||
422 | logg->logError(__FILE__, __LINE__, | ||
423 | "Unable to locate gator.ko driver:\n" | ||
424 | " >>> gator.ko should be co-located with gatord in the same directory\n" | ||
425 | " >>> OR insmod gator.ko prior to launching gatord\n" | ||
426 | " >>> OR specify the location of gator.ko on the command line\n" | ||
427 | " >>> OR run Linux 3.12 or later with perf support to collect data via userspace only"); | ||
428 | handleException(); | ||
429 | } | ||
430 | } | ||
431 | |||
432 | gSessionData->hwmon.setup(); | ||
423 | 433 | ||
424 | // Handle child exit codes | 434 | // Handle child exit codes |
425 | signal(SIGCHLD, child_exit); | 435 | signal(SIGCHLD, child_exit); |
@@ -439,11 +449,11 @@ int main(int argc, char** argv) { | |||
439 | logg->logError(__FILE__, __LINE__, "Failed to create answer thread"); | 449 | logg->logError(__FILE__, __LINE__, "Failed to create answer thread"); |
440 | handleException(); | 450 | handleException(); |
441 | } | 451 | } |
442 | sock = new OlySocket(cmdline.port, true); | 452 | sock = new OlyServerSocket(cmdline.port); |
443 | // Forever loop, can be exited via a signal or exception | 453 | // Forever loop, can be exited via a signal or exception |
444 | while (1) { | 454 | while (1) { |
445 | logg->logMessage("Waiting on connection..."); | 455 | logg->logMessage("Waiting on connection..."); |
446 | sock->acceptConnection(); | 456 | OlySocket client(sock->acceptConnection()); |
447 | 457 | ||
448 | int pid = fork(); | 458 | int pid = fork(); |
449 | if (pid < 0) { | 459 | if (pid < 0) { |
@@ -452,13 +462,13 @@ int main(int argc, char** argv) { | |||
452 | } else if (pid == 0) { | 462 | } else if (pid == 0) { |
453 | // Child | 463 | // Child |
454 | sock->closeServerSocket(); | 464 | sock->closeServerSocket(); |
455 | child = new Child(sock, numSessions + 1); | 465 | child = new Child(&client, numSessions + 1); |
456 | child->run(); | 466 | child->run(); |
457 | delete child; | 467 | delete child; |
458 | exit(0); | 468 | exit(0); |
459 | } else { | 469 | } else { |
460 | // Parent | 470 | // Parent |
461 | sock->closeSocket(); | 471 | client.closeSocket(); |
462 | 472 | ||
463 | pthread_mutex_lock(&numSessions_mutex); | 473 | pthread_mutex_lock(&numSessions_mutex); |
464 | numSessions++; | 474 | numSessions++; |