Index: systrace/atrace_helper/jni/process_info.cc |
diff --git a/systrace/atrace_helper/jni/process_info.cc b/systrace/atrace_helper/jni/process_info.cc |
index 172217bf6536509045593eb3a9ae9c0c2114d7dc..b5c66a8417923e9da4ebfc2684348d7e205d7af0 100644 |
--- a/systrace/atrace_helper/jni/process_info.cc |
+++ b/systrace/atrace_helper/jni/process_info.cc |
@@ -13,90 +13,179 @@ |
#include "file_utils.h" |
#include "logging.h" |
-ProcessInfo::ProcessInfo(int pid) : memory_(pid), pid_(pid) {} |
+using file_utils::ForEachPidInProcPath; |
+using file_utils::ReadProcFile; |
+using file_utils::ReadProcFileTrimmed; |
-bool ProcessInfo::IsProcess(int pid) { |
- char buf[256]; |
- ssize_t rsize = file_utils::ReadProcFile(pid, "status", buf, sizeof(buf)); |
+ |
+namespace { |
+ |
+const char kAppExe[] = "/system/bin/app_process"; |
+const char kZygote[] = "zygote"; |
+ |
+std::string ReadProcString(int pid, const char* path) { |
+ char buf[512]; |
+ if (!file_utils::ReadProcFileTrimmed(pid, path, buf, sizeof(buf))) |
+ return ""; |
+ return buf; |
+} |
+ |
+int ReadTgid(int pid) { |
+ char buf[512]; |
+ ssize_t rsize = ReadProcFile(pid, "status", buf, sizeof(buf)); |
if (rsize <= 0) |
- return false; |
+ return -1; |
const char kTgid[] = "\nTgid:"; |
const char* tgid_line = strstr(buf, kTgid); |
- CHECK(tgid_line); |
- int tgid = 0; |
+ if (!tgid_line) |
+ return -1; |
+ int tgid = -1; |
if (sscanf(tgid_line + strlen(kTgid), "%d", &tgid) != 1) |
- CHECK(false); |
- return tgid == pid; |
+ return -1; |
+ return tgid; |
} |
-bool ProcessInfo::ReadProcessName() { |
- if (!file_utils::ReadProcFileTrimmed(pid_, "cmdline", name_, sizeof(name_))) |
- return false; |
- |
- // Fallback on "comm" for kernel threads. |
- if (strlen(name_) == 0) { |
- if (!file_utils::ReadProcFileTrimmed(pid_, "comm", name_, sizeof(name_))) |
- return false; |
- } |
- |
- // Get also the exe path, to distinguish system vs java apps and bitness. |
+std::string ReadExe(int pid) { |
+ char exe[PATH_MAX]; |
char exe_path[64]; |
- sprintf(exe_path, "/proc/%d/exe", pid_); |
- exe_[0] = '\0'; |
- ssize_t res = readlink(exe_path, exe_, sizeof(exe_) - 1); |
+ sprintf(exe_path, "/proc/%d/exe", pid); |
+ exe[0] = '\0'; |
+ ssize_t res = readlink(exe_path, exe, sizeof(exe) - 1); |
if (res >= 0) |
- exe_[res] = '\0'; |
- |
- return true; |
+ exe[res] = '\0'; |
+ return exe; |
} |
-bool ProcessInfo::ReadThreadNames() { |
- char tasks_path[64]; |
- sprintf(tasks_path, "/proc/%d/task", pid_); |
- CHECK(threads_.empty()); |
- ThreadInfoMap* threads = &threads_; |
- const int pid = pid_; |
- file_utils::ForEachPidInProcPath(tasks_path, [pid, threads](int tid) { |
- char comm[64]; |
- std::unique_ptr<ThreadInfo> thread_info(new ThreadInfo()); |
- sprintf(comm, "task/%d/comm", tid); |
- if (!file_utils::ReadProcFileTrimmed(pid, comm, thread_info->name, |
- sizeof(thread_info->name))) { |
- return; |
- } |
- (*threads)[tid] = std::move(thread_info); |
- }); |
+bool ReadOomStats(int pid, InstantProcessInfo::ProcessSnapshot* snapshot) { |
+ char buf[64]; |
+ if (ReadProcFileTrimmed(pid, "oom_score", buf, sizeof(buf))) |
+ snapshot->oom_score = atoi(buf); |
+ else |
+ return false; |
+ if (ReadProcFileTrimmed(pid, "oom_score_adj", buf, sizeof(buf))) |
+ snapshot->oom_score_adj = atoi(buf); |
+ else |
+ return false; |
return true; |
} |
-bool ProcessInfo::ReadOOMStats() { |
+bool ReadPageFaultsAndCpuTimeStats( |
+ int pid, InstantProcessInfo::ProcessSnapshot* snapshot) { |
+ |
char buf[512]; |
- if (file_utils::ReadProcFileTrimmed(pid_, "oom_adj", buf, sizeof(buf))) |
- oom_adj_ = atoi(buf); |
- else |
+ if (!ReadProcFileTrimmed(pid, "stat", buf, sizeof(buf))) |
return false; |
+ int ret = sscanf(buf, |
+ "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*u %lu %*lu %lu %*lu %lu %lu", |
+ &snapshot->minor_faults, &snapshot->major_faults, |
+ &snapshot->utime, &snapshot->stime); |
+ CHECK(ret == 4); |
+ return true; |
+} |
- if (file_utils::ReadProcFileTrimmed(pid_, "oom_score", buf, sizeof(buf))) |
- oom_score_ = atoi(buf); |
- else |
- return false; |
+} // namespace |
- if (file_utils::ReadProcFileTrimmed(pid_, "oom_score_adj", buf, sizeof(buf))) |
- oom_score_adj_ = atoi(buf); |
- else |
- return false; |
+ |
+bool PersistentProcessInfo::UpdateProcessInfo(int pid) { |
+ ProcessInfo* process; |
+ bool first_update = false; |
+ |
+ if (!processes_.count(pid)) { |
+ if (ReadTgid(pid) != pid) |
+ return false; |
+ process = new ProcessInfo(); |
+ processes_[pid] = std::unique_ptr<ProcessInfo>(process); |
+ first_update = true; |
+ } else { |
+ process = processes_[pid].get(); |
+ } |
+ |
+ // Get process name. |
+ if (first_update) { |
+ process->pid = pid; |
+ process->name = ReadProcString(pid, "cmdline"); |
+ if (process->name.empty()) { |
+ process->in_kernel = true; |
+ process->name = ReadProcString(pid, "comm"); |
+ } else { |
+ process->in_kernel = false; |
+ process->exe = ReadExe(pid); |
+ } |
+ } |
+ |
+ // Detect apps. |
+ process->is_app = !process->in_kernel && |
+ !strncmp(process->exe.c_str(), kAppExe, sizeof(kAppExe) - 1) && |
+ strncmp(process->name.c_str(), kZygote, sizeof(kZygote) - 1); |
+ |
+ // Get thread names. |
+ if (!process->in_kernel) { |
+ char tasks_path[64]; |
+ sprintf(tasks_path, "/proc/%d/task", pid); |
+ ForEachPidInProcPath(tasks_path, [process, pid](int tid) { |
+ if (process->threads.count(tid)) |
+ return; |
+ ThreadInfo* thread = new ThreadInfo(); |
+ process->threads[tid] = std::unique_ptr<ThreadInfo>(thread); |
+ |
+ char task_comm[64]; |
+ sprintf(task_comm, "task/%d/comm", tid); |
+ thread->tid = tid; |
+ thread->name = ReadProcString(pid, task_comm); |
+ if (process->is_app && thread->name.empty()) |
+ thread->name = "UI Thread"; |
+ }); |
+ } |
return true; |
} |
-bool ProcessInfo::ReadPageFaultsAndCPUTimeStats() { |
- char buf[512]; |
- if (!file_utils::ReadProcFileTrimmed(pid_, "stat", buf, sizeof(buf))) |
- return false; |
- int ret = sscanf(buf, |
- "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*u %lu %*lu " |
- "%lu %*lu %lu %lu %*ld %*ld %*ld %*ld %*ld %*ld %llu", |
- &minflt_, &majflt_, &utime_, &stime_, &start_time_); |
- CHECK(ret == 5); |
+void ProcessDumpManager::SetFullDumpPredicate(const DumpPredicate& predicate) { |
+ full_dump_predicate_ = |
+ std::unique_ptr<DumpPredicate>(new DumpPredicate(predicate)); |
+} |
+ |
+void ProcessDumpManager::SetGraphicsDumpPredicate( |
+ const DumpPredicate& predicate) { |
+ graphics_dump_predicate_ = |
+ std::unique_ptr<DumpPredicate>(new DumpPredicate(predicate)); |
+} |
+ |
+bool ProcessDumpManager::TakeSnapshot() { |
+ global_snapshot_.processes_.clear(); |
+ global_snapshot_.timestamp_ = GetTimestamp(); |
+ |
+ ForEachPidInProcPath("/proc", [this](int pid) { |
+ if (!persistent_.UpdateProcessInfo(pid)) |
+ return; |
+ |
+ const PersistentProcessInfo::ProcessInfo* process = |
+ persistent_.processes_[pid].get(); |
+ |
+ // Snapshot can't be obtained for kernel workers. |
+ if (process->in_kernel) |
+ return; |
+ |
+ InstantProcessInfo::ProcessSnapshot* process_snapshot = |
+ new InstantProcessInfo::ProcessSnapshot(); |
+ global_snapshot_.processes_[pid] = |
+ std::unique_ptr<InstantProcessInfo::ProcessSnapshot>(process_snapshot); |
+ process_snapshot->pid = pid; |
+ |
+ ReadOomStats(pid, process_snapshot); |
+ ReadPageFaultsAndCpuTimeStats(pid, process_snapshot); |
+ |
+ if (full_dump_predicate_ && |
+ (*full_dump_predicate_.get())(process)) { |
+ process_snapshot->memory.ReadFullStats(pid); |
+ } else { |
+ process_snapshot->memory.ReadLightStats(pid); |
+ } |
+ |
+ if (graphics_dump_predicate_ && |
+ (*graphics_dump_predicate_.get())(process)) { |
+ process_snapshot->memory.ReadGpuStats(pid); |
+ } |
+ }); |
return true; |
} |