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