gator: Version 5.21.1
[firefly-linux-kernel-4.4.55.git] / tools / gator / daemon / Proc.cpp
1 /**
2  * Copyright (C) ARM Limited 2013-2015. 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 <fcntl.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18
19 #include "Buffer.h"
20 #include "DynBuf.h"
21 #include "Logging.h"
22 #include "SessionData.h"
23
24 struct 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
32 static bool readProcStat(ProcStat *const ps, const char *const pathname, DynBuf *const b) {
33         if (!b->read(pathname)) {
34                 logg->logMessage("DynBuf::read failed, likely because the thread exited");
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("parsing stat failed");
42                 return false;
43         }
44         ++comm;
45         char *const str = strrchr(comm, ')');
46         if (str == NULL) {
47                 logg->logMessage("parsing stat failed");
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("sscanf failed");
57                 return false;
58         }
59
60         return true;
61 }
62
63 static const char APP_PROCESS[] = "app_process";
64
65 static 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("DynBuf::printf failed");
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("DynBuf::readlink failed");
86                 return NULL;
87         }
88
89         // Android apps are run by app_process but the cmdline is changed to reference the actual app name
90         // On 64-bit android app_process can be app_process32 or app_process64
91         if (strncmp(image, APP_PROCESS, sizeof(APP_PROCESS) - 1) != 0) {
92                 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("DynBuf::printf failed");
98                 return NULL;
99         }
100
101         if (!b->read(printb->getBuf())) {
102                 logg->logMessage("DynBuf::read failed, likely because the thread exited");
103                 return NULL;
104         }
105
106         return b->getBuf();
107 }
108
109 static bool readProcTask(const uint64_t currTime, Buffer *const buffer, const int pid, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2) {
110         bool result = false;
111
112         if (!b1->printf("/proc/%i/task", pid)) {
113                 logg->logMessage("DynBuf::printf failed");
114                 return result;
115         }
116         DIR *task = opendir(b1->getBuf());
117         if (task == NULL) {
118                 logg->logMessage("opendir failed");
119                 // This is not a fatal error - the thread just doesn't exist any more
120                 return true;
121         }
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("DynBuf::printf failed");
134                         goto fail;
135                 }
136                 ProcStat ps;
137                 if (!readProcStat(&ps, printb->getBuf(), b1)) {
138                         logg->logMessage("readProcStat failed");
139                         goto fail;
140                 }
141
142                 const char *const image = readProcExe(printb, pid, tid, b2);
143                 if (image == NULL) {
144                         logg->logMessage("readImage failed");
145                         goto fail;
146                 }
147
148                 buffer->marshalComm(currTime, pid, tid, image, ps.comm);
149         }
150
151         result = true;
152
153  fail:
154         closedir(task);
155
156         return result;
157 }
158
159 bool readProcComms(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2) {
160         bool result = false;
161
162         DIR *proc = opendir("/proc");
163         if (proc == NULL) {
164                 logg->logMessage("opendir failed");
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("DynBuf::printf failed");
179                         goto fail;
180                 }
181                 ProcStat ps;
182                 if (!readProcStat(&ps, printb->getBuf(), b1)) {
183                         logg->logMessage("readProcStat failed");
184                         goto fail;
185                 }
186
187                 if (ps.numThreads <= 1) {
188                         const char *const image = readProcExe(printb, pid, -1, b1);
189                         if (image == NULL) {
190                                 logg->logMessage("readImage failed");
191                                 goto fail;
192                         }
193
194                         buffer->marshalComm(currTime, pid, pid, image, ps.comm);
195                 } else {
196                         if (!readProcTask(currTime, buffer, pid, printb, b1, b2)) {
197                                 logg->logMessage("readProcTask failed");
198                                 goto fail;
199                         }
200                 }
201         }
202
203         result = true;
204
205  fail:
206         closedir(proc);
207
208         return result;
209 }
210
211 bool 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("opendir failed");
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("DynBuf::printf failed");
231                         goto fail;
232                 }
233                 if (!b->read(printb->getBuf())) {
234                         logg->logMessage("DynBuf::read failed, likely because the process exited");
235                         // This is not a fatal error - the process just doesn't exist any more
236                         continue;
237                 }
238
239                 buffer->marshalMaps(currTime, pid, pid, b->getBuf());
240         }
241
242         result = true;
243
244  fail:
245         closedir(proc);
246
247         return result;
248 }
249
250 bool 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("open failed");
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("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("read failed");
272                                 handleException();
273                         }
274                         if (bytes == 0) {
275                                 // Assert the buffer is empty
276                                 if (pos != 0) {
277                                         logg->logError("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->marshalKallsyms(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("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 }