aboutsummaryrefslogtreecommitdiff
path: root/catapult/systrace/atrace_helper/jni/atrace_process_dump.cc
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/systrace/atrace_helper/jni/atrace_process_dump.cc')
-rw-r--r--catapult/systrace/atrace_helper/jni/atrace_process_dump.cc250
1 files changed, 250 insertions, 0 deletions
diff --git a/catapult/systrace/atrace_helper/jni/atrace_process_dump.cc b/catapult/systrace/atrace_helper/jni/atrace_process_dump.cc
new file mode 100644
index 00000000..ef72f789
--- /dev/null
+++ b/catapult/systrace/atrace_helper/jni/atrace_process_dump.cc
@@ -0,0 +1,250 @@
+// 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 <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\": \"%llu\", \"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\":\"%llu\",\"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\":%llu,\"rss\":%llu",
+ 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\":%llu,\"swp\":%llu"
+ ",\"pc\":%llu,\"pd\":%llu,\"sc\":%llu,\"sd\":%llu",
+ 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\":%llu,\"gpu_egl_pss\":%llu"
+ ",\"gpu_gl\":%llu,\"gpu_gl_pss\":%llu"
+ ",\"gpu_etc\":%llu,\"gpu_etc_pss\":%llu",
+ 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\":\"%llx-%llx\",\"file\":\"%s\",\"flags\":\"%s\","
+ "\"pss\":%llu,\"rss\":%llu,\"swp\":%llu,"
+ "\"pc\":%llu,\"pd\":%llu,"
+ "\"sc\":%llu,\"sd\":%llu}",
+ 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\":\"%llu\",\"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\":%llu", it->first.c_str(), it->second);
+ }
+ fprintf(out_, "}}");
+}
+
+void AtraceProcessDump::Cleanup() {
+ processes_.clear();
+ snapshot_.clear();
+ full_dump_whitelisted_pids_.clear();
+ snapshot_timer_ = nullptr;
+}