blob: e6b26b1199fa9d997f16a5305871e5515caf7bc9 [file] [log] [blame]
Jon Medhurst15ce78d2014-04-10 09:02:02 +01001/**
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>
Jon Medhurst96b56152014-10-30 18:01:15 +000013#include <fcntl.h>
Jon Medhurst15ce78d2014-04-10 09:02:02 +010014#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
Jon Medhurst96b56152014-10-30 18:01:15 +000017#include <unistd.h>
Jon Medhurst15ce78d2014-04-10 09:02:02 +010018
19#include "Buffer.h"
20#include "DynBuf.h"
21#include "Logging.h"
Jon Medhurst96b56152014-10-30 18:01:15 +000022#include "SessionData.h"
Jon Medhurst15ce78d2014-04-10 09:02:02 +010023
24struct ProcStat {
25 // From linux-dev/include/linux/sched.h
26#define TASK_COMM_LEN 16
27 // TASK_COMM_LEN may grow, so be ready for it to get larger
28 char comm[2*TASK_COMM_LEN];
29 long numThreads;
30};
31
32static bool readProcStat(ProcStat *const ps, const char *const pathname, DynBuf *const b) {
33 if (!b->read(pathname)) {
34 logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__);
35 // This is not a fatal error - the thread just doesn't exist any more
36 return true;
37 }
38
39 char *comm = strchr(b->getBuf(), '(');
40 if (comm == NULL) {
41 logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__);
42 return false;
43 }
44 ++comm;
45 char *const str = strrchr(comm, ')');
46 if (str == NULL) {
47 logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__);
48 return false;
49 }
50 *str = '\0';
51 strncpy(ps->comm, comm, sizeof(ps->comm) - 1);
52 ps->comm[sizeof(ps->comm) - 1] = '\0';
53
54 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);
55 if (count != 1) {
56 logg->logMessage("%s(%s:%i): sscanf failed", __FUNCTION__, __FILE__, __LINE__);
57 return false;
58 }
59
60 return true;
61}
62
Jon Medhurst96b56152014-10-30 18:01:15 +000063static const char APP_PROCESS[] = "app_process";
64
Jon Medhurste31266f2014-08-04 15:47:44 +010065static const char *readProcExe(DynBuf *const printb, const int pid, const int tid, DynBuf *const b) {
66 if (tid == -1 ? !printb->printf("/proc/%i/exe", pid)
67 : !printb->printf("/proc/%i/task/%i/exe", pid, tid)) {
68 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
69 return NULL;
70 }
71
72 const int err = b->readlink(printb->getBuf());
73 const char *image;
74 if (err == 0) {
75 image = strrchr(b->getBuf(), '/');
76 if (image == NULL) {
77 image = b->getBuf();
78 } else {
79 ++image;
80 }
81 } else if (err == -ENOENT) {
82 // readlink /proc/[pid]/exe returns ENOENT for kernel threads
83 image = "\0";
84 } else {
85 logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__);
86 return NULL;
87 }
88
89 // Android apps are run by app_process but the cmdline is changed to reference the actual app name
Jon Medhurst96b56152014-10-30 18:01:15 +000090 // On 64-bit android app_process can be app_process32 or app_process64
91 if (strncmp(image, APP_PROCESS, sizeof(APP_PROCESS) - 1) != 0) {
Jon Medhurste31266f2014-08-04 15:47:44 +010092 return image;
93 }
94
95 if (tid == -1 ? !printb->printf("/proc/%i/cmdline", pid)
96 : !printb->printf("/proc/%i/task/%i/cmdline", pid, tid)) {
97 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
98 return NULL;
99 }
100
101 if (!b->read(printb->getBuf())) {
102 logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__);
103 return NULL;
104 }
105
106 return b->getBuf();
107}
108
Jon Medhurst96b56152014-10-30 18:01:15 +0000109static bool readProcTask(const uint64_t currTime, Buffer *const buffer, const int pid, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100110 bool result = false;
111
Jon Medhurste31266f2014-08-04 15:47:44 +0100112 if (!b1->printf("/proc/%i/task", pid)) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100113 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
114 return result;
115 }
Jon Medhurste31266f2014-08-04 15:47:44 +0100116 DIR *task = opendir(b1->getBuf());
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100117 if (task == NULL) {
118 logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__);
Jon Medhurst96b56152014-10-30 18:01:15 +0000119 // This is not a fatal error - the thread just doesn't exist any more
120 return true;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100121 }
122
123 struct dirent *dirent;
124 while ((dirent = readdir(task)) != NULL) {
125 char *endptr;
126 const int tid = strtol(dirent->d_name, &endptr, 10);
127 if (*endptr != '\0') {
128 // Ignore task items that are not integers like ., etc...
129 continue;
130 }
131
132 if (!printb->printf("/proc/%i/task/%i/stat", pid, tid)) {
133 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
134 goto fail;
135 }
136 ProcStat ps;
Jon Medhurste31266f2014-08-04 15:47:44 +0100137 if (!readProcStat(&ps, printb->getBuf(), b1)) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100138 logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__);
139 goto fail;
140 }
141
Jon Medhurste31266f2014-08-04 15:47:44 +0100142 const char *const image = readProcExe(printb, pid, tid, b2);
143 if (image == NULL) {
144 logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__);
145 goto fail;
146 }
147
Jon Medhurst96b56152014-10-30 18:01:15 +0000148 buffer->comm(currTime, pid, tid, image, ps.comm);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100149 }
150
151 result = true;
152
153 fail:
154 closedir(task);
155
156 return result;
157}
158
Jon Medhurst96b56152014-10-30 18:01:15 +0000159bool readProcComms(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100160 bool result = false;
161
162 DIR *proc = opendir("/proc");
163 if (proc == NULL) {
164 logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__);
165 return result;
166 }
167
168 struct dirent *dirent;
169 while ((dirent = readdir(proc)) != NULL) {
170 char *endptr;
171 const int pid = strtol(dirent->d_name, &endptr, 10);
172 if (*endptr != '\0') {
173 // Ignore proc items that are not integers like ., cpuinfo, etc...
174 continue;
175 }
176
177 if (!printb->printf("/proc/%i/stat", pid)) {
178 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
179 goto fail;
180 }
181 ProcStat ps;
182 if (!readProcStat(&ps, printb->getBuf(), b1)) {
183 logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__);
184 goto fail;
185 }
186
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100187 if (ps.numThreads <= 1) {
Jon Medhurste31266f2014-08-04 15:47:44 +0100188 const char *const image = readProcExe(printb, pid, -1, b1);
189 if (image == NULL) {
190 logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__);
191 goto fail;
192 }
193
Jon Medhurst96b56152014-10-30 18:01:15 +0000194 buffer->comm(currTime, pid, pid, image, ps.comm);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100195 } else {
Jon Medhurst96b56152014-10-30 18:01:15 +0000196 if (!readProcTask(currTime, buffer, pid, printb, b1, b2)) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100197 logg->logMessage("%s(%s:%i): readProcTask failed", __FUNCTION__, __FILE__, __LINE__);
198 goto fail;
199 }
200 }
201 }
202
203 result = true;
204
205 fail:
206 closedir(proc);
207
208 return result;
209}
Jon Medhurst96b56152014-10-30 18:01:15 +0000210
211bool readProcMaps(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b) {
212 bool result = false;
213
214 DIR *proc = opendir("/proc");
215 if (proc == NULL) {
216 logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__);
217 return result;
218 }
219
220 struct dirent *dirent;
221 while ((dirent = readdir(proc)) != NULL) {
222 char *endptr;
223 const int pid = strtol(dirent->d_name, &endptr, 10);
224 if (*endptr != '\0') {
225 // Ignore proc items that are not integers like ., cpuinfo, etc...
226 continue;
227 }
228
229 if (!printb->printf("/proc/%i/maps", pid)) {
230 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
231 goto fail;
232 }
233 if (!b->read(printb->getBuf())) {
234 logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__);
235 // This is not a fatal error - the process just doesn't exist any more
236 continue;
237 }
238
239 buffer->maps(currTime, pid, pid, b->getBuf());
240 }
241
242 result = true;
243
244 fail:
245 closedir(proc);
246
247 return result;
248}
249
250bool readKallsyms(const uint64_t currTime, Buffer *const buffer, const bool *const isDone) {
251 int fd = ::open("/proc/kallsyms", O_RDONLY | O_CLOEXEC);
252
253 if (fd < 0) {
254 logg->logMessage("%s(%s:%i): open failed", __FUNCTION__, __FILE__, __LINE__);
255 return true;
256 };
257
258 char buf[1<<12];
259 ssize_t pos = 0;
260 while (gSessionData->mSessionIsActive && !ACCESS_ONCE(*isDone)) {
261 // Assert there is still space in the buffer
262 if (sizeof(buf) - pos - 1 == 0) {
263 logg->logError(__FILE__, __LINE__, "no space left in buffer");
264 handleException();
265 }
266
267 {
268 // -1 to reserve space for \0
269 const ssize_t bytes = ::read(fd, buf + pos, sizeof(buf) - pos - 1);
270 if (bytes < 0) {
271 logg->logError(__FILE__, __LINE__, "read failed", __FUNCTION__, __FILE__, __LINE__);
272 handleException();
273 }
274 if (bytes == 0) {
275 // Assert the buffer is empty
276 if (pos != 0) {
277 logg->logError(__FILE__, __LINE__, "buffer not empty on eof");
278 handleException();
279 }
280 break;
281 }
282 pos += bytes;
283 }
284
285 ssize_t newline;
286 // Find the last '\n'
287 for (newline = pos - 1; newline >= 0; --newline) {
288 if (buf[newline] == '\n') {
289 const char was = buf[newline + 1];
290 buf[newline + 1] = '\0';
291 buffer->kallsyms(currTime, buf);
292 // Sleep 3 ms to avoid sending out too much data too quickly
293 usleep(3000);
294 buf[0] = was;
295 // Assert the memory regions do not overlap
296 if (pos - newline >= newline + 1) {
297 logg->logError(__FILE__, __LINE__, "memcpy src and dst overlap");
298 handleException();
299 }
300 if (pos - newline - 2 > 0) {
301 memcpy(buf + 1, buf + newline + 2, pos - newline - 2);
302 }
303 pos -= newline + 1;
304 break;
305 }
306 }
307 }
308
309 close(fd);
310
311 return true;
312}