gator: Version 5.18
[firefly-linux-kernel-4.4.55.git] / tools / gator / daemon / Proc.cpp
1 /**
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
21 struct 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
29 static 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
60 static bool readProcTask(Buffer *const buffer, const int pid, const char *const image, DynBuf *const printb, DynBuf *const b) {
61         bool result = false;
62
63         if (!b->printf("/proc/%i/task", pid)) {
64                 logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
65                 return result;
66         }
67         DIR *task = opendir(b->getBuf());
68         if (task == NULL) {
69                 logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__);
70                 return result;
71         }
72
73         struct dirent *dirent;
74         while ((dirent = readdir(task)) != NULL) {
75                 char *endptr;
76                 const int tid = strtol(dirent->d_name, &endptr, 10);
77                 if (*endptr != '\0') {
78                         // Ignore task items that are not integers like ., etc...
79                         continue;
80                 }
81
82                 if (!printb->printf("/proc/%i/task/%i/stat", pid, tid)) {
83                         logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
84                         goto fail;
85                 }
86                 ProcStat ps;
87                 if (!readProcStat(&ps, printb->getBuf(), b)) {
88                         logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__);
89                         goto fail;
90                 }
91
92                 buffer->comm(pid, tid, image, ps.comm);
93         }
94
95         result = true;
96
97  fail:
98         closedir(task);
99
100         return result;
101 }
102
103 bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3) {
104         bool result = false;
105
106         DIR *proc = opendir("/proc");
107         if (proc == NULL) {
108                 logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__);
109                 return result;
110         }
111
112         struct dirent *dirent;
113         while ((dirent = readdir(proc)) != NULL) {
114                 char *endptr;
115                 const int pid = strtol(dirent->d_name, &endptr, 10);
116                 if (*endptr != '\0') {
117                         // Ignore proc items that are not integers like ., cpuinfo, etc...
118                         continue;
119                 }
120
121                 if (!printb->printf("/proc/%i/stat", pid)) {
122                         logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
123                         goto fail;
124                 }
125                 ProcStat ps;
126                 if (!readProcStat(&ps, printb->getBuf(), b1)) {
127                         logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__);
128                         goto fail;
129                 }
130
131                 if (!printb->printf("/proc/%i/exe", pid)) {
132                         logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
133                         goto fail;
134                 }
135                 const int err = b1->readlink(printb->getBuf());
136                 const char *image;
137                 if (err == 0) {
138                         image = strrchr(b1->getBuf(), '/');
139                         if (image == NULL) {
140                                 image = b1->getBuf();
141                         } else {
142                                 ++image;
143                         }
144                 } else if (err == -ENOENT) {
145                         // readlink /proc/[pid]/exe returns ENOENT for kernel threads
146                         image = "\0";
147                 } else {
148                         logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__);
149                         goto fail;
150                 }
151
152                 if (!printb->printf("/proc/%i/maps", pid)) {
153                         logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
154                         goto fail;
155                 }
156                 if (!b2->read(printb->getBuf())) {
157                         logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__);
158                         // This is not a fatal error - the process just doesn't exist any more
159                         continue;
160                 }
161
162                 buffer->maps(pid, pid, b2->getBuf());
163                 if (ps.numThreads <= 1) {
164                         buffer->comm(pid, pid, image, ps.comm);
165                 } else {
166                         if (!readProcTask(buffer, pid, image, printb, b3)) {
167                                 logg->logMessage("%s(%s:%i): readProcTask failed", __FUNCTION__, __FILE__, __LINE__);
168                                 goto fail;
169                         }
170                 }
171         }
172
173         result = true;
174
175  fail:
176         closedir(proc);
177
178         return result;
179 }