text: // Copyright 2017 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "atrace_process_dump.h"
  
  #include <inttypes.h>
  #include <stdint.h>
  
  #include <limits>
  
  #include "file_utils.h"
  #include "logging.h"
  #include "procfs_utils.h"
  
  namespace {
  
  const int kMemInfoIntervalMs = 100;  // 100ms-ish.
  
  }  // namespace
  
  AtraceProcessDump::AtraceProcessDump() {
    self_pid_ = static_cast<int>(getpid());
  }
  
  AtraceProcessDump::~AtraceProcessDump() {
  }
  
  void AtraceProcessDump::SetDumpInterval(int interval_ms) {
    CHECK(interval_ms >= kMemInfoIntervalMs);
    dump_interval_in_timer_ticks_ = interval_ms / kMemInfoIntervalMs;
    // Approximately equals to kMemInfoIntervalMs.
    int tick_interval_ms = interval_ms / dump_interval_in_timer_ticks_;
    snapshot_timer_ = std::unique_ptr<time_utils::PeriodicTimer>(
        new time_utils::PeriodicTimer(tick_interval_ms));
  }
  
  void AtraceProcessDump::RunAndPrintJson(FILE* stream) {
    out_ = stream;
  
    fprintf(out_, "{\"start_ts\": \"%" PRIu64 "\", \"snapshots\":[\n",
        time_utils::GetTimestamp());
  
    CHECK(snapshot_timer_);
    snapshot_timer_->Start();
  
    int tick_count = std::numeric_limits<int>::max();
    if (dump_count_ > 0)
      tick_count = dump_count_ * dump_interval_in_timer_ticks_;
  
    for (int tick = 0; tick < tick_count; tick++) {
      if (tick > 0) {
        if (!snapshot_timer_->Wait())
          break;  // Interrupted by signal.
        fprintf(out_, ",\n");
      }
      TakeAndSerializeMemInfo();
      if (!(tick % dump_interval_in_timer_ticks_)) {
        fprintf(out_, ",\n");
        TakeGlobalSnapshot();
        SerializeSnapshot();
      }
      fflush(out_);
    }
  
    fprintf(out_, "],\n");
    SerializePersistentProcessInfo();
    fprintf(out_, "}\n");
    fflush(out_);
    Cleanup();
  }
  
  void AtraceProcessDump::Stop() {
    CHECK(snapshot_timer_);
    snapshot_timer_->Stop();
  }
  
  void AtraceProcessDump::TakeGlobalSnapshot() {
    snapshot_.clear();
    snapshot_timestamp_ = time_utils::GetTimestamp();
  
    file_utils::ForEachPidInProcPath("/proc", [this](int pid) {
      // Skip if not regognized as a process.
      if (!UpdatePersistentProcessInfo(pid))
        return;
      const ProcessInfo* process = processes_[pid].get();
      // Snapshot can't be obtained for kernel workers.
      if (process->in_kernel)
        return;
  
      ProcessSnapshot* process_snapshot = new ProcessSnapshot();
      snapshot_[pid] = std::unique_ptr<ProcessSnapshot>(process_snapshot);
  
      process_snapshot->pid = pid;
      procfs_utils::ReadOomStats(process_snapshot);
      procfs_utils::ReadPageFaultsAndCpuTimeStats(process_snapshot);
  
      if (ShouldTakeFullDump(process)) {
        process_snapshot->memory.ReadFullStats(pid);
      } else {
        process_snapshot->memory.ReadLightStats(pid);
      }
      if (graphics_stats_ && process->is_app) {
        process_snapshot->memory.ReadGpuStats(pid);
      }
    });
  }
  
  bool AtraceProcessDump::UpdatePersistentProcessInfo(int pid) {
    if (!processes_.count(pid)) {
      if (procfs_utils::ReadTgid(pid) != pid)
        return false;
      processes_[pid] = procfs_utils::ReadProcessInfo(pid);
    }
    ProcessInfo* process = processes_[pid].get();
    procfs_utils::ReadProcessThreads(process);
  
    if (full_dump_mode_ == FullDumpMode::kOnlyWhitelisted &&
        full_dump_whitelist_.count(process->name)) {
      full_dump_whitelisted_pids_.insert(pid);
    }
    return true;
  }
  
  bool AtraceProcessDump::ShouldTakeFullDump(const ProcessInfo* process) {
    if (full_dump_mode_ == FullDumpMode::kAllProcesses)
      return !process->in_kernel && (process->pid != self_pid_);
    if (full_dump_mode_ == FullDumpMode::kAllJavaApps)
      return process->is_app;
    if (full_dump_mode_ == FullDumpMode::kDisabled)
      return false;
    return full_dump_whitelisted_pids_.count(process->pid) > 0;
  }
  
  void AtraceProcessDump::SerializeSnapshot() {
    fprintf(out_, "{\"ts\":\"%" PRIu64 "\",\"memdump\":{\n",
            snapshot_timestamp_);
    for (auto it = snapshot_.begin(); it != snapshot_.end();) {
      const ProcessSnapshot* process = it->second.get();
      const ProcessMemoryStats* mem = &process->memory;
      fprintf(out_, "\"%d\":{", process->pid);
  
      fprintf(out_, "\"vm\":%" PRIu64 ",\"rss\":%" PRIu64,
              mem->virt_kb(), mem->rss_kb());
  
      fprintf(out_, ",\"oom_sc\":%d,\"oom_sc_adj\":%d"
                    ",\"min_flt\":%lu,\"maj_flt\":%lu"
                    ",\"utime\":%lu,\"stime\":%lu",
              process->oom_score, process->oom_score_adj,
              process->minor_faults, process->major_faults,
              process->utime, process->stime);
  
      if (mem->full_stats_available()) {
        fprintf(out_, ",\"pss\":%" PRIu64 ",\"swp\":%" PRIu64
                      ",\"pc\":%" PRIu64 ",\"pd\":%" PRIu64
                      ",\"sc\":%" PRIu64 ",\"sd\":%" PRIu64,
                mem->pss_kb(), mem->swapped_kb(),
                mem->private_clean_kb(), mem->private_dirty_kb(),
                mem->shared_clean_kb(), mem->shared_dirty_kb());
      }
  
      if (mem->gpu_stats_available()) {
        fprintf(out_, ",\"gpu_egl\":%" PRIu64 ",\"gpu_egl_pss\":%" PRIu64
                      ",\"gpu_gl\":%" PRIu64 ",\"gpu_gl_pss\":%" PRIu64
                      ",\"gpu_etc\":%" PRIu64 ",\"gpu_etc_pss\":%" PRIu64,
                mem->gpu_graphics_kb(), mem->gpu_graphics_pss_kb(),
                mem->gpu_gl_kb(), mem->gpu_gl_pss_kb(),
                mem->gpu_other_kb(), mem->gpu_other_pss_kb());
      }
  
      // Memory maps are too heavy to serialize. Enable only in whitelisting mode.
      if (print_smaps_ &&
          full_dump_mode_ == FullDumpMode::kOnlyWhitelisted &&
          mem->full_stats_available() &&
          full_dump_whitelisted_pids_.count(process->pid)) {
  
        fprintf(out_, ", \"mmaps\":[");
        size_t n_mmaps = mem->mmaps_count();
        for (size_t k = 0; k < n_mmaps; ++k) {
          const ProcessMemoryStats::MmapInfo* mm = mem->mmap(k);
          fprintf(out_,
                  "{\"vm\":\"%" PRIx64 "-%" PRIx64 "\","
                  "\"file\":\"%s\",\"flags\":\"%s\","
                  "\"pss\":%" PRIu64 ",\"rss\":%" PRIu64 ",\"swp\":%" PRIu64 ","
                  "\"pc\":%" PRIu64 ",\"pd\":%" PRIu64 ","
                  "\"sc\":%" PRIu64 ",\"sd\":%" PRIu64 "}",
                  mm->start_addr, mm->end_addr,
                  mm->mapped_file, mm->prot_flags,
                  mm->pss_kb, mm->rss_kb, mm->swapped_kb,
                  mm->private_clean_kb, mm->private_dirty_kb,
                  mm->shared_clean_kb, mm->shared_dirty_kb);
          if (k < n_mmaps - 1)
            fprintf(out_, ", ");
        }
        fprintf(out_, "]");
      }
  
      if (++it != snapshot_.end())
        fprintf(out_, "},\n");
      else
        fprintf(out_, "}}\n");
    }
    fprintf(out_, "}");
  }
  
  void AtraceProcessDump::SerializePersistentProcessInfo() {
    fprintf(out_, "\"processes\":{");
    for (auto it = processes_.begin(); it != processes_.end();) {
      const ProcessInfo* process = it->second.get();
      fprintf(out_, "\"%d\":{", process->pid);
      fprintf(out_, "\"name\":\"%s\"", process->name);
  
      if (!process->in_kernel) {
        fprintf(out_, ",\"exe\":\"%s\",", process->exe);
        fprintf(out_, "\"threads\":{\n");
        const auto threads = &process->threads;
        for (auto thread_it = threads->begin(); thread_it != threads->end();) {
          const ThreadInfo* thread = &(thread_it->second);
          fprintf(out_, "\"%d\":{", thread->tid);
          fprintf(out_, "\"name\":\"%s\"", thread->name);
  
          if (++thread_it != threads->end())
            fprintf(out_, "},\n");
          else
            fprintf(out_, "}\n");
        }
        fprintf(out_, "}");
      }
  
      if (++it != processes_.end())
        fprintf(out_, "},\n");
      else
        fprintf(out_, "}\n");
    }
    fprintf(out_, "}");
  }
  
  void AtraceProcessDump::TakeAndSerializeMemInfo() {
    std::map<std::string, uint64_t> mem_info;
    CHECK(procfs_utils::ReadMemInfoStats(&mem_info));
    fprintf(out_, "{\"ts\":\"%" PRIu64 "\",\"meminfo\":{\n",
            time_utils::GetTimestamp());
    for (auto it = mem_info.begin(); it != mem_info.end(); ++it) {
      if (it != mem_info.begin())
        fprintf(out_, ",");
      fprintf(out_, "\"%s\":%" PRIu64, it->first.c_str(), it->second);
    }
    fprintf(out_, "}}");
  }
  
  void AtraceProcessDump::Cleanup() {
    processes_.clear();
    snapshot_.clear();
    full_dump_whitelisted_pids_.clear();
    snapshot_timer_ = nullptr;
  }
  