OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "process_info.h" | 5 #include "process_info.h" |
6 | 6 |
7 #include <ctype.h> | 7 #include <ctype.h> |
8 #include <dirent.h> | 8 #include <dirent.h> |
9 #include <stdio.h> | 9 #include <stdio.h> |
10 #include <stdlib.h> | 10 #include <stdlib.h> |
11 #include <sys/types.h> | 11 #include <sys/types.h> |
12 | 12 |
13 #include "file_utils.h" | 13 #include "file_utils.h" |
14 #include "logging.h" | 14 #include "logging.h" |
15 | 15 |
16 ProcessInfo::ProcessInfo(int pid) : memory_(pid), pid_(pid) {} | 16 using file_utils::ForEachPidInProcPath; |
| 17 using file_utils::ReadProcFile; |
| 18 using file_utils::ReadProcFileTrimmed; |
17 | 19 |
18 bool ProcessInfo::IsProcess(int pid) { | 20 |
19 char buf[256]; | 21 namespace { |
20 ssize_t rsize = file_utils::ReadProcFile(pid, "status", buf, sizeof(buf)); | 22 |
| 23 const char kAppExe[] = "/system/bin/app_process"; |
| 24 const char kZygote[] = "zygote"; |
| 25 |
| 26 std::string ReadProcString(int pid, const char* path) { |
| 27 char buf[512]; |
| 28 if (!file_utils::ReadProcFileTrimmed(pid, path, buf, sizeof(buf))) |
| 29 return ""; |
| 30 return buf; |
| 31 } |
| 32 |
| 33 int ReadTgid(int pid) { |
| 34 char buf[512]; |
| 35 ssize_t rsize = ReadProcFile(pid, "status", buf, sizeof(buf)); |
21 if (rsize <= 0) | 36 if (rsize <= 0) |
22 return false; | 37 return -1; |
23 const char kTgid[] = "\nTgid:"; | 38 const char kTgid[] = "\nTgid:"; |
24 const char* tgid_line = strstr(buf, kTgid); | 39 const char* tgid_line = strstr(buf, kTgid); |
25 CHECK(tgid_line); | 40 if (!tgid_line) |
26 int tgid = 0; | 41 return -1; |
| 42 int tgid = -1; |
27 if (sscanf(tgid_line + strlen(kTgid), "%d", &tgid) != 1) | 43 if (sscanf(tgid_line + strlen(kTgid), "%d", &tgid) != 1) |
28 CHECK(false); | 44 return -1; |
29 return tgid == pid; | 45 return tgid; |
30 } | 46 } |
31 | 47 |
32 bool ProcessInfo::ReadProcessName() { | 48 std::string ReadExe(int pid) { |
33 if (!file_utils::ReadProcFileTrimmed(pid_, "cmdline", name_, sizeof(name_))) | 49 char exe[PATH_MAX]; |
| 50 char exe_path[64]; |
| 51 sprintf(exe_path, "/proc/%d/exe", pid); |
| 52 exe[0] = '\0'; |
| 53 ssize_t res = readlink(exe_path, exe, sizeof(exe) - 1); |
| 54 if (res >= 0) |
| 55 exe[res] = '\0'; |
| 56 return exe; |
| 57 } |
| 58 |
| 59 bool ReadOomStats(int pid, InstantProcessInfo::ProcessSnapshot* snapshot) { |
| 60 char buf[64]; |
| 61 if (ReadProcFileTrimmed(pid, "oom_score", buf, sizeof(buf))) |
| 62 snapshot->oom_score = atoi(buf); |
| 63 else |
34 return false; | 64 return false; |
| 65 if (ReadProcFileTrimmed(pid, "oom_score_adj", buf, sizeof(buf))) |
| 66 snapshot->oom_score_adj = atoi(buf); |
| 67 else |
| 68 return false; |
| 69 return true; |
| 70 } |
35 | 71 |
36 // Fallback on "comm" for kernel threads. | 72 bool ReadPageFaultsAndCpuTimeStats( |
37 if (strlen(name_) == 0) { | 73 int pid, InstantProcessInfo::ProcessSnapshot* snapshot) { |
38 if (!file_utils::ReadProcFileTrimmed(pid_, "comm", name_, sizeof(name_))) | 74 |
| 75 char buf[512]; |
| 76 if (!ReadProcFileTrimmed(pid, "stat", buf, sizeof(buf))) |
| 77 return false; |
| 78 int ret = sscanf(buf, |
| 79 "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*u %lu %*lu %lu %*lu %lu %lu", |
| 80 &snapshot->minor_faults, &snapshot->major_faults, |
| 81 &snapshot->utime, &snapshot->stime); |
| 82 CHECK(ret == 4); |
| 83 return true; |
| 84 } |
| 85 |
| 86 } // namespace |
| 87 |
| 88 |
| 89 bool PersistentProcessInfo::UpdateProcessInfo(int pid) { |
| 90 ProcessInfo* process; |
| 91 bool first_update = false; |
| 92 |
| 93 if (!processes_.count(pid)) { |
| 94 if (ReadTgid(pid) != pid) |
39 return false; | 95 return false; |
| 96 process = new ProcessInfo(); |
| 97 processes_[pid] = std::unique_ptr<ProcessInfo>(process); |
| 98 first_update = true; |
| 99 } else { |
| 100 process = processes_[pid].get(); |
40 } | 101 } |
41 | 102 |
42 // Get also the exe path, to distinguish system vs java apps and bitness. | 103 // Get process name. |
43 char exe_path[64]; | 104 if (first_update) { |
44 sprintf(exe_path, "/proc/%d/exe", pid_); | 105 process->pid = pid; |
45 exe_[0] = '\0'; | 106 process->name = ReadProcString(pid, "cmdline"); |
46 ssize_t res = readlink(exe_path, exe_, sizeof(exe_) - 1); | 107 if (process->name.empty()) { |
47 if (res >= 0) | 108 process->in_kernel = true; |
48 exe_[res] = '\0'; | 109 process->name = ReadProcString(pid, "comm"); |
| 110 } else { |
| 111 process->in_kernel = false; |
| 112 process->exe = ReadExe(pid); |
| 113 } |
| 114 } |
| 115 |
| 116 // Detect apps. |
| 117 process->is_app = !process->in_kernel && |
| 118 !strncmp(process->exe.c_str(), kAppExe, sizeof(kAppExe) - 1) && |
| 119 strncmp(process->name.c_str(), kZygote, sizeof(kZygote) - 1); |
| 120 |
| 121 // Get thread names. |
| 122 if (!process->in_kernel) { |
| 123 char tasks_path[64]; |
| 124 sprintf(tasks_path, "/proc/%d/task", pid); |
| 125 ForEachPidInProcPath(tasks_path, [process, pid](int tid) { |
| 126 if (process->threads.count(tid)) |
| 127 return; |
| 128 ThreadInfo* thread = new ThreadInfo(); |
| 129 process->threads[tid] = std::unique_ptr<ThreadInfo>(thread); |
| 130 |
| 131 char task_comm[64]; |
| 132 sprintf(task_comm, "task/%d/comm", tid); |
| 133 thread->tid = tid; |
| 134 thread->name = ReadProcString(pid, task_comm); |
| 135 if (process->is_app && thread->name.empty()) |
| 136 thread->name = "UI Thread"; |
| 137 }); |
| 138 } |
49 | 139 |
50 return true; | 140 return true; |
51 } | 141 } |
52 | 142 |
53 bool ProcessInfo::ReadThreadNames() { | 143 void ProcessDumpManager::SetFullDumpPredicate(const DumpPredicate& predicate) { |
54 char tasks_path[64]; | 144 full_dump_predicate_ = |
55 sprintf(tasks_path, "/proc/%d/task", pid_); | 145 std::unique_ptr<DumpPredicate>(new DumpPredicate(predicate)); |
56 CHECK(threads_.empty()); | 146 } |
57 ThreadInfoMap* threads = &threads_; | 147 |
58 const int pid = pid_; | 148 void ProcessDumpManager::SetGraphicsDumpPredicate( |
59 file_utils::ForEachPidInProcPath(tasks_path, [pid, threads](int tid) { | 149 const DumpPredicate& predicate) { |
60 char comm[64]; | 150 graphics_dump_predicate_ = |
61 std::unique_ptr<ThreadInfo> thread_info(new ThreadInfo()); | 151 std::unique_ptr<DumpPredicate>(new DumpPredicate(predicate)); |
62 sprintf(comm, "task/%d/comm", tid); | 152 } |
63 if (!file_utils::ReadProcFileTrimmed(pid, comm, thread_info->name, | 153 |
64 sizeof(thread_info->name))) { | 154 bool ProcessDumpManager::TakeSnapshot() { |
| 155 global_snapshot_.processes_.clear(); |
| 156 global_snapshot_.timestamp_ = GetTimestamp(); |
| 157 |
| 158 ForEachPidInProcPath("/proc", [this](int pid) { |
| 159 if (!persistent_.UpdateProcessInfo(pid)) |
65 return; | 160 return; |
| 161 |
| 162 const PersistentProcessInfo::ProcessInfo* process = |
| 163 persistent_.processes_[pid].get(); |
| 164 |
| 165 // Snapshot can't be obtained for kernel workers. |
| 166 if (process->in_kernel) |
| 167 return; |
| 168 |
| 169 InstantProcessInfo::ProcessSnapshot* process_snapshot = |
| 170 new InstantProcessInfo::ProcessSnapshot(); |
| 171 global_snapshot_.processes_[pid] = |
| 172 std::unique_ptr<InstantProcessInfo::ProcessSnapshot>(process_snapshot); |
| 173 process_snapshot->pid = pid; |
| 174 |
| 175 ReadOomStats(pid, process_snapshot); |
| 176 ReadPageFaultsAndCpuTimeStats(pid, process_snapshot); |
| 177 |
| 178 if (full_dump_predicate_ && |
| 179 (*full_dump_predicate_.get())(process)) { |
| 180 process_snapshot->memory.ReadFullStats(pid); |
| 181 } else { |
| 182 process_snapshot->memory.ReadLightStats(pid); |
66 } | 183 } |
67 (*threads)[tid] = std::move(thread_info); | 184 |
| 185 if (graphics_dump_predicate_ && |
| 186 (*graphics_dump_predicate_.get())(process)) { |
| 187 process_snapshot->memory.ReadGpuStats(pid); |
| 188 } |
68 }); | 189 }); |
69 return true; | 190 return true; |
70 } | 191 } |
71 | |
72 bool ProcessInfo::ReadOOMStats() { | |
73 char buf[512]; | |
74 if (file_utils::ReadProcFileTrimmed(pid_, "oom_adj", buf, sizeof(buf))) | |
75 oom_adj_ = atoi(buf); | |
76 else | |
77 return false; | |
78 | |
79 if (file_utils::ReadProcFileTrimmed(pid_, "oom_score", buf, sizeof(buf))) | |
80 oom_score_ = atoi(buf); | |
81 else | |
82 return false; | |
83 | |
84 if (file_utils::ReadProcFileTrimmed(pid_, "oom_score_adj", buf, sizeof(buf))) | |
85 oom_score_adj_ = atoi(buf); | |
86 else | |
87 return false; | |
88 | |
89 return true; | |
90 } | |
91 | |
92 bool ProcessInfo::ReadPageFaultsAndCPUTimeStats() { | |
93 char buf[512]; | |
94 if (!file_utils::ReadProcFileTrimmed(pid_, "stat", buf, sizeof(buf))) | |
95 return false; | |
96 int ret = sscanf(buf, | |
97 "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*u %lu %*lu " | |
98 "%lu %*lu %lu %lu %*ld %*ld %*ld %*ld %*ld %*ld %llu", | |
99 &minflt_, &majflt_, &utime_, &stime_, &start_time_); | |
100 CHECK(ret == 5); | |
101 return true; | |
102 } | |
OLD | NEW |