Home | History | Annotate | Download | only in jni
      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