blob: 9f01770d6609afdd21e4c1287f072024c47fc134 [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>
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
21struct 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
29static 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
Jon Medhurste31266f2014-08-04 15:47:44 +010060static const char *readProcExe(DynBuf *const printb, const int pid, const int tid, DynBuf *const b) {
61 if (tid == -1 ? !printb->printf("/proc/%i/exe", pid)
62 : !printb->printf("/proc/%i/task/%i/exe", pid, tid)) {
63 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
64 return NULL;
65 }
66
67 const int err = b->readlink(printb->getBuf());
68 const char *image;
69 if (err == 0) {
70 image = strrchr(b->getBuf(), '/');
71 if (image == NULL) {
72 image = b->getBuf();
73 } else {
74 ++image;
75 }
76 } else if (err == -ENOENT) {
77 // readlink /proc/[pid]/exe returns ENOENT for kernel threads
78 image = "\0";
79 } else {
80 logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__);
81 return NULL;
82 }
83
84 // Android apps are run by app_process but the cmdline is changed to reference the actual app name
85 if (strcmp(image, "app_process") != 0) {
86 return image;
87 }
88
89 if (tid == -1 ? !printb->printf("/proc/%i/cmdline", pid)
90 : !printb->printf("/proc/%i/task/%i/cmdline", pid, tid)) {
91 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
92 return NULL;
93 }
94
95 if (!b->read(printb->getBuf())) {
96 logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__);
97 return NULL;
98 }
99
100 return b->getBuf();
101}
102
103static bool readProcTask(Buffer *const buffer, const int pid, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100104 bool result = false;
105
Jon Medhurste31266f2014-08-04 15:47:44 +0100106 if (!b1->printf("/proc/%i/task", pid)) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100107 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
108 return result;
109 }
Jon Medhurste31266f2014-08-04 15:47:44 +0100110 DIR *task = opendir(b1->getBuf());
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100111 if (task == NULL) {
112 logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__);
113 return result;
114 }
115
116 struct dirent *dirent;
117 while ((dirent = readdir(task)) != NULL) {
118 char *endptr;
119 const int tid = strtol(dirent->d_name, &endptr, 10);
120 if (*endptr != '\0') {
121 // Ignore task items that are not integers like ., etc...
122 continue;
123 }
124
125 if (!printb->printf("/proc/%i/task/%i/stat", pid, tid)) {
126 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
127 goto fail;
128 }
129 ProcStat ps;
Jon Medhurste31266f2014-08-04 15:47:44 +0100130 if (!readProcStat(&ps, printb->getBuf(), b1)) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100131 logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__);
132 goto fail;
133 }
134
Jon Medhurste31266f2014-08-04 15:47:44 +0100135 const char *const image = readProcExe(printb, pid, tid, b2);
136 if (image == NULL) {
137 logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__);
138 goto fail;
139 }
140
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100141 buffer->comm(pid, tid, image, ps.comm);
142 }
143
144 result = true;
145
146 fail:
147 closedir(task);
148
149 return result;
150}
151
Jon Medhurste31266f2014-08-04 15:47:44 +0100152bool readProc(Buffer *const buffer, bool sendMaps, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100153 bool result = false;
154
155 DIR *proc = opendir("/proc");
156 if (proc == NULL) {
157 logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__);
158 return result;
159 }
160
161 struct dirent *dirent;
162 while ((dirent = readdir(proc)) != NULL) {
163 char *endptr;
164 const int pid = strtol(dirent->d_name, &endptr, 10);
165 if (*endptr != '\0') {
166 // Ignore proc items that are not integers like ., cpuinfo, etc...
167 continue;
168 }
169
170 if (!printb->printf("/proc/%i/stat", pid)) {
171 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
172 goto fail;
173 }
174 ProcStat ps;
175 if (!readProcStat(&ps, printb->getBuf(), b1)) {
176 logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__);
177 goto fail;
178 }
179
Jon Medhurste31266f2014-08-04 15:47:44 +0100180 if (sendMaps) {
181 if (!printb->printf("/proc/%i/maps", pid)) {
182 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
183 goto fail;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100184 }
Jon Medhurste31266f2014-08-04 15:47:44 +0100185 if (!b2->read(printb->getBuf())) {
186 logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__);
187 // This is not a fatal error - the process just doesn't exist any more
188 continue;
189 }
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100190
Jon Medhurste31266f2014-08-04 15:47:44 +0100191 buffer->maps(pid, pid, b2->getBuf());
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100192 }
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100193 if (ps.numThreads <= 1) {
Jon Medhurste31266f2014-08-04 15:47:44 +0100194 const char *const image = readProcExe(printb, pid, -1, b1);
195 if (image == NULL) {
196 logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__);
197 goto fail;
198 }
199
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100200 buffer->comm(pid, pid, image, ps.comm);
201 } else {
Jon Medhurste31266f2014-08-04 15:47:44 +0100202 if (!readProcTask(buffer, pid, printb, b1, b3)) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100203 logg->logMessage("%s(%s:%i): readProcTask failed", __FUNCTION__, __FILE__, __LINE__);
204 goto fail;
205 }
206 }
207 }
208
209 result = true;
210
211 fail:
212 closedir(proc);
213
214 return result;
215}