Chromium Code Reviews| Index: systrace/atrace_helper/jni/atrace_process_dump.cc |
| diff --git a/systrace/atrace_helper/jni/atrace_process_dump.cc b/systrace/atrace_helper/jni/atrace_process_dump.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8a02adc0e180da2d71f899a686a891ee860d4491 |
| --- /dev/null |
| +++ b/systrace/atrace_helper/jni/atrace_process_dump.cc |
| @@ -0,0 +1,218 @@ |
| +// 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 <sys/time.h> |
| +#include <sys/timerfd.h> |
| +#include <stdint.h> |
| + |
| +#include <memory> |
| +#include <sstream> |
| + |
| +#include "logging.h" |
| + |
| +void AtraceProcessDump::SetFullDumpConfig(const std::string& config) { |
| + self_pid_ = (int) getpid(); |
| + if (config == "all") { |
| + full_dump_all_ = true; |
| + } else if (config == "apps") { |
| + full_dump_apps_ = true; |
| + } else { |
| + full_dump_whitelist_.clear(); |
| + std::istringstream ss(config); |
| + std::string entry; |
| + while (std::getline(ss, entry, ',')) { |
| + full_dump_whitelist_.insert(entry); |
| + } |
| + } |
| +} |
| + |
| +void AtraceProcessDump::SerializeSnapshot( |
| + const InstantProcessInfo* snapshot, const std::set<int>& smaps_pids) { |
| + |
| + fprintf(out_, "{\"ts\":\"%llu\",\"memdump\":{\n", snapshot->timestamp()); |
| + const InstantProcessInfo::ProcessSnapshotMap* processes = |
| + snapshot->processes(); |
| + for (auto pit = processes->begin(); pit != processes->end();) { |
| + const InstantProcessInfo::ProcessSnapshot* process = pit->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()); |
| + } |
| + |
| + if (mem->full_stats_available() && smaps_pids.count(process->pid)) { |
| + fprintf(out_, ", \"mmaps\":["); |
| + int n_mmaps = mem->mmaps_count(); |
|
Primiano Tucci (use gerrit)
2017/06/22 07:55:51
size_t, here and below
kraynov
2017/06/22 09:19:44
Done.
|
| + for (int 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_, "]"); |
| + } |
| + |
| + // TODO(kraynov): Return OOM numbers. |
| + |
| + ++pit; |
| + if (pit != processes->end()) { |
| + fprintf(out_, "},\n"); |
| + } else { |
| + fprintf(out_, "}}\n"); |
| + } |
| + } |
| + fprintf(out_, "}"); |
| +} |
| + |
| +void AtraceProcessDump::SerializeProcessInfo( |
| + const PersistentProcessInfo::ProcessMap* processes) { |
| + |
| + fprintf(out_, "\"processes\":{"); |
| + for (auto pit = processes->begin(); pit != processes->end();) { |
| + const PersistentProcessInfo::ProcessInfo* process = pit->second.get(); |
| + fprintf(out_, "\"%d\":{", process->pid); |
| + fprintf(out_, "\"name\":\"%s\"", process->name.c_str()); |
| + |
| + if (!process->in_kernel) { |
| + fprintf(out_, ",\"exe\":\"%s\",", process->exe.c_str()); |
| + fprintf(out_, "\"threads\":{\n"); |
| + const PersistentProcessInfo::ThreadMap* threads = &process->threads; |
| + for (auto tit = threads->begin(); tit != threads->end();) { |
| + const PersistentProcessInfo::ThreadInfo* thread = tit->second.get(); |
| + fprintf(out_, "\"%d\":{", thread->tid); |
| + fprintf(out_, "\"name\":\"%s\"", thread->name.c_str()); |
| + |
| + ++tit; |
| + if (tit != threads->end()) { |
| + fprintf(out_, "},\n"); |
| + } else { |
| + fprintf(out_, "}\n"); |
| + } |
| + } |
| + fprintf(out_, "}"); |
| + } |
| + |
| + ++pit; |
| + if (pit != processes->end()) { |
| + fprintf(out_, "},\n"); |
| + } else { |
| + fprintf(out_, "}\n"); |
| + } |
| + } |
| + fprintf(out_, "}"); |
| +} |
| + |
| +void AtraceProcessDump::RunAndPrintJson(FILE* stream) { |
| + using ProcessInfo = PersistentProcessInfo::ProcessInfo; |
| + out_ = stream; |
| + |
| + std::set<int> smaps_pids; |
| + std::unique_ptr<ProcessDumpManager> mgr(new ProcessDumpManager()); |
| + mgr->SetFullDumpPredicate([this](const ProcessInfo* process) -> bool { |
| + if (full_dump_all_) |
| + return !process->in_kernel && (process->pid != self_pid_); |
| + if (full_dump_apps_) |
| + return process->is_app; |
| + if (full_dump_whitelist_.empty()) |
| + return false; |
| + return full_dump_whitelist_.count(process->name) > 0; |
| + }); |
| + mgr->SetGraphicsDumpPredicate([this](const ProcessInfo* process) -> bool { |
| + if (!graphics_stats_) |
| + return false; |
| + return process->is_app; |
| + }); |
| + |
| + SetupTimer(); |
| + fprintf(out_, "{\"start_ts\": \"%llu\", \"snapshots\":[\n", GetTimestamp()); |
| + for (int dump_number = 0; dump_number < dump_count_; dump_number++) { |
| + if (dump_number > 0) { |
| + fprintf(out_, ",\n"); |
| + if (!WaitForTimer()) |
| + break; // Interrupted by signal. |
| + } |
| + mgr->TakeSnapshot(); |
| + |
| + // Smaps are too heavy to serialize. Using only in whitelist mode. |
| + if (print_smaps_ && !full_dump_whitelist_.empty()) { |
| + smaps_pids.clear(); |
| + auto processes = mgr->all_processes(); |
| + for (auto pit = processes->begin(); pit != processes->end(); ++pit) { |
| + if (full_dump_whitelist_.count(pit->second->name)) |
| + smaps_pids.insert(pit->second->pid); |
| + } |
| + } |
| + SerializeSnapshot(mgr->last_snapshot(), smaps_pids); |
| + fflush(out_); |
| + } |
| + fprintf(out_, "],\n"); |
| + SerializeProcessInfo(mgr->all_processes()); |
| + CloseTimer(); |
| + |
| + fprintf(out_, "}\n"); |
| + fclose(out_); |
| +} |
| + |
| +void AtraceProcessDump::SetupTimer() { |
| + timer_fd_ = timerfd_create(CLOCK_MONOTONIC, 0); |
| + CHECK(timer_fd_ >= 0); |
| + int sec = dump_interval_ms_ / 1000; |
| + int nsec = (dump_interval_ms_ % 1000) * 1000000; |
| + struct itimerspec ts = {}; |
| + ts.it_value.tv_nsec = nsec; |
| + ts.it_value.tv_sec = sec; |
| + ts.it_interval.tv_nsec = nsec; |
| + ts.it_interval.tv_sec = sec; |
| + CHECK(timerfd_settime(timer_fd_, 0, &ts, nullptr) == 0); |
| +} |
| + |
| +bool AtraceProcessDump::WaitForTimer() { |
| + uint64_t stub = 0; |
| + int res = read(timer_fd_, &stub, sizeof(stub)); |
| + if (res < 0 && errno == EBADF) |
| + return false; // Received SIGINT/SIGTERM signal. |
| + CHECK(res > 0); |
| + return true; |
| +} |
| + |
| +void AtraceProcessDump::CloseTimer() { |
| + if (timer_fd_ < 0) |
| + return; |
| + close(timer_fd_); |
| + timer_fd_ = 0; |
| +} |