1 /**
2 * Copyright (C) ARM Limited 2010-2012. 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 */
9 #include <fcntl.h>
10 #include <malloc.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <errno.h>
15 #include <sys/time.h>
16 #include "Collector.h"
17 #include "SessionData.h"
18 #include "Logging.h"
19 #include "Sender.h"
21 extern void handleException();
23 // Driver initialization independent of session settings
24 Collector::Collector() {
25 char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events/<types>/<file>
27 mBufferFD = 0;
29 checkVersion();
31 int enable = -1;
32 if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) {
33 logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress.");
34 handleException();
35 }
37 readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores);
38 if (gSessionData->mCores == 0) {
39 gSessionData->mCores = 1;
40 }
42 mBufferSize = 0;
43 if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) {
44 logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size");
45 handleException();
46 }
48 getCoreName();
50 enablePerfCounters();
52 // Read unchanging keys from driver which are created at insmod'ing of gator.ko
53 for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
54 if (gSessionData->mPerfCounterEnabled[i]) {
55 snprintf(text, sizeof(text), "/dev/gator/events/%s/key", gSessionData->mPerfCounterType[i]);
56 readIntDriver(text, &gSessionData->mPerfCounterKey[i]);
57 }
58 }
59 }
61 Collector::~Collector() {
62 // Write zero for safety, as a zero should have already been written
63 writeDriver("/dev/gator/enable", "0");
65 // Calls event_buffer_release in the driver
66 if (mBufferFD) {
67 close(mBufferFD);
68 }
69 }
71 #include <dirent.h>
72 void Collector::enablePerfCounters() {
73 char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events/<types>/enabled
75 // Initialize all perf counters in the driver, i.e. set enabled to zero
76 struct dirent *ent;
77 DIR* dir = opendir("/dev/gator/events");
78 if (dir) {
79 while ((ent = readdir(dir)) != NULL) {
80 // skip hidden files, current dir, and parent dir
81 if (ent->d_name[0] == '.')
82 continue;
83 snprintf(text, sizeof(text), "/dev/gator/events/%s/enabled", ent->d_name);
84 writeDriver(text, 0);
85 }
86 closedir (dir);
87 }
89 for (int i=0; i<MAX_PERFORMANCE_COUNTERS; i++) {
90 if (!gSessionData->mPerfCounterEnabled[i]) {
91 continue;
92 }
93 snprintf(text, sizeof(text), "/dev/gator/events/%s/enabled", gSessionData->mPerfCounterType[i]);
94 if (writeReadDriver(text, &gSessionData->mPerfCounterEnabled[i])) {
95 // Disable those events that don't exist on this hardware platform even though they exist in configuration.xml
96 gSessionData->mPerfCounterEnabled[i] = 0;
97 continue;
98 }
99 }
100 }
102 void Collector::setupPerfCounters() {
103 char base[sizeof(gSessionData->mPerfCounterType[0]) + 20]; // sufficiently large to hold all /dev/gator/events/<types>
104 char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events/<types>/<file>
106 for (int i=0; i<MAX_PERFORMANCE_COUNTERS; i++) {
107 if (!gSessionData->mPerfCounterEnabled[i]) {
108 continue;
109 }
110 snprintf(base, sizeof(base), "/dev/gator/events/%s", gSessionData->mPerfCounterType[i]);
111 snprintf(text, sizeof(text), "%s/event", base);
112 writeDriver(text, gSessionData->mPerfCounterEvent[i]);
113 if (gSessionData->mPerfCounterEBSCapable[i]) {
114 snprintf(text, sizeof(text), "%s/count", base);
115 if (access(text, F_OK) == 0) {
116 if (writeReadDriver(text, &gSessionData->mPerfCounterCount[i]) && gSessionData->mPerfCounterCount[i] > 0) {
117 logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%s with a count of %d\n", gSessionData->mPerfCounterTitle[i], gSessionData->mPerfCounterName[i], gSessionData->mPerfCounterCount[i]);
118 handleException();
119 }
120 } else if (gSessionData->mPerfCounterCount[i] > 0) {
121 logg->logError(__FILE__, __LINE__, "Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y\n");
122 handleException();
123 }
124 }
125 }
126 }
128 void Collector::checkVersion() {
129 int driver_version = 0;
131 if (readIntDriver("/dev/gator/version", &driver_version) == -1) {
132 logg->logError(__FILE__, __LINE__, "Error reading gator driver version");
133 handleException();
134 }
136 // Verify the driver version matches the daemon version
137 if (driver_version != PROTOCOL_VERSION) {
138 if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) {
139 // One of the mismatched versions is development version
140 logg->logError(__FILE__, __LINE__,
141 "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n"
142 ">> The following must be synchronized from engineering repository:\n"
143 ">> * gator driver\n"
144 ">> * gator daemon\n"
145 ">> * Streamline", driver_version, PROTOCOL_VERSION);
146 handleException();
147 } else {
148 // Release version mismatch
149 logg->logError(__FILE__, __LINE__,
150 "gator driver version \"%d\" is different than gator daemon version \"%d\".\n"
151 ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION);
152 handleException();
153 }
154 }
155 }
157 void Collector::start() {
158 // Set the maximum backtrace depth
159 if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) {
160 logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth");
161 handleException();
162 }
164 // open the buffer which calls userspace_buffer_open() in the driver
165 mBufferFD = open("/dev/gator/buffer", O_RDONLY);
166 if (mBufferFD < 0) {
167 logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure.");
168 handleException();
169 }
171 // set the tick rate of the profiling timer
172 if (writeReadDriver("/dev/gator/tick", &gSessionData->mSampleRate) != 0) {
173 logg->logError(__FILE__, __LINE__, "Unable to set the driver tick");
174 handleException();
175 }
177 // notify the kernel of the response type
178 int response_type = gSessionData->mLocalCapture ? 0 : RESPONSE_APC_DATA;
179 if (writeDriver("/dev/gator/response_type", response_type)) {
180 logg->logError(__FILE__, __LINE__, "Unable to write the response type");
181 handleException();
182 }
184 logg->logMessage("Start the driver");
186 // This command makes the driver start profiling by calling gator_op_start() in the driver
187 if (writeDriver("/dev/gator/enable", "1") != 0) {
188 logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure.");
189 handleException();
190 }
192 lseek(mBufferFD, 0, SEEK_SET);
193 }
195 // These commands should cause the read() function in collect() to return
196 void Collector::stop() {
197 // This will stop the driver from profiling
198 if (writeDriver("/dev/gator/enable", "0") != 0) {
199 logg->logMessage("Stopping kernel failed");
200 }
201 }
203 int Collector::collect(char* buffer) {
204 // Calls event_buffer_read in the driver
205 int bytesRead = read(mBufferFD, buffer, mBufferSize);
207 // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data
208 if (bytesRead == -1 && errno == EINTR) {
209 bytesRead = read(mBufferFD, buffer, mBufferSize);
210 }
212 // return the total bytes written
213 logg->logMessage("Driver read of %d bytes", bytesRead);
214 return bytesRead;
215 }
217 void Collector::getCoreName() {
218 char temp[256]; // arbitrarily large amount
219 strcpy(gSessionData->mCoreName, "unknown");
221 FILE* f = fopen("/proc/cpuinfo", "r");
222 if (f == NULL) {
223 logg->logMessage("Error opening /proc/cpuinfo\n"
224 "The core name in the captured xml file will be 'unknown'.");
225 return;
226 }
228 while (fgets(temp, sizeof(temp), f)) {
229 if (strlen(temp) > 0) {
230 temp[strlen(temp) - 1] = 0; // Replace the line feed with a null
231 }
233 if (strstr(temp, "Hardware") != 0) {
234 char* position = strchr(temp, ':');
235 if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) {
236 logg->logMessage("Unknown format of /proc/cpuinfo\n"
237 "The core name in the captured xml file will be 'unknown'.");
238 return;
239 }
240 strncpy(gSessionData->mCoreName, (char*)((int)position + 2), sizeof(gSessionData->mCoreName));
241 gSessionData->mCoreName[sizeof(gSessionData->mCoreName) - 1] = 0; // strncpy does not guarantee a null-terminated string
242 fclose(f);
243 return;
244 }
245 }
247 logg->logMessage("Could not determine core name from /proc/cpuinfo\n"
248 "The core name in the captured xml file will be 'unknown'.");
249 fclose(f);
250 }
252 int Collector::readIntDriver(const char* fullpath, int* value) {
253 FILE* file = fopen(fullpath, "r");
254 if (file == NULL) {
255 return -1;
256 }
257 if (fscanf(file, "%u", value) != 1) {
258 fclose(file);
259 logg->logMessage("Invalid value in file %s", fullpath);
260 return -1;
261 }
262 fclose(file);
263 return 0;
264 }
266 int Collector::writeDriver(const char* path, int value) {
267 char data[40]; // Sufficiently large to hold any integer
268 snprintf(data, sizeof(data), "%d", value);
269 return writeDriver(path, data);
270 }
272 int Collector::writeDriver(const char* fullpath, const char* data) {
273 int fd = open(fullpath, O_WRONLY);
274 if (fd < 0) {
275 return -1;
276 }
277 if (write(fd, data, strlen(data)) < 0) {
278 close(fd);
279 logg->logMessage("Opened but could not write to %s", fullpath);
280 return -1;
281 }
282 close(fd);
283 return 0;
284 }
286 int Collector::writeReadDriver(const char* path, int* value) {
287 if (writeDriver(path, *value) || readIntDriver(path, value)) {
288 return -1;
289 }
290 return 0;
291 }