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