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 22 AtraceProcessDump::AtraceProcessDump() { 23 self_pid_ = static_cast<int>(getpid()); 24 } 25 26 AtraceProcessDump::~AtraceProcessDump() { 27 } 28 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 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 73 void AtraceProcessDump::Stop() { 74 CHECK(snapshot_timer_); 75 snapshot_timer_->Stop(); 76 } 77 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 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 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 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 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 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 251 void AtraceProcessDump::Cleanup() { 252 processes_.clear(); 253 snapshot_.clear(); 254 full_dump_whitelisted_pids_.clear(); 255 snapshot_timer_ = nullptr; 256 } 257