blob: 4ba59b632836f2b508f1bf66c30482404d84a355 [file] [log] [blame]
Jon Medhurst15ce78d2014-04-10 09:02:02 +01001/**
Jon Medhurstb1d07442015-05-08 12:04:18 +01002 * Copyright (C) ARM Limited 2013-2015. All rights reserved.
Jon Medhurst15ce78d2014-04-10 09:02:02 +01003 *
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)) {
Jon Medhurstb1d07442015-05-08 12:04:18 +010034 logg->logMessage("DynBuf::read failed, likely because the thread exited");
Jon Medhurst15ce78d2014-04-10 09:02:02 +010035 // 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) {
Jon Medhurstb1d07442015-05-08 12:04:18 +010041 logg->logMessage("parsing stat failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +010042 return false;
43 }
44 ++comm;
45 char *const str = strrchr(comm, ')');
46 if (str == NULL) {
Jon Medhurstb1d07442015-05-08 12:04:18 +010047 logg->logMessage("parsing stat failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +010048 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) {
Jon Medhurstb1d07442015-05-08 12:04:18 +010056 logg->logMessage("sscanf failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +010057 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)) {
Jon Medhurstb1d07442015-05-08 12:04:18 +010068 logg->logMessage("DynBuf::printf failed");
Jon Medhurste31266f2014-08-04 15:47:44 +010069 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 {
Jon Medhurstb1d07442015-05-08 12:04:18 +010085 logg->logMessage("DynBuf::readlink failed");
Jon Medhurste31266f2014-08-04 15:47:44 +010086 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)) {
Jon Medhurstb1d07442015-05-08 12:04:18 +010097 logg->logMessage("DynBuf::printf failed");
Jon Medhurste31266f2014-08-04 15:47:44 +010098 return NULL;
99 }
100
101 if (!b->read(printb->getBuf())) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100102 logg->logMessage("DynBuf::read failed, likely because the thread exited");
Jon Medhurste31266f2014-08-04 15:47:44 +0100103 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 Medhurstb1d07442015-05-08 12:04:18 +0100113 logg->logMessage("DynBuf::printf failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100114 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) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100118 logg->logMessage("opendir failed");
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)) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100133 logg->logMessage("DynBuf::printf failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100134 goto fail;
135 }
136 ProcStat ps;
Jon Medhurste31266f2014-08-04 15:47:44 +0100137 if (!readProcStat(&ps, printb->getBuf(), b1)) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100138 logg->logMessage("readProcStat failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100139 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) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100144 logg->logMessage("readImage failed");
Jon Medhurste31266f2014-08-04 15:47:44 +0100145 goto fail;
146 }
147
Jon Medhurstb1d07442015-05-08 12:04:18 +0100148 buffer->marshalComm(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) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100164 logg->logMessage("opendir failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100165 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)) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100178 logg->logMessage("DynBuf::printf failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100179 goto fail;
180 }
181 ProcStat ps;
182 if (!readProcStat(&ps, printb->getBuf(), b1)) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100183 logg->logMessage("readProcStat failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100184 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) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100190 logg->logMessage("readImage failed");
Jon Medhurste31266f2014-08-04 15:47:44 +0100191 goto fail;
192 }
193
Jon Medhurstb1d07442015-05-08 12:04:18 +0100194 buffer->marshalComm(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 Medhurstb1d07442015-05-08 12:04:18 +0100197 logg->logMessage("readProcTask failed");
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100198 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) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100216 logg->logMessage("opendir failed");
Jon Medhurst96b56152014-10-30 18:01:15 +0000217 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)) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100230 logg->logMessage("DynBuf::printf failed");
Jon Medhurst96b56152014-10-30 18:01:15 +0000231 goto fail;
232 }
233 if (!b->read(printb->getBuf())) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100234 logg->logMessage("DynBuf::read failed, likely because the process exited");
Jon Medhurst96b56152014-10-30 18:01:15 +0000235 // This is not a fatal error - the process just doesn't exist any more
236 continue;
237 }
238
Jon Medhurstb1d07442015-05-08 12:04:18 +0100239 buffer->marshalMaps(currTime, pid, pid, b->getBuf());
Jon Medhurst96b56152014-10-30 18:01:15 +0000240 }
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) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100254 logg->logMessage("open failed");
Jon Medhurst96b56152014-10-30 18:01:15 +0000255 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) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100263 logg->logError("no space left in buffer");
Jon Medhurst96b56152014-10-30 18:01:15 +0000264 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) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100271 logg->logError("read failed");
Jon Medhurst96b56152014-10-30 18:01:15 +0000272 handleException();
273 }
274 if (bytes == 0) {
275 // Assert the buffer is empty
276 if (pos != 0) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100277 logg->logError("buffer not empty on eof");
Jon Medhurst96b56152014-10-30 18:01:15 +0000278 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';
Jon Medhurstb1d07442015-05-08 12:04:18 +0100291 buffer->marshalKallsyms(currTime, buf);
Jon Medhurst96b56152014-10-30 18:01:15 +0000292 // 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) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100297 logg->logError("memcpy src and dst overlap");
Jon Medhurst96b56152014-10-30 18:01:15 +0000298 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}