1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #define LOG_TAG "ProcessMonitor"
17 
18 #include <mutex>
19 #include <sys/param.h>
20 #include <dirent.h>
21 
22 #include "ProcessMonitor.h"
23 
24 #define DBG_VERBOSE
25 #ifdef DBG_VERBOSE
26 #define LOG_VERBOSE(x...) ALOGD(x)
27 #else
28 #define LOG_VERBOSE(x...)
29 #endif
30 
31 #define MAX_LINE 256
32 #define SELF_IO  "/proc/self/io"
33 #define NUM_PROC_DUMP 10
34 
35 namespace android {
36 
procDeltaCpuCmp(const std::pair<pid_t,const std::shared_ptr<ProcInfo>> a,const std::pair<pid_t,const std::shared_ptr<ProcInfo>> b)37 static bool procDeltaCpuCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
38                             const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
39     return a.second->delta_time > b.second->delta_time;
40 }
41 
procDeltaMemCmp(const std::pair<pid_t,const std::shared_ptr<ProcInfo>> a,const std::pair<pid_t,const std::shared_ptr<ProcInfo>> b)42 static bool procDeltaMemCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
43                             const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
44     return a.second->delta_rss > b.second->delta_rss;
45 }
46 
procDeltaWbytesCmp(const std::pair<pid_t,const std::shared_ptr<ProcInfo>> a,const std::pair<pid_t,const std::shared_ptr<ProcInfo>> b)47 static bool procDeltaWbytesCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
48                                const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
49     return a.second->delta_wbytes > b.second->delta_wbytes;
50 }
51 
procCpuCmp(const std::pair<pid_t,const std::shared_ptr<ProcInfo>> a,const std::pair<pid_t,const std::shared_ptr<ProcInfo>> b)52 static bool procCpuCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
53                        const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
54     return (a.second->utime + a.second->utime) > (b.second->stime + b.second->stime);
55 }
56 
procMemCmp(const std::pair<pid_t,const std::shared_ptr<ProcInfo>> a,const std::pair<pid_t,const std::shared_ptr<ProcInfo>> b)57 static bool procMemCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
58                       const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
59     return a.second->rss > b.second->rss;
60 }
61 
procWbytesCmp(const std::pair<pid_t,const std::shared_ptr<ProcInfo>> a,const std::pair<pid_t,const std::shared_ptr<ProcInfo>> b)62 static bool procWbytesCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
63                       const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
64     return a.second->wbytes > b.second->wbytes;
65 }
66 
ProcessMonitor()67 ProcessMonitor::ProcessMonitor() {
68     //TODO: read config from policy files.
69     if (access(SELF_IO, F_OK) == -1) {
70         mIoSupported = false;
71         ALOGE("**** DISK I/O PROFILING DISABLED!!!!****\n"
72               "Kernel doesn't support I/O profiling.");
73     } else {
74         mIoSupported = true;
75     }
76 }
77 
~ProcessMonitor()78 ProcessMonitor::~ProcessMonitor() {
79 }
80 
dump(String8 & msg)81 void ProcessMonitor::dump(String8& msg) {
82     std::shared_lock<std::shared_timed_mutex> lock(mMutex);
83     msg.append("ProcessMonitor\n");
84     msg.appendFormat("Processes count: %d\n", (int) mProcInfoMap.size());
85     msg.append("Top CPU usage\n");
86     dumpTopProcesses(msg, procCpuCmp);
87     msg.append("Top CPU usage increase\n");
88     dumpTopProcesses(msg, procDeltaCpuCmp);
89     msg.append("Top memory usage\n");
90     dumpTopProcesses(msg, procMemCmp);
91     msg.append("Top memory usage increase\n");
92     dumpTopProcesses(msg, procDeltaMemCmp);
93     if (mIoSupported) {
94         msg.append("Top disk IO \n");
95         dumpTopProcesses(msg, procWbytesCmp);
96         msg.append("Top disk IO increase \n");
97         dumpTopProcesses(msg, procDeltaWbytesCmp);
98     } else {
99         msg.append("Disk IO monitoring not supported.\n");
100     }
101 }
102 
dumpTopProcesses(String8 & msg,bool (* procCmpFn)(const std::pair<pid_t,const std::shared_ptr<ProcInfo>>,const std::pair<pid_t,const std::shared_ptr<ProcInfo>>))103 void ProcessMonitor::dumpTopProcesses(
104         String8& msg,
105         bool (*procCmpFn) (
106                 const std::pair<pid_t, const std::shared_ptr<ProcInfo>>,
107                 const std::pair<pid_t, const std::shared_ptr<ProcInfo>>)) {
108 
109     std::vector<std::pair<pid_t, std::shared_ptr<ProcInfo>>> topPids(NUM_PROC_DUMP);
110     std::partial_sort_copy(mProcInfoMap.begin(),
111                            mProcInfoMap.end(),
112                            topPids.begin(),
113                            topPids.end(),
114                            *procCmpFn);
115     for (auto it = topPids.begin(); it != topPids.end(); ++it) {
116         msg.appendFormat("(%s) PID: %d: delta_time: %" PRIu64 ", delta_rss: %" PRIu64 ", "
117                          "delta_wbytes: %" PRIu64 ", utime: %" PRIu64" , stime: %" PRIu64 ", "
118                          "rss: %" PRIu64 ", wbytes: %" PRIu64 "\n",
119                          it->second->name.c_str(),
120                          it->first,
121                          it->second->delta_time,
122                          it->second->delta_rss,
123                          it->second->delta_wbytes,
124                          it->second->utime,
125                          it->second->stime,
126                          it->second->rss,
127                          it->second->wbytes);
128     }
129 
130 }
131 
setAppPriority(uint32_t,uint32_t,uint32_t)132 status_t ProcessMonitor::setAppPriority(uint32_t , uint32_t, uint32_t) {
133     std::unique_lock<std::shared_timed_mutex> lock(mMutex);
134     // TODO implement.
135     return NO_ERROR;
136 }
137 
process()138 status_t ProcessMonitor::process() {
139     status_t status = updateProcessInfo();
140     if (status != NO_ERROR) {
141         return status;
142     }
143     return improveSystemHealth();
144 }
145 
improveSystemHealth()146 status_t ProcessMonitor::improveSystemHealth() {
147     // TODO: implement policy enforcer. kill apps that abuse system.
148     return NO_ERROR;
149 }
150 
updateProcessInfo()151 status_t ProcessMonitor::updateProcessInfo() {
152     std::unique_lock<std::shared_timed_mutex> lock(mMutex);
153     std::set<pid_t> oldPids;
154     populateExistingPids(oldPids);
155     DIR *procDir;
156     procDir = opendir("/proc");
157     if (!procDir) {
158         ALOGE("Failed to open /proc dir");
159         return PERMISSION_DENIED;
160     }
161     struct dirent *pidDir;
162     pid_t pid;
163     while ((pidDir = readdir(procDir))) {
164         if (!isdigit(pidDir->d_name[0])) {
165             continue;
166         }
167         pid = atoi(pidDir->d_name);
168         updateOrAddProcess(pid);
169         oldPids.erase(pid);
170     }
171     deleteOutdatedPids(oldPids);
172     return NO_ERROR;
173 }
174 
deleteOutdatedPids(std::set<pid_t> & pidSet)175 void ProcessMonitor::deleteOutdatedPids(std::set<pid_t>& pidSet) {
176     for(auto it = pidSet.begin(); it != pidSet.end(); ++it) {
177         LOG_VERBOSE("Process %d ended. Removing from process map", *it);
178         mProcInfoMap.erase(*it);
179     }
180 }
181 
populateExistingPids(std::set<pid_t> & pidSet)182 void ProcessMonitor::populateExistingPids(std::set<pid_t>& pidSet) {
183     for(auto it = mProcInfoMap.begin(); it != mProcInfoMap.end(); ++it) {
184         pidSet.insert(it->first);
185     }
186 }
187 
updateOrAddProcess(pid_t pid)188 void ProcessMonitor::updateOrAddProcess(pid_t pid) {
189     auto pidDataIt = mProcInfoMap.find(pid);
190     std::shared_ptr<ProcInfo> pidData;
191     if (pidDataIt == mProcInfoMap.end()) {
192         pidData = std::make_shared<ProcInfo>();
193         mProcInfoMap.insert(std::pair<pid_t, std::shared_ptr<ProcInfo>>(pid, pidData));
194     } else {
195         pidData = pidDataIt->second;
196     }
197     auto originalPidData = std::make_shared<ProcInfo>(*pidData);
198     readStat(pidData, pid);
199     if (mIoSupported) {
200         readIo(pidData, pid);
201     }
202     readCmdline(pidData, pid);
203     readStatus(pidData, pid);
204     updateDiffs(pidData, originalPidData);
205 }
206 
readStat(std::shared_ptr<ProcInfo> pidData,pid_t pid)207 void ProcessMonitor::readStat(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
208     char filename[64];
209     sprintf(filename, "/proc/%d/stat", pid);
210     FILE *file;
211     file = fopen(filename, "r");
212     if (!file) {
213         ALOGD("Failed to open file %s for reading", filename);
214         return;
215     }
216     fscanf(file,
217            "%*d %*s %*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
218            "%" SCNu64
219            "%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d "
220            "%*" SCNu64
221            "%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
222            "%*d",
223            &pidData->utime,
224            &pidData->stime,
225            &pidData->rss);
226     fclose(file);
227 }
228 
readIo(std::shared_ptr<ProcInfo> pidData,pid_t pid)229 void ProcessMonitor::readIo(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
230     char filename[64];
231     sprintf(filename, "/proc/%d/io", pid);
232     FILE *file;
233     file = fopen(filename, "r");
234     if (!file) {
235         ALOGD("Failed to open file %s for reading", filename);
236         return;
237     }
238     char buf[MAX_LINE];
239     while (fgets(buf, MAX_LINE, file)) {
240         sscanf(buf, "write_bytes: %" PRIu64, &pidData->wbytes);
241     }
242     fclose(file);
243 }
244 
readCmdline(std::shared_ptr<ProcInfo> pidData,pid_t pid)245 void ProcessMonitor::readCmdline(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
246     char filename[64];
247     sprintf(filename, "/proc/%d/cmdline", pid);
248     FILE *file;
249     file = fopen(filename, "r");
250     if (!file) {
251         ALOGD("Failed to open file %s for reading", filename);
252         return;
253     }
254     char buf[MAXPATHLEN];
255     fgets(buf, MAXPATHLEN, file);
256     fclose(file);
257     if (strlen(buf) > 0) {
258         pidData->name.assign(buf);
259     }
260 }
261 
readStatus(std::shared_ptr<ProcInfo> pidData,pid_t pid)262 void ProcessMonitor::readStatus(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
263     char filename[64];
264     sprintf(filename, "/proc/%d/status", pid);
265     FILE *file;
266     file = fopen(filename, "r");
267     if (!file) {
268         ALOGD("Failed to open file %s for reading", filename);
269         return;
270     }
271     char line[MAX_LINE];
272     unsigned int uid;
273     while (fgets(line, MAX_LINE, file)) {
274         sscanf(line, "Uid: %u", &uid);
275     }
276     fclose(file);
277     pidData->uid = uid;
278 
279 }
280 
updateDiffs(std::shared_ptr<ProcInfo> pidData,std::shared_ptr<ProcInfo> oldPidData)281 void ProcessMonitor::updateDiffs(std::shared_ptr<ProcInfo> pidData,
282                  std::shared_ptr<ProcInfo> oldPidData) {
283     pidData->delta_utime = pidData->utime - oldPidData->utime;
284     pidData->delta_stime = pidData->stime - oldPidData->stime;
285     pidData->delta_time = pidData->delta_utime + pidData->delta_stime;
286     pidData->delta_rss = pidData->rss - oldPidData->rss;
287     pidData->delta_wbytes = pidData->wbytes - oldPidData->wbytes;
288 }
289 
290 }; // namespace android
291