• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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