blob: 521473ff49c29dfc8eab0b1cbcf3f1264ccf899c [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "ProcessMonitor"
#include <mutex>
#include <sys/param.h>
#include <dirent.h>
#include "ProcessMonitor.h"
#define DBG_VERBOSE
#ifdef DBG_VERBOSE
#define LOG_VERBOSE(x...) ALOGD(x)
#else
#define LOG_VERBOSE(x...)
#endif
#define MAX_LINE 256
#define SELF_IO "/proc/self/io"
#define NUM_PROC_DUMP 10
namespace android {
static bool procDeltaCpuCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
return a.second->delta_time > b.second->delta_time;
}
static bool procDeltaMemCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
return a.second->delta_rss > b.second->delta_rss;
}
static bool procDeltaWbytesCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
return a.second->delta_wbytes > b.second->delta_wbytes;
}
static bool procCpuCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
return (a.second->utime + a.second->utime) > (b.second->stime + b.second->stime);
}
static bool procMemCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
return a.second->rss > b.second->rss;
}
static bool procWbytesCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
return a.second->wbytes > b.second->wbytes;
}
ProcessMonitor::ProcessMonitor() {
//TODO: read config from policy files.
if (access(SELF_IO, F_OK) == -1) {
mIoSupported = false;
ALOGE("**** DISK I/O PROFILING DISABLED!!!!****\n"
"Kernel doesn't support I/O profiling.");
} else {
mIoSupported = true;
}
}
ProcessMonitor::~ProcessMonitor() {
}
void ProcessMonitor::dump(String8& msg) {
std::shared_lock<std::shared_timed_mutex> lock(mMutex);
msg.append("ProcessMonitor\n");
msg.appendFormat("Processes count: %d\n", (int) mProcInfoMap.size());
msg.append("Top CPU usage\n");
dumpTopProcesses(msg, procCpuCmp);
msg.append("Top CPU usage increase\n");
dumpTopProcesses(msg, procDeltaCpuCmp);
msg.append("Top memory usage\n");
dumpTopProcesses(msg, procMemCmp);
msg.append("Top memory usage increase\n");
dumpTopProcesses(msg, procDeltaMemCmp);
if (mIoSupported) {
msg.append("Top disk IO \n");
dumpTopProcesses(msg, procWbytesCmp);
msg.append("Top disk IO increase \n");
dumpTopProcesses(msg, procDeltaWbytesCmp);
} else {
msg.append("Disk IO monitoring not supported.\n");
}
}
void ProcessMonitor::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>>)) {
std::vector<std::pair<pid_t, std::shared_ptr<ProcInfo>>> topPids(NUM_PROC_DUMP);
std::partial_sort_copy(mProcInfoMap.begin(),
mProcInfoMap.end(),
topPids.begin(),
topPids.end(),
*procCmpFn);
for (auto it = topPids.begin(); it != topPids.end(); ++it) {
msg.appendFormat("(%s) PID: %d: delta_time: %" PRIu64 ", delta_rss: %" PRIu64 ", "
"delta_wbytes: %" PRIu64 ", utime: %" PRIu64" , stime: %" PRIu64 ", "
"rss: %" PRIu64 ", wbytes: %" PRIu64 "\n",
it->second->name.c_str(),
it->first,
it->second->delta_time,
it->second->delta_rss,
it->second->delta_wbytes,
it->second->utime,
it->second->stime,
it->second->rss,
it->second->wbytes);
}
}
status_t ProcessMonitor::setAppPriority(uint32_t , uint32_t, uint32_t) {
std::unique_lock<std::shared_timed_mutex> lock(mMutex);
// TODO implement.
return NO_ERROR;
}
status_t ProcessMonitor::process() {
status_t status = updateProcessInfo();
if (status != NO_ERROR) {
return status;
}
return improveSystemHealth();
}
status_t ProcessMonitor::improveSystemHealth() {
// TODO: implement policy enforcer. kill apps that abuse system.
return NO_ERROR;
}
status_t ProcessMonitor::updateProcessInfo() {
std::unique_lock<std::shared_timed_mutex> lock(mMutex);
std::set<pid_t> oldPids;
populateExistingPids(oldPids);
DIR *procDir;
procDir = opendir("/proc");
if (!procDir) {
ALOGE("Failed to open /proc dir");
return PERMISSION_DENIED;
}
struct dirent *pidDir;
pid_t pid;
while ((pidDir = readdir(procDir))) {
if (!isdigit(pidDir->d_name[0])) {
continue;
}
pid = atoi(pidDir->d_name);
updateOrAddProcess(pid);
oldPids.erase(pid);
}
deleteOutdatedPids(oldPids);
return NO_ERROR;
}
void ProcessMonitor::deleteOutdatedPids(std::set<pid_t>& pidSet) {
for(auto it = pidSet.begin(); it != pidSet.end(); ++it) {
LOG_VERBOSE("Process %d ended. Removing from process map", *it);
mProcInfoMap.erase(*it);
}
}
void ProcessMonitor::populateExistingPids(std::set<pid_t>& pidSet) {
for(auto it = mProcInfoMap.begin(); it != mProcInfoMap.end(); ++it) {
pidSet.insert(it->first);
}
}
void ProcessMonitor::updateOrAddProcess(pid_t pid) {
auto pidDataIt = mProcInfoMap.find(pid);
std::shared_ptr<ProcInfo> pidData;
if (pidDataIt == mProcInfoMap.end()) {
pidData = std::make_shared<ProcInfo>();
mProcInfoMap.insert(std::pair<pid_t, std::shared_ptr<ProcInfo>>(pid, pidData));
} else {
pidData = pidDataIt->second;
}
auto originalPidData = std::make_shared<ProcInfo>(*pidData);
readStat(pidData, pid);
if (mIoSupported) {
readIo(pidData, pid);
}
readCmdline(pidData, pid);
readStatus(pidData, pid);
updateDiffs(pidData, originalPidData);
}
void ProcessMonitor::readStat(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
char filename[64];
sprintf(filename, "/proc/%d/stat", pid);
FILE *file;
file = fopen(filename, "r");
if (!file) {
ALOGD("Failed to open file %s for reading", filename);
return;
}
fscanf(file,
"%*d %*s %*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
"%" SCNu64
"%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d "
"%*" SCNu64
"%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
"%*d",
&pidData->utime,
&pidData->stime,
&pidData->rss);
fclose(file);
}
void ProcessMonitor::readIo(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
char filename[64];
sprintf(filename, "/proc/%d/io", pid);
FILE *file;
file = fopen(filename, "r");
if (!file) {
ALOGD("Failed to open file %s for reading", filename);
return;
}
char buf[MAX_LINE];
while (fgets(buf, MAX_LINE, file)) {
sscanf(buf, "write_bytes: %" PRIu64, &pidData->wbytes);
}
fclose(file);
}
void ProcessMonitor::readCmdline(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
char filename[64];
sprintf(filename, "/proc/%d/cmdline", pid);
FILE *file;
file = fopen(filename, "r");
if (!file) {
ALOGD("Failed to open file %s for reading", filename);
return;
}
char buf[MAXPATHLEN];
fgets(buf, MAXPATHLEN, file);
fclose(file);
if (strlen(buf) > 0) {
pidData->name.assign(buf);
}
}
void ProcessMonitor::readStatus(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
char filename[64];
sprintf(filename, "/proc/%d/status", pid);
FILE *file;
file = fopen(filename, "r");
if (!file) {
ALOGD("Failed to open file %s for reading", filename);
return;
}
char line[MAX_LINE];
unsigned int uid;
while (fgets(line, MAX_LINE, file)) {
sscanf(line, "Uid: %u", &uid);
}
fclose(file);
pidData->uid = uid;
}
void ProcessMonitor::updateDiffs(std::shared_ptr<ProcInfo> pidData,
std::shared_ptr<ProcInfo> oldPidData) {
pidData->delta_utime = pidData->utime - oldPidData->utime;
pidData->delta_stime = pidData->stime - oldPidData->stime;
pidData->delta_time = pidData->delta_utime + pidData->delta_stime;
pidData->delta_rss = pidData->rss - oldPidData->rss;
pidData->delta_wbytes = pidData->wbytes - oldPidData->wbytes;
}
}; // namespace android