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
3 // found in the LICENSE file.
4
5 #include "atrace_process_dump.h"
6
7 #include <inttypes.h>
8 #include <stdint.h>
9
10 #include <limits>
11
12 #include "file_utils.h"
13 #include "logging.h"
14 #include "procfs_utils.h"
15
16 namespace {
17
18 const int kMemInfoIntervalMs = 100; // 100ms-ish.
19
20 } // namespace
21
AtraceProcessDump()22 AtraceProcessDump::AtraceProcessDump() {
23 self_pid_ = static_cast<int>(getpid());
24 }
25
~AtraceProcessDump()26 AtraceProcessDump::~AtraceProcessDump() {
27 }
28
SetDumpInterval(int interval_ms)29 void AtraceProcessDump::SetDumpInterval(int interval_ms) {
30 CHECK(interval_ms >= kMemInfoIntervalMs);
31 dump_interval_in_timer_ticks_ = interval_ms / kMemInfoIntervalMs;
32 // Approximately equals to kMemInfoIntervalMs.
33 int tick_interval_ms = interval_ms / dump_interval_in_timer_ticks_;
34 snapshot_timer_ = std::unique_ptr<time_utils::PeriodicTimer>(
35 new time_utils::PeriodicTimer(tick_interval_ms));
36 }
37
RunAndPrintJson(FILE * stream)38 void AtraceProcessDump::RunAndPrintJson(FILE* stream) {
39 out_ = stream;
40
41 fprintf(out_, "{\"start_ts\": \"%" PRIu64 "\", \"snapshots\":[\n",
42 time_utils::GetTimestamp());
43
44 CHECK(snapshot_timer_);
45 snapshot_timer_->Start();
46
47 int tick_count = std::numeric_limits<int>::max();
48 if (dump_count_ > 0)
49 tick_count = dump_count_ * dump_interval_in_timer_ticks_;
50
51 for (int tick = 0; tick < tick_count; tick++) {
52 if (tick > 0) {
53 if (!snapshot_timer_->Wait())
54 break; // Interrupted by signal.
55 fprintf(out_, ",\n");
56 }
57 TakeAndSerializeMemInfo();
58 if (!(tick % dump_interval_in_timer_ticks_)) {
59 fprintf(out_, ",\n");
60 TakeGlobalSnapshot();
61 SerializeSnapshot();
62 }
63 fflush(out_);
64 }
65
66 fprintf(out_, "],\n");
67 SerializePersistentProcessInfo();
68 fprintf(out_, "}\n");
69 fflush(out_);
70 Cleanup();
71 }
72
Stop()73 void AtraceProcessDump::Stop() {
74 CHECK(snapshot_timer_);
75 snapshot_timer_->Stop();
76 }
77
TakeGlobalSnapshot()78 void AtraceProcessDump::TakeGlobalSnapshot() {
79 snapshot_.clear();
80 snapshot_timestamp_ = time_utils::GetTimestamp();
81
82 file_utils::ForEachPidInProcPath("/proc", [this](int pid) {
83 // Skip if not regognized as a process.
84 if (!UpdatePersistentProcessInfo(pid))
85 return;
86 const ProcessInfo* process = processes_[pid].get();
87 // Snapshot can't be obtained for kernel workers.
88 if (process->in_kernel)
89 return;
90
91 ProcessSnapshot* process_snapshot = new ProcessSnapshot();
92 snapshot_[pid] = std::unique_ptr<ProcessSnapshot>(process_snapshot);
93
94 process_snapshot->pid = pid;
95 procfs_utils::ReadOomStats(process_snapshot);
96 procfs_utils::ReadPageFaultsAndCpuTimeStats(process_snapshot);
97
98 if (ShouldTakeFullDump(process)) {
99 process_snapshot->memory.ReadFullStats(pid);
100 } else {
101 process_snapshot->memory.ReadLightStats(pid);
102 }
103 if (graphics_stats_ && process->is_app) {
104 process_snapshot->memory.ReadGpuStats(pid);
105 }
106 });
107 }
108
UpdatePersistentProcessInfo(int pid)109 bool AtraceProcessDump::UpdatePersistentProcessInfo(int pid) {
110 if (!processes_.count(pid)) {
111 if (procfs_utils::ReadTgid(pid) != pid)
112 return false;
113 processes_[pid] = procfs_utils::ReadProcessInfo(pid);
114 }
115 ProcessInfo* process = processes_[pid].get();
116 procfs_utils::ReadProcessThreads(process);
117
118 if (full_dump_mode_ == FullDumpMode::kOnlyWhitelisted &&
119 full_dump_whitelist_.count(process->name)) {
120 full_dump_whitelisted_pids_.insert(pid);
121 }
122 return true;
123 }
124
ShouldTakeFullDump(const ProcessInfo * process)125 bool AtraceProcessDump::ShouldTakeFullDump(const ProcessInfo* process) {
126 if (full_dump_mode_ == FullDumpMode::kAllProcesses)
127 return !process->in_kernel && (process->pid != self_pid_);
128 if (full_dump_mode_ == FullDumpMode::kAllJavaApps)
129 return process->is_app;
130 if (full_dump_mode_ == FullDumpMode::kDisabled)
131 return false;
132 return full_dump_whitelisted_pids_.count(process->pid) > 0;
133 }
134
SerializeSnapshot()135 void AtraceProcessDump::SerializeSnapshot() {
136 fprintf(out_, "{\"ts\":\"%" PRIu64 "\",\"memdump\":{\n",
137 snapshot_timestamp_);
138 for (auto it = snapshot_.begin(); it != snapshot_.end();) {
139 const ProcessSnapshot* process = it->second.get();
140 const ProcessMemoryStats* mem = &process->memory;
141 fprintf(out_, "\"%d\":{", process->pid);
142
143 fprintf(out_, "\"vm\":%" PRIu64 ",\"rss\":%" PRIu64,
144 mem->virt_kb(), mem->rss_kb());
145
146 fprintf(out_, ",\"oom_sc\":%d,\"oom_sc_adj\":%d"
147 ",\"min_flt\":%lu,\"maj_flt\":%lu"
148 ",\"utime\":%lu,\"stime\":%lu",
149 process->oom_score, process->oom_score_adj,
150 process->minor_faults, process->major_faults,
151 process->utime, process->stime);
152
153 if (mem->full_stats_available()) {
154 fprintf(out_, ",\"pss\":%" PRIu64 ",\"swp\":%" PRIu64
155 ",\"pc\":%" PRIu64 ",\"pd\":%" PRIu64
156 ",\"sc\":%" PRIu64 ",\"sd\":%" PRIu64,
157 mem->pss_kb(), mem->swapped_kb(),
158 mem->private_clean_kb(), mem->private_dirty_kb(),
159 mem->shared_clean_kb(), mem->shared_dirty_kb());
160 }
161
162 if (mem->gpu_stats_available()) {
163 fprintf(out_, ",\"gpu_egl\":%" PRIu64 ",\"gpu_egl_pss\":%" PRIu64
164 ",\"gpu_gl\":%" PRIu64 ",\"gpu_gl_pss\":%" PRIu64
165 ",\"gpu_etc\":%" PRIu64 ",\"gpu_etc_pss\":%" PRIu64,
166 mem->gpu_graphics_kb(), mem->gpu_graphics_pss_kb(),
167 mem->gpu_gl_kb(), mem->gpu_gl_pss_kb(),
168 mem->gpu_other_kb(), mem->gpu_other_pss_kb());
169 }
170
171 // Memory maps are too heavy to serialize. Enable only in whitelisting mode.
172 if (print_smaps_ &&
173 full_dump_mode_ == FullDumpMode::kOnlyWhitelisted &&
174 mem->full_stats_available() &&
175 full_dump_whitelisted_pids_.count(process->pid)) {
176
177 fprintf(out_, ", \"mmaps\":[");
178 size_t n_mmaps = mem->mmaps_count();
179 for (size_t k = 0; k < n_mmaps; ++k) {
180 const ProcessMemoryStats::MmapInfo* mm = mem->mmap(k);
181 fprintf(out_,
182 "{\"vm\":\"%" PRIx64 "-%" PRIx64 "\","
183 "\"file\":\"%s\",\"flags\":\"%s\","
184 "\"pss\":%" PRIu64 ",\"rss\":%" PRIu64 ",\"swp\":%" PRIu64 ","
185 "\"pc\":%" PRIu64 ",\"pd\":%" PRIu64 ","
186 "\"sc\":%" PRIu64 ",\"sd\":%" PRIu64 "}",
187 mm->start_addr, mm->end_addr,
188 mm->mapped_file, mm->prot_flags,
189 mm->pss_kb, mm->rss_kb, mm->swapped_kb,
190 mm->private_clean_kb, mm->private_dirty_kb,
191 mm->shared_clean_kb, mm->shared_dirty_kb);
192 if (k < n_mmaps - 1)
193 fprintf(out_, ", ");
194 }
195 fprintf(out_, "]");
196 }
197
198 if (++it != snapshot_.end())
199 fprintf(out_, "},\n");
200 else
201 fprintf(out_, "}}\n");
202 }
203 fprintf(out_, "}");
204 }
205
SerializePersistentProcessInfo()206 void AtraceProcessDump::SerializePersistentProcessInfo() {
207 fprintf(out_, "\"processes\":{");
208 for (auto it = processes_.begin(); it != processes_.end();) {
209 const ProcessInfo* process = it->second.get();
210 fprintf(out_, "\"%d\":{", process->pid);
211 fprintf(out_, "\"name\":\"%s\"", process->name);
212
213 if (!process->in_kernel) {
214 fprintf(out_, ",\"exe\":\"%s\",", process->exe);
215 fprintf(out_, "\"threads\":{\n");
216 const auto threads = &process->threads;
217 for (auto thread_it = threads->begin(); thread_it != threads->end();) {
218 const ThreadInfo* thread = &(thread_it->second);
219 fprintf(out_, "\"%d\":{", thread->tid);
220 fprintf(out_, "\"name\":\"%s\"", thread->name);
221
222 if (++thread_it != threads->end())
223 fprintf(out_, "},\n");
224 else
225 fprintf(out_, "}\n");
226 }
227 fprintf(out_, "}");
228 }
229
230 if (++it != processes_.end())
231 fprintf(out_, "},\n");
232 else
233 fprintf(out_, "}\n");
234 }
235 fprintf(out_, "}");
236 }
237
TakeAndSerializeMemInfo()238 void AtraceProcessDump::TakeAndSerializeMemInfo() {
239 std::map<std::string, uint64_t> mem_info;
240 CHECK(procfs_utils::ReadMemInfoStats(&mem_info));
241 fprintf(out_, "{\"ts\":\"%" PRIu64 "\",\"meminfo\":{\n",
242 time_utils::GetTimestamp());
243 for (auto it = mem_info.begin(); it != mem_info.end(); ++it) {
244 if (it != mem_info.begin())
245 fprintf(out_, ",");
246 fprintf(out_, "\"%s\":%" PRIu64, it->first.c_str(), it->second);
247 }
248 fprintf(out_, "}}");
249 }
250
Cleanup()251 void AtraceProcessDump::Cleanup() {
252 processes_.clear();
253 snapshot_.clear();
254 full_dump_whitelisted_pids_.clear();
255 snapshot_timer_ = nullptr;
256 }
257