Reimport
This commit is contained in:
35
tools/adb/systrace/catapult/systrace/PRESUBMIT.py
Normal file
35
tools/adb/systrace/catapult/systrace/PRESUBMIT.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
|
||||
def CheckChangeOnUpload(input_api, output_api):
|
||||
return _CommonChecks(input_api, output_api)
|
||||
|
||||
|
||||
def CheckChangeOnCommit(input_api, output_api):
|
||||
return _CommonChecks(input_api, output_api)
|
||||
|
||||
|
||||
def _CommonChecks(input_api, output_api):
|
||||
results = []
|
||||
results += input_api.RunTests(input_api.canned_checks.GetPylint(
|
||||
input_api, output_api, extra_paths_list=_GetPathsToPrepend(input_api),
|
||||
pylintrc='pylintrc'))
|
||||
return results
|
||||
|
||||
|
||||
def _GetPathsToPrepend(input_api):
|
||||
project_dir = input_api.PresubmitLocalPath()
|
||||
catapult_dir = input_api.os_path.join(project_dir, '..')
|
||||
return [
|
||||
project_dir,
|
||||
|
||||
input_api.os_path.join(catapult_dir, 'common', 'py_trace_event'),
|
||||
input_api.os_path.join(catapult_dir, 'common', 'py_utils'),
|
||||
input_api.os_path.join(catapult_dir, 'devil'),
|
||||
input_api.os_path.join(catapult_dir, 'telemetry'),
|
||||
input_api.os_path.join(catapult_dir, 'third_party', 'mock'),
|
||||
input_api.os_path.join(catapult_dir, 'tracing'),
|
||||
|
||||
]
|
||||
10
tools/adb/systrace/catapult/systrace/README.md
Normal file
10
tools/adb/systrace/catapult/systrace/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
<!-- Copyright 2015 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.
|
||||
-->
|
||||
Systrace
|
||||
========
|
||||
|
||||
Systrace provides command-line tools to analyze the performance of your
|
||||
application. It currently includes
|
||||
[Android Systrace](http://developer.android.com/tools/help/systrace.html).
|
||||
29
tools/adb/systrace/catapult/systrace/atrace_helper/README.md
Normal file
29
tools/adb/systrace/catapult/systrace/atrace_helper/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
<!-- 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.
|
||||
-->
|
||||
atrace_helper is an optional binary which can be pushed onto the device running
|
||||
systrace in order to enrich the traces with further details (memory, I/O, etc).
|
||||
|
||||
Which problem is it solving?
|
||||
---------------------------
|
||||
Some nice-to-have details are not present in the systrace, specifically:
|
||||
- Memory snapshots of running processes (PSS/RSS).
|
||||
- Periodic snapshotting of processes and thread names.
|
||||
- File paths for filesystem events (today they report only inode numbers).
|
||||
|
||||
How is it solving it?
|
||||
---------------------
|
||||
atrace_helper is a small userspace binary which is meant to be pushed on the
|
||||
device and run together with atrace by a dedicated tracing agent. When stopped,
|
||||
the helper produces a JSON file which contains all the relevant details
|
||||
(see --help). The JSON file is consumed by the TraceViewer importers and the
|
||||
extra details are merged into the final model.
|
||||
|
||||
Build instructions
|
||||
------------------
|
||||
Building the binary requires the Android NDK to be installed. See
|
||||
[Android NDK page](https://developer.android.com/ndk).
|
||||
Once installed the binary can be just built as follows:
|
||||
`$(NDK_HOME)/ndk-build`
|
||||
The binary will be built in `libs/armeabi-v7a/`
|
||||
@@ -0,0 +1,7 @@
|
||||
# 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.
|
||||
|
||||
APP_ABI := armeabi-v7a
|
||||
APP_PLATFORM := android-21
|
||||
APP_STL := c++_static
|
||||
@@ -0,0 +1,256 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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.
|
||||
|
||||
#ifndef ATRACE_PROCESS_DUMP_H_
|
||||
#define ATRACE_PROCESS_DUMP_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "logging.h"
|
||||
#include "process_info.h"
|
||||
#include "time_utils.h"
|
||||
|
||||
// Program that collects processes, thread names, per-process memory stats and
|
||||
// other minor metrics from /proc filesystem. It's aimed to extend systrace
|
||||
// with more actionable number to hit performance issues.
|
||||
class AtraceProcessDump {
|
||||
public:
|
||||
enum FullDumpMode {
|
||||
kDisabled,
|
||||
kAllProcesses,
|
||||
kAllJavaApps,
|
||||
kOnlyWhitelisted,
|
||||
};
|
||||
|
||||
AtraceProcessDump();
|
||||
~AtraceProcessDump();
|
||||
|
||||
void RunAndPrintJson(FILE* stream);
|
||||
void Stop();
|
||||
|
||||
void SetDumpInterval(int interval_ms);
|
||||
|
||||
// Negative number or zero means unlimited number of dumps.
|
||||
void set_dump_count(int count) { dump_count_ = count; }
|
||||
|
||||
void set_full_dump_mode(FullDumpMode mode) { full_dump_mode_ = mode; }
|
||||
void set_full_dump_whitelist(const std::set<std::string> &whitelist) {
|
||||
CHECK(full_dump_mode_ == FullDumpMode::kOnlyWhitelisted);
|
||||
full_dump_whitelist_ = whitelist;
|
||||
}
|
||||
void enable_graphics_stats() { graphics_stats_ = true; }
|
||||
void enable_print_smaps() { print_smaps_ = true; }
|
||||
|
||||
private:
|
||||
AtraceProcessDump(const AtraceProcessDump&) = delete;
|
||||
void operator=(const AtraceProcessDump&) = delete;
|
||||
|
||||
using ProcessMap = std::map<int, std::unique_ptr<ProcessInfo>>;
|
||||
using ProcessSnapshotMap = std::map<int, std::unique_ptr<ProcessSnapshot>>;
|
||||
|
||||
void TakeGlobalSnapshot();
|
||||
void TakeAndSerializeMemInfo();
|
||||
bool UpdatePersistentProcessInfo(int pid);
|
||||
bool ShouldTakeFullDump(const ProcessInfo* process);
|
||||
void SerializeSnapshot();
|
||||
void SerializePersistentProcessInfo();
|
||||
void Cleanup();
|
||||
|
||||
int self_pid_;
|
||||
int dump_count_;
|
||||
bool graphics_stats_ = false;
|
||||
bool print_smaps_ = false;
|
||||
FullDumpMode full_dump_mode_ = FullDumpMode::kDisabled;
|
||||
std::set<std::string> full_dump_whitelist_;
|
||||
|
||||
FILE* out_;
|
||||
ProcessMap processes_;
|
||||
ProcessSnapshotMap snapshot_;
|
||||
uint64_t snapshot_timestamp_;
|
||||
std::set<int> full_dump_whitelisted_pids_;
|
||||
std::unique_ptr<time_utils::PeriodicTimer> snapshot_timer_;
|
||||
int dump_interval_in_timer_ticks_;
|
||||
};
|
||||
|
||||
#endif // ATRACE_PROCESS_DUMP_H_
|
||||
@@ -0,0 +1,117 @@
|
||||
// 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 "file_utils.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsNumeric(const char* str) {
|
||||
if (!str[0])
|
||||
return false;
|
||||
for (const char* c = str; *c; c++) {
|
||||
if (!isdigit(*c))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace file_utils {
|
||||
|
||||
void ForEachPidInProcPath(const char* proc_path,
|
||||
std::function<void(int)> predicate) {
|
||||
DIR* root_dir = opendir(proc_path);
|
||||
ScopedDir autoclose(root_dir);
|
||||
struct dirent* child_dir;
|
||||
while ((child_dir = readdir(root_dir))) {
|
||||
if (child_dir->d_type != DT_DIR || !IsNumeric(child_dir->d_name))
|
||||
continue;
|
||||
predicate(atoi(child_dir->d_name));
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t ReadFile(const char* path, char* buf, size_t length) {
|
||||
buf[0] = '\0';
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0 && errno == ENOENT)
|
||||
return -1;
|
||||
ScopedFD autoclose(fd);
|
||||
size_t tot_read = 0;
|
||||
do {
|
||||
ssize_t rsize = read(fd, buf + tot_read, length - tot_read);
|
||||
if (rsize == 0)
|
||||
break;
|
||||
if (rsize == -1 && errno == EINTR)
|
||||
continue;
|
||||
else if (rsize < 0)
|
||||
return -1;
|
||||
tot_read += static_cast<size_t>(rsize);
|
||||
} while (tot_read < length);
|
||||
buf[tot_read < length ? tot_read : length - 1] = '\0';
|
||||
return tot_read;
|
||||
}
|
||||
|
||||
bool ReadFileTrimmed(const char* path, char* buf, size_t length) {
|
||||
ssize_t rsize = ReadFile(path, buf, length);
|
||||
if (rsize < 0)
|
||||
return false;
|
||||
for (ssize_t i = 0; i < rsize; i++) {
|
||||
const char c = buf[i];
|
||||
if (c == '\0' || c == '\r' || c == '\n') {
|
||||
buf[i] = '\0';
|
||||
break;
|
||||
}
|
||||
buf[i] = isprint(c) ? c : '?';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t ReadProcFile(int pid, const char* proc_file, char* buf, size_t length) {
|
||||
char proc_path[128];
|
||||
snprintf(proc_path, sizeof(proc_path), "/proc/%d/%s", pid, proc_file);
|
||||
return ReadFile(proc_path, buf, length);
|
||||
}
|
||||
|
||||
// Reads a single-line proc file, stripping out any \0, \r, \n and replacing
|
||||
// non-printable charcters with '?'.
|
||||
bool ReadProcFileTrimmed(int pid,
|
||||
const char* proc_file,
|
||||
char* buf,
|
||||
size_t length) {
|
||||
char proc_path[128];
|
||||
snprintf(proc_path, sizeof(proc_path), "/proc/%d/%s", pid, proc_file);
|
||||
return ReadFileTrimmed(proc_path, buf, length);
|
||||
}
|
||||
|
||||
LineReader::LineReader(char* buf, size_t size)
|
||||
: ptr_(buf), end_(buf + size) {
|
||||
}
|
||||
|
||||
LineReader::~LineReader() {
|
||||
}
|
||||
|
||||
const char* LineReader::NextLine() {
|
||||
if (ptr_ >= end_)
|
||||
return nullptr;
|
||||
const char* cur = ptr_;
|
||||
char* next = strchr(ptr_, '\n');
|
||||
if (next) {
|
||||
*next = '\0';
|
||||
ptr_ = next + 1;
|
||||
} else {
|
||||
ptr_ = end_;
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
} // namespace file_utils
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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.
|
||||
|
||||
#ifndef FILE_UTILS_H_
|
||||
#define FILE_UTILS_H_
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
namespace file_utils {
|
||||
|
||||
// RAII classes for auto-releasing fd/dirs.
|
||||
template <typename RESOURCE_TYPE, int (*CLOSE_FN)(RESOURCE_TYPE)>
|
||||
struct ScopedResource {
|
||||
explicit ScopedResource(RESOURCE_TYPE r) : r_(r) { CHECK(r); }
|
||||
~ScopedResource() { CLOSE_FN(r_); }
|
||||
RESOURCE_TYPE r_;
|
||||
};
|
||||
|
||||
using ScopedFD = ScopedResource<int, close>;
|
||||
using ScopedDir = ScopedResource<DIR*, closedir>;
|
||||
|
||||
// Invokes predicate(pid) for each folder in |proc_path|/[0-9]+ which has
|
||||
// a numeric name (typically pids and tids).
|
||||
void ForEachPidInProcPath(const char* proc_path,
|
||||
std::function<void(int)> predicate);
|
||||
|
||||
// Reads the contents of |path| fully into |buf| up to |length| chars.
|
||||
// |buf| is guaranteed to be null terminated.
|
||||
ssize_t ReadFile(const char* path, char* buf, size_t length);
|
||||
|
||||
// Reads a single-line file, stripping out any \0, \r, \n and replacing
|
||||
// non-printable charcters with '?'. |buf| is guaranteed to be null terminated.
|
||||
bool ReadFileTrimmed(const char* path, char* buf, size_t length);
|
||||
|
||||
// Convenience wrappers for /proc/|pid|/|proc_file| paths.
|
||||
ssize_t ReadProcFile(int pid, const char* proc_file, char* buf, size_t length);
|
||||
bool ReadProcFileTrimmed(int pid,
|
||||
const char* proc_file,
|
||||
char* buf,
|
||||
size_t length);
|
||||
|
||||
// Takes a C string buffer and chunks it into lines without creating any
|
||||
// copies. It modifies the original buffer, by replacing \n with \0.
|
||||
class LineReader {
|
||||
public:
|
||||
LineReader(char* buf, size_t size);
|
||||
~LineReader();
|
||||
|
||||
const char* NextLine();
|
||||
|
||||
private:
|
||||
char* ptr_;
|
||||
char* end_;
|
||||
};
|
||||
|
||||
} // namespace file_utils
|
||||
|
||||
#endif // FILE_UTILS_H_
|
||||
@@ -0,0 +1,142 @@
|
||||
// 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 "libmemtrack_wrapper.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Init memtrack service. Removed in the latest version.
|
||||
int (*memtrack_init)(void);
|
||||
|
||||
// Allocate and dispose memory stats.
|
||||
libmemtrack_proc* (*memtrack_proc_new)(void);
|
||||
void (*memtrack_proc_destroy)(libmemtrack_proc* p);
|
||||
|
||||
// Query memory stats for given process.
|
||||
int (*memtrack_proc_get)(libmemtrack_proc* p, pid_t pid);
|
||||
|
||||
// Since memory stats is opaque structure, there are helpers to parse it.
|
||||
ssize_t (*memtrack_proc_graphics_total)(libmemtrack_proc* p);
|
||||
ssize_t (*memtrack_proc_graphics_pss)(libmemtrack_proc* p);
|
||||
ssize_t (*memtrack_proc_gl_total)(libmemtrack_proc* p);
|
||||
ssize_t (*memtrack_proc_gl_pss)(libmemtrack_proc* p);
|
||||
ssize_t (*memtrack_proc_other_total)(libmemtrack_proc* p);
|
||||
ssize_t (*memtrack_proc_other_pss)(libmemtrack_proc* p);
|
||||
|
||||
typedef ssize_t (*libmemtrack_getter_t)(libmemtrack_proc*);
|
||||
|
||||
bool g_initialized = false;
|
||||
bool g_broken = false;
|
||||
|
||||
template <typename T>
|
||||
void Import(T** func, void* lib, const char* name) {
|
||||
*(reinterpret_cast<void**>(func)) = dlsym(lib, name);
|
||||
}
|
||||
|
||||
bool ImportLibmemtrackSymbols(void* handle) {
|
||||
Import(&memtrack_init, handle, "memtrack_init");
|
||||
Import(&memtrack_proc_new, handle, "memtrack_proc_new");
|
||||
Import(&memtrack_proc_destroy, handle, "memtrack_proc_destroy");
|
||||
Import(&memtrack_proc_get, handle, "memtrack_proc_get");
|
||||
Import(&memtrack_proc_graphics_total, handle, "memtrack_proc_graphics_total");
|
||||
Import(&memtrack_proc_graphics_pss, handle, "memtrack_proc_graphics_pss");
|
||||
Import(&memtrack_proc_gl_total, handle, "memtrack_proc_gl_total");
|
||||
Import(&memtrack_proc_gl_pss, handle, "memtrack_proc_gl_pss");
|
||||
Import(&memtrack_proc_other_total, handle, "memtrack_proc_other_total");
|
||||
Import(&memtrack_proc_other_pss, handle, "memtrack_proc_other_pss");
|
||||
|
||||
if (!memtrack_proc_new || !memtrack_proc_destroy || !memtrack_proc_get) {
|
||||
LogError("Couldn't use libmemtrack. Probably it's API has been changed.");
|
||||
return false;
|
||||
}
|
||||
// Initialization is required on pre-O Android.
|
||||
if (memtrack_init && memtrack_init() != 0) {
|
||||
LogError("Failed to initialize libmemtrack. "
|
||||
"Probably implementation is missing in the ROM.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LazyOpenLibmemtrack() {
|
||||
if (g_initialized)
|
||||
return true;
|
||||
if (g_broken)
|
||||
return false;
|
||||
|
||||
void *handle = dlopen("libmemtrack.so", RTLD_GLOBAL | RTLD_NOW);
|
||||
if (handle == nullptr) {
|
||||
LogError("Failed to open libmemtrack library.");
|
||||
g_broken = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ImportLibmemtrackSymbols(handle)) {
|
||||
dlclose(handle);
|
||||
g_broken = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
g_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t GetOrZero(libmemtrack_getter_t getter, libmemtrack_proc* proc) {
|
||||
if (!getter || !proc)
|
||||
return 0;
|
||||
return static_cast<uint64_t>(getter(proc));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MemtrackProc::MemtrackProc(int pid) {
|
||||
if (!LazyOpenLibmemtrack())
|
||||
return;
|
||||
|
||||
proc_ = memtrack_proc_new();
|
||||
if (!proc_) {
|
||||
LogError("Failed to create libmemtrack proc. "
|
||||
"Probably it's API has been changed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (memtrack_proc_get(proc_, pid) != 0) {
|
||||
// Don't log an error since not every process has memtrack stats.
|
||||
memtrack_proc_destroy(proc_);
|
||||
proc_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MemtrackProc::~MemtrackProc() {
|
||||
if (proc_)
|
||||
memtrack_proc_destroy(proc_);
|
||||
}
|
||||
|
||||
uint64_t MemtrackProc::graphics_total() const {
|
||||
return GetOrZero(memtrack_proc_graphics_total, proc_);
|
||||
}
|
||||
|
||||
uint64_t MemtrackProc::graphics_pss() const {
|
||||
return GetOrZero(memtrack_proc_graphics_pss, proc_);
|
||||
}
|
||||
|
||||
uint64_t MemtrackProc::gl_total() const {
|
||||
return GetOrZero(memtrack_proc_gl_total, proc_);
|
||||
}
|
||||
|
||||
uint64_t MemtrackProc::gl_pss() const {
|
||||
return GetOrZero(memtrack_proc_gl_pss, proc_);
|
||||
}
|
||||
|
||||
uint64_t MemtrackProc::other_total() const {
|
||||
return GetOrZero(memtrack_proc_other_total, proc_);
|
||||
}
|
||||
|
||||
uint64_t MemtrackProc::other_pss() const {
|
||||
return GetOrZero(memtrack_proc_other_pss, proc_);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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.
|
||||
|
||||
#ifndef LIBMEMTRACK_WRAPPER_H_
|
||||
#define LIBMEMTRACK_WRAPPER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Wrapper on top of libmemtrack API.
|
||||
|
||||
// Opaque structure with memory stats.
|
||||
// See $ANDROID/system/core/libmemtrack/include/memtrack/memtrack.h for details.
|
||||
struct libmemtrack_proc;
|
||||
|
||||
// These numbers are vendor-specific and can't be trusted as a stable metric
|
||||
// across different hardware or driver versions.
|
||||
class MemtrackProc {
|
||||
public:
|
||||
explicit MemtrackProc(int pid);
|
||||
~MemtrackProc();
|
||||
|
||||
uint64_t graphics_total() const;
|
||||
uint64_t graphics_pss() const;
|
||||
uint64_t gl_total() const;
|
||||
uint64_t gl_pss() const;
|
||||
uint64_t other_total() const;
|
||||
uint64_t other_pss() const;
|
||||
|
||||
bool has_errors() const { return proc_ == nullptr; };
|
||||
|
||||
private:
|
||||
MemtrackProc(const MemtrackProc&) = delete;
|
||||
void operator=(const MemtrackProc&) = delete;
|
||||
|
||||
libmemtrack_proc* proc_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // LIBMEMTRACK_WRAPPER_H_
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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.
|
||||
|
||||
#ifndef LOGGING_H_
|
||||
#define LOGGING_H_
|
||||
|
||||
#include <android/log.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define CHECK_ARGS(COND, ERR) \
|
||||
"FAILED CHECK(%s) @ %s:%d (errno: %s)\n", #COND, __FILE__, __LINE__, \
|
||||
strerror(ERR)
|
||||
|
||||
#define CHECK(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
const int e = errno; \
|
||||
__android_log_print(ANDROID_LOG_FATAL, "atrace_helper", \
|
||||
CHECK_ARGS(x, e)); \
|
||||
fprintf(stderr, "\n" CHECK_ARGS(x, e)); \
|
||||
fflush(stderr); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
inline void LogError(const char* message) {
|
||||
__android_log_write(ANDROID_LOG_ERROR, "atrace_helper", message);
|
||||
fprintf(stderr, "\n%s\n", message);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
#endif // LOGGING_H_
|
||||
134
tools/adb/systrace/catapult/systrace/atrace_helper/jni/main.cc
Normal file
134
tools/adb/systrace/catapult/systrace/atrace_helper/jni/main.cc
Normal file
@@ -0,0 +1,134 @@
|
||||
// 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 <inttypes.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "atrace_process_dump.h"
|
||||
#include "logging.h"
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<AtraceProcessDump> g_prog;
|
||||
|
||||
void ParseFullDumpConfig(const std::string& config, AtraceProcessDump* prog) {
|
||||
using FullDumpMode = AtraceProcessDump::FullDumpMode;
|
||||
if (config == "all") {
|
||||
prog->set_full_dump_mode(FullDumpMode::kAllProcesses);
|
||||
} else if (config == "apps") {
|
||||
prog->set_full_dump_mode(FullDumpMode::kAllJavaApps);
|
||||
} else {
|
||||
std::set<std::string> whitelist;
|
||||
std::istringstream ss(config);
|
||||
std::string entry;
|
||||
while (std::getline(ss, entry, ',')) {
|
||||
whitelist.insert(entry);
|
||||
}
|
||||
if (whitelist.empty())
|
||||
return;
|
||||
prog->set_full_dump_mode(FullDumpMode::kOnlyWhitelisted);
|
||||
prog->set_full_dump_whitelist(whitelist);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc == 2 && !strcmp(argv[1], "--echo-ts")) {
|
||||
// Used by clock sync marker to correct the difference between
|
||||
// Linux monotonic clocks on the device and host.
|
||||
printf("%" PRIu64 "\n", time_utils::GetTimestamp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool background = false;
|
||||
int dump_interval_ms = 5000;
|
||||
char out_file[PATH_MAX] = {};
|
||||
bool dump_to_file = false;
|
||||
int count = -1;
|
||||
|
||||
AtraceProcessDump* prog = new AtraceProcessDump();
|
||||
g_prog = std::unique_ptr<AtraceProcessDump>(prog);
|
||||
|
||||
if (geteuid()) {
|
||||
fprintf(stderr, "Must run as root\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "bm:gst:o:c:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'b':
|
||||
background = true;
|
||||
break;
|
||||
case 'm':
|
||||
ParseFullDumpConfig(optarg, prog);
|
||||
break;
|
||||
case 'g':
|
||||
prog->enable_graphics_stats();
|
||||
break;
|
||||
case 's':
|
||||
prog->enable_print_smaps();
|
||||
break;
|
||||
case 't':
|
||||
dump_interval_ms = atoi(optarg);
|
||||
CHECK(dump_interval_ms > 0);
|
||||
break;
|
||||
case 'c':
|
||||
count = atoi(optarg);
|
||||
CHECK(count > 0);
|
||||
break;
|
||||
case 'o':
|
||||
strncpy(out_file, optarg, sizeof(out_file));
|
||||
dump_to_file = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,
|
||||
"Usage: %s [-b] [-m full_dump_filter] [-g] [-s] "
|
||||
"[-t dump_interval_ms] "
|
||||
"[-c dumps_count] [-o out.json]\n",
|
||||
argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
prog->set_dump_count(count);
|
||||
prog->SetDumpInterval(dump_interval_ms);
|
||||
|
||||
FILE* out_stream = stdout;
|
||||
char tmp_file[PATH_MAX];
|
||||
if (dump_to_file) {
|
||||
unlink(out_file);
|
||||
sprintf(tmp_file, "%s.tmp", out_file);
|
||||
out_stream = fopen(tmp_file, "w");
|
||||
CHECK(out_stream);
|
||||
}
|
||||
|
||||
if (background) {
|
||||
if (!dump_to_file) {
|
||||
fprintf(stderr, "-b requires -o for output dump path.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
printf("Continuing in background. kill -TERM to terminate the daemon.\n");
|
||||
CHECK(daemon(0 /* nochdir */, 0 /* noclose */) == 0);
|
||||
}
|
||||
|
||||
auto on_exit = [](int) { g_prog->Stop(); };
|
||||
signal(SIGINT, on_exit);
|
||||
signal(SIGTERM, on_exit);
|
||||
|
||||
prog->RunAndPrintJson(out_stream);
|
||||
fclose(out_stream);
|
||||
|
||||
if (dump_to_file)
|
||||
rename(tmp_file, out_file);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PROCESS_INFO_H_
|
||||
#define PROCESS_INFO_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "process_memory_stats.h"
|
||||
|
||||
struct ThreadInfo {
|
||||
int tid;
|
||||
char name[16];
|
||||
};
|
||||
|
||||
struct ProcessInfo {
|
||||
int pid;
|
||||
bool in_kernel;
|
||||
bool is_app;
|
||||
char name[256];
|
||||
char exe[256];
|
||||
std::map<int, ThreadInfo> threads;
|
||||
};
|
||||
|
||||
struct ProcessSnapshot {
|
||||
int pid;
|
||||
ProcessMemoryStats memory;
|
||||
// OOM badness and tolerance (oom_adj is deprecated).
|
||||
int oom_score;
|
||||
int oom_score_adj;
|
||||
// Page faults.
|
||||
unsigned long minor_faults;
|
||||
unsigned long major_faults;
|
||||
// Time spent in userspace and in the kernel.
|
||||
unsigned long utime;
|
||||
unsigned long stime;
|
||||
};
|
||||
|
||||
#endif // PROCESS_INFO_H_
|
||||
@@ -0,0 +1,139 @@
|
||||
// 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 "process_memory_stats.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "file_utils.h"
|
||||
#include "libmemtrack_wrapper.h"
|
||||
#include "logging.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const int kKbPerPage = 4;
|
||||
|
||||
const char kRss[] = "Rss";
|
||||
const char kPss[] = "Pss";
|
||||
const char kSwap[] = "Swap";
|
||||
const char kSharedClean[] = "Shared_Clean";
|
||||
const char kSharedDirty[] = "Shared_Dirty";
|
||||
const char kPrivateClean[] = "Private_Clean";
|
||||
const char kPrivateDirty[] = "Private_Dirty";
|
||||
|
||||
bool ReadSmapsMetric(
|
||||
const char* line, const char* metric, int metric_size, uint64_t* res) {
|
||||
if (strncmp(line, metric, metric_size - 1))
|
||||
return false;
|
||||
if (line[metric_size - 1] != ':')
|
||||
return false;
|
||||
*res = strtoull(line + metric_size, nullptr, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ProcessMemoryStats::ReadLightStats(int pid) {
|
||||
char buf[64];
|
||||
if (file_utils::ReadProcFile(pid, "statm", buf, sizeof(buf)) <= 0)
|
||||
return false;
|
||||
uint32_t vm_size_pages;
|
||||
uint32_t rss_pages;
|
||||
int res = sscanf(buf, "%u %u", &vm_size_pages, &rss_pages);
|
||||
CHECK(res == 2);
|
||||
rss_kb_ = rss_pages * kKbPerPage;
|
||||
virt_kb_ = vm_size_pages * kKbPerPage;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProcessMemoryStats::ReadFullStats(int pid) {
|
||||
const size_t kBufSize = 8u * 1024 * 1024;
|
||||
std::unique_ptr<char[]> buf(new char[kBufSize]);
|
||||
ssize_t rsize = file_utils::ReadProcFile(pid, "smaps", &buf[0], kBufSize);
|
||||
if (rsize <= 0)
|
||||
return false;
|
||||
MmapInfo* last_mmap_entry = nullptr;
|
||||
std::unique_ptr<MmapInfo> new_mmap(new MmapInfo());
|
||||
CHECK(mmaps_.empty());
|
||||
CHECK(rss_kb_ == 0);
|
||||
|
||||
// Iterate over all lines in /proc/PID/smaps.
|
||||
file_utils::LineReader rd(&buf[0], rsize);
|
||||
for (const char* line = rd.NextLine(); line; line = rd.NextLine()) {
|
||||
if (!line[0])
|
||||
continue;
|
||||
// Performance optimization (hack).
|
||||
// Any header line starts with lowercase hex digit but subsequent lines
|
||||
// start with uppercase letter.
|
||||
if (line[0] < 'A' || line[0] > 'Z') {
|
||||
// Note that the mapped file name ([stack]) is optional and won't be
|
||||
// present on anonymous memory maps (hence res >= 3 below).
|
||||
int res = sscanf(line,
|
||||
"%" PRIx64 "-%" PRIx64 " %4s %*" PRIx64 " %*[:0-9a-f] "
|
||||
"%*[0-9a-f]%*[ \t]%127[^\n]",
|
||||
&new_mmap->start_addr, &new_mmap->end_addr, new_mmap->prot_flags,
|
||||
new_mmap->mapped_file);
|
||||
last_mmap_entry = new_mmap.get();
|
||||
CHECK(new_mmap->end_addr >= new_mmap->start_addr);
|
||||
new_mmap->virt_kb =
|
||||
(new_mmap->end_addr - new_mmap->start_addr) / 1024;
|
||||
if (res == 3)
|
||||
new_mmap->mapped_file[0] = '\0';
|
||||
virt_kb_ += new_mmap->virt_kb;
|
||||
mmaps_.push_back(std::move(new_mmap));
|
||||
new_mmap.reset(new MmapInfo());
|
||||
} else {
|
||||
// The current line is a metrics line within a mmap entry, e.g.:
|
||||
// Size: 4 kB
|
||||
uint64_t size = 0;
|
||||
CHECK(last_mmap_entry);
|
||||
if (ReadSmapsMetric(line, kRss, sizeof(kRss), &size)) {
|
||||
last_mmap_entry->rss_kb = size;
|
||||
rss_kb_ += size;
|
||||
} else if (ReadSmapsMetric(line, kPss, sizeof(kPss), &size)) {
|
||||
last_mmap_entry->pss_kb = size;
|
||||
pss_kb_ += size;
|
||||
} else if (ReadSmapsMetric(line, kSwap, sizeof(kSwap), &size)) {
|
||||
last_mmap_entry->swapped_kb = size;
|
||||
swapped_kb_ += size;
|
||||
} else if (ReadSmapsMetric(
|
||||
line, kSharedClean, sizeof(kSharedClean), &size)) {
|
||||
last_mmap_entry->shared_clean_kb = size;
|
||||
shared_clean_kb_ += size;
|
||||
} else if (ReadSmapsMetric(
|
||||
line, kSharedDirty, sizeof(kSharedDirty), &size)) {
|
||||
last_mmap_entry->shared_dirty_kb = size;
|
||||
shared_dirty_kb_ += size;
|
||||
} else if (ReadSmapsMetric(
|
||||
line, kPrivateClean, sizeof(kPrivateClean), &size)) {
|
||||
last_mmap_entry->private_clean_kb = size;
|
||||
private_clean_kb_ += size;
|
||||
} else if (ReadSmapsMetric(
|
||||
line, kPrivateDirty, sizeof(kPrivateDirty), &size)) {
|
||||
last_mmap_entry->private_dirty_kb = size;
|
||||
private_dirty_kb_ += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
full_stats_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProcessMemoryStats::ReadGpuStats(int pid) {
|
||||
MemtrackProc mt(pid);
|
||||
gpu_graphics_kb_ = mt.graphics_total() / 1024;
|
||||
gpu_graphics_pss_kb_ = mt.graphics_pss() / 1024;
|
||||
gpu_gl_kb_ = mt.gl_total() / 1024;
|
||||
gpu_gl_pss_kb_ = mt.gl_pss() / 1024;
|
||||
gpu_other_kb_ = mt.other_total() / 1024;
|
||||
gpu_other_pss_kb_ = mt.other_pss() / 1024;
|
||||
|
||||
gpu_stats_ = !mt.has_errors() &&
|
||||
(gpu_graphics_kb_ != 0 || gpu_gl_kb_ != 0 || gpu_other_kb_ != 0);
|
||||
return gpu_stats_;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PROCESS_MEMORY_STATS_H_
|
||||
#define PROCESS_MEMORY_STATS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// Reads process memory stats from /proc/pid/{statm,smaps}.
|
||||
class ProcessMemoryStats {
|
||||
public:
|
||||
struct MmapInfo {
|
||||
char mapped_file[128] = {};
|
||||
char prot_flags[5] = {};
|
||||
uint64_t start_addr = 0;
|
||||
uint64_t end_addr = 0;
|
||||
uint64_t virt_kb = 0;
|
||||
uint64_t pss_kb = 0; // Proportional Set Size.
|
||||
uint64_t rss_kb = 0; // Resident Set Size.
|
||||
uint64_t private_clean_kb = 0;
|
||||
uint64_t private_dirty_kb = 0;
|
||||
uint64_t shared_clean_kb = 0;
|
||||
uint64_t shared_dirty_kb = 0;
|
||||
uint64_t swapped_kb = 0;
|
||||
};
|
||||
|
||||
ProcessMemoryStats() {}
|
||||
|
||||
bool ReadLightStats(int pid);
|
||||
bool ReadFullStats(int pid);
|
||||
bool ReadGpuStats(int pid);
|
||||
|
||||
// Available after ReadLightStats().
|
||||
uint64_t virt_kb() const { return virt_kb_; }
|
||||
uint64_t rss_kb() const { return rss_kb_; }
|
||||
|
||||
// Available after ReadFullStats().
|
||||
bool full_stats_available() const { return full_stats_; }
|
||||
uint64_t pss_kb() const { return pss_kb_; }
|
||||
uint64_t private_clean_kb() const { return private_clean_kb_; }
|
||||
uint64_t private_dirty_kb() const { return private_dirty_kb_; }
|
||||
uint64_t shared_clean_kb() const { return shared_clean_kb_; }
|
||||
uint64_t shared_dirty_kb() const { return shared_dirty_kb_; }
|
||||
uint64_t swapped_kb() const { return swapped_kb_; }
|
||||
|
||||
// Available after ReadMemtrackStats().
|
||||
bool gpu_stats_available() const { return gpu_stats_; }
|
||||
uint64_t gpu_graphics_kb() const { return gpu_graphics_kb_; }
|
||||
uint64_t gpu_graphics_pss_kb() const { return gpu_graphics_pss_kb_; }
|
||||
uint64_t gpu_gl_kb() const { return gpu_gl_kb_; }
|
||||
uint64_t gpu_gl_pss_kb() const { return gpu_gl_pss_kb_; }
|
||||
uint64_t gpu_other_kb() const { return gpu_other_kb_; }
|
||||
uint64_t gpu_other_pss_kb() const { return gpu_other_pss_kb_; }
|
||||
|
||||
size_t mmaps_count() const { return mmaps_.size(); }
|
||||
const MmapInfo* mmap(size_t index) const { return mmaps_[index].get(); }
|
||||
|
||||
private:
|
||||
ProcessMemoryStats(const ProcessMemoryStats&) = delete;
|
||||
void operator=(const ProcessMemoryStats&) = delete;
|
||||
|
||||
// Light stats.
|
||||
uint64_t virt_kb_ = 0;
|
||||
uint64_t rss_kb_ = 0;
|
||||
|
||||
// Full stats.
|
||||
bool full_stats_ = false;
|
||||
uint64_t pss_kb_ = 0;
|
||||
uint64_t private_clean_kb_ = 0;
|
||||
uint64_t private_dirty_kb_ = 0;
|
||||
uint64_t shared_clean_kb_ = 0;
|
||||
uint64_t shared_dirty_kb_ = 0;
|
||||
uint64_t swapped_kb_ = 0;
|
||||
|
||||
// Graphics stats.
|
||||
bool gpu_stats_ = false;
|
||||
uint64_t gpu_graphics_kb_ = 0;
|
||||
uint64_t gpu_graphics_pss_kb_ = 0;
|
||||
uint64_t gpu_gl_kb_ = 0;
|
||||
uint64_t gpu_gl_pss_kb_ = 0;
|
||||
uint64_t gpu_other_kb_ = 0;
|
||||
uint64_t gpu_other_pss_kb_ = 0;
|
||||
|
||||
std::vector<std::unique_ptr<const MmapInfo>> mmaps_;
|
||||
};
|
||||
|
||||
#endif // PROCESS_MEMORY_STATS_H_
|
||||
@@ -0,0 +1,136 @@
|
||||
// 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 "procfs_utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "file_utils.h"
|
||||
#include "logging.h"
|
||||
|
||||
using file_utils::ForEachPidInProcPath;
|
||||
using file_utils::ReadProcFile;
|
||||
using file_utils::ReadProcFileTrimmed;
|
||||
|
||||
namespace procfs_utils {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kJavaAppPrefix[] = "/system/bin/app_process";
|
||||
const char kZygotePrefix[] = "zygote";
|
||||
|
||||
inline void ReadProcString(int pid, const char* path, char* buf, size_t size) {
|
||||
if (!file_utils::ReadProcFileTrimmed(pid, path, buf, size))
|
||||
buf[0] = '\0';
|
||||
}
|
||||
|
||||
inline void ReadExePath(int pid, char* buf, size_t size) {
|
||||
char exe_path[64];
|
||||
sprintf(exe_path, "/proc/%d/exe", pid);
|
||||
ssize_t res = readlink(exe_path, buf, size - 1);
|
||||
if (res >= 0)
|
||||
buf[res] = '\0';
|
||||
else
|
||||
buf[0] = '\0';
|
||||
}
|
||||
|
||||
inline bool IsApp(const char* name, const char* exe) {
|
||||
return strncmp(exe, kJavaAppPrefix, sizeof(kJavaAppPrefix) - 1) == 0 &&
|
||||
strncmp(name, kZygotePrefix, sizeof(kZygotePrefix) - 1) != 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int ReadTgid(int pid) {
|
||||
static const char kTgid[] = "\nTgid:";
|
||||
char buf[512];
|
||||
ssize_t rsize = ReadProcFile(pid, "status", buf, sizeof(buf));
|
||||
if (rsize <= 0)
|
||||
return -1;
|
||||
const char* tgid_line = strstr(buf, kTgid);
|
||||
CHECK(tgid_line);
|
||||
return atoi(tgid_line + sizeof(kTgid) - 1);
|
||||
}
|
||||
|
||||
std::unique_ptr<ProcessInfo> ReadProcessInfo(int pid) {
|
||||
ProcessInfo* process = new ProcessInfo();
|
||||
process->pid = pid;
|
||||
ReadProcString(pid, "cmdline", process->name, sizeof(process->name));
|
||||
if (process->name[0] != 0) {
|
||||
ReadExePath(pid, process->exe, sizeof(process->exe));
|
||||
process->is_app = IsApp(process->name, process->exe);
|
||||
} else {
|
||||
ReadProcString(pid, "comm", process->name, sizeof(process->name));
|
||||
CHECK(process->name[0]);
|
||||
process->in_kernel = true;
|
||||
}
|
||||
return std::unique_ptr<ProcessInfo>(process);
|
||||
}
|
||||
|
||||
void ReadProcessThreads(ProcessInfo* process) {
|
||||
if (process->in_kernel)
|
||||
return;
|
||||
|
||||
char tasks_path[64];
|
||||
sprintf(tasks_path, "/proc/%d/task", process->pid);
|
||||
ForEachPidInProcPath(tasks_path, [process](int tid) {
|
||||
if (process->threads.count(tid))
|
||||
return;
|
||||
ThreadInfo thread = { tid, "" };
|
||||
char task_comm[64];
|
||||
sprintf(task_comm, "task/%d/comm", tid);
|
||||
ReadProcString(process->pid, task_comm, thread.name, sizeof(thread.name));
|
||||
if (thread.name[0] == '\0' && process->is_app)
|
||||
strcpy(thread.name, "UI Thread");
|
||||
process->threads[tid] = thread;
|
||||
});
|
||||
}
|
||||
|
||||
bool ReadOomStats(ProcessSnapshot* snapshot) {
|
||||
char buf[64];
|
||||
if (ReadProcFileTrimmed(snapshot->pid, "oom_score", buf, sizeof(buf)))
|
||||
snapshot->oom_score = atoi(buf);
|
||||
else
|
||||
return false;
|
||||
if (ReadProcFileTrimmed(snapshot->pid, "oom_score_adj", buf, sizeof(buf)))
|
||||
snapshot->oom_score_adj = atoi(buf);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadPageFaultsAndCpuTimeStats(ProcessSnapshot* snapshot) {
|
||||
char buf[512];
|
||||
if (!ReadProcFileTrimmed(snapshot->pid, "stat", buf, sizeof(buf)))
|
||||
return false;
|
||||
int ret = sscanf(buf,
|
||||
"%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*u %lu %*lu %lu %*lu %lu %lu",
|
||||
&snapshot->minor_faults, &snapshot->major_faults,
|
||||
&snapshot->utime, &snapshot->stime);
|
||||
CHECK(ret == 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadMemInfoStats(std::map<std::string, uint64_t>* mem_info) {
|
||||
char buf[1024];
|
||||
ssize_t rsize = file_utils::ReadFile("/proc/meminfo", buf, sizeof(buf));
|
||||
if (rsize <= 0)
|
||||
return false;
|
||||
|
||||
file_utils::LineReader reader(buf, rsize);
|
||||
for (const char* line = reader.NextLine();
|
||||
line && line[0];
|
||||
line = reader.NextLine()) {
|
||||
|
||||
const char* pos_colon = strstr(line, ":");
|
||||
if (pos_colon == nullptr)
|
||||
continue; // Should not happen.
|
||||
std::string name(line, pos_colon - line);
|
||||
(*mem_info)[name] = strtoull(&pos_colon[1], nullptr, 10);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace procfs_utils
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
#ifndef PROCFS_UTILS_H_
|
||||
#define PROCFS_UTILS_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "process_info.h"
|
||||
|
||||
namespace procfs_utils {
|
||||
|
||||
// ProcFS doesn't necessarly distinguish PID vs. TID, but all threads of a
|
||||
// process have the same Thread Group ID which is equal to Process ID.
|
||||
int ReadTgid(int pid);
|
||||
|
||||
std::unique_ptr<ProcessInfo> ReadProcessInfo(int pid);
|
||||
void ReadProcessThreads(ProcessInfo* process);
|
||||
|
||||
bool ReadOomStats(ProcessSnapshot* snapshot);
|
||||
bool ReadPageFaultsAndCpuTimeStats(ProcessSnapshot* snapshot);
|
||||
|
||||
bool ReadMemInfoStats(std::map<std::string, uint64_t>* mem_info);
|
||||
|
||||
} // namespace procfs_utils
|
||||
|
||||
#endif // PROCFS_UTILS_H_
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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 "time_utils.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
namespace time_utils {
|
||||
|
||||
uint64_t GetTimestamp() {
|
||||
struct timespec ts = {};
|
||||
CHECK(clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == 0);
|
||||
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000ul;
|
||||
}
|
||||
|
||||
PeriodicTimer::PeriodicTimer(int interval_ms) : interval_ms_(interval_ms) {
|
||||
timer_fd_ = -1;
|
||||
}
|
||||
|
||||
PeriodicTimer::~PeriodicTimer() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void PeriodicTimer::Start() {
|
||||
Stop();
|
||||
timer_fd_ = timerfd_create(CLOCK_MONOTONIC, 0);
|
||||
CHECK(timer_fd_ >= 0);
|
||||
int sec = interval_ms_ / 1000;
|
||||
int nsec = (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);
|
||||
}
|
||||
|
||||
void PeriodicTimer::Stop() {
|
||||
if (timer_fd_ < 0)
|
||||
return;
|
||||
close(timer_fd_);
|
||||
timer_fd_ = -1;
|
||||
}
|
||||
|
||||
bool PeriodicTimer::Wait() {
|
||||
if (timer_fd_ < 0)
|
||||
return false; // Not started yet.
|
||||
uint64_t stub = 0;
|
||||
int res = read(timer_fd_, &stub, sizeof(stub));
|
||||
if (res < 0 && errno == EBADF)
|
||||
return false; // Interrupted by Stop().
|
||||
CHECK(res > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace time_utils
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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.
|
||||
|
||||
#ifndef TIME_UTILS_H_
|
||||
#define TIME_UTILS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace time_utils {
|
||||
|
||||
uint64_t GetTimestamp();
|
||||
|
||||
class PeriodicTimer {
|
||||
public:
|
||||
PeriodicTimer(int interval_ms);
|
||||
~PeriodicTimer();
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
// Wait for next tick. Returns false if interrupted by Stop() or not started.
|
||||
bool Wait();
|
||||
|
||||
private:
|
||||
PeriodicTimer(const PeriodicTimer&) = delete;
|
||||
void operator=(const PeriodicTimer&) = delete;
|
||||
|
||||
const int interval_ms_;
|
||||
int timer_fd_;
|
||||
};
|
||||
|
||||
} // namespace time_utils
|
||||
|
||||
#endif // TIME_UTILS_
|
||||
14
tools/adb/systrace/catapult/systrace/bin/adb_profile_chrome
Normal file
14
tools/adb/systrace/catapult/systrace/bin/adb_profile_chrome
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2016 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.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
systrace_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
sys.path.append(systrace_path)
|
||||
from profile_chrome import main
|
||||
sys.exit(main.main())
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2015 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.
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import webbrowser
|
||||
|
||||
_SYSTRACE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
sys.path.append(_SYSTRACE_DIR)
|
||||
|
||||
from profile_chrome import chrome_startup_tracing_agent
|
||||
from profile_chrome import flags
|
||||
from profile_chrome import profiler
|
||||
from profile_chrome import ui
|
||||
from systrace import util
|
||||
from systrace.tracing_agents import atrace_agent
|
||||
|
||||
_CATAPULT_DIR = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), '..', '..')
|
||||
sys.path.append(os.path.join(_CATAPULT_DIR, 'devil'))
|
||||
|
||||
from devil.android import device_utils
|
||||
from devil.android.sdk import adb_wrapper
|
||||
|
||||
|
||||
_CHROME_STARTUP_MODULES = [atrace_agent, chrome_startup_tracing_agent]
|
||||
_DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES'
|
||||
|
||||
|
||||
def _CreateOptionParser():
|
||||
parser = optparse.OptionParser(description='Record about://tracing profiles '
|
||||
'from Android browsers startup, combined with '
|
||||
'Android systrace. See http://dev.chromium.org'
|
||||
'/developers/how-tos/trace-event-profiling-'
|
||||
'tool for detailed instructions for '
|
||||
'profiling.', conflict_handler='resolve')
|
||||
parser = util.get_main_options(parser)
|
||||
|
||||
browsers = sorted(util.get_supported_browsers().keys())
|
||||
parser.add_option('-b', '--browser', help='Select among installed browsers. '
|
||||
'One of ' + ', '.join(browsers) + ', "stable" is used by '
|
||||
'default.', type='choice', choices=browsers,
|
||||
default='stable')
|
||||
parser.add_option('-v', '--verbose', help='Verbose logging.',
|
||||
action='store_true')
|
||||
parser.add_option('-z', '--compress', help='Compress the resulting trace '
|
||||
'with gzip. ', action='store_true')
|
||||
parser.add_option('-t', '--time', help='Stops tracing after N seconds, 0 to '
|
||||
'manually stop (startup trace ends after at most 5s).',
|
||||
default=5, metavar='N', type='int', dest='trace_time')
|
||||
parser.add_option('-c', '--chrome_categories', help='Chrome tracing '
|
||||
'categories to record.', default=_DEFAULT_CHROME_CATEGORIES,
|
||||
type='string')
|
||||
parser.add_option('-u', '--atrace-buffer-size', help='Number of bytes to'
|
||||
' be used for capturing atrace data', type='int',
|
||||
default=None, dest='trace_buf_size')
|
||||
|
||||
parser.add_option_group(chrome_startup_tracing_agent.add_options(parser))
|
||||
parser.add_option_group(atrace_agent.add_options(parser))
|
||||
parser.add_option_group(flags.OutputOptions(parser))
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
parser = _CreateOptionParser()
|
||||
options, _ = parser.parse_args()
|
||||
|
||||
if not options.device_serial_number:
|
||||
devices = [a.GetDeviceSerial() for a in adb_wrapper.AdbWrapper.Devices()]
|
||||
if len(devices) == 0:
|
||||
raise RuntimeError('No ADB devices connected.')
|
||||
elif len(devices) >= 2:
|
||||
raise RuntimeError('Multiple devices connected, serial number required')
|
||||
options.device_serial_number = devices[0]
|
||||
|
||||
if options.verbose:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
devices = device_utils.DeviceUtils.HealthyDevices()
|
||||
if len(devices) != 1:
|
||||
logging.error('Exactly 1 device must be attached.')
|
||||
return 1
|
||||
device = devices[0]
|
||||
package_info = util.get_supported_browsers()[options.browser]
|
||||
|
||||
options.device = device
|
||||
options.package_info = package_info
|
||||
|
||||
# TODO(washingtonp): Once Systrace uses all of the profile_chrome agents,
|
||||
# manually setting these options will no longer be necessary and should be
|
||||
# removed.
|
||||
options.ring_buffer = False
|
||||
options.trace_memory = False
|
||||
|
||||
if options.atrace_categories in ['list', 'help']:
|
||||
atrace_agent.list_categories(atrace_agent.get_config(options))
|
||||
print '\n'
|
||||
return 0
|
||||
result = profiler.CaptureProfile(options,
|
||||
options.trace_time,
|
||||
_CHROME_STARTUP_MODULES,
|
||||
output=options.output_file,
|
||||
compress=options.compress,
|
||||
write_json=options.write_json)
|
||||
if options.view:
|
||||
if sys.platform == 'darwin':
|
||||
os.system('/usr/bin/open %s' % os.path.abspath(result))
|
||||
else:
|
||||
webbrowser.open(result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
46
tools/adb/systrace/catapult/systrace/bin/run_tests
Normal file
46
tools/adb/systrace/catapult/systrace/bin/run_tests
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python2.7
|
||||
|
||||
# Copyright 2015 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.
|
||||
|
||||
"""Runs the unit test suite for systrace."""
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
_SYSTRACE_DIR = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), os.path.pardir))
|
||||
sys.path.insert(0, _SYSTRACE_DIR)
|
||||
from systrace import decorators
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option("-d", "--device", dest="device",
|
||||
help="device the test runs on", metavar="DEVICE")
|
||||
options, _args = parser.parse_args() # pylint: disable=unused-variable
|
||||
unfiltered_suite = unittest.TestLoader().discover(
|
||||
_SYSTRACE_DIR,
|
||||
pattern = '*_unittest.py',
|
||||
top_level_dir=_SYSTRACE_DIR)
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
for test_group in unfiltered_suite._tests:
|
||||
for inner_group in test_group:
|
||||
for test in inner_group:
|
||||
method = getattr(
|
||||
test, test._testMethodName) # pylint: disable=protected-access
|
||||
if not decorators.ShouldSkip(method, options.device):
|
||||
suite.addTest(test)
|
||||
|
||||
result = unittest.TextTestRunner(verbosity=2).run(suite)
|
||||
if result.wasSuccessful():
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
16
tools/adb/systrace/catapult/systrace/bin/systrace
Normal file
16
tools/adb/systrace/catapult/systrace/bin/systrace
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2016 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.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
_SYSTRACE_DIR = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), os.path.pardir))
|
||||
sys.path.insert(0, _SYSTRACE_DIR)
|
||||
from systrace import run_systrace
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(run_systrace.main())
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright 2014 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.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
_CATAPULT_DIR = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), '..', '..')
|
||||
sys.path.append(os.path.join(_CATAPULT_DIR, 'devil'))
|
||||
sys.path.append(os.path.join(_CATAPULT_DIR, 'systrace'))
|
||||
sys.path.append(os.path.join(_CATAPULT_DIR, 'common', 'py_utils'))
|
||||
@@ -0,0 +1,154 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
|
||||
import py_utils
|
||||
|
||||
from devil.android import flag_changer
|
||||
from devil.android.constants import webapk
|
||||
from devil.android.perf import cache_control
|
||||
from devil.android.sdk import intent
|
||||
|
||||
from systrace import trace_result
|
||||
from systrace import tracing_agents
|
||||
|
||||
|
||||
class ChromeStartupTracingAgent(tracing_agents.TracingAgent):
|
||||
def __init__(self, device, package_info, webapk_package, cold, url,
|
||||
trace_time=None):
|
||||
tracing_agents.TracingAgent.__init__(self)
|
||||
self._device = device
|
||||
self._package_info = package_info
|
||||
self._webapk_package = webapk_package
|
||||
self._cold = cold
|
||||
self._logcat_monitor = self._device.GetLogcatMonitor()
|
||||
self._url = url
|
||||
self._trace_time = trace_time
|
||||
self._trace_file = None
|
||||
self._trace_finish_re = re.compile(r' Completed startup tracing to (.*)')
|
||||
self._flag_changer = flag_changer.FlagChanger(
|
||||
self._device, self._package_info.cmdline_file)
|
||||
|
||||
def __repr__(self):
|
||||
return 'Browser Startup Trace'
|
||||
|
||||
def _SetupTracing(self):
|
||||
# TODO(lizeb): Figure out how to clean up the command-line file when
|
||||
# _TearDownTracing() is not executed in StopTracing().
|
||||
flags = ['--trace-startup', '--enable-perfetto']
|
||||
if self._trace_time is not None:
|
||||
flags.append('--trace-startup-duration={}'.format(self._trace_time))
|
||||
self._flag_changer.AddFlags(flags)
|
||||
self._device.ForceStop(self._package_info.package)
|
||||
if self._webapk_package:
|
||||
self._device.ForceStop(self._webapk_package)
|
||||
logging.warning('Forces to stop the WebAPK and the browser provided by '
|
||||
'--browser: %s. Please make sure that this browser '
|
||||
'matches the host browser of the WebAPK %s. ',
|
||||
self._package_info.package,
|
||||
self._webapk_package)
|
||||
if self._cold:
|
||||
self._device.EnableRoot()
|
||||
cache_control.CacheControl(self._device).DropRamCaches()
|
||||
launch_intent = None
|
||||
if self._webapk_package:
|
||||
launch_intent = intent.Intent(
|
||||
package=self._webapk_package,
|
||||
activity=webapk.WEBAPK_MAIN_ACTIVITY,
|
||||
data=self._url)
|
||||
elif self._url == '':
|
||||
launch_intent = intent.Intent(
|
||||
action='android.intent.action.MAIN',
|
||||
package=self._package_info.package,
|
||||
activity=self._package_info.activity)
|
||||
else:
|
||||
launch_intent = intent.Intent(
|
||||
package=self._package_info.package,
|
||||
activity=self._package_info.activity,
|
||||
data=self._url,
|
||||
extras={'create_new_tab': True})
|
||||
self._logcat_monitor.Start()
|
||||
self._device.StartActivity(launch_intent, blocking=True)
|
||||
|
||||
def _TearDownTracing(self):
|
||||
self._flag_changer.Restore()
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
self._SetupTracing()
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
try:
|
||||
self._trace_file = self._logcat_monitor.WaitFor(
|
||||
self._trace_finish_re).group(1)
|
||||
finally:
|
||||
self._TearDownTracing()
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
||||
def GetResults(self, timeout=None):
|
||||
with open(self._PullTrace(), 'r') as f:
|
||||
trace_data = f.read()
|
||||
return trace_result.TraceResult('traceEvents', trace_data)
|
||||
|
||||
def _PullTrace(self):
|
||||
trace_file = self._trace_file.replace('/storage/emulated/0/', '/sdcard/')
|
||||
host_file = os.path.join(os.path.curdir, os.path.basename(trace_file))
|
||||
self._device.PullFile(trace_file, host_file)
|
||||
return host_file
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
return False
|
||||
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
||||
# pylint: disable=unused-argument
|
||||
assert self.SupportsExplicitClockSync(), ('Clock sync marker cannot be '
|
||||
'recorded since explicit clock sync is not supported.')
|
||||
|
||||
|
||||
class ChromeStartupConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self, device, package_info, webapk_package, cold, url,
|
||||
chrome_categories, trace_time):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
self.device = device
|
||||
self.package_info = package_info
|
||||
self.webapk_package = webapk_package
|
||||
self.cold = cold
|
||||
self.url = url
|
||||
self.chrome_categories = chrome_categories
|
||||
self.trace_time = trace_time
|
||||
|
||||
|
||||
def try_create_agent(config):
|
||||
return ChromeStartupTracingAgent(config.device, config.package_info,
|
||||
config.webapk_package,
|
||||
config.cold, config.url, config.trace_time)
|
||||
|
||||
def add_options(parser):
|
||||
options = optparse.OptionGroup(parser, 'Chrome startup tracing')
|
||||
options.add_option('--url', help='URL to visit on startup. Default: '
|
||||
'https://www.google.com. An empty URL launches Chrome '
|
||||
'with a MAIN action instead of VIEW.',
|
||||
default='https://www.google.com', metavar='URL')
|
||||
options.add_option('--cold', help='Flush the OS page cache before starting '
|
||||
'the browser. Note that this require a device with root '
|
||||
'access.', default=False, action='store_true')
|
||||
options.add_option('--webapk-package', help='Specify the package name '
|
||||
'of the WebAPK to launch the given URL. An empty URL '
|
||||
'laucnhes the host browser of the WebAPK with an new '
|
||||
'tab.', default=None)
|
||||
|
||||
return options
|
||||
|
||||
def get_config(options):
|
||||
return ChromeStartupConfig(options.device, options.package_info,
|
||||
options.webapk_package, options.cold,
|
||||
options.url, options.chrome_categories,
|
||||
options.trace_time)
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
import json
|
||||
|
||||
from profile_chrome import chrome_startup_tracing_agent
|
||||
from systrace import decorators
|
||||
from systrace.tracing_agents import agents_unittest
|
||||
|
||||
|
||||
class ChromeAgentTest(agents_unittest.BaseAgentTest):
|
||||
# TODO(washingtonp): This test seems to fail on the version of Android
|
||||
# currently on the Trybot servers (KTU84P), although it works on Android M.
|
||||
# Either upgrade the version of Android on the Trybot servers or determine
|
||||
# if there is a way to run this agent on Android KTU84P.
|
||||
@decorators.Disabled
|
||||
def testTracing(self):
|
||||
agent = chrome_startup_tracing_agent.ChromeStartupTracingAgent(
|
||||
self.device, self.package_info,
|
||||
'', # webapk_package
|
||||
False, # cold
|
||||
'https://www.google.com' # url
|
||||
)
|
||||
|
||||
try:
|
||||
agent.StartAgentTracing(None)
|
||||
finally:
|
||||
agent.StopAgentTracing()
|
||||
|
||||
result = agent.GetResults()
|
||||
json.loads(result.raw_data)
|
||||
@@ -0,0 +1,217 @@
|
||||
# Copyright 2014 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.
|
||||
|
||||
import json
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
|
||||
import py_utils
|
||||
|
||||
from devil.android import device_errors
|
||||
from devil.android.sdk import intent
|
||||
from systrace import trace_result
|
||||
from systrace import tracing_agents
|
||||
|
||||
|
||||
DEFAULT_CHROME_CATEGORIES = '_DEFAULT_CHROME_CATEGORIES'
|
||||
_HEAP_PROFILE_MMAP_PROPERTY = 'heapprof.mmap'
|
||||
|
||||
|
||||
class ChromeTracingAgent(tracing_agents.TracingAgent):
|
||||
def __init__(self, device, package_info, ring_buffer, trace_memory=False):
|
||||
tracing_agents.TracingAgent.__init__(self)
|
||||
self._device = device
|
||||
self._package_info = package_info
|
||||
self._ring_buffer = ring_buffer
|
||||
self._logcat_monitor = self._device.GetLogcatMonitor()
|
||||
self._trace_file = None
|
||||
self._trace_memory = trace_memory
|
||||
self._is_tracing = False
|
||||
self._trace_start_re = \
|
||||
re.compile(r'Logging performance trace to file')
|
||||
self._trace_finish_re = \
|
||||
re.compile(r'Profiler finished[.] Results are in (.*)[.]')
|
||||
self._categories = None
|
||||
|
||||
def __repr__(self):
|
||||
return 'chrome trace'
|
||||
|
||||
@staticmethod
|
||||
def GetCategories(device, package_info):
|
||||
with device.GetLogcatMonitor() as logmon:
|
||||
device.BroadcastIntent(intent.Intent(
|
||||
action='%s.GPU_PROFILER_LIST_CATEGORIES' % package_info.package))
|
||||
try:
|
||||
json_category_list = logmon.WaitFor(
|
||||
re.compile(r'{"traceCategoriesList(.*)'), timeout=5).group(0)
|
||||
except device_errors.CommandTimeoutError:
|
||||
raise RuntimeError('Performance trace category list marker not found. '
|
||||
'Is the correct version of the browser running?')
|
||||
|
||||
record_categories = set()
|
||||
disabled_by_default_categories = set()
|
||||
json_data = json.loads(json_category_list)['traceCategoriesList']
|
||||
for item in json_data:
|
||||
for category in item.split(','):
|
||||
if category.startswith('disabled-by-default'):
|
||||
disabled_by_default_categories.add(category)
|
||||
else:
|
||||
record_categories.add(category)
|
||||
|
||||
return list(record_categories), list(disabled_by_default_categories)
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
self._categories = _ComputeChromeCategories(config)
|
||||
self._logcat_monitor.Start()
|
||||
start_extras = {'categories': ','.join(self._categories)}
|
||||
if self._ring_buffer:
|
||||
start_extras['continuous'] = None
|
||||
self._device.BroadcastIntent(intent.Intent(
|
||||
action='%s.GPU_PROFILER_START' % self._package_info.package,
|
||||
extras=start_extras))
|
||||
|
||||
if self._trace_memory:
|
||||
self._device.EnableRoot()
|
||||
self._device.SetProp(_HEAP_PROFILE_MMAP_PROPERTY, 1)
|
||||
|
||||
# Chrome logs two different messages related to tracing:
|
||||
#
|
||||
# 1. "Logging performance trace to file"
|
||||
# 2. "Profiler finished. Results are in [...]"
|
||||
#
|
||||
# The first one is printed when tracing starts and the second one indicates
|
||||
# that the trace file is ready to be pulled.
|
||||
try:
|
||||
self._logcat_monitor.WaitFor(self._trace_start_re, timeout=5)
|
||||
self._is_tracing = True
|
||||
except device_errors.CommandTimeoutError:
|
||||
raise RuntimeError(
|
||||
'Trace start marker not found. Possible causes: 1) Is the correct '
|
||||
'version of the browser running? 2) Is the browser already launched?')
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
if self._is_tracing:
|
||||
self._device.BroadcastIntent(intent.Intent(
|
||||
action='%s.GPU_PROFILER_STOP' % self._package_info.package))
|
||||
self._trace_file = self._logcat_monitor.WaitFor(
|
||||
self._trace_finish_re, timeout=120).group(1)
|
||||
self._is_tracing = False
|
||||
if self._trace_memory:
|
||||
self._device.SetProp(_HEAP_PROFILE_MMAP_PROPERTY, 0)
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
||||
def GetResults(self, timeout=None):
|
||||
with open(self._PullTrace(), 'r') as f:
|
||||
trace_data = f.read()
|
||||
return trace_result.TraceResult('traceEvents', trace_data)
|
||||
|
||||
def _PullTrace(self):
|
||||
trace_file = self._trace_file.replace('/storage/emulated/0/', '/sdcard/')
|
||||
host_file = os.path.join(os.path.curdir, os.path.basename(trace_file))
|
||||
try:
|
||||
self._device.PullFile(trace_file, host_file)
|
||||
except device_errors.AdbCommandFailedError:
|
||||
raise RuntimeError(
|
||||
'Cannot pull the trace file. Have you granted Storage permission to '
|
||||
'the browser? (Android Settings -> Apps -> [the browser app] -> '
|
||||
'Permissions -> Storage)')
|
||||
return host_file
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
return False
|
||||
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
||||
# pylint: disable=unused-argument
|
||||
assert self.SupportsExplicitClockSync(), ('Clock sync marker cannot be '
|
||||
'recorded since explicit clock sync is not supported.')
|
||||
|
||||
|
||||
class ChromeConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self, chrome_categories, trace_cc, trace_frame_viewer,
|
||||
trace_ubercompositor, trace_gpu, trace_flow, trace_memory,
|
||||
trace_scheduler, ring_buffer, device, package_info):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
self.chrome_categories = chrome_categories
|
||||
self.trace_cc = trace_cc
|
||||
self.trace_frame_viewer = trace_frame_viewer
|
||||
self.trace_ubercompositor = trace_ubercompositor
|
||||
self.trace_gpu = trace_gpu
|
||||
self.trace_flow = trace_flow
|
||||
self.trace_memory = trace_memory
|
||||
self.trace_scheduler = trace_scheduler
|
||||
self.ring_buffer = ring_buffer
|
||||
self.device = device
|
||||
self.package_info = package_info
|
||||
|
||||
|
||||
def try_create_agent(config):
|
||||
if config.chrome_categories:
|
||||
return ChromeTracingAgent(config.device, config.package_info,
|
||||
config.ring_buffer, config.trace_memory)
|
||||
return None
|
||||
|
||||
def add_options(parser):
|
||||
chrome_opts = optparse.OptionGroup(parser, 'Chrome tracing options')
|
||||
chrome_opts.add_option('-c', '--categories', help='Select Chrome tracing '
|
||||
'categories with comma-delimited wildcards, '
|
||||
'e.g., "*", "cat1*,-cat1a". Omit this option to trace '
|
||||
'Chrome\'s default categories. Chrome tracing can be '
|
||||
'disabled with "--categories=\'\'". Use "list" to '
|
||||
'see the available categories.',
|
||||
metavar='CHROME_CATEGORIES', dest='chrome_categories')
|
||||
chrome_opts.add_option('--trace-cc',
|
||||
help='Deprecated, use --trace-frame-viewer.',
|
||||
action='store_true')
|
||||
chrome_opts.add_option('--trace-frame-viewer',
|
||||
help='Enable enough trace categories for '
|
||||
'compositor frame viewing.', action='store_true')
|
||||
chrome_opts.add_option('--trace-ubercompositor',
|
||||
help='Enable enough trace categories for '
|
||||
'ubercompositor frame data.', action='store_true')
|
||||
chrome_opts.add_option('--trace-gpu', help='Enable extra trace categories '
|
||||
'for GPU data.', action='store_true')
|
||||
chrome_opts.add_option('--trace-flow', help='Enable extra trace categories '
|
||||
'for IPC message flows.', action='store_true')
|
||||
chrome_opts.add_option('--trace-memory', help='Enable extra trace categories '
|
||||
'for memory profile. (tcmalloc required)',
|
||||
action='store_true')
|
||||
chrome_opts.add_option('--trace-scheduler', help='Enable extra trace '
|
||||
'categories for scheduler state',
|
||||
action='store_true')
|
||||
return chrome_opts
|
||||
|
||||
def get_config(options):
|
||||
return ChromeConfig(options.chrome_categories, options.trace_cc,
|
||||
options.trace_frame_viewer, options.trace_ubercompositor,
|
||||
options.trace_gpu, options.trace_flow,
|
||||
options.trace_memory, options.trace_scheduler,
|
||||
options.ring_buffer, options.device,
|
||||
options.package_info)
|
||||
|
||||
def _ComputeChromeCategories(config):
|
||||
categories = []
|
||||
if config.trace_frame_viewer:
|
||||
categories.append('disabled-by-default-cc.debug')
|
||||
if config.trace_ubercompositor:
|
||||
categories.append('disabled-by-default-cc.debug*')
|
||||
if config.trace_gpu:
|
||||
categories.append('disabled-by-default-gpu.debug*')
|
||||
if config.trace_flow:
|
||||
categories.append('disabled-by-default-toplevel.flow')
|
||||
if config.trace_memory:
|
||||
categories.append('disabled-by-default-memory')
|
||||
if config.trace_scheduler:
|
||||
categories.append('disabled-by-default-blink.scheduler')
|
||||
categories.append('disabled-by-default-cc.debug.scheduler')
|
||||
categories.append('disabled-by-default-renderer.scheduler')
|
||||
categories.append('disabled-by-default-sequence_manager')
|
||||
categories.append('sequence_manager')
|
||||
if config.chrome_categories:
|
||||
categories += config.chrome_categories.split(',')
|
||||
return categories
|
||||
@@ -0,0 +1,51 @@
|
||||
# Copyright 2014 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.
|
||||
|
||||
import json
|
||||
|
||||
from profile_chrome import chrome_tracing_agent
|
||||
from systrace import decorators
|
||||
from systrace.tracing_agents import agents_unittest
|
||||
|
||||
|
||||
class ChromeAgentTest(agents_unittest.BaseAgentTest):
|
||||
# TODO(washingtonp): This test seems to fail on the version of Android
|
||||
# currently on the Trybot servers (KTU84P), although it works on Android M.
|
||||
# Either upgrade the version of Android on the Trybot servers or determine
|
||||
# if there is a way to run this agent on Android KTU84P.
|
||||
@decorators.Disabled
|
||||
def testGetCategories(self):
|
||||
curr_browser = self.GetChromeProcessID()
|
||||
if curr_browser is None:
|
||||
self.StartBrowser()
|
||||
|
||||
categories = \
|
||||
chrome_tracing_agent.ChromeTracingAgent.GetCategories(
|
||||
self.device, self.package_info)
|
||||
|
||||
self.assertEquals(len(categories), 2)
|
||||
self.assertTrue(categories[0])
|
||||
self.assertTrue(categories[1])
|
||||
|
||||
# TODO(washingtonp): This test is pretty flaky on the version of Android
|
||||
# currently on the Trybot servers (KTU84P), although it works on Android M.
|
||||
# Either upgrade the version of Android on the Trybot servers or determine
|
||||
# if there is a way to run this agent on Android KTU84P.
|
||||
@decorators.Disabled
|
||||
def testTracing(self):
|
||||
curr_browser = self.GetChromeProcessID()
|
||||
if curr_browser is None:
|
||||
self.StartBrowser()
|
||||
|
||||
categories = '*'
|
||||
ring_buffer = False
|
||||
agent = chrome_tracing_agent.ChromeTracingAgent(self.device,
|
||||
self.package_info,
|
||||
ring_buffer)
|
||||
agent.StartAgentTracing(chrome_tracing_agent.ChromeConfig(categories, None,
|
||||
None, None, None, None, None, None, ring_buffer, self.device,
|
||||
self.package_info))
|
||||
agent.StopAgentTracing()
|
||||
result = agent.GetResults()
|
||||
json.loads(result.raw_data)
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
|
||||
import py_utils
|
||||
|
||||
from profile_chrome import util
|
||||
from systrace import trace_result
|
||||
from systrace import tracing_agents
|
||||
|
||||
|
||||
_DDMS_SAMPLING_FREQUENCY_US = 100
|
||||
|
||||
|
||||
class DdmsAgent(tracing_agents.TracingAgent):
|
||||
def __init__(self, device, package_info):
|
||||
tracing_agents.TracingAgent.__init__(self)
|
||||
self._device = device
|
||||
self._package = package_info.package
|
||||
self._output_file = None
|
||||
self._supports_sampling = self._SupportsSampling()
|
||||
|
||||
def __repr__(self):
|
||||
return 'ddms profile'
|
||||
|
||||
def _SupportsSampling(self):
|
||||
for line in self._device.RunShellCommand(
|
||||
['am', '--help'], check_return=True):
|
||||
if re.match(r'.*am profile start.*--sampling', line):
|
||||
return True
|
||||
return False
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
self._output_file = (
|
||||
'/data/local/tmp/ddms-profile-%s' % util.GetTraceTimestamp())
|
||||
cmd = ['am', 'profile', 'start']
|
||||
if self._supports_sampling:
|
||||
cmd.extend(['--sampling', str(_DDMS_SAMPLING_FREQUENCY_US)])
|
||||
cmd.extend([self._package, self._output_file])
|
||||
self._device.RunShellCommand(cmd, check_return=True)
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
self._device.RunShellCommand(
|
||||
['am', 'profile', 'stop', self._package], check_return=True)
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
||||
def GetResults(self, timeout=None):
|
||||
with open(self._PullTrace(), 'r') as f:
|
||||
trace_data = f.read()
|
||||
return trace_result.TraceResult('ddms', trace_data)
|
||||
|
||||
def _PullTrace(self):
|
||||
if not self._output_file:
|
||||
return None
|
||||
|
||||
host_file = os.path.join(
|
||||
os.path.curdir, os.path.basename(self._output_file))
|
||||
self._device.PullFile(self._output_file, host_file)
|
||||
return host_file
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
return False
|
||||
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
||||
# pylint: disable=unused-argument
|
||||
assert self.SupportsExplicitClockSync(), ('Clock sync marker cannot be '
|
||||
'recorded since explicit clock sync is not supported.')
|
||||
|
||||
|
||||
class DdmsConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self, device, package_info, ddms):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
self.device = device
|
||||
self.package_info = package_info
|
||||
self.ddms = ddms
|
||||
|
||||
|
||||
def try_create_agent(config):
|
||||
if config.ddms:
|
||||
return DdmsAgent(config.device, config.package_info)
|
||||
return None
|
||||
|
||||
def add_options(parser):
|
||||
options = optparse.OptionGroup(parser, 'Java tracing')
|
||||
options.add_option('--ddms', help='Trace Java execution using DDMS '
|
||||
'sampling.', action='store_true')
|
||||
return options
|
||||
|
||||
def get_config(options):
|
||||
return DdmsConfig(options.device, options.package_info, options.ddms)
|
||||
@@ -0,0 +1,24 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
from profile_chrome import ddms_tracing_agent
|
||||
from systrace import decorators
|
||||
from systrace.tracing_agents import agents_unittest
|
||||
|
||||
|
||||
class DdmsAgentTest(agents_unittest.BaseAgentTest):
|
||||
# TODO(washingtonp): The DDMS test is flaky on the Tryserver, but it
|
||||
# consistently passes on Android M. Need to figure out why the result data
|
||||
# does not start with '*version' and why the test is flaky.
|
||||
@decorators.Disabled
|
||||
def testTracing(self):
|
||||
agent = ddms_tracing_agent.DdmsAgent(self.device, self.package_info)
|
||||
|
||||
try:
|
||||
agent.StartAgentTracing(None)
|
||||
finally:
|
||||
agent.StopAgentTracing()
|
||||
|
||||
result = agent.GetResults()
|
||||
self.assertTrue(result.raw_data.startswith('*version'))
|
||||
@@ -0,0 +1,69 @@
|
||||
# Copyright 2016 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.
|
||||
|
||||
import optparse
|
||||
import tempfile
|
||||
|
||||
from systrace import trace_result
|
||||
from systrace import tracing_agents
|
||||
|
||||
|
||||
class FakeAgent(object):
|
||||
def __init__(self, contents='fake-contents'):
|
||||
self.contents = contents
|
||||
self.stopped = False
|
||||
self.filename = None
|
||||
self.config = None
|
||||
self.timeout = None
|
||||
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
self.config = config
|
||||
self.timeout = timeout
|
||||
return True
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
self.stopped = True
|
||||
return True
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def GetResults(self, timeout=None):
|
||||
trace_data = open(self._PullTrace()).read()
|
||||
return trace_result.TraceResult('fakeData', trace_data)
|
||||
|
||||
def _PullTrace(self):
|
||||
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||
self.filename = f.name
|
||||
f.write(self.contents)
|
||||
return f.name
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def SupportsExplicitClockSync(self):
|
||||
return False
|
||||
|
||||
# pylint: disable=unused-argument, no-self-use
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
||||
print ('Clock sync marker cannot be recorded since explicit clock sync '
|
||||
'is not supported.')
|
||||
|
||||
def __repr__(self):
|
||||
return 'faketrace'
|
||||
|
||||
|
||||
class FakeConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def try_create_agent(config):
|
||||
return FakeAgent()
|
||||
|
||||
def add_options(parser):
|
||||
options = optparse.OptionGroup(parser, 'Fake options.')
|
||||
return options
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def get_config(options):
|
||||
return FakeConfig()
|
||||
@@ -0,0 +1,68 @@
|
||||
# Copyright 2016 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.
|
||||
|
||||
import optparse
|
||||
import tempfile
|
||||
|
||||
from systrace import trace_result
|
||||
from systrace import tracing_agents
|
||||
|
||||
|
||||
class FakeAgent2(object):
|
||||
def __init__(self, contents='fake-contents'):
|
||||
self.contents = contents
|
||||
self.stopped = False
|
||||
self.config = None
|
||||
self.filename = None
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
self.config = config
|
||||
return True
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
self.stopped = True
|
||||
return True
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def GetResults(self, timeout=None):
|
||||
trace_data = open(self._PullTrace()).read()
|
||||
return trace_result.TraceResult('fakeDataTwo', trace_data)
|
||||
|
||||
def _PullTrace(self):
|
||||
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||
self.filename = f.name
|
||||
f.write(self.contents)
|
||||
return f.name
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def SupportsExplicitClockSync(self):
|
||||
return False
|
||||
|
||||
# pylint: disable=unused-argument, no-self-use
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
||||
print ('Clock sync marker cannot be recorded since explicit clock sync '
|
||||
'is not supported.')
|
||||
|
||||
def __repr__(self):
|
||||
return 'faketracetwo'
|
||||
|
||||
|
||||
class FakeConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def try_create_agent(config):
|
||||
return FakeAgent2()
|
||||
|
||||
def add_options(parser):
|
||||
options = optparse.OptionGroup(parser, 'Fake options.')
|
||||
return options
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def get_config(options):
|
||||
return FakeConfig()
|
||||
16
tools/adb/systrace/catapult/systrace/profile_chrome/flags.py
Normal file
16
tools/adb/systrace/catapult/systrace/profile_chrome/flags.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
import optparse
|
||||
|
||||
|
||||
def OutputOptions(parser):
|
||||
output_options = optparse.OptionGroup(parser, 'Output options')
|
||||
output_options.add_option('-o', '--output', dest='output_file',
|
||||
help='Save trace output to file.')
|
||||
output_options.add_option('--json', help='Save trace as raw JSON instead of '
|
||||
'HTML.', dest='write_json')
|
||||
output_options.add_option('--view', help='Open resulting trace file in a '
|
||||
'browser.', action='store_true')
|
||||
return output_options
|
||||
160
tools/adb/systrace/catapult/systrace/profile_chrome/main.py
Normal file
160
tools/adb/systrace/catapult/systrace/profile_chrome/main.py
Normal file
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2014 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.
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import webbrowser
|
||||
|
||||
from profile_chrome import chrome_tracing_agent
|
||||
from profile_chrome import ddms_tracing_agent
|
||||
from profile_chrome import flags
|
||||
from profile_chrome import perf_tracing_agent
|
||||
from profile_chrome import profiler
|
||||
from profile_chrome import ui
|
||||
from systrace import util
|
||||
from systrace.tracing_agents import atrace_agent
|
||||
|
||||
from devil.android import device_utils
|
||||
from devil.android.sdk import adb_wrapper
|
||||
|
||||
|
||||
_PROFILE_CHROME_AGENT_MODULES = [chrome_tracing_agent, ddms_tracing_agent,
|
||||
perf_tracing_agent, atrace_agent]
|
||||
|
||||
|
||||
def _CreateOptionParser():
|
||||
parser = optparse.OptionParser(description='Record about://tracing profiles '
|
||||
'from Android browsers. See http://dev.'
|
||||
'chromium.org/developers/how-tos/trace-event-'
|
||||
'profiling-tool for detailed instructions for '
|
||||
'profiling.', conflict_handler='resolve')
|
||||
|
||||
parser = util.get_main_options(parser)
|
||||
|
||||
timed_options = optparse.OptionGroup(parser, 'Timed tracing')
|
||||
timed_options.add_option('-t', '--time', help='Profile for N seconds and '
|
||||
'download the resulting trace.', metavar='N',
|
||||
type='float', dest='trace_time')
|
||||
parser.add_option_group(timed_options)
|
||||
|
||||
cont_options = optparse.OptionGroup(parser, 'Continuous tracing')
|
||||
cont_options.add_option('--continuous', help='Profile continuously until '
|
||||
'stopped.', action='store_true')
|
||||
cont_options.add_option('--ring-buffer', help='Use the trace buffer as a '
|
||||
'ring buffer and save its contents when stopping '
|
||||
'instead of appending events into one long trace.',
|
||||
action='store_true')
|
||||
parser.add_option_group(cont_options)
|
||||
|
||||
parser.add_option_group(flags.OutputOptions(parser))
|
||||
|
||||
browsers = sorted(util.get_supported_browsers().keys())
|
||||
parser.add_option('-b', '--browser', help='Select among installed browsers. '
|
||||
'One of ' + ', '.join(browsers) + ', "stable" is used by '
|
||||
'default.', type='choice', choices=browsers,
|
||||
default='stable')
|
||||
parser.add_option('-v', '--verbose', help='Verbose logging.',
|
||||
action='store_true')
|
||||
parser.add_option('-z', '--compress', help='Compress the resulting trace '
|
||||
'with gzip. ', action='store_true')
|
||||
|
||||
# Add options from profile_chrome agents.
|
||||
for module in _PROFILE_CHROME_AGENT_MODULES:
|
||||
parser.add_option_group(module.add_options(parser))
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
parser = _CreateOptionParser()
|
||||
options, _args = parser.parse_args() # pylint: disable=unused-variable
|
||||
if options.trace_cc:
|
||||
parser.error("""--trace-cc is deprecated.
|
||||
|
||||
For basic jank busting uses, use --trace-frame-viewer
|
||||
For detailed study of ubercompositor, pass --trace-ubercompositor.
|
||||
|
||||
When in doubt, just try out --trace-frame-viewer.
|
||||
""")
|
||||
|
||||
logging.basicConfig()
|
||||
|
||||
if options.verbose:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
if not options.device_serial_number:
|
||||
devices = [a.GetDeviceSerial() for a in adb_wrapper.AdbWrapper.Devices()]
|
||||
if len(devices) == 0:
|
||||
raise RuntimeError('No ADB devices connected.')
|
||||
elif len(devices) >= 2:
|
||||
raise RuntimeError('Multiple devices connected, serial number required')
|
||||
options.device_serial_number = devices[0]
|
||||
device = device_utils.DeviceUtils.HealthyDevices(device_arg=
|
||||
options.device_serial_number)[0]
|
||||
package_info = util.get_supported_browsers()[options.browser]
|
||||
|
||||
options.device = device
|
||||
options.package_info = package_info
|
||||
|
||||
# Include Chrome categories by default in profile_chrome.
|
||||
if not options.chrome_categories:
|
||||
options.chrome_categories = chrome_tracing_agent.DEFAULT_CHROME_CATEGORIES
|
||||
|
||||
if options.chrome_categories in ['list', 'help']:
|
||||
ui.PrintMessage('Collecting record categories list...', eol='')
|
||||
record_categories = []
|
||||
disabled_by_default_categories = []
|
||||
record_categories, disabled_by_default_categories = \
|
||||
chrome_tracing_agent.ChromeTracingAgent.GetCategories(
|
||||
device, package_info)
|
||||
|
||||
ui.PrintMessage('done')
|
||||
ui.PrintMessage('Record Categories:')
|
||||
ui.PrintMessage('\n'.join('\t%s' % item \
|
||||
for item in sorted(record_categories)))
|
||||
|
||||
ui.PrintMessage('\nDisabled by Default Categories:')
|
||||
ui.PrintMessage('\n'.join('\t%s' % item \
|
||||
for item in sorted(disabled_by_default_categories)))
|
||||
|
||||
return 0
|
||||
|
||||
if options.atrace_categories in ['list', 'help']:
|
||||
atrace_agent.list_categories(atrace_agent.get_config(options))
|
||||
print '\n'
|
||||
return 0
|
||||
|
||||
if (perf_tracing_agent.PerfProfilerAgent.IsSupported() and
|
||||
options.perf_categories in ['list', 'help']):
|
||||
ui.PrintMessage('\n'.join(
|
||||
perf_tracing_agent.PerfProfilerAgent.GetCategories(device)))
|
||||
return 0
|
||||
|
||||
if not options.trace_time and not options.continuous:
|
||||
ui.PrintMessage('Time interval or continuous tracing should be specified.')
|
||||
return 1
|
||||
|
||||
if (options.chrome_categories and options.atrace_categories and
|
||||
'webview' in options.atrace_categories):
|
||||
logging.warning('Using the "webview" category in atrace together with '
|
||||
'Chrome tracing results in duplicate trace events.')
|
||||
|
||||
if options.output_file:
|
||||
options.output_file = os.path.expanduser(options.output_file)
|
||||
result = profiler.CaptureProfile(
|
||||
options,
|
||||
options.trace_time if not options.continuous else 0,
|
||||
_PROFILE_CHROME_AGENT_MODULES,
|
||||
output=options.output_file,
|
||||
compress=options.compress,
|
||||
write_json=options.write_json)
|
||||
if options.view:
|
||||
if sys.platform == 'darwin':
|
||||
os.system('/usr/bin/open %s' % os.path.abspath(result))
|
||||
else:
|
||||
webbrowser.open(result)
|
||||
@@ -0,0 +1,254 @@
|
||||
# Copyright 2014 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.
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import py_utils
|
||||
|
||||
from devil.android import device_temp_file
|
||||
from devil.android.perf import perf_control
|
||||
|
||||
from profile_chrome import ui
|
||||
from systrace import trace_result
|
||||
from systrace import tracing_agents
|
||||
|
||||
_CATAPULT_DIR = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), '..', '..')
|
||||
sys.path.append(os.path.join(_CATAPULT_DIR, 'telemetry'))
|
||||
try:
|
||||
# pylint: disable=F0401,no-name-in-module,wrong-import-position
|
||||
from telemetry.internal.platform.profiler import android_profiling_helper
|
||||
from telemetry.internal.util import binary_manager
|
||||
except ImportError:
|
||||
android_profiling_helper = None
|
||||
binary_manager = None
|
||||
|
||||
|
||||
_PERF_OPTIONS = [
|
||||
# Sample across all processes and CPUs to so that the current CPU gets
|
||||
# recorded to each sample.
|
||||
'--all-cpus',
|
||||
# In perf 3.13 --call-graph requires an argument, so use the -g short-hand
|
||||
# which does not.
|
||||
'-g',
|
||||
# Increase priority to avoid dropping samples. Requires root.
|
||||
'--realtime', '80',
|
||||
# Record raw samples to get CPU information.
|
||||
'--raw-samples',
|
||||
# Increase sampling frequency for better coverage.
|
||||
'--freq', '2000',
|
||||
]
|
||||
|
||||
|
||||
class _PerfProfiler(object):
|
||||
def __init__(self, device, perf_binary, categories):
|
||||
self._device = device
|
||||
self._output_file = device_temp_file.DeviceTempFile(
|
||||
self._device.adb, prefix='perf_output')
|
||||
self._log_file = tempfile.TemporaryFile()
|
||||
|
||||
# TODO(jbudorick) Look at providing a way to unhandroll this once the
|
||||
# adb rewrite has fully landed.
|
||||
device_param = (['-s', str(self._device)] if str(self._device) else [])
|
||||
cmd = ['adb'] + device_param + \
|
||||
['shell', perf_binary, 'record',
|
||||
'--output', self._output_file.name] + _PERF_OPTIONS
|
||||
if categories:
|
||||
cmd += ['--event', ','.join(categories)]
|
||||
self._perf_control = perf_control.PerfControl(self._device)
|
||||
self._perf_control.SetPerfProfilingMode()
|
||||
self._perf_process = subprocess.Popen(cmd,
|
||||
stdout=self._log_file,
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
def SignalAndWait(self):
|
||||
self._device.KillAll('perf', signum=signal.SIGINT)
|
||||
self._perf_process.wait()
|
||||
self._perf_control.SetDefaultPerfMode()
|
||||
|
||||
def _FailWithLog(self, msg):
|
||||
self._log_file.seek(0)
|
||||
log = self._log_file.read()
|
||||
raise RuntimeError('%s. Log output:\n%s' % (msg, log))
|
||||
|
||||
def PullResult(self, output_path):
|
||||
if not self._device.FileExists(self._output_file.name):
|
||||
self._FailWithLog('Perf recorded no data')
|
||||
|
||||
perf_profile = os.path.join(output_path,
|
||||
os.path.basename(self._output_file.name))
|
||||
self._device.PullFile(self._output_file.name, perf_profile)
|
||||
if not os.stat(perf_profile).st_size:
|
||||
os.remove(perf_profile)
|
||||
self._FailWithLog('Perf recorded a zero-sized file')
|
||||
|
||||
self._log_file.close()
|
||||
self._output_file.close()
|
||||
return perf_profile
|
||||
|
||||
|
||||
class PerfProfilerAgent(tracing_agents.TracingAgent):
|
||||
def __init__(self, device):
|
||||
tracing_agents.TracingAgent.__init__(self)
|
||||
self._device = device
|
||||
self._perf_binary = self._PrepareDevice(device)
|
||||
self._perf_instance = None
|
||||
self._categories = None
|
||||
|
||||
def __repr__(self):
|
||||
return 'perf profile'
|
||||
|
||||
@staticmethod
|
||||
def IsSupported():
|
||||
return bool(android_profiling_helper)
|
||||
|
||||
@staticmethod
|
||||
def _PrepareDevice(device):
|
||||
if not 'BUILDTYPE' in os.environ:
|
||||
os.environ['BUILDTYPE'] = 'Release'
|
||||
if binary_manager.NeedsInit():
|
||||
binary_manager.InitDependencyManager(None)
|
||||
return android_profiling_helper.PrepareDeviceForPerf(device)
|
||||
|
||||
@classmethod
|
||||
def GetCategories(cls, device):
|
||||
perf_binary = cls._PrepareDevice(device)
|
||||
# Perf binary returns non-zero exit status on "list" command.
|
||||
return device.RunShellCommand([perf_binary, 'list'], check_return=False)
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
self._categories = _ComputePerfCategories(config)
|
||||
self._perf_instance = _PerfProfiler(self._device,
|
||||
self._perf_binary,
|
||||
self._categories)
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
if not self._perf_instance:
|
||||
return
|
||||
self._perf_instance.SignalAndWait()
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
||||
def GetResults(self, timeout=None):
|
||||
with open(self._PullTrace(), 'r') as f:
|
||||
trace_data = f.read()
|
||||
return trace_result.TraceResult('perf', trace_data)
|
||||
|
||||
@staticmethod
|
||||
def _GetInteractivePerfCommand(perfhost_path, perf_profile, symfs_dir,
|
||||
required_libs, kallsyms):
|
||||
cmd = '%s report -n -i %s --symfs %s --kallsyms %s' % (
|
||||
os.path.relpath(perfhost_path, '.'), perf_profile, symfs_dir, kallsyms)
|
||||
for lib in required_libs:
|
||||
lib = os.path.join(symfs_dir, lib[1:])
|
||||
if not os.path.exists(lib):
|
||||
continue
|
||||
objdump_path = android_profiling_helper.GetToolchainBinaryPath(
|
||||
lib, 'objdump')
|
||||
if objdump_path:
|
||||
cmd += ' --objdump %s' % os.path.relpath(objdump_path, '.')
|
||||
break
|
||||
return cmd
|
||||
|
||||
def _PullTrace(self):
|
||||
symfs_dir = os.path.join(tempfile.gettempdir(),
|
||||
os.path.expandvars('$USER-perf-symfs'))
|
||||
if not os.path.exists(symfs_dir):
|
||||
os.makedirs(symfs_dir)
|
||||
required_libs = set()
|
||||
|
||||
# Download the recorded perf profile.
|
||||
perf_profile = self._perf_instance.PullResult(symfs_dir)
|
||||
required_libs = \
|
||||
android_profiling_helper.GetRequiredLibrariesForPerfProfile(
|
||||
perf_profile)
|
||||
if not required_libs:
|
||||
logging.warning('No libraries required by perf trace. Most likely there '
|
||||
'are no samples in the trace.')
|
||||
|
||||
# Build a symfs with all the necessary libraries.
|
||||
kallsyms = android_profiling_helper.CreateSymFs(self._device,
|
||||
symfs_dir,
|
||||
required_libs,
|
||||
use_symlinks=False)
|
||||
perfhost_path = binary_manager.FetchPath(
|
||||
android_profiling_helper.GetPerfhostName(), 'x86_64', 'linux')
|
||||
|
||||
ui.PrintMessage('\nNote: to view the profile in perf, run:')
|
||||
ui.PrintMessage(' ' + self._GetInteractivePerfCommand(perfhost_path,
|
||||
perf_profile, symfs_dir, required_libs, kallsyms))
|
||||
|
||||
# Convert the perf profile into JSON.
|
||||
perf_script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
'third_party', 'perf_to_tracing.py')
|
||||
json_file_name = os.path.basename(perf_profile)
|
||||
with open(os.devnull, 'w') as dev_null, \
|
||||
open(json_file_name, 'w') as json_file:
|
||||
cmd = [perfhost_path, 'script', '-s', perf_script_path, '-i',
|
||||
perf_profile, '--symfs', symfs_dir, '--kallsyms', kallsyms]
|
||||
if subprocess.call(cmd, stdout=json_file, stderr=dev_null):
|
||||
logging.warning('Perf data to JSON conversion failed. The result will '
|
||||
'not contain any perf samples. You can still view the '
|
||||
'perf data manually as shown above.')
|
||||
return None
|
||||
|
||||
return json_file_name
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
return False
|
||||
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
||||
# pylint: disable=unused-argument
|
||||
assert self.SupportsExplicitClockSync(), ('Clock sync marker cannot be '
|
||||
'recorded since explicit clock sync is not supported.')
|
||||
|
||||
def _OptionalValueCallback(default_value):
|
||||
def callback(option, _, __, parser): # pylint: disable=unused-argument
|
||||
value = default_value
|
||||
if parser.rargs and not parser.rargs[0].startswith('-'):
|
||||
value = parser.rargs.pop(0)
|
||||
setattr(parser.values, option.dest, value)
|
||||
return callback
|
||||
|
||||
|
||||
class PerfConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self, perf_categories, device):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
self.perf_categories = perf_categories
|
||||
self.device = device
|
||||
|
||||
|
||||
def try_create_agent(config):
|
||||
if config.perf_categories:
|
||||
return PerfProfilerAgent(config.device)
|
||||
return None
|
||||
|
||||
def add_options(parser):
|
||||
options = optparse.OptionGroup(parser, 'Perf profiling options')
|
||||
options.add_option('-p', '--perf', help='Capture a perf profile with '
|
||||
'the chosen comma-delimited event categories. '
|
||||
'Samples CPU cycles by default. Use "list" to see '
|
||||
'the available sample types.', action='callback',
|
||||
default='', callback=_OptionalValueCallback('cycles'),
|
||||
metavar='PERF_CATEGORIES', dest='perf_categories')
|
||||
return options
|
||||
|
||||
def get_config(options):
|
||||
return PerfConfig(options.perf_categories, options.device)
|
||||
|
||||
def _ComputePerfCategories(config):
|
||||
if not PerfProfilerAgent.IsSupported():
|
||||
return []
|
||||
if not config.perf_categories:
|
||||
return []
|
||||
return config.perf_categories.split(',')
|
||||
@@ -0,0 +1,39 @@
|
||||
# Copyright 2014 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.
|
||||
|
||||
import json
|
||||
|
||||
from profile_chrome import perf_tracing_agent
|
||||
from profile_chrome import ui
|
||||
from systrace import decorators
|
||||
from systrace.tracing_agents import agents_unittest
|
||||
|
||||
|
||||
class PerfProfilerAgentTest(agents_unittest.BaseAgentTest):
|
||||
@decorators.ClientOnlyTest
|
||||
def testGetCategories(self):
|
||||
if not perf_tracing_agent.PerfProfilerAgent.IsSupported():
|
||||
return
|
||||
categories = \
|
||||
perf_tracing_agent.PerfProfilerAgent.GetCategories(self.device)
|
||||
assert 'cycles' in ' '.join(categories)
|
||||
|
||||
# TODO(washingtonp): Try enabling this test for the SimpleperfProfilerAgent,
|
||||
# which will be added later.
|
||||
@decorators.Disabled
|
||||
def testTracing(self):
|
||||
if not perf_tracing_agent.PerfProfilerAgent.IsSupported():
|
||||
return
|
||||
ui.EnableTestMode()
|
||||
categories = 'cycles'
|
||||
agent = perf_tracing_agent.PerfProfilerAgent(self.device)
|
||||
|
||||
try:
|
||||
agent.StartAgentTracing(perf_tracing_agent.PerfConfig(categories,
|
||||
self.device))
|
||||
finally:
|
||||
agent.StopAgentTracing()
|
||||
|
||||
result = agent.GetResults()
|
||||
json.loads(result.raw_data)
|
||||
108
tools/adb/systrace/catapult/systrace/profile_chrome/profiler.py
Normal file
108
tools/adb/systrace/catapult/systrace/profile_chrome/profiler.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# Copyright 2014 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.
|
||||
|
||||
import time
|
||||
|
||||
from profile_chrome import chrome_startup_tracing_agent
|
||||
from profile_chrome import chrome_tracing_agent
|
||||
from profile_chrome import ui
|
||||
from profile_chrome import util
|
||||
from systrace import output_generator
|
||||
from systrace import tracing_controller
|
||||
|
||||
|
||||
def _GetResults(trace_results, controller, output, compress, write_json,
|
||||
interval):
|
||||
ui.PrintMessage('Downloading...')
|
||||
|
||||
# Wait for the trace file to get written.
|
||||
time.sleep(1)
|
||||
|
||||
for agent in controller.get_child_agents:
|
||||
if isinstance(agent, chrome_tracing_agent.ChromeTracingAgent):
|
||||
time.sleep(interval / 4)
|
||||
|
||||
# Ignore the systraceController because it will not contain any results,
|
||||
# instead being in charge of collecting results.
|
||||
trace_results = [x for x in controller.all_results if not (x.source_name ==
|
||||
'systraceController')]
|
||||
|
||||
if not trace_results:
|
||||
ui.PrintMessage('No results')
|
||||
return ''
|
||||
|
||||
result = None
|
||||
trace_results = output_generator.MergeTraceResultsIfNeeded(trace_results)
|
||||
if not write_json:
|
||||
ui.PrintMessage('Writing trace HTML...')
|
||||
html_file = output or trace_results[0].source_name + '.html'
|
||||
result = output_generator.GenerateHTMLOutput(trace_results, html_file)
|
||||
ui.PrintMessage('\nWrote file://%s' % result)
|
||||
elif compress and len(trace_results) == 1:
|
||||
result = output or trace_results[0].source_name + '.gz'
|
||||
util.WriteDataToCompressedFile(trace_results[0].raw_data, result)
|
||||
elif len(trace_results) > 1:
|
||||
result = (output or 'chrome-combined-trace-%s.zip' %
|
||||
util.GetTraceTimestamp())
|
||||
util.ArchiveData(trace_results, result)
|
||||
elif output:
|
||||
result = output
|
||||
with open(result, 'wb') as f:
|
||||
f.write(trace_results[0].raw_data)
|
||||
else:
|
||||
result = trace_results[0].source_name
|
||||
with open(result, 'wb') as f:
|
||||
f.write(trace_results[0].raw_data)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def CaptureProfile(options, interval, modules, output=None,
|
||||
compress=False, write_json=False):
|
||||
"""Records a profiling trace saves the result to a file.
|
||||
|
||||
Args:
|
||||
options: Command line options.
|
||||
interval: Time interval to capture in seconds. An interval of None (or 0)
|
||||
continues tracing until stopped by the user.
|
||||
modules: The list of modules to initialize the tracing controller with.
|
||||
output: Output file name or None to use an automatically generated name.
|
||||
compress: If True, the result will be compressed either with gzip or zip
|
||||
depending on the number of captured subtraces.
|
||||
write_json: If True, prefer JSON output over HTML.
|
||||
|
||||
Returns:
|
||||
Path to saved profile.
|
||||
"""
|
||||
agents_with_config = tracing_controller.CreateAgentsWithConfig(options,
|
||||
modules)
|
||||
if chrome_startup_tracing_agent in modules:
|
||||
controller_config = tracing_controller.GetChromeStartupControllerConfig(
|
||||
options)
|
||||
else:
|
||||
controller_config = tracing_controller.GetControllerConfig(options)
|
||||
controller = tracing_controller.TracingController(agents_with_config,
|
||||
controller_config)
|
||||
try:
|
||||
result = controller.StartTracing()
|
||||
trace_type = controller.GetTraceType()
|
||||
if not result:
|
||||
ui.PrintMessage('Trace starting failed.')
|
||||
if interval:
|
||||
ui.PrintMessage(('Capturing %d-second %s. Press Enter to stop early...' %
|
||||
(interval, trace_type)), eol='')
|
||||
ui.WaitForEnter(interval)
|
||||
else:
|
||||
ui.PrintMessage('Capturing %s. Press Enter to stop...' % trace_type,
|
||||
eol='')
|
||||
raw_input()
|
||||
|
||||
ui.PrintMessage('Stopping...')
|
||||
all_results = controller.StopTracing()
|
||||
finally:
|
||||
if interval:
|
||||
ui.PrintMessage('done')
|
||||
|
||||
return _GetResults(all_results, controller, output, compress, write_json,
|
||||
interval)
|
||||
@@ -0,0 +1,58 @@
|
||||
# Copyright 2014 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.
|
||||
|
||||
import os
|
||||
import unittest
|
||||
import zipfile
|
||||
|
||||
from profile_chrome import profiler
|
||||
from profile_chrome import ui
|
||||
from profile_chrome import fake_agent_1
|
||||
from profile_chrome import fake_agent_2
|
||||
from systrace import decorators
|
||||
from systrace import tracing_controller
|
||||
|
||||
|
||||
class ProfilerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
ui.EnableTestMode()
|
||||
self._tracing_options = tracing_controller.TracingControllerConfig(None,
|
||||
None, None, None, None, None, None, None, None, None)
|
||||
|
||||
@decorators.ClientOnlyTest
|
||||
def testCaptureBasicProfile(self):
|
||||
result = profiler.CaptureProfile(self._tracing_options, 1, [fake_agent_1])
|
||||
|
||||
try:
|
||||
self.assertTrue(os.path.exists(result))
|
||||
self.assertTrue(result.endswith('.html'))
|
||||
finally:
|
||||
if os.path.exists(result):
|
||||
os.remove(result)
|
||||
|
||||
@decorators.ClientOnlyTest
|
||||
def testCaptureJsonProfile(self):
|
||||
result = profiler.CaptureProfile(self._tracing_options, 1,
|
||||
[fake_agent_2], write_json=True)
|
||||
|
||||
try:
|
||||
self.assertFalse(result.endswith('.html'))
|
||||
with open(result) as f:
|
||||
self.assertEquals(f.read(), 'fake-contents')
|
||||
finally:
|
||||
if os.path.exists(result):
|
||||
os.remove(result)
|
||||
|
||||
@decorators.ClientOnlyTest
|
||||
def testCaptureMultipleProfiles(self):
|
||||
result = profiler.CaptureProfile(self._tracing_options, 1,
|
||||
[fake_agent_1, fake_agent_2],
|
||||
write_json=True)
|
||||
|
||||
try:
|
||||
self.assertTrue(result.endswith('.zip'))
|
||||
self.assertTrue(zipfile.is_zipfile(result))
|
||||
finally:
|
||||
if os.path.exists(result):
|
||||
os.remove(result)
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd $(dirname $0)/../
|
||||
exec python -m unittest discover profile_chrome '*_unittest.py' $@
|
||||
339
tools/adb/systrace/catapult/systrace/profile_chrome/third_party/COPYING
vendored
Normal file
339
tools/adb/systrace/catapult/systrace/profile_chrome/third_party/COPYING
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
15
tools/adb/systrace/catapult/systrace/profile_chrome/third_party/README.chromium
vendored
Normal file
15
tools/adb/systrace/catapult/systrace/profile_chrome/third_party/README.chromium
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
Name: Perf to JSON conversion script
|
||||
Short Name: perf_to_json
|
||||
URL: http://www.chromium.org
|
||||
Version: 0
|
||||
Date: 21.7.2014
|
||||
Revision: 0
|
||||
License: GPL
|
||||
License File: NOT_SHIPPED
|
||||
Security Critical: No
|
||||
|
||||
Description:
|
||||
Script for converting perf script events into tracing JSON.
|
||||
|
||||
Local Modifications:
|
||||
None.
|
||||
248
tools/adb/systrace/catapult/systrace/profile_chrome/third_party/perf_to_tracing.py
vendored
Normal file
248
tools/adb/systrace/catapult/systrace/profile_chrome/third_party/perf_to_tracing.py
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
# Script for converting perf script events into tracing JSON.
|
||||
#
|
||||
# Generated by perf script -g python
|
||||
# Licensed under the terms of the GNU GPL License version 2
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
from collections import deque
|
||||
|
||||
|
||||
# Categorize DSOs by component.
|
||||
dso_to_comp = {
|
||||
'libdvm.so': 'Java',
|
||||
'libart.so': 'Java',
|
||||
'libjavacore.so': 'Java',
|
||||
'libandroid_runtime.so': 'Android',
|
||||
'libgui.so': 'Android',
|
||||
'libui.so': 'Android',
|
||||
'libbinder.so': 'Android',
|
||||
'libmemalloc.so': 'Android',
|
||||
'libcrypto.so': 'Android',
|
||||
'libcutils.so':'Android',
|
||||
'libutils.so': 'Android',
|
||||
'[kernel.kallsyms]': 'Kernel',
|
||||
'libc.so': 'Standard Lib',
|
||||
'libstdc++.so': 'Standard Lib',
|
||||
'libm.so':'Standard Lib',
|
||||
'libGLESv2_adreno.so': 'GPU Driver',
|
||||
'libGLESv2_adreno200.so': 'GPU Driver',
|
||||
'libq3dtools_adreno200.so': 'GPU Driver',
|
||||
'libEGL_adreno.so': 'GPU Driver',
|
||||
'libEGL_adreno200.so': 'GPU Driver',
|
||||
'libEGL.so': 'GPU Driver',
|
||||
'libgsl.so': 'GPU Driver',
|
||||
'libGLESv2.so': 'GPU Driver',
|
||||
'libsc-a3xx.so': 'GPU Driver',
|
||||
'libadreno_utils.so': 'GPU Driver',
|
||||
'eglsubAndroid.so': 'GPU Driver',
|
||||
'gralloc.msm8960.so': 'GPU Driver',
|
||||
'libadreno_utils': 'GPU Driver',
|
||||
'libGLES_mali.so': 'GPU Driver',
|
||||
'libchromeview.so': 'Chrome',
|
||||
'[unknown]': '<unknown>',
|
||||
'[UNKNOWN]': '<unknown>',
|
||||
}
|
||||
|
||||
|
||||
def FilterSymbolModule(module):
|
||||
m = dso_to_comp.get(module, None)
|
||||
if m:
|
||||
return m
|
||||
if module.find('libchrome.') == 0:
|
||||
return 'Chrome'
|
||||
if module.find('dalvik') >= 0 or module.find('@') >= 0:
|
||||
return 'Java'
|
||||
return module
|
||||
|
||||
|
||||
def FilterSymbolName(module, orign_module, name):
|
||||
if module == 'Java':
|
||||
return name
|
||||
elif module == 'GPU Driver':
|
||||
return name
|
||||
if name == '':
|
||||
return orign_module + ':unknown'
|
||||
if name[0].isdigit() or name == '(nil)':
|
||||
return orign_module + ':unknown'
|
||||
return name
|
||||
|
||||
|
||||
class StackFrameNode:
|
||||
def __init__(self, stack_id, name, category):
|
||||
self.stack_id = stack_id
|
||||
self.parent_id = 0
|
||||
self.children = {}
|
||||
self.category = category
|
||||
self.name = name
|
||||
self.samples = []
|
||||
self.total_weight = 0.0
|
||||
self.have_total_weight = False
|
||||
self.parent = None
|
||||
|
||||
def ToDict(self, out_dict):
|
||||
if self.stack_id:
|
||||
node_dict = {}
|
||||
node_dict['name'] = self.name
|
||||
node_dict['category'] = self.category
|
||||
if self.parent_id:
|
||||
node_dict['parent'] = self.parent_id
|
||||
|
||||
out_dict[self.stack_id] = node_dict
|
||||
|
||||
for child in self.children.values():
|
||||
child.ToDict(out_dict)
|
||||
return out_dict
|
||||
|
||||
def GetTotalWeight(self):
|
||||
if self.have_total_weight:
|
||||
return self.total_weight
|
||||
else:
|
||||
# Sum up self samples weight, and children's total weights.
|
||||
for s in self.samples:
|
||||
self.total_weight += s.weight
|
||||
for c in self.children.values():
|
||||
self.total_weight += c.GetTotalWeight()
|
||||
self.have_total_weight = True
|
||||
return self.total_weight
|
||||
|
||||
|
||||
class PerfSample:
|
||||
def __init__(self, stack_id, ts, cpu, tid, weight, samp_type, comm):
|
||||
self.stack_id = stack_id
|
||||
self.ts = ts
|
||||
self.cpu = cpu
|
||||
self.tid = tid
|
||||
self.weight = weight
|
||||
self.type = samp_type
|
||||
self.comm = comm
|
||||
|
||||
def ToDict(self):
|
||||
ret = {}
|
||||
ret['ts'] = self.ts / 1000.0 # Timestamp in microseconds
|
||||
ret['tid'] = self.tid # Thread id
|
||||
ret['cpu'] = self.cpu # Sampled CPU
|
||||
ret['weight'] = self.weight # Sample weight
|
||||
ret['name'] = self.type # Sample type
|
||||
ret['comm'] = self.comm # Sample type
|
||||
assert self.stack_id != 0
|
||||
if self.stack_id:
|
||||
ret['sf'] = self.stack_id # Stack frame id
|
||||
return ret
|
||||
|
||||
|
||||
samples = []
|
||||
root_chain = StackFrameNode(0, 'root', '[unknown]')
|
||||
next_stack_id = 1
|
||||
tot_period = 0
|
||||
saved_period = 0
|
||||
|
||||
|
||||
def process_event(param_dict):
|
||||
global next_stack_id
|
||||
global saved_period
|
||||
global tot_period
|
||||
|
||||
samp_comm = param_dict['comm']
|
||||
samp_tid = param_dict['tid']
|
||||
samp_cpu = param_dict['cpu']
|
||||
samp_ts = param_dict['time']
|
||||
samp_period = param_dict['period']
|
||||
samp_type = param_dict['ev_name']
|
||||
tot_period += samp_period
|
||||
|
||||
# Parse call chain.
|
||||
seen_syms = set()
|
||||
chain = deque()
|
||||
for cs in param_dict['cs']:
|
||||
cs_name = cs[0]
|
||||
cs_dso = os.path.basename(cs[1])
|
||||
cs_category = FilterSymbolModule(cs_dso)
|
||||
cs_name = FilterSymbolName(cs_category, cs_dso, cs_name)
|
||||
|
||||
if cs_category != '<unknown>' or len(chain) == 0:
|
||||
sym = (cs_name, cs_category)
|
||||
if sym in seen_syms:
|
||||
while chain[0] != sym:
|
||||
seen_syms.remove(chain[0])
|
||||
chain.popleft()
|
||||
else:
|
||||
seen_syms.add(sym)
|
||||
chain.appendleft(sym)
|
||||
|
||||
# Discard garbage stacktrace before __pthread_start()
|
||||
if cs_name == '__pthread_start(void*)':
|
||||
break
|
||||
|
||||
# Done reading call chain. Add to stack frame tree.
|
||||
stack_frame = root_chain
|
||||
for call in chain:
|
||||
if call in stack_frame.children:
|
||||
stack_frame = stack_frame.children[call]
|
||||
else:
|
||||
new_node = StackFrameNode(next_stack_id, call[0], call[1])
|
||||
next_stack_id += 1
|
||||
new_node.parent_id = stack_frame.stack_id
|
||||
stack_frame.children[call] = new_node
|
||||
stack_frame = new_node
|
||||
|
||||
# Save sample.
|
||||
sample = PerfSample(stack_frame.stack_id,
|
||||
samp_ts,
|
||||
samp_cpu,
|
||||
samp_tid,
|
||||
samp_period,
|
||||
samp_type,
|
||||
samp_comm)
|
||||
samples.append(sample)
|
||||
stack_frame.samples.append(sample)
|
||||
saved_period += samp_period
|
||||
|
||||
|
||||
def trace_begin():
|
||||
pass
|
||||
|
||||
|
||||
def trace_end():
|
||||
# Return siblings of a call tree node.
|
||||
def GetNodeSiblings(node):
|
||||
if not node:
|
||||
return []
|
||||
if not node.parent:
|
||||
return []
|
||||
return node.parent.children.values()
|
||||
|
||||
# Try to reduce misplaced stack leaves by moving them up into sibling nodes.
|
||||
def FixCallTree(node, parent):
|
||||
# Get siblings of node's parent.
|
||||
node.parent = parent
|
||||
parent_siblings = GetNodeSiblings(parent)
|
||||
|
||||
# If parent's sibling has same node name, has no children and small weight,
|
||||
# transplant sibling's samples into the current node.
|
||||
for sibling in parent_siblings:
|
||||
if sibling.name == node.name and \
|
||||
len(sibling.children) == 0 and \
|
||||
sibling.GetTotalWeight() <= node.GetTotalWeight() * 0.15:
|
||||
|
||||
# Transplant samples from sibling to current node.
|
||||
for samp in sibling.samples:
|
||||
samp.stack_id = node.stack_id
|
||||
node.samples.append(samp)
|
||||
sibling.samples = []
|
||||
break
|
||||
|
||||
# Recurse child nodes.
|
||||
for c in node.children.values():
|
||||
FixCallTree(c, node)
|
||||
|
||||
FixCallTree(root_chain, None)
|
||||
|
||||
trace_dict = {}
|
||||
trace_dict['samples'] = [s.ToDict() for s in samples]
|
||||
trace_dict['stackFrames'] = root_chain.ToDict({})
|
||||
trace_dict['traceEvents'] = []
|
||||
|
||||
json.dump(trace_dict, sys.stdout, indent=1)
|
||||
27
tools/adb/systrace/catapult/systrace/profile_chrome/ui.py
Normal file
27
tools/adb/systrace/catapult/systrace/profile_chrome/ui.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copyright 2014 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.
|
||||
|
||||
import logging
|
||||
import select
|
||||
import sys
|
||||
|
||||
|
||||
def PrintMessage(heading, eol='\n'):
|
||||
sys.stdout.write('%s%s' % (heading, eol))
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def WaitForEnter(timeout):
|
||||
select.select([sys.stdin], [], [], timeout)
|
||||
|
||||
|
||||
def EnableTestMode():
|
||||
def NoOp(*_, **__): # pylint: disable=unused-argument
|
||||
pass
|
||||
# pylint: disable=W0601
|
||||
global PrintMessage
|
||||
global WaitForEnter
|
||||
PrintMessage = NoOp
|
||||
WaitForEnter = NoOp
|
||||
logging.getLogger().disabled = True
|
||||
35
tools/adb/systrace/catapult/systrace/profile_chrome/util.py
Normal file
35
tools/adb/systrace/catapult/systrace/profile_chrome/util.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# Copyright 2014 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.
|
||||
|
||||
import gzip
|
||||
import os
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
|
||||
def ArchiveFiles(host_files, output):
|
||||
with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as z:
|
||||
for host_file in host_files:
|
||||
z.write(host_file)
|
||||
os.unlink(host_file)
|
||||
|
||||
def CompressFile(host_file, output):
|
||||
with gzip.open(output, 'wb') as out, open(host_file, 'rb') as input_file:
|
||||
out.write(input_file.read())
|
||||
os.unlink(host_file)
|
||||
|
||||
def ArchiveData(trace_results, output):
|
||||
with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as z:
|
||||
for result in trace_results:
|
||||
trace_file = result.source_name + GetTraceTimestamp()
|
||||
WriteDataToCompressedFile(result.raw_data, trace_file)
|
||||
z.write(trace_file)
|
||||
os.unlink(trace_file)
|
||||
|
||||
def WriteDataToCompressedFile(data, output):
|
||||
with gzip.open(output, 'wb') as out:
|
||||
out.write(data)
|
||||
|
||||
def GetTraceTimestamp():
|
||||
return time.strftime('%Y-%m-%d-%H%M%S', time.localtime())
|
||||
67
tools/adb/systrace/catapult/systrace/pylintrc
Normal file
67
tools/adb/systrace/catapult/systrace/pylintrc
Normal file
@@ -0,0 +1,67 @@
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s).
|
||||
# TODO: Shrink this list to as small as possible.
|
||||
disable=
|
||||
design,
|
||||
similarities,
|
||||
|
||||
abstract-class-not-used,
|
||||
bad-builtin,
|
||||
bad-continuation,
|
||||
eval-used,
|
||||
fixme,
|
||||
invalid-name,
|
||||
locally-disabled,
|
||||
missing-docstring,
|
||||
protected-access,
|
||||
star-args,
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Don't write out full reports, just messages.
|
||||
reports=no
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Regular expression which should only match correct function names.
|
||||
function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*))$
|
||||
|
||||
# Regular expression which should only match correct method names.
|
||||
method-rgx=^(?:(?P<exempt>_[a-z0-9_]+__|get|post|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass)|(?P<camel_case>(_{0,2}|test|assert)[A-Z][a-zA-Z0-9_]*))$
|
||||
|
||||
# Regular expression which should only match correct argument names.
|
||||
argument-rgx=^[a-z][a-z0-9_]*$
|
||||
|
||||
# Regular expression which should only match correct variable names.
|
||||
variable-rgx=^[a-z][a-z0-9_]*$
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma.
|
||||
good-names=main,_
|
||||
|
||||
# List of builtins function names that should not be used, separated by a comma.
|
||||
bad-functions=apply,input,reduce
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# Tells wether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# A regular expression matching names used for dummy variables (i.e. not used).
|
||||
dummy-variables-rgx=^\*{0,2}(_$|unused_)
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# Tells wether missing members accessed in mixin class should be ignored. A
|
||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||
ignore-mixin-members=yes
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# We use two spaces for indents, instead of the usual four spaces or tab.
|
||||
indent-string=' '
|
||||
12
tools/adb/systrace/catapult/systrace/systrace/AUTHORS
Normal file
12
tools/adb/systrace/catapult/systrace/systrace/AUTHORS
Normal file
@@ -0,0 +1,12 @@
|
||||
# Names should be added to this file with this pattern:
|
||||
#
|
||||
# For individuals:
|
||||
# Name <email address>
|
||||
#
|
||||
# For organizations:
|
||||
# Organization <fnmatch pattern>
|
||||
#
|
||||
# See python fnmatch module documentation for more information.
|
||||
|
||||
The Chromium Authors <*@chromium.org>
|
||||
Google Inc. <*@google.com>
|
||||
27
tools/adb/systrace/catapult/systrace/systrace/LICENSE
Normal file
27
tools/adb/systrace/catapult/systrace/systrace/LICENSE
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
16
tools/adb/systrace/catapult/systrace/systrace/README.md
Normal file
16
tools/adb/systrace/catapult/systrace/systrace/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
<!-- Copyright 2015 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.
|
||||
-->
|
||||
Systrace
|
||||
========
|
||||
|
||||
Systrace relies on
|
||||
[Trace-Viewer](https://github.com/catapult-project/catapult/blob/master/tracing/README.md)
|
||||
to visualize the traces. The development of Trace-Viewer and Systrace is
|
||||
decoupled by the systrace_trace_viewer.html file.
|
||||
* The update_systrace_trace_viewer.py script generates
|
||||
systrace_trace_viewer.html based on the Trace-Viewer code.
|
||||
* Systrace visualizes the trace result based on systrace_trace_viewer.html.
|
||||
* Systrace will auto update systrace_trace_viewer.html if
|
||||
update_systrace_trace_viewer.py exists.
|
||||
27
tools/adb/systrace/catapult/systrace/systrace/__init__.py
Normal file
27
tools/adb/systrace/catapult/systrace/systrace/__init__.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copyright 2016 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.
|
||||
import os
|
||||
import sys
|
||||
|
||||
def _JoinPath(*path_parts):
|
||||
return os.path.abspath(os.path.join(*path_parts))
|
||||
|
||||
|
||||
def _AddDirToPythonPath(*path_parts):
|
||||
path = _JoinPath(*path_parts)
|
||||
if os.path.isdir(path) and path not in sys.path:
|
||||
# Some call sites that use Telemetry assume that sys.path[0] is the
|
||||
# directory containing the script, so we add these extra paths to right
|
||||
# after sys.path[0].
|
||||
sys.path.insert(1, path)
|
||||
|
||||
_CATAPULT_DIR = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), os.path.pardir, os.path.pardir)
|
||||
|
||||
_AddDirToPythonPath(_CATAPULT_DIR, 'common', 'py_utils')
|
||||
_AddDirToPythonPath(_CATAPULT_DIR, 'common', 'py_trace_event')
|
||||
_AddDirToPythonPath(_CATAPULT_DIR, 'common', 'py_trace_event', 'py_trace_event')
|
||||
_AddDirToPythonPath(_CATAPULT_DIR, 'devil')
|
||||
_AddDirToPythonPath(_CATAPULT_DIR, 'systrace')
|
||||
_AddDirToPythonPath(_CATAPULT_DIR, 'tracing')
|
||||
41
tools/adb/systrace/catapult/systrace/systrace/decorators.py
Normal file
41
tools/adb/systrace/catapult/systrace/systrace/decorators.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# Copyright 2016 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.
|
||||
|
||||
|
||||
def HostOnlyTest(func):
|
||||
"""Decorator for running unit tests only on the host device.
|
||||
|
||||
This will disable unit tests from running on Android devices.
|
||||
"""
|
||||
return _SkipTestDecoratorHelper(func, ['android'])
|
||||
|
||||
|
||||
def ClientOnlyTest(func):
|
||||
"""Decorator for running unit tests only on client devices (Android).
|
||||
"""
|
||||
return _SkipTestDecoratorHelper(func, ['win', 'linux', 'mac'])
|
||||
|
||||
|
||||
def Disabled(func):
|
||||
"""Decorator for not running a unit test on any Trybot platform.
|
||||
"""
|
||||
return _SkipTestDecoratorHelper(func, ['win', 'linux', 'mac', 'android'])
|
||||
|
||||
|
||||
def LinuxMacTest(func):
|
||||
return _SkipTestDecoratorHelper(func, ['win', 'android'])
|
||||
|
||||
|
||||
def _SkipTestDecoratorHelper(func, disabled_strings):
|
||||
if not hasattr(func, '_disabled_strings'):
|
||||
setattr(func, '_disabled_strings', set(disabled_strings))
|
||||
return func
|
||||
|
||||
|
||||
def ShouldSkip(test, device):
|
||||
"""Returns whether the test should be skipped and the reason for it."""
|
||||
if hasattr(test, '_disabled_strings'):
|
||||
disabled_devices = getattr(test, '_disabled_strings')
|
||||
return device in disabled_devices
|
||||
return False
|
||||
@@ -0,0 +1,52 @@
|
||||
# Copyright 2016 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.
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from systrace import decorators
|
||||
from systrace import update_systrace_trace_viewer
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
STABLE_VIEWER_PATH = os.path.join(SCRIPT_DIR, 'systrace_trace_viewer.html')
|
||||
|
||||
# Tests presence and content of static HTML files used not only for Python
|
||||
# systrace capture, but also Java-based capture in the android SDK tools.
|
||||
#
|
||||
# NOTE: changes to this file should typically be accompanied by changes to the
|
||||
# Android SDK's method of systrace capture.
|
||||
class MonitorTest(unittest.TestCase):
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_systrace_trace_viewer(self):
|
||||
self.assertEqual(STABLE_VIEWER_PATH,
|
||||
update_systrace_trace_viewer.SYSTRACE_TRACE_VIEWER_HTML_FILE)
|
||||
|
||||
update_systrace_trace_viewer.update(force_update=True)
|
||||
|
||||
with open(STABLE_VIEWER_PATH) as f:
|
||||
content = f.read().strip()
|
||||
|
||||
# expect big html file
|
||||
self.assertGreater(5 * 1024 * 1024, len(content))
|
||||
self.assertEqual('<', content[0])
|
||||
os.remove(f.name)
|
||||
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_prefix(self):
|
||||
with open(os.path.join(SCRIPT_DIR, 'prefix.html')) as f:
|
||||
content = f.read().strip()
|
||||
|
||||
self.assertTrue("<html>" in content)
|
||||
self.assertTrue("<title>Android System Trace</title>" in content)
|
||||
self.assertTrue("{{SYSTRACE_TRACE_VIEWER_HTML}}" in content)
|
||||
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_suffix(self):
|
||||
with open(os.path.join(SCRIPT_DIR, 'suffix.html')) as f:
|
||||
content = f.read().strip()
|
||||
|
||||
self.assertTrue("</html>" in content)
|
||||
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2016 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.
|
||||
|
||||
import base64
|
||||
import gzip
|
||||
import json
|
||||
import os
|
||||
import StringIO
|
||||
|
||||
from systrace import tracing_controller
|
||||
from systrace import trace_result
|
||||
from tracing.trace_data import trace_data
|
||||
|
||||
|
||||
# TODO(alexandermont): Current version of trace viewer does not support
|
||||
# the controller tracing agent output. Thus we use this variable to
|
||||
# suppress this tracing agent's output. This should be removed once
|
||||
# trace viewer is working again.
|
||||
OUTPUT_CONTROLLER_TRACE_ = False
|
||||
CONTROLLER_TRACE_DATA_KEY = 'controllerTraceDataKey'
|
||||
_SYSTRACE_TO_TRACE_DATA_NAME_MAPPING = {
|
||||
'androidProcessDump': trace_data.ANDROID_PROCESS_DATA_PART,
|
||||
'atraceProcessDump': trace_data.ATRACE_PROCESS_DUMP_PART,
|
||||
'systemTraceEvents': trace_data.ATRACE_PART,
|
||||
'systraceController': trace_data.TELEMETRY_PART,
|
||||
'traceEvents': trace_data.CHROME_TRACE_PART,
|
||||
'waltTrace': trace_data.WALT_TRACE_PART,
|
||||
}
|
||||
_SYSTRACE_HEADER = 'Systrace'
|
||||
|
||||
|
||||
def NewGenerateHTMLOutput(trace_results, output_file_name):
|
||||
with trace_data.TraceDataBuilder() as builder:
|
||||
for trace in trace_results:
|
||||
trace_data_part = _SYSTRACE_TO_TRACE_DATA_NAME_MAPPING.get(
|
||||
trace.source_name)
|
||||
builder.AddTraceFor(
|
||||
trace_data_part, trace.raw_data, allow_unstructured=True)
|
||||
builder.Serialize(output_file_name, _SYSTRACE_HEADER)
|
||||
|
||||
|
||||
def GenerateHTMLOutput(trace_results, output_file_name):
|
||||
"""Write the results of systrace to an HTML file.
|
||||
|
||||
Args:
|
||||
trace_results: A list of TraceResults.
|
||||
output_file_name: The name of the HTML file that the trace viewer
|
||||
results should be written to.
|
||||
"""
|
||||
def _ReadAsset(src_dir, filename):
|
||||
return open(os.path.join(src_dir, filename)).read()
|
||||
|
||||
# TODO(rnephew): The tracing output formatter is able to handle a single
|
||||
# systrace trace just as well as it handles multiple traces. The obvious thing
|
||||
# to do here would be to use it all for all systrace output: however, we want
|
||||
# to continue using the legacy way of formatting systrace output when a single
|
||||
# systrace and the tracing controller trace are present in order to match the
|
||||
# Java verison of systrace. Java systrace is expected to be deleted at a later
|
||||
# date. We should consolidate this logic when that happens.
|
||||
|
||||
if len(trace_results) > 3:
|
||||
NewGenerateHTMLOutput(trace_results, output_file_name)
|
||||
return os.path.abspath(output_file_name)
|
||||
|
||||
systrace_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
try:
|
||||
from systrace import update_systrace_trace_viewer
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
update_systrace_trace_viewer.update()
|
||||
|
||||
trace_viewer_html = _ReadAsset(systrace_dir, 'systrace_trace_viewer.html')
|
||||
|
||||
# Open the file in binary mode to prevent python from changing the
|
||||
# line endings, then write the prefix.
|
||||
systrace_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
html_prefix = _ReadAsset(systrace_dir, 'prefix.html')
|
||||
html_suffix = _ReadAsset(systrace_dir, 'suffix.html')
|
||||
trace_viewer_html = _ReadAsset(systrace_dir,
|
||||
'systrace_trace_viewer.html')
|
||||
|
||||
# Open the file in binary mode to prevent python from changing the
|
||||
# line endings, then write the prefix.
|
||||
html_file = open(output_file_name, 'wb')
|
||||
html_file.write(html_prefix.replace('{{SYSTRACE_TRACE_VIEWER_HTML}}',
|
||||
trace_viewer_html))
|
||||
|
||||
# Write the trace data itself. There is a separate section of the form
|
||||
# <script class="trace-data" type="application/text"> ... </script>
|
||||
# for each tracing agent (including the controller tracing agent).
|
||||
html_file.write('<!-- BEGIN TRACE -->\n')
|
||||
for result in trace_results:
|
||||
html_file.write(' <script class="trace-data" type="application/text">\n')
|
||||
html_file.write(_ConvertToHtmlString(result.raw_data))
|
||||
html_file.write(' </script>\n')
|
||||
html_file.write('<!-- END TRACE -->\n')
|
||||
|
||||
# Write the suffix and finish.
|
||||
html_file.write(html_suffix)
|
||||
html_file.close()
|
||||
|
||||
final_path = os.path.abspath(output_file_name)
|
||||
return final_path
|
||||
|
||||
def _ConvertToHtmlString(result):
|
||||
"""Convert a trace result to the format to be output into HTML.
|
||||
|
||||
If the trace result is a dictionary or list, JSON-encode it.
|
||||
If the trace result is a string, leave it unchanged.
|
||||
"""
|
||||
if isinstance(result, dict) or isinstance(result, list):
|
||||
return json.dumps(result)
|
||||
elif isinstance(result, str):
|
||||
return result
|
||||
else:
|
||||
raise ValueError('Invalid trace result format for HTML output')
|
||||
|
||||
def GenerateJSONOutput(trace_results, output_file_name):
|
||||
"""Write the results of systrace to a JSON file.
|
||||
|
||||
Args:
|
||||
trace_results: A list of TraceResults.
|
||||
output_file_name: The name of the JSON file that the trace viewer
|
||||
results should be written to.
|
||||
"""
|
||||
results = _ConvertTraceListToDictionary(trace_results)
|
||||
results[CONTROLLER_TRACE_DATA_KEY] = (
|
||||
tracing_controller.TRACE_DATA_CONTROLLER_NAME)
|
||||
with open(output_file_name, 'w') as json_file:
|
||||
json.dump(results, json_file)
|
||||
final_path = os.path.abspath(output_file_name)
|
||||
return final_path
|
||||
|
||||
def MergeTraceResultsIfNeeded(trace_results):
|
||||
"""Merge a list of trace data, if possible. This function can take any list
|
||||
of trace data, but it will only merge the JSON data (since that's all
|
||||
we can merge).
|
||||
|
||||
Args:
|
||||
trace_results: A list of TraceResults containing trace data.
|
||||
"""
|
||||
if len(trace_results) <= 1:
|
||||
return trace_results
|
||||
merge_candidates = []
|
||||
for result in trace_results:
|
||||
# Try to detect a JSON file cheaply since that's all we can merge.
|
||||
if result.raw_data[0] != '{':
|
||||
continue
|
||||
try:
|
||||
json_data = json.loads(result.raw_data)
|
||||
except ValueError:
|
||||
continue
|
||||
merge_candidates.append(trace_result.TraceResult(result.source_name,
|
||||
json_data))
|
||||
|
||||
if len(merge_candidates) <= 1:
|
||||
return trace_results
|
||||
|
||||
other_results = [r for r in trace_results
|
||||
if not r.source_name in
|
||||
[c.source_name for c in merge_candidates]]
|
||||
|
||||
merged_data = merge_candidates[0].raw_data
|
||||
|
||||
for candidate in merge_candidates[1:]:
|
||||
json_data = candidate.raw_data
|
||||
for key, value in json_data.items():
|
||||
if not str(key) in merged_data or not merged_data[str(key)]:
|
||||
merged_data[str(key)] = value
|
||||
|
||||
return ([trace_result.TraceResult('merged-data', json.dumps(merged_data))]
|
||||
+ other_results)
|
||||
|
||||
def _EncodeTraceData(trace_string):
|
||||
compressed_trace = StringIO.StringIO()
|
||||
with gzip.GzipFile(fileobj=compressed_trace, mode='w') as f:
|
||||
f.write(trace_string)
|
||||
b64_content = base64.b64encode(compressed_trace.getvalue())
|
||||
return b64_content
|
||||
|
||||
def _ConvertTraceListToDictionary(trace_list):
|
||||
trace_dict = {}
|
||||
for trace in trace_list:
|
||||
trace_dict[trace.source_name] = trace.raw_data
|
||||
return trace_dict
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2016 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.
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from py_utils import tempfile_ext
|
||||
from systrace import decorators
|
||||
from systrace import output_generator
|
||||
from systrace import trace_result
|
||||
from systrace import update_systrace_trace_viewer
|
||||
from tracing.trace_data import trace_data as trace_data_module
|
||||
|
||||
|
||||
TEST_DIR = os.path.join(os.path.dirname(__file__), 'test_data')
|
||||
ATRACE_DATA = os.path.join(TEST_DIR, 'atrace_data')
|
||||
ATRACE_PROCESS_DUMP_DATA = os.path.join(TEST_DIR, 'atrace_procfs_dump')
|
||||
COMBINED_PROFILE_CHROME_DATA = os.path.join(
|
||||
TEST_DIR, 'profile-chrome_systrace_perf_chrome_data')
|
||||
|
||||
|
||||
class OutputGeneratorTest(unittest.TestCase):
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def testJsonTraceMerging(self):
|
||||
update_systrace_trace_viewer.update(force_update=True)
|
||||
self.assertTrue(os.path.exists(
|
||||
update_systrace_trace_viewer.SYSTRACE_TRACE_VIEWER_HTML_FILE))
|
||||
t1 = "{'traceEvents': [{'ts': 123, 'ph': 'b'}]}"
|
||||
t2 = "{'traceEvents': [], 'stackFrames': ['blah']}"
|
||||
results = [trace_result.TraceResult('a', t1),
|
||||
trace_result.TraceResult('b', t2)]
|
||||
|
||||
merged_results = output_generator.MergeTraceResultsIfNeeded(results)
|
||||
for r in merged_results:
|
||||
if r.source_name == 'a':
|
||||
self.assertEquals(r.raw_data, t1)
|
||||
elif r.source_name == 'b':
|
||||
self.assertEquals(r.raw_data, t2)
|
||||
self.assertEquals(len(merged_results), len(results))
|
||||
os.remove(update_systrace_trace_viewer.SYSTRACE_TRACE_VIEWER_HTML_FILE)
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def testHtmlOutputGenerationFormatsSingleTrace(self):
|
||||
update_systrace_trace_viewer.update(force_update=True)
|
||||
self.assertTrue(os.path.exists(
|
||||
update_systrace_trace_viewer.SYSTRACE_TRACE_VIEWER_HTML_FILE))
|
||||
with open(ATRACE_DATA) as f:
|
||||
atrace_data = f.read().replace(" ", "").strip()
|
||||
trace_results = [trace_result.TraceResult('systemTraceEvents', atrace_data)]
|
||||
with tempfile_ext.TemporaryFileName() as output_file_name:
|
||||
output_generator.GenerateHTMLOutput(trace_results, output_file_name)
|
||||
with open(output_file_name, 'r') as f:
|
||||
html_output = f.read()
|
||||
trace_data = (html_output.split(
|
||||
'<script class="trace-data" type="application/text">')[1].split(
|
||||
'</script>'))[0].replace(" ", "").strip()
|
||||
|
||||
# Ensure the trace data written in HTML is located within the
|
||||
# correct place in the HTML document and that the data is not
|
||||
# malformed.
|
||||
self.assertEquals(trace_data, atrace_data)
|
||||
os.remove(update_systrace_trace_viewer.SYSTRACE_TRACE_VIEWER_HTML_FILE)
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def testHtmlOutputGenerationFormatsMultipleTraces(self):
|
||||
trace_results = []
|
||||
with trace_data_module.TraceDataBuilder() as trace_data_builder:
|
||||
with open(ATRACE_DATA) as fp:
|
||||
atrace_data = fp.read()
|
||||
trace_results.append(
|
||||
trace_result.TraceResult('systemTraceEvents', atrace_data))
|
||||
trace_data_builder.AddTraceFor(trace_data_module.ATRACE_PART, atrace_data,
|
||||
allow_unstructured=True)
|
||||
|
||||
with open(ATRACE_PROCESS_DUMP_DATA) as fp:
|
||||
atrace_process_dump_data = fp.read()
|
||||
trace_results.append(trace_result.TraceResult(
|
||||
'atraceProcessDump', atrace_process_dump_data))
|
||||
trace_data_builder.AddTraceFor(trace_data_module.ATRACE_PROCESS_DUMP_PART,
|
||||
atrace_process_dump_data,
|
||||
allow_unstructured=True)
|
||||
|
||||
with open(COMBINED_PROFILE_CHROME_DATA) as fp:
|
||||
chrome_data = json.load(fp)
|
||||
trace_results.append(
|
||||
trace_result.TraceResult('traceEvents', chrome_data))
|
||||
trace_data_builder.AddTraceFor(
|
||||
trace_data_module.CHROME_TRACE_PART, chrome_data)
|
||||
|
||||
trace_results.append(
|
||||
trace_result.TraceResult('systraceController', str({})))
|
||||
trace_data_builder.AddTraceFor(trace_data_module.TELEMETRY_PART, {})
|
||||
|
||||
with tempfile_ext.NamedTemporaryDirectory() as temp_dir:
|
||||
data_builder_out = os.path.join(temp_dir, 'data_builder.html')
|
||||
output_generator_out = os.path.join(temp_dir, 'output_generator.html')
|
||||
output_generator.GenerateHTMLOutput(trace_results, output_generator_out)
|
||||
trace_data_builder.Serialize(data_builder_out, 'Systrace')
|
||||
|
||||
output_generator_md5sum = hashlib.md5(
|
||||
open(output_generator_out, 'rb').read()).hexdigest()
|
||||
data_builder_md5sum = hashlib.md5(
|
||||
open(data_builder_out, 'rb').read()).hexdigest()
|
||||
|
||||
self.assertEqual(output_generator_md5sum, data_builder_md5sum)
|
||||
70
tools/adb/systrace/catapult/systrace/systrace/prefix.html
Normal file
70
tools/adb/systrace/catapult/systrace/systrace/prefix.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head i18n-values="dir:textdirection;">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta charset="utf-8"/>
|
||||
<title>Android System Trace</title>
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-flex-direction: column;
|
||||
display: -webkit-flex;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body > tr-ui-timeline-view {
|
||||
-webkit-flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
}
|
||||
body > tr-ui-timeline-view:focus {
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
{{SYSTRACE_TRACE_VIEWER_HTML}}
|
||||
</head>
|
||||
<body>
|
||||
<tr-ui-timeline-view>
|
||||
<track-view-container id='track_view_container'></track-view-container>
|
||||
</tr-ui-timeline-view>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
var timelineViewEl;
|
||||
|
||||
function onLoad() {
|
||||
timelineViewEl = document.querySelector('tr-ui-timeline-view');
|
||||
timelineViewEl.globalMode = true;
|
||||
|
||||
var traceDataEls = document.body.querySelectorAll('.trace-data');
|
||||
var traces = [];
|
||||
for (var i = 0; i < traceDataEls.length; i++) {
|
||||
var traceText = traceDataEls[i].textContent;
|
||||
// Remove the leading newline.
|
||||
traceText = traceText.substring(1);
|
||||
traces.push(traceText);
|
||||
}
|
||||
|
||||
var m = new tr.Model();
|
||||
var i = new tr.importer.Import(m);
|
||||
var p = i.importTracesWithProgressDialog(traces);
|
||||
p.then(
|
||||
function() {
|
||||
timelineViewEl.model = m;
|
||||
timelineViewEl.updateDocumentFavicon();
|
||||
timelineViewEl.globalMode = true;
|
||||
timelineViewEl.viewTitle = 'Android System Trace';
|
||||
},
|
||||
function(err) {
|
||||
var overlay = new tr.ui.b.Overlay();
|
||||
overlay.textContent = tr.b.normalizeException(err).message;
|
||||
overlay.title = 'Import error';
|
||||
overlay.visible = true;
|
||||
});
|
||||
}
|
||||
window.addEventListener('load', onLoad);
|
||||
</script>
|
||||
207
tools/adb/systrace/catapult/systrace/systrace/run_systrace.py
Normal file
207
tools/adb/systrace/catapult/systrace/systrace/run_systrace.py
Normal file
@@ -0,0 +1,207 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) 2011 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.
|
||||
|
||||
"""Android system-wide tracing utility.
|
||||
|
||||
This is a tool for capturing a trace that includes data from both userland and
|
||||
the kernel. It creates an HTML file for visualizing the trace.
|
||||
"""
|
||||
|
||||
# Make sure we're using a new enough version of Python.
|
||||
# The flags= parameter of re.sub() is new in Python 2.7. And Systrace does not
|
||||
# support Python 3 yet.
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
import sys
|
||||
|
||||
version = sys.version_info[:2]
|
||||
if version != (2, 7):
|
||||
sys.stderr.write('This script does not support Python %d.%d. '
|
||||
'Please use Python 2.7.\n' % version)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import time
|
||||
|
||||
_SYSTRACE_DIR = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), os.path.pardir))
|
||||
_CATAPULT_DIR = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), os.path.pardir, os.path.pardir)
|
||||
_DEVIL_DIR = os.path.join(_CATAPULT_DIR, 'devil')
|
||||
if _DEVIL_DIR not in sys.path:
|
||||
sys.path.insert(0, _DEVIL_DIR)
|
||||
if _SYSTRACE_DIR not in sys.path:
|
||||
sys.path.insert(0, _SYSTRACE_DIR)
|
||||
|
||||
from devil import devil_env
|
||||
from devil.android.sdk import adb_wrapper
|
||||
from systrace import systrace_runner
|
||||
from systrace import util
|
||||
from systrace.tracing_agents import atrace_agent
|
||||
from systrace.tracing_agents import atrace_from_file_agent
|
||||
from systrace.tracing_agents import atrace_process_dump
|
||||
from systrace.tracing_agents import ftrace_agent
|
||||
from systrace.tracing_agents import walt_agent
|
||||
|
||||
|
||||
ALL_MODULES = [atrace_agent, atrace_from_file_agent, atrace_process_dump,
|
||||
ftrace_agent, walt_agent]
|
||||
|
||||
|
||||
def parse_options(argv):
|
||||
"""Parses and checks the command-line options.
|
||||
|
||||
Returns:
|
||||
A tuple containing the options structure and a list of categories to
|
||||
be traced.
|
||||
"""
|
||||
usage = 'Usage: %prog [options] [category1 [category2 ...]]'
|
||||
desc = 'Example: %prog -b 32768 -t 15 gfx input view sched freq'
|
||||
parser = optparse.OptionParser(usage=usage, description=desc,
|
||||
conflict_handler='resolve')
|
||||
parser = util.get_main_options(parser)
|
||||
|
||||
parser.add_option('-l', '--list-categories', dest='list_categories',
|
||||
default=False, action='store_true',
|
||||
help='list the available categories and exit')
|
||||
|
||||
# Add the other agent parsing options to the parser. For Systrace on the
|
||||
# command line, all agents are added. For Android, only the compatible agents
|
||||
# will be added.
|
||||
for module in ALL_MODULES:
|
||||
option_group = module.add_options(parser)
|
||||
if option_group:
|
||||
parser.add_option_group(option_group)
|
||||
|
||||
options, categories = parser.parse_args(argv[1:])
|
||||
|
||||
if options.output_file is None:
|
||||
base = 'trace'
|
||||
if options.from_file is not None:
|
||||
base = os.path.splitext(options.from_file)[0]
|
||||
suffix = '.json' if options.write_json else '.html'
|
||||
options.output_file = base + suffix
|
||||
|
||||
if options.link_assets or options.asset_dir != 'trace-viewer':
|
||||
parser.error('--link-assets and --asset-dir are deprecated.')
|
||||
|
||||
if options.trace_time and options.trace_time < 0:
|
||||
parser.error('the trace time must be a non-negative number')
|
||||
|
||||
if (options.trace_buf_size is not None) and (options.trace_buf_size <= 0):
|
||||
parser.error('the trace buffer size must be a positive number')
|
||||
|
||||
return (options, categories)
|
||||
|
||||
def find_adb():
|
||||
"""Finds adb on the path.
|
||||
|
||||
This method is provided to avoid the issue of diskutils.spawn's
|
||||
find_executable which first searches the current directory before
|
||||
searching $PATH. That behavior results in issues where systrace.py
|
||||
uses a different adb than the one in the path.
|
||||
"""
|
||||
paths = os.environ['PATH'].split(os.pathsep)
|
||||
executable = 'adb'
|
||||
if sys.platform == 'win32':
|
||||
executable = executable + '.exe'
|
||||
for p in paths:
|
||||
f = os.path.join(p, executable)
|
||||
if os.path.isfile(f):
|
||||
return f
|
||||
return None
|
||||
|
||||
def initialize_devil():
|
||||
"""Initialize devil to use adb from $PATH"""
|
||||
adb_path = find_adb()
|
||||
if adb_path is None:
|
||||
print >> sys.stderr, "Unable to find adb, is it in your path?"
|
||||
sys.exit(1)
|
||||
devil_dynamic_config = {
|
||||
'config_type': 'BaseConfig',
|
||||
'dependencies': {
|
||||
'adb': {
|
||||
'file_info': {
|
||||
devil_env.GetPlatform(): {
|
||||
'local_paths': [os.path.abspath(adb_path)]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
devil_env.config.Initialize(configs=[devil_dynamic_config])
|
||||
|
||||
|
||||
def main_impl(arguments):
|
||||
# Parse the command line options.
|
||||
options, categories = parse_options(arguments)
|
||||
|
||||
# Override --atrace-categories and --ftrace-categories flags if command-line
|
||||
# categories are provided.
|
||||
if categories:
|
||||
if options.target == 'android':
|
||||
options.atrace_categories = categories
|
||||
elif options.target == 'linux':
|
||||
options.ftrace_categories = categories
|
||||
else:
|
||||
raise RuntimeError('Categories are only valid for atrace/ftrace. Target '
|
||||
'platform must be either Android or Linux.')
|
||||
|
||||
# Include atrace categories by default in Systrace.
|
||||
if options.target == 'android' and not options.atrace_categories:
|
||||
options.atrace_categories = atrace_agent.DEFAULT_CATEGORIES
|
||||
|
||||
if options.target == 'android' and not options.from_file:
|
||||
initialize_devil()
|
||||
devices = [a.GetDeviceSerial() for a in adb_wrapper.AdbWrapper.Devices()]
|
||||
if not options.device_serial_number:
|
||||
if len(devices) == 0:
|
||||
raise RuntimeError('No ADB devices connected.')
|
||||
elif len(devices) >= 2:
|
||||
raise RuntimeError('Multiple devices connected, serial number required')
|
||||
options.device_serial_number = devices[0]
|
||||
elif options.device_serial_number not in devices:
|
||||
raise RuntimeError('Device with the serial number "%s" is not connected.'
|
||||
% options.device_serial_number)
|
||||
|
||||
# If list_categories is selected, just print the list of categories.
|
||||
# In this case, use of the tracing controller is not necessary.
|
||||
if options.list_categories:
|
||||
if options.target == 'android':
|
||||
atrace_agent.list_categories(options)
|
||||
elif options.target == 'linux':
|
||||
ftrace_agent.list_categories(options)
|
||||
return
|
||||
|
||||
# Set up the systrace runner and start tracing.
|
||||
controller = systrace_runner.SystraceRunner(
|
||||
os.path.dirname(os.path.abspath(__file__)), options)
|
||||
controller.StartTracing()
|
||||
|
||||
# Wait for the given number of seconds or until the user presses enter.
|
||||
# pylint: disable=superfluous-parens
|
||||
# (need the parens so no syntax error if trying to load with Python 3)
|
||||
if options.from_file is not None:
|
||||
print('Reading results from file.')
|
||||
elif options.trace_time:
|
||||
print('Starting tracing (%d seconds)' % options.trace_time)
|
||||
time.sleep(options.trace_time)
|
||||
else:
|
||||
raw_input('Starting tracing (stop with enter)')
|
||||
|
||||
# Stop tracing and collect the output.
|
||||
print('Tracing completed. Collecting output...')
|
||||
controller.StopTracing()
|
||||
print('Outputting Systrace results...')
|
||||
controller.OutputSystraceResults(write_json=options.write_json)
|
||||
|
||||
def main():
|
||||
main_impl(sys.argv)
|
||||
|
||||
if __name__ == '__main__' and __package__ is None:
|
||||
main()
|
||||
@@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2016 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.
|
||||
|
||||
'''Implementation of tracing controller for systrace. This class creates the
|
||||
necessary tracing agents for systrace, runs them, and outputs the results
|
||||
as an HTML or JSON file.'''
|
||||
|
||||
from systrace import output_generator
|
||||
from systrace import tracing_controller
|
||||
from systrace.tracing_agents import android_process_data_agent
|
||||
from systrace.tracing_agents import atrace_agent
|
||||
from systrace.tracing_agents import atrace_from_file_agent
|
||||
from systrace.tracing_agents import atrace_process_dump
|
||||
from systrace.tracing_agents import ftrace_agent
|
||||
from systrace.tracing_agents import walt_agent
|
||||
|
||||
AGENT_MODULES = [android_process_data_agent, atrace_agent,
|
||||
atrace_from_file_agent, atrace_process_dump,
|
||||
ftrace_agent, walt_agent]
|
||||
|
||||
class SystraceRunner(object):
|
||||
def __init__(self, script_dir, options):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
script_dir: Directory containing the trace viewer script
|
||||
(systrace_trace_viewer.html)
|
||||
options: Object containing command line options.
|
||||
"""
|
||||
# Parse command line arguments and create agents.
|
||||
self._script_dir = script_dir
|
||||
self._out_filename = options.output_file
|
||||
agents_with_config = tracing_controller.CreateAgentsWithConfig(
|
||||
options, AGENT_MODULES)
|
||||
controller_config = tracing_controller.GetControllerConfig(options)
|
||||
|
||||
# Set up tracing controller.
|
||||
self._tracing_controller = tracing_controller.TracingController(
|
||||
agents_with_config, controller_config)
|
||||
|
||||
def StartTracing(self):
|
||||
self._tracing_controller.StartTracing()
|
||||
|
||||
def StopTracing(self):
|
||||
self._tracing_controller.StopTracing()
|
||||
|
||||
def OutputSystraceResults(self, write_json=False):
|
||||
"""Output the results of systrace to a file.
|
||||
|
||||
If output is necessary, then write the results of systrace to either (a)
|
||||
a standalone HTML file, or (b) a json file which can be read by the
|
||||
trace viewer.
|
||||
|
||||
Args:
|
||||
write_json: Whether to output to a json file (if false, use HTML file)
|
||||
"""
|
||||
print 'Tracing complete, writing results'
|
||||
if write_json:
|
||||
result = output_generator.GenerateJSONOutput(
|
||||
self._tracing_controller.all_results,
|
||||
self._out_filename)
|
||||
else:
|
||||
result = output_generator.GenerateHTMLOutput(
|
||||
self._tracing_controller.all_results,
|
||||
self._out_filename)
|
||||
print '\nWrote trace %s file: file://%s\n' % (('JSON' if write_json
|
||||
else 'HTML'), result)
|
||||
10401
tools/adb/systrace/catapult/systrace/systrace/systrace_trace_viewer.html
Normal file
10401
tools/adb/systrace/catapult/systrace/systrace/systrace_trace_viewer.html
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,127 @@
|
||||
# tracer: nop
|
||||
#
|
||||
# entries-in-buffer/entries-written: 116/116 #P:1
|
||||
#
|
||||
# _-----=> irqs-off
|
||||
# / _----=> need-resched
|
||||
# | / _---=> hardirq/softirq
|
||||
# || / _--=> preempt-depth
|
||||
# ||| / delay
|
||||
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
|
||||
# | | | |||| | |
|
||||
atrace-14446 [000] ...2 1212.465062: sched_switch: prev_comm=atrace prev_pid=14446 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.465074: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465082: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.465092: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.465102: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465126: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.465132: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.465139: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465145: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.465227: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h5 1212.465297: sched_wakeup: comm=adbd pid=212 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465306: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=adbd next_pid=212 next_prio=120
|
||||
dsx_exp_workque-212 [000] d..4 1212.465329: sched_wakeup: comm=adbd pid=209 prio=120 success=1 target_cpu=000
|
||||
dsx_exp_workque-212 [000] ...2 1212.465348: sched_switch: prev_comm=adbd prev_pid=212 prev_prio=120 prev_state=S ==> next_comm=adbd next_pid=209 next_prio=120
|
||||
uether-209 [000] d..4 1212.465395: sched_wakeup: comm=adbd pid=211 prio=120 success=1 target_cpu=000
|
||||
uether-209 [000] ...2 1212.465441: sched_switch: prev_comm=adbd prev_pid=209 prev_prio=120 prev_state=S ==> next_comm=adbd next_pid=211 next_prio=120
|
||||
dsx_rebuild_wor-211 [000] ...2 1212.465448: sched_switch: prev_comm=adbd prev_pid=211 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h5 1212.574554: sched_wakeup: comm=sensors.qcom pid=292 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.574566: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=sensors.qcom next_pid=292 next_prio=120
|
||||
irq/363-ARM64 s-292 [000] ...2 1212.574665: sched_switch: prev_comm=sensors.qcom prev_pid=292 prev_prio=120 prev_state=S ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 [000] d..4 1212.574797: sched_wakeup: comm=sensors.qcom pid=1593 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-14447 [000] ...2 1212.574802: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=R+ ==> next_comm=sensors.qcom next_pid=1593 next_prio=120
|
||||
sensors.qcom-1593 [000] ...2 1212.574819: sched_switch: prev_comm=sensors.qcom prev_pid=1593 prev_prio=120 prev_state=D ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 [000] d..3 1212.574823: sched_wakeup: comm=sensors.qcom pid=1593 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-14447 [000] ...2 1212.574827: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=R+ ==> next_comm=sensors.qcom next_pid=1593 next_prio=120
|
||||
sensors.qcom-1593 [000] d..4 1212.574865: sched_wakeup: comm=sensors.qcom pid=760 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-1593 [000] ...2 1212.574876: sched_switch: prev_comm=sensors.qcom prev_pid=1593 prev_prio=120 prev_state=S ==> next_comm=sensors.qcom next_pid=760 next_prio=120
|
||||
sensors.qcom-760 [000] d..4 1212.574905: sched_wakeup: comm=system_server pid=782 prio=118 success=1 target_cpu=000
|
||||
sensors.qcom-760 [000] ...2 1212.574917: sched_switch: prev_comm=sensors.qcom prev_pid=760 prev_prio=120 prev_state=S ==> next_comm=system_server next_pid=782 next_prio=118
|
||||
system_server-782 [000] d..4 1212.574981: sched_wakeup: comm=system_server pid=785 prio=118 success=1 target_cpu=000
|
||||
system_server-782 [000] ...2 1212.575009: sched_switch: prev_comm=system_server prev_pid=782 prev_prio=118 prev_state=S ==> next_comm=system_server next_pid=785 next_prio=118
|
||||
system_server-785 [000] ...2 1212.575045: sched_switch: prev_comm=system_server prev_pid=785 prev_prio=118 prev_state=S ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 [000] ...3 1212.575143: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=x ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.575153: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.575159: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.575167: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.575175: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.575181: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.575188: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.575195: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.575201: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.575211: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1212.649601: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.649614: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1212.649630: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.729539: sched_wakeup: comm=kworker/u:1 pid=21 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNs6 1212.729550: sched_wakeup: comm=kworker/0:2H pid=557 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.729563: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:1 next_pid=21 next_prio=120
|
||||
kworker/u:1-21 [000] d..5 1212.729571: sched_wakeup: comm=mpdecision pid=2046 prio=113 success=1 target_cpu=000
|
||||
kworker/u:1-21 [000] ...2 1212.729578: sched_switch: prev_comm=kworker/u:1 prev_pid=21 prev_prio=120 prev_state=S ==> next_comm=kworker/0:2H next_pid=557 next_prio=100
|
||||
thermal-engine-557 [000] d..4 1212.729597: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
thermal-engine-557 [000] ...2 1212.729600: sched_switch: prev_comm=kworker/0:2H prev_pid=557 prev_prio=100 prev_state=D ==> next_comm=mpdecision next_pid=2046 next_prio=113
|
||||
mpdecision-2046 [000] ...2 1212.729801: sched_switch: prev_comm=mpdecision prev_pid=2046 prev_prio=113 prev_state=S ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] ...2 1212.730104: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.730134: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730154: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.730176: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.730201: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730220: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.730241: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.730262: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730280: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.730303: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.730638: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730669: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
migration/0-7 [000] d..6 1212.730707: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
migration/0-7 [000] ...2 1212.730728: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] ...2 1212.730916: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.731632: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.731661: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
migration/0-7 [000] d..6 1212.731702: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
migration/0-7 [000] ...2 1212.731722: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] ...2 1212.731832: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.732685: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.732714: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
migration/0-7 [000] d..6 1212.732747: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
migration/0-7 [000] ...2 1212.732767: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] d..4 1212.732810: sched_wakeup: comm=kworker/0:2H pid=557 prio=100 success=1 target_cpu=000
|
||||
kworker/0:1H-17 [000] ...2 1212.732829: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=S ==> next_comm=kworker/0:2H next_pid=557 next_prio=100
|
||||
thermal-engine-557 [000] ...2 1212.732854: sched_switch: prev_comm=kworker/0:2H prev_pid=557 prev_prio=100 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1212.876266: sched_wakeup: comm=RILSender0 pid=1365 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNh4 1212.876284: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.876316: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=RILSender0 next_pid=1365 next_prio=120
|
||||
RILSender0-1365 [000] ...2 1212.876415: sched_switch: prev_comm=RILSender0 prev_pid=1365 prev_prio=120 prev_state=S ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1212.876454: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1213.089569: sched_wakeup: comm=Thread-625 pid=5750 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNh4 1213.089587: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.089622: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=Thread-625 next_pid=5750 next_prio=120
|
||||
AsyncTask #1-5750 [000] ...2 1213.089842: sched_switch: prev_comm=Thread-625 prev_pid=5750 prev_prio=120 prev_state=S ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1213.089879: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1213.327439: sched_wakeup: comm=pandora.android pid=5395 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNh4 1213.327455: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.327487: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1213.327518: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 [000] d..4 1213.327718: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
pandora.android-5395 [000] ...2 1213.327739: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=R+ ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 [000] ...2 1213.327763: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=D ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 [000] d..3 1213.327781: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
pandora.android-5395 [000] ...2 1213.327795: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=R+ ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 [000] d..4 1213.328056: sched_wakeup: comm=Binder_1 pid=878 prio=120 success=1 target_cpu=000
|
||||
Binder_1-780 [000] ...2 1213.328095: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=S ==> next_comm=Binder_1 next_pid=878 next_prio=120
|
||||
Binder_1-878 [000] d..4 1213.328263: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
Binder_1-878 [000] ...2 1213.328345: sched_switch: prev_comm=Binder_1 prev_pid=878 prev_prio=120 prev_state=S ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 [000] ...2 1213.328558: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=S ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 [000] ...2 1213.328743: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1213.328773: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.328793: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1213.328821: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1213.328846: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.328866: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1213.328891: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1213.328913: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.328931: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1213.328964: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1213.465138: sched_wakeup: comm=atrace pid=14446 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.465171: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=atrace next_pid=14446 next_prio=120
|
||||
@@ -0,0 +1,128 @@
|
||||
|
||||
# tracer: nop
|
||||
#
|
||||
# entries-in-buffer/entries-written: 116/116 #P:1
|
||||
#
|
||||
# _-----=> irqs-off
|
||||
# / _----=> need-resched
|
||||
# | / _---=> hardirq/softirq
|
||||
# || / _--=> preempt-depth
|
||||
# ||| / delay
|
||||
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
|
||||
# | | | |||| | |
|
||||
atrace-14446 [000] ...2 1212.465062: sched_switch: prev_comm=atrace prev_pid=14446 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.465074: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465082: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.465092: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.465102: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465126: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.465132: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.465139: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465145: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.465227: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h5 1212.465297: sched_wakeup: comm=adbd pid=212 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465306: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=adbd next_pid=212 next_prio=120
|
||||
adbd-212 [000] d..4 1212.465329: sched_wakeup: comm=adbd pid=209 prio=120 success=1 target_cpu=000
|
||||
adbd-212 [000] ...2 1212.465348: sched_switch: prev_comm=adbd prev_pid=212 prev_prio=120 prev_state=S ==> next_comm=adbd next_pid=209 next_prio=120
|
||||
adbd-209 [000] d..4 1212.465395: sched_wakeup: comm=adbd pid=211 prio=120 success=1 target_cpu=000
|
||||
adbd-209 [000] ...2 1212.465441: sched_switch: prev_comm=adbd prev_pid=209 prev_prio=120 prev_state=S ==> next_comm=adbd next_pid=211 next_prio=120
|
||||
adbd-211 [000] ...2 1212.465448: sched_switch: prev_comm=adbd prev_pid=211 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h5 1212.574554: sched_wakeup: comm=sensors.qcom pid=292 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.574566: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=sensors.qcom next_pid=292 next_prio=120
|
||||
sensors.qcom-292 [000] ...2 1212.574665: sched_switch: prev_comm=sensors.qcom prev_pid=292 prev_prio=120 prev_state=S ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 [000] d..4 1212.574797: sched_wakeup: comm=sensors.qcom pid=1593 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-14447 [000] ...2 1212.574802: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=R+ ==> next_comm=sensors.qcom next_pid=1593 next_prio=120
|
||||
sensors.qcom-1593 [000] ...2 1212.574819: sched_switch: prev_comm=sensors.qcom prev_pid=1593 prev_prio=120 prev_state=D ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 [000] d..3 1212.574823: sched_wakeup: comm=sensors.qcom pid=1593 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-14447 [000] ...2 1212.574827: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=R+ ==> next_comm=sensors.qcom next_pid=1593 next_prio=120
|
||||
sensors.qcom-1593 [000] d..4 1212.574865: sched_wakeup: comm=sensors.qcom pid=760 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-1593 [000] ...2 1212.574876: sched_switch: prev_comm=sensors.qcom prev_pid=1593 prev_prio=120 prev_state=S ==> next_comm=sensors.qcom next_pid=760 next_prio=120
|
||||
sensors.qcom-760 [000] d..4 1212.574905: sched_wakeup: comm=system_server pid=782 prio=118 success=1 target_cpu=000
|
||||
sensors.qcom-760 [000] ...2 1212.574917: sched_switch: prev_comm=sensors.qcom prev_pid=760 prev_prio=120 prev_state=S ==> next_comm=system_server next_pid=782 next_prio=118
|
||||
system_server-782 [000] d..4 1212.574981: sched_wakeup: comm=system_server pid=785 prio=118 success=1 target_cpu=000
|
||||
system_server-782 [000] ...2 1212.575009: sched_switch: prev_comm=system_server prev_pid=782 prev_prio=118 prev_state=S ==> next_comm=system_server next_pid=785 next_prio=118
|
||||
system_server-785 [000] ...2 1212.575045: sched_switch: prev_comm=system_server prev_pid=785 prev_prio=118 prev_state=S ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 [000] ...3 1212.575143: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=x ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.575153: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.575159: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.575167: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.575175: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.575181: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.575188: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.575195: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.575201: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.575211: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1212.649601: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.649614: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1212.649630: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.729539: sched_wakeup: comm=kworker/u:1 pid=21 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNs6 1212.729550: sched_wakeup: comm=kworker/0:2H pid=557 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.729563: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:1 next_pid=21 next_prio=120
|
||||
kworker/u:1-21 [000] d..5 1212.729571: sched_wakeup: comm=mpdecision pid=2046 prio=113 success=1 target_cpu=000
|
||||
kworker/u:1-21 [000] ...2 1212.729578: sched_switch: prev_comm=kworker/u:1 prev_pid=21 prev_prio=120 prev_state=S ==> next_comm=kworker/0:2H next_pid=557 next_prio=100
|
||||
kworker/0:2H-557 [000] d..4 1212.729597: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/0:2H-557 [000] ...2 1212.729600: sched_switch: prev_comm=kworker/0:2H prev_pid=557 prev_prio=100 prev_state=D ==> next_comm=mpdecision next_pid=2046 next_prio=113
|
||||
mpdecision-2046 [000] ...2 1212.729801: sched_switch: prev_comm=mpdecision prev_pid=2046 prev_prio=113 prev_state=S ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] ...2 1212.730104: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.730134: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730154: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.730176: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.730201: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730220: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.730241: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.730262: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730280: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.730303: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.730638: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730669: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 [000] d..6 1212.730707: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 [000] ...2 1212.730728: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] ...2 1212.730916: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.731632: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.731661: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 [000] d..6 1212.731702: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 [000] ...2 1212.731722: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] ...2 1212.731832: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.732685: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.732714: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 [000] d..6 1212.732747: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 [000] ...2 1212.732767: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] d..4 1212.732810: sched_wakeup: comm=kworker/0:2H pid=557 prio=100 success=1 target_cpu=000
|
||||
kworker/0:1H-17 [000] ...2 1212.732829: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=S ==> next_comm=kworker/0:2H next_pid=557 next_prio=100
|
||||
kworker/0:2H-557 [000] ...2 1212.732854: sched_switch: prev_comm=kworker/0:2H prev_pid=557 prev_prio=100 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1212.876266: sched_wakeup: comm=RILSender0 pid=1365 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNh4 1212.876284: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.876316: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=RILSender0 next_pid=1365 next_prio=120
|
||||
RILSender0-1365 [000] ...2 1212.876415: sched_switch: prev_comm=RILSender0 prev_pid=1365 prev_prio=120 prev_state=S ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1212.876454: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1213.089569: sched_wakeup: comm=Thread-625 pid=5750 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNh4 1213.089587: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.089622: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=Thread-625 next_pid=5750 next_prio=120
|
||||
Thread-625-5750 [000] ...2 1213.089842: sched_switch: prev_comm=Thread-625 prev_pid=5750 prev_prio=120 prev_state=S ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1213.089879: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1213.327439: sched_wakeup: comm=pandora.android pid=5395 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNh4 1213.327455: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.327487: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1213.327518: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 [000] d..4 1213.327718: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
pandora.android-5395 [000] ...2 1213.327739: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=R+ ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 [000] ...2 1213.327763: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=D ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 [000] d..3 1213.327781: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
pandora.android-5395 [000] ...2 1213.327795: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=R+ ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 [000] d..4 1213.328056: sched_wakeup: comm=Binder_1 pid=878 prio=120 success=1 target_cpu=000
|
||||
Binder_1-780 [000] ...2 1213.328095: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=S ==> next_comm=Binder_1 next_pid=878 next_prio=120
|
||||
Binder_1-878 [000] d..4 1213.328263: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
Binder_1-878 [000] ...2 1213.328345: sched_switch: prev_comm=Binder_1 prev_pid=878 prev_prio=120 prev_state=S ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 [000] ...2 1213.328558: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=S ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 [000] ...2 1213.328743: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1213.328773: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.328793: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1213.328821: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1213.328846: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.328866: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1213.328891: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1213.328913: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.328931: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1213.328964: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1213.465138: sched_wakeup: comm=atrace pid=14446 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.465171: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=atrace next_pid=14446 next_prio=120
|
||||
@@ -0,0 +1,127 @@
|
||||
# tracer: nop
|
||||
#
|
||||
# entries-in-buffer/entries-written: 116/116 #P:1
|
||||
#
|
||||
# _-----=> irqs-off
|
||||
# / _----=> need-resched
|
||||
# | / _---=> hardirq/softirq
|
||||
# || / _--=> preempt-depth
|
||||
# ||| / delay
|
||||
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
|
||||
# | | | |||| | |
|
||||
atrace-14446 [000] ...2 1212.465062: sched_switch: prev_comm=atrace prev_pid=14446 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.465074: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465082: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.465092: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.465102: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465126: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.465132: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.465139: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465145: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.465227: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h5 1212.465297: sched_wakeup: comm=adbd pid=212 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.465306: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=adbd next_pid=212 next_prio=120
|
||||
adbd-212 [000] d..4 1212.465329: sched_wakeup: comm=adbd pid=209 prio=120 success=1 target_cpu=000
|
||||
adbd-212 [000] ...2 1212.465348: sched_switch: prev_comm=adbd prev_pid=212 prev_prio=120 prev_state=S ==> next_comm=adbd next_pid=209 next_prio=120
|
||||
adbd-209 [000] d..4 1212.465395: sched_wakeup: comm=adbd pid=211 prio=120 success=1 target_cpu=000
|
||||
adbd-209 [000] ...2 1212.465441: sched_switch: prev_comm=adbd prev_pid=209 prev_prio=120 prev_state=S ==> next_comm=adbd next_pid=211 next_prio=120
|
||||
adbd-211 [000] ...2 1212.465448: sched_switch: prev_comm=adbd prev_pid=211 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h5 1212.574554: sched_wakeup: comm=sensors.qcom pid=292 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.574566: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=sensors.qcom next_pid=292 next_prio=120
|
||||
sensors.qcom-292 [000] ...2 1212.574665: sched_switch: prev_comm=sensors.qcom prev_pid=292 prev_prio=120 prev_state=S ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 [000] d..4 1212.574797: sched_wakeup: comm=sensors.qcom pid=1593 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-14447 [000] ...2 1212.574802: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=R+ ==> next_comm=sensors.qcom next_pid=1593 next_prio=120
|
||||
sensors.qcom-1593 [000] ...2 1212.574819: sched_switch: prev_comm=sensors.qcom prev_pid=1593 prev_prio=120 prev_state=D ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 [000] d..3 1212.574823: sched_wakeup: comm=sensors.qcom pid=1593 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-14447 [000] ...2 1212.574827: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=R+ ==> next_comm=sensors.qcom next_pid=1593 next_prio=120
|
||||
sensors.qcom-1593 [000] d..4 1212.574865: sched_wakeup: comm=sensors.qcom pid=760 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-1593 [000] ...2 1212.574876: sched_switch: prev_comm=sensors.qcom prev_pid=1593 prev_prio=120 prev_state=S ==> next_comm=sensors.qcom next_pid=760 next_prio=120
|
||||
sensors.qcom-760 [000] d..4 1212.574905: sched_wakeup: comm=system_server pid=782 prio=118 success=1 target_cpu=000
|
||||
sensors.qcom-760 [000] ...2 1212.574917: sched_switch: prev_comm=sensors.qcom prev_pid=760 prev_prio=120 prev_state=S ==> next_comm=system_server next_pid=782 next_prio=118
|
||||
system_server-782 [000] d..4 1212.574981: sched_wakeup: comm=system_server pid=785 prio=118 success=1 target_cpu=000
|
||||
system_server-782 [000] ...2 1212.575009: sched_switch: prev_comm=system_server prev_pid=782 prev_prio=118 prev_state=S ==> next_comm=system_server next_pid=785 next_prio=118
|
||||
system_server-785 [000] ...2 1212.575045: sched_switch: prev_comm=system_server prev_pid=785 prev_prio=118 prev_state=S ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 [000] ...3 1212.575143: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=x ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.575153: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.575159: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.575167: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.575175: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.575181: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.575188: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.575195: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.575201: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.575211: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1212.649601: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.649614: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1212.649630: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.729539: sched_wakeup: comm=kworker/u:1 pid=21 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNs6 1212.729550: sched_wakeup: comm=kworker/0:2H pid=557 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.729563: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:1 next_pid=21 next_prio=120
|
||||
kworker/u:1-21 [000] d..5 1212.729571: sched_wakeup: comm=mpdecision pid=2046 prio=113 success=1 target_cpu=000
|
||||
kworker/u:1-21 [000] ...2 1212.729578: sched_switch: prev_comm=kworker/u:1 prev_pid=21 prev_prio=120 prev_state=S ==> next_comm=kworker/0:2H next_pid=557 next_prio=100
|
||||
kworker/0:2H-557 [000] d..4 1212.729597: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/0:2H-557 [000] ...2 1212.729600: sched_switch: prev_comm=kworker/0:2H prev_pid=557 prev_prio=100 prev_state=D ==> next_comm=mpdecision next_pid=2046 next_prio=113
|
||||
mpdecision-2046 [000] ...2 1212.729801: sched_switch: prev_comm=mpdecision prev_pid=2046 prev_prio=113 prev_state=S ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] ...2 1212.730104: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.730134: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730154: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.730176: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.730201: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730220: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.730241: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1212.730262: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730280: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1212.730303: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.730638: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.730669: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 [000] d..6 1212.730707: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 [000] ...2 1212.730728: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] ...2 1212.730916: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.731632: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.731661: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 [000] d..6 1212.731702: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 [000] ...2 1212.731722: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] ...2 1212.731832: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h6 1212.732685: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.732714: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 [000] d..6 1212.732747: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 [000] ...2 1212.732767: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 [000] d..4 1212.732810: sched_wakeup: comm=kworker/0:2H pid=557 prio=100 success=1 target_cpu=000
|
||||
kworker/0:1H-17 [000] ...2 1212.732829: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=S ==> next_comm=kworker/0:2H next_pid=557 next_prio=100
|
||||
kworker/0:2H-557 [000] ...2 1212.732854: sched_switch: prev_comm=kworker/0:2H prev_pid=557 prev_prio=100 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1212.876266: sched_wakeup: comm=RILSender0 pid=1365 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNh4 1212.876284: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1212.876316: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=RILSender0 next_pid=1365 next_prio=120
|
||||
RILSender0-1365 [000] ...2 1212.876415: sched_switch: prev_comm=RILSender0 prev_pid=1365 prev_prio=120 prev_state=S ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1212.876454: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1213.089569: sched_wakeup: comm=Thread-625 pid=5750 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNh4 1213.089587: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.089622: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=Thread-625 next_pid=5750 next_prio=120
|
||||
Thread-625-5750 [000] ...2 1213.089842: sched_switch: prev_comm=Thread-625 prev_pid=5750 prev_prio=120 prev_state=S ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1213.089879: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1213.327439: sched_wakeup: comm=pandora.android pid=5395 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] dNh4 1213.327455: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.327487: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 [000] ...2 1213.327518: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 [000] d..4 1213.327718: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
pandora.android-5395 [000] ...2 1213.327739: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=R+ ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 [000] ...2 1213.327763: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=D ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 [000] d..3 1213.327781: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
pandora.android-5395 [000] ...2 1213.327795: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=R+ ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 [000] d..4 1213.328056: sched_wakeup: comm=Binder_1 pid=878 prio=120 success=1 target_cpu=000
|
||||
Binder_1-780 [000] ...2 1213.328095: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=S ==> next_comm=Binder_1 next_pid=878 next_prio=120
|
||||
Binder_1-878 [000] d..4 1213.328263: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
Binder_1-878 [000] ...2 1213.328345: sched_switch: prev_comm=Binder_1 prev_pid=878 prev_prio=120 prev_state=S ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 [000] ...2 1213.328558: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=S ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 [000] ...2 1213.328743: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1213.328773: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.328793: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1213.328821: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1213.328846: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.328866: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1213.328891: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d..4 1213.328913: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.328931: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 [000] ...2 1213.328964: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 [000] d.h4 1213.465138: sched_wakeup: comm=atrace pid=14446 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 [000] ...2 1213.465171: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=atrace next_pid=14446 next_prio=120
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{1: '/init', 2: 'kthreadd', 3: 'ksoftirqd/0', 7: 'kworker/u:0H', 8: 'migration/0', 13: 'khelper', 14: 'netns'}
|
||||
@@ -0,0 +1,127 @@
|
||||
# tracer: nop
|
||||
#
|
||||
# entries-in-buffer/entries-written: 116/116 #P:1
|
||||
#
|
||||
# _-----=> irqs-off
|
||||
# / _----=> need-resched
|
||||
# | / _---=> hardirq/softirq
|
||||
# || / _--=> preempt-depth
|
||||
# ||| / delay
|
||||
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
|
||||
# | | | |||| | |
|
||||
atrace-14446 (-----) [000] ...2 1212.465062: sched_switch: prev_comm=atrace prev_pid=14446 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.465074: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.465082: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1212.465092: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.465102: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.465126: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1212.465132: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.465139: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.465145: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1212.465227: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h5 1212.465297: sched_wakeup: comm=adbd pid=212 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.465306: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=adbd next_pid=212 next_prio=120
|
||||
adbd-212 ( 212) [000] d..4 1212.465329: sched_wakeup: comm=adbd pid=209 prio=120 success=1 target_cpu=000
|
||||
adbd-212 ( 212) [000] ...2 1212.465348: sched_switch: prev_comm=adbd prev_pid=212 prev_prio=120 prev_state=S ==> next_comm=adbd next_pid=209 next_prio=120
|
||||
adbd-209 ( 209) [000] d..4 1212.465395: sched_wakeup: comm=adbd pid=211 prio=120 success=1 target_cpu=000
|
||||
adbd-209 ( 209) [000] ...2 1212.465441: sched_switch: prev_comm=adbd prev_pid=209 prev_prio=120 prev_state=S ==> next_comm=adbd next_pid=211 next_prio=120
|
||||
adbd-211 ( 211) [000] ...2 1212.465448: sched_switch: prev_comm=adbd prev_pid=211 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h5 1212.574554: sched_wakeup: comm=sensors.qcom pid=292 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.574566: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=sensors.qcom next_pid=292 next_prio=120
|
||||
sensors.qcom-292 (-----) [000] ...2 1212.574665: sched_switch: prev_comm=sensors.qcom prev_pid=292 prev_prio=120 prev_state=S ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 (-----) [000] d..4 1212.574797: sched_wakeup: comm=sensors.qcom pid=1593 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-14447 (-----) [000] ...2 1212.574802: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=R+ ==> next_comm=sensors.qcom next_pid=1593 next_prio=120
|
||||
sensors.qcom-1593 (-----) [000] ...2 1212.574819: sched_switch: prev_comm=sensors.qcom prev_pid=1593 prev_prio=120 prev_state=D ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 (-----) [000] d..3 1212.574823: sched_wakeup: comm=sensors.qcom pid=1593 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-14447 (-----) [000] ...2 1212.574827: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=R+ ==> next_comm=sensors.qcom next_pid=1593 next_prio=120
|
||||
sensors.qcom-1593 (-----) [000] d..4 1212.574865: sched_wakeup: comm=sensors.qcom pid=760 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-1593 (-----) [000] ...2 1212.574876: sched_switch: prev_comm=sensors.qcom prev_pid=1593 prev_prio=120 prev_state=S ==> next_comm=sensors.qcom next_pid=760 next_prio=120
|
||||
sensors.qcom-760 (-----) [000] d..4 1212.574905: sched_wakeup: comm=system_server pid=782 prio=118 success=1 target_cpu=000
|
||||
sensors.qcom-760 (-----) [000] ...2 1212.574917: sched_switch: prev_comm=sensors.qcom prev_pid=760 prev_prio=120 prev_state=S ==> next_comm=system_server next_pid=782 next_prio=118
|
||||
system_server-782 (-----) [000] d..4 1212.574981: sched_wakeup: comm=system_server pid=785 prio=118 success=1 target_cpu=000
|
||||
system_server-782 (-----) [000] ...2 1212.575009: sched_switch: prev_comm=system_server prev_pid=782 prev_prio=118 prev_state=S ==> next_comm=system_server next_pid=785 next_prio=118
|
||||
system_server-785 ( 785) [000] ...2 1212.575045: sched_switch: prev_comm=system_server prev_pid=785 prev_prio=118 prev_state=S ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 (-----) [000] ...3 1212.575143: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=x ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.575153: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.575159: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1212.575167: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.575175: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.575181: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1212.575188: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.575195: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.575201: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1212.575211: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h4 1212.649601: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.649614: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 (-----) [000] ...2 1212.649630: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h6 1212.729539: sched_wakeup: comm=kworker/u:1 pid=21 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] dNs6 1212.729550: sched_wakeup: comm=kworker/0:2H pid=557 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.729563: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:1 next_pid=21 next_prio=120
|
||||
kworker/u:1-21 (-----) [000] d..5 1212.729571: sched_wakeup: comm=mpdecision pid=2046 prio=113 success=1 target_cpu=000
|
||||
kworker/u:1-21 (-----) [000] ...2 1212.729578: sched_switch: prev_comm=kworker/u:1 prev_pid=21 prev_prio=120 prev_state=S ==> next_comm=kworker/0:2H next_pid=557 next_prio=100
|
||||
kworker/0:2H-557 (-----) [000] d..4 1212.729597: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/0:2H-557 (-----) [000] ...2 1212.729600: sched_switch: prev_comm=kworker/0:2H prev_pid=557 prev_prio=100 prev_state=D ==> next_comm=mpdecision next_pid=2046 next_prio=113
|
||||
mpdecision-2046 (-----) [000] ...2 1212.729801: sched_switch: prev_comm=mpdecision prev_pid=2046 prev_prio=113 prev_state=S ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 (-----) [000] ...2 1212.730104: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.730134: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.730154: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1212.730176: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.730201: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.730220: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1212.730241: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.730262: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.730280: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1212.730303: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h6 1212.730638: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.730669: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 ( 7) [000] d..6 1212.730707: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 ( 7) [000] ...2 1212.730728: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 (-----) [000] ...2 1212.730916: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h6 1212.731632: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.731661: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 ( 7) [000] d..6 1212.731702: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 ( 7) [000] ...2 1212.731722: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 (-----) [000] ...2 1212.731832: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h6 1212.732685: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.732714: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 ( 7) [000] d..6 1212.732747: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 ( 7) [000] ...2 1212.732767: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 (-----) [000] d..4 1212.732810: sched_wakeup: comm=kworker/0:2H pid=557 prio=100 success=1 target_cpu=000
|
||||
kworker/0:1H-17 (-----) [000] ...2 1212.732829: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=S ==> next_comm=kworker/0:2H next_pid=557 next_prio=100
|
||||
kworker/0:2H-557 (-----) [000] ...2 1212.732854: sched_switch: prev_comm=kworker/0:2H prev_pid=557 prev_prio=100 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h4 1212.876266: sched_wakeup: comm=RILSender0 pid=1365 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] dNh4 1212.876284: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.876316: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=RILSender0 next_pid=1365 next_prio=120
|
||||
RILSender0-1365 ( 1345) [000] ...2 1212.876415: sched_switch: prev_comm=RILSender0 prev_pid=1365 prev_prio=120 prev_state=S ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 (-----) [000] ...2 1212.876454: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h4 1213.089569: sched_wakeup: comm=Thread-625 pid=5750 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] dNh4 1213.089587: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.089622: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=Thread-625 next_pid=5750 next_prio=120
|
||||
Thread-625-5750 (-----) [000] ...2 1213.089842: sched_switch: prev_comm=Thread-625 prev_pid=5750 prev_prio=120 prev_state=S ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 (-----) [000] ...2 1213.089879: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h4 1213.327439: sched_wakeup: comm=pandora.android pid=5395 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] dNh4 1213.327455: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.327487: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 (-----) [000] ...2 1213.327518: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 (-----) [000] d..4 1213.327718: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
pandora.android-5395 (-----) [000] ...2 1213.327739: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=R+ ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 (-----) [000] ...2 1213.327763: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=D ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 (-----) [000] d..3 1213.327781: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
pandora.android-5395 (-----) [000] ...2 1213.327795: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=R+ ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 (-----) [000] d..4 1213.328056: sched_wakeup: comm=Binder_1 pid=878 prio=120 success=1 target_cpu=000
|
||||
Binder_1-780 (-----) [000] ...2 1213.328095: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=S ==> next_comm=Binder_1 next_pid=878 next_prio=120
|
||||
Binder_1-878 ( 877) [000] d..4 1213.328263: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
Binder_1-878 ( 877) [000] ...2 1213.328345: sched_switch: prev_comm=Binder_1 prev_pid=878 prev_prio=120 prev_state=S ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 (-----) [000] ...2 1213.328558: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=S ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 (-----) [000] ...2 1213.328743: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1213.328773: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.328793: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1213.328821: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1213.328846: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.328866: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1213.328891: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1213.328913: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.328931: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 ( 3) [000] ...2 1213.328964: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h4 1213.465138: sched_wakeup: comm=atrace pid=14446 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.465171: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=atrace next_pid=14446 next_prio=120
|
||||
@@ -0,0 +1,127 @@
|
||||
# tracer: nop
|
||||
#
|
||||
# entries-in-buffer/entries-written: 116/116 #P:1
|
||||
#
|
||||
# _-----=> irqs-off
|
||||
# / _----=> need-resched
|
||||
# | / _---=> hardirq/softirq
|
||||
# || / _--=> preempt-depth
|
||||
# ||| / delay
|
||||
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
|
||||
# | | | |||| | |
|
||||
atrace-14446 (-----) [000] ...2 1212.465062: sched_switch: prev_comm=atrace prev_pid=14446 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.465074: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.465082: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1212.465092: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.465102: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.465126: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1212.465132: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.465139: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.465145: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1212.465227: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h5 1212.465297: sched_wakeup: comm=adbd pid=212 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.465306: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=adbd next_pid=212 next_prio=120
|
||||
adbd-212 (-----) [000] d..4 1212.465329: sched_wakeup: comm=adbd pid=209 prio=120 success=1 target_cpu=000
|
||||
adbd-212 (-----) [000] ...2 1212.465348: sched_switch: prev_comm=adbd prev_pid=212 prev_prio=120 prev_state=S ==> next_comm=adbd next_pid=209 next_prio=120
|
||||
adbd-209 (-----) [000] d..4 1212.465395: sched_wakeup: comm=adbd pid=211 prio=120 success=1 target_cpu=000
|
||||
adbd-209 (-----) [000] ...2 1212.465441: sched_switch: prev_comm=adbd prev_pid=209 prev_prio=120 prev_state=S ==> next_comm=adbd next_pid=211 next_prio=120
|
||||
adbd-211 (-----) [000] ...2 1212.465448: sched_switch: prev_comm=adbd prev_pid=211 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h5 1212.574554: sched_wakeup: comm=sensors.qcom pid=292 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.574566: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=sensors.qcom next_pid=292 next_prio=120
|
||||
sensors.qcom-292 (-----) [000] ...2 1212.574665: sched_switch: prev_comm=sensors.qcom prev_pid=292 prev_prio=120 prev_state=S ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 (-----) [000] d..4 1212.574797: sched_wakeup: comm=sensors.qcom pid=1593 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-14447 (-----) [000] ...2 1212.574802: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=R+ ==> next_comm=sensors.qcom next_pid=1593 next_prio=120
|
||||
sensors.qcom-1593 (-----) [000] ...2 1212.574819: sched_switch: prev_comm=sensors.qcom prev_pid=1593 prev_prio=120 prev_state=D ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 (-----) [000] d..3 1212.574823: sched_wakeup: comm=sensors.qcom pid=1593 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-14447 (-----) [000] ...2 1212.574827: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=R+ ==> next_comm=sensors.qcom next_pid=1593 next_prio=120
|
||||
sensors.qcom-1593 (-----) [000] d..4 1212.574865: sched_wakeup: comm=sensors.qcom pid=760 prio=120 success=1 target_cpu=000
|
||||
sensors.qcom-1593 (-----) [000] ...2 1212.574876: sched_switch: prev_comm=sensors.qcom prev_pid=1593 prev_prio=120 prev_state=S ==> next_comm=sensors.qcom next_pid=760 next_prio=120
|
||||
sensors.qcom-760 (-----) [000] d..4 1212.574905: sched_wakeup: comm=system_server pid=782 prio=118 success=1 target_cpu=000
|
||||
sensors.qcom-760 (-----) [000] ...2 1212.574917: sched_switch: prev_comm=sensors.qcom prev_pid=760 prev_prio=120 prev_state=S ==> next_comm=system_server next_pid=782 next_prio=118
|
||||
system_server-782 (-----) [000] d..4 1212.574981: sched_wakeup: comm=system_server pid=785 prio=118 success=1 target_cpu=000
|
||||
system_server-782 (-----) [000] ...2 1212.575009: sched_switch: prev_comm=system_server prev_pid=782 prev_prio=118 prev_state=S ==> next_comm=system_server next_pid=785 next_prio=118
|
||||
system_server-785 (-----) [000] ...2 1212.575045: sched_switch: prev_comm=system_server prev_pid=785 prev_prio=118 prev_state=S ==> next_comm=sensors.qcom next_pid=14447 next_prio=120
|
||||
sensors.qcom-14447 (-----) [000] ...3 1212.575143: sched_switch: prev_comm=sensors.qcom prev_pid=14447 prev_prio=120 prev_state=x ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.575153: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.575159: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1212.575167: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.575175: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.575181: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1212.575188: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.575195: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.575201: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1212.575211: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h4 1212.649601: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.649614: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 (-----) [000] ...2 1212.649630: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h6 1212.729539: sched_wakeup: comm=kworker/u:1 pid=21 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] dNs6 1212.729550: sched_wakeup: comm=kworker/0:2H pid=557 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.729563: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:1 next_pid=21 next_prio=120
|
||||
kworker/u:1-21 (-----) [000] d..5 1212.729571: sched_wakeup: comm=mpdecision pid=2046 prio=113 success=1 target_cpu=000
|
||||
kworker/u:1-21 (-----) [000] ...2 1212.729578: sched_switch: prev_comm=kworker/u:1 prev_pid=21 prev_prio=120 prev_state=S ==> next_comm=kworker/0:2H next_pid=557 next_prio=100
|
||||
kworker/0:2H-557 (-----) [000] d..4 1212.729597: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/0:2H-557 (-----) [000] ...2 1212.729600: sched_switch: prev_comm=kworker/0:2H prev_pid=557 prev_prio=100 prev_state=D ==> next_comm=mpdecision next_pid=2046 next_prio=113
|
||||
mpdecision-2046 (-----) [000] ...2 1212.729801: sched_switch: prev_comm=mpdecision prev_pid=2046 prev_prio=113 prev_state=S ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 (-----) [000] ...2 1212.730104: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.730134: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.730154: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1212.730176: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.730201: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.730220: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1212.730241: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1212.730262: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.730280: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1212.730303: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h6 1212.730638: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.730669: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 (-----) [000] d..6 1212.730707: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 (-----) [000] ...2 1212.730728: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 (-----) [000] ...2 1212.730916: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h6 1212.731632: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.731661: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 (-----) [000] d..6 1212.731702: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 (-----) [000] ...2 1212.731722: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 (-----) [000] ...2 1212.731832: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h6 1212.732685: sched_wakeup: comm=kworker/u:0H pid=7 prio=100 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.732714: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/u:0H next_pid=7 next_prio=100
|
||||
kworker/u:0H-7 (-----) [000] d..6 1212.732747: sched_wakeup: comm=kworker/0:1H pid=17 prio=100 success=1 target_cpu=000
|
||||
kworker/u:0H-7 (-----) [000] ...2 1212.732767: sched_switch: prev_comm=kworker/u:0H prev_pid=7 prev_prio=100 prev_state=D ==> next_comm=kworker/0:1H next_pid=17 next_prio=100
|
||||
kworker/0:1H-17 (-----) [000] d..4 1212.732810: sched_wakeup: comm=kworker/0:2H pid=557 prio=100 success=1 target_cpu=000
|
||||
kworker/0:1H-17 (-----) [000] ...2 1212.732829: sched_switch: prev_comm=kworker/0:1H prev_pid=17 prev_prio=100 prev_state=S ==> next_comm=kworker/0:2H next_pid=557 next_prio=100
|
||||
kworker/0:2H-557 (-----) [000] ...2 1212.732854: sched_switch: prev_comm=kworker/0:2H prev_pid=557 prev_prio=100 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h4 1212.876266: sched_wakeup: comm=RILSender0 pid=1365 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] dNh4 1212.876284: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1212.876316: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=RILSender0 next_pid=1365 next_prio=120
|
||||
RILSender0-1365 (-----) [000] ...2 1212.876415: sched_switch: prev_comm=RILSender0 prev_pid=1365 prev_prio=120 prev_state=S ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 (-----) [000] ...2 1212.876454: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h4 1213.089569: sched_wakeup: comm=Thread-625 pid=5750 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] dNh4 1213.089587: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.089622: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=Thread-625 next_pid=5750 next_prio=120
|
||||
Thread-625-5750 (-----) [000] ...2 1213.089842: sched_switch: prev_comm=Thread-625 prev_pid=5750 prev_prio=120 prev_state=S ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 (-----) [000] ...2 1213.089879: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h4 1213.327439: sched_wakeup: comm=pandora.android pid=5395 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] dNh4 1213.327455: sched_wakeup: comm=MMHandlerThread pid=7231 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.327487: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=MMHandlerThread next_pid=7231 next_prio=120
|
||||
MMHandlerThread-7231 (-----) [000] ...2 1213.327518: sched_switch: prev_comm=MMHandlerThread prev_pid=7231 prev_prio=120 prev_state=S ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 (-----) [000] d..4 1213.327718: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
pandora.android-5395 (-----) [000] ...2 1213.327739: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=R+ ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 (-----) [000] ...2 1213.327763: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=D ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 (-----) [000] d..3 1213.327781: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
pandora.android-5395 (-----) [000] ...2 1213.327795: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=R+ ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 (-----) [000] d..4 1213.328056: sched_wakeup: comm=Binder_1 pid=878 prio=120 success=1 target_cpu=000
|
||||
Binder_1-780 (-----) [000] ...2 1213.328095: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=S ==> next_comm=Binder_1 next_pid=878 next_prio=120
|
||||
Binder_1-878 (-----) [000] d..4 1213.328263: sched_wakeup: comm=Binder_1 pid=780 prio=120 success=1 target_cpu=000
|
||||
Binder_1-878 (-----) [000] ...2 1213.328345: sched_switch: prev_comm=Binder_1 prev_pid=878 prev_prio=120 prev_state=S ==> next_comm=Binder_1 next_pid=780 next_prio=120
|
||||
Binder_1-780 (-----) [000] ...2 1213.328558: sched_switch: prev_comm=Binder_1 prev_pid=780 prev_prio=120 prev_state=S ==> next_comm=pandora.android next_pid=5395 next_prio=120
|
||||
pandora.android-5395 (-----) [000] ...2 1213.328743: sched_switch: prev_comm=pandora.android prev_pid=5395 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1213.328773: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.328793: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1213.328821: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1213.328846: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.328866: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1213.328891: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d..4 1213.328913: sched_wakeup: comm=ksoftirqd/0 pid=3 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.328931: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=3 next_prio=120
|
||||
ksoftirqd/0-3 (-----) [000] ...2 1213.328964: sched_switch: prev_comm=ksoftirqd/0 prev_pid=3 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
|
||||
<idle>-0 (-----) [000] d.h4 1213.465138: sched_wakeup: comm=atrace pid=14446 prio=120 success=1 target_cpu=000
|
||||
<idle>-0 (-----) [000] ...2 1213.465171: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=atrace next_pid=14446 next_prio=120
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
TRACE:
|
||||
x<EFBFBD>½]ΫnG’}χW4 ΟƒμΚ[U–±3€―³ΖzlνΚ<CEBD>}X,„6Ωυ<>dsΊ›¶µΰΗo$-Κ&+£OEd5€dhΔs²ς<C2B2>qβςluά―Ο6ϋOVW»λ<0F>}πlµΉ:ξ·›ΓσνΥσo^Ώήμ?Ύ<>›_φΫγqsυΙΚuΡ\~[VΟ^|<7C>ίύά¬_―<5F>—_ώΛj»<6A>Χαωξυλ™?ψρo?I?xµΩ<C2B5>?ίogo6ησ~ψφέOΣΏYοΟ‰ωγΓξυ‘ώ<E28098>ωσο<CF83><CEBF>m<EFBFBD>"9t<39><74>„FϋΖ‘ιφΝβ „#>ΌΜZRPβΐk‰1•Κ΅@‚Ή–Tf<54>‘CµqhkI5<16>@r¶σ@`-‘%uσ–<CF83><13><>z·ΠΡ¨xΪvL <4C>-Υ%ψ|‘<><E28098>ήt‘θu/θ‰ρΏ“εQ‘Nfµ+πω#‡©Χϊό¦q„CΛύσ<1B>γψωM>Α£ΪβώΧ»Η―θ.D[@-΄” „8Ϋ<16>ΤΫjΈ
|
||||
OoΊ‡Θ€'rfΔβ<CE94>·‚β΅ ΄%AΑ@R]U±50<35>εVθ_6<5F>C<EFBFBD>΅±ΛΖΏ]τE} 2Ι|Ρ"!Η°:
|
||||
@ΠΤ<CEA0>\®!ΗΐƒΓΧ>· ‡<>c0V±›ώ49δLµ”A¬ήyD•<44>η<EFBFBD> 'O•sP<06>[2w<32>χΚ(ΰάv<>ηgOΣ ΰΏ‰„fΉ<€<>ΤT?¶9x―Πj¦θ4΄ΪQ)ΩΙ†σµ5
|
||||
9ζ[·Σ
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2016 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.
|
||||
|
||||
'''Tracing agent result wrapper for systrace.
|
||||
|
||||
This class represents the captured trace results from a particular
|
||||
tool (e.g. atrace, ftrace.)
|
||||
'''
|
||||
|
||||
|
||||
class TraceResult(object):
|
||||
def __init__(self, source_name, raw_data):
|
||||
self.source_name = source_name
|
||||
self.raw_data = raw_data
|
||||
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2016 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.
|
||||
|
||||
'''Tracing agent interface for systrace.
|
||||
|
||||
This class represents an agent that captures traces from a particular
|
||||
tool (e.g. atrace, ftrace.)
|
||||
'''
|
||||
|
||||
# Timeout interval constants.
|
||||
|
||||
START_STOP_TIMEOUT = 10.0
|
||||
GET_RESULTS_TIMEOUT = 30.0
|
||||
|
||||
|
||||
class TracingConfig(object):
|
||||
'''Store the tracing configuration options for all Systrace agents. If there
|
||||
are ever any options that are to be shared between all of the agents, those
|
||||
options should go here.
|
||||
'''
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class TracingAgent(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
'''Starts running the trace for this agent. Stops with timeout if
|
||||
not completed within timeout interval.
|
||||
|
||||
Args:
|
||||
config: TracingConfig subclass containing agent-specific options
|
||||
and categories.
|
||||
timeout: Timeout interval in seconds.
|
||||
|
||||
Returns:
|
||||
Boolean value indicating whether or not the trace started successfully.
|
||||
'''
|
||||
pass
|
||||
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
'''Stops running the trace for this agent and returns immediately.
|
||||
Stops with timeout if not completed within timeout interval.
|
||||
|
||||
Args:
|
||||
timeout: Timeout interval in seconds.
|
||||
|
||||
Returns:
|
||||
Boolean value indicating whether or not the trace started successfully.
|
||||
'''
|
||||
pass
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
'''Find out if this agent supports recording of clock sync markers.
|
||||
|
||||
Returns:
|
||||
Boolean value indicating whether this agent supports recording
|
||||
of clock sync markers.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
||||
'''Record a clock sync marker for this agent.
|
||||
|
||||
Args:
|
||||
sync_id: Clock sync ID string.
|
||||
did_record_sync_marker_callback: Callback function to call
|
||||
(with arguments: timestamp and sync_id) after the
|
||||
clock sync marker is recorded.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def GetResults(self, timeout=None):
|
||||
'''Get the completed trace for this agent, stopping with timeout
|
||||
|
||||
Get the completed trace for this agent. Call only after
|
||||
StopAgentTracing is done. This function blocks until the result
|
||||
is collected (note; this may take several seconds). Stops with timeout
|
||||
if not completed within self._options.collection_timeout seconds.
|
||||
|
||||
Args:
|
||||
timeout: Timeout interval in seconds.
|
||||
Returns:
|
||||
Completed trace for this agent.
|
||||
'''
|
||||
pass
|
||||
@@ -0,0 +1,47 @@
|
||||
# Copyright 2014 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.
|
||||
|
||||
import unittest
|
||||
|
||||
from systrace import util
|
||||
|
||||
from devil.android import device_utils
|
||||
from devil.android.sdk import intent
|
||||
from devil.android.sdk import keyevent
|
||||
|
||||
|
||||
class BaseAgentTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
devices = device_utils.DeviceUtils.HealthyDevices()
|
||||
self.browser = 'stable'
|
||||
self.package_info = util.get_supported_browsers()[self.browser]
|
||||
self.device = devices[0]
|
||||
|
||||
curr_browser = self.GetChromeProcessID()
|
||||
if curr_browser is None:
|
||||
self.StartBrowser()
|
||||
|
||||
def tearDown(self):
|
||||
# Stop the browser after each test to ensure that it doesn't interfere
|
||||
# with subsequent tests, e.g. by holding the devtools socket open.
|
||||
self.device.ForceStop(self.package_info.package)
|
||||
|
||||
def StartBrowser(self):
|
||||
# Turn on the device screen.
|
||||
self.device.SetScreen(True)
|
||||
|
||||
# Unlock device.
|
||||
self.device.SendKeyEvent(keyevent.KEYCODE_MENU)
|
||||
|
||||
# Start browser.
|
||||
self.device.StartActivity(
|
||||
intent.Intent(activity=self.package_info.activity,
|
||||
package=self.package_info.package,
|
||||
data='about:blank',
|
||||
extras={'create_new_tab': True}),
|
||||
blocking=True, force_stop=True)
|
||||
|
||||
def GetChromeProcessID(self):
|
||||
return self.device.GetApplicationPids(
|
||||
self.package_info.package, at_most_one=True)
|
||||
@@ -0,0 +1,95 @@
|
||||
# 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.
|
||||
|
||||
# Tracing agent that captures friendly process and thread data - names, pids and
|
||||
# tids and names, etc to enrich display in the trace viewer. Captures snapshots
|
||||
# of the output of 'ps' on the device at intervals.
|
||||
|
||||
import logging
|
||||
import py_utils
|
||||
|
||||
from devil.android import device_utils
|
||||
from devil.android.device_errors import AdbShellCommandFailedError
|
||||
from systrace import tracing_agents
|
||||
from systrace import trace_result
|
||||
|
||||
# Leftmost output columns match those used on legacy devices.
|
||||
# Get thread names separately as there may be spaces that breaks col
|
||||
# splitting.
|
||||
# TODO(benm): Refactor device_utils.GetPids to get threads and use that here.
|
||||
PS_COMMAND_PROC = "ps -A -o USER,PID,PPID,VSIZE,RSS,WCHAN,ADDR=PC,S,NAME,COMM" \
|
||||
"&& ps -AT -o USER,PID,TID,CMD"
|
||||
|
||||
# Fallback for old devices.
|
||||
PS_COMMAND_PROC_LEGACY = "ps && ps -t"
|
||||
|
||||
# identify this as trace of thread / process state
|
||||
TRACE_HEADER = 'PROCESS DUMP\n'
|
||||
|
||||
def try_create_agent(config):
|
||||
if config.target != 'android':
|
||||
return None
|
||||
if config.from_file is not None:
|
||||
return None
|
||||
if config.process_dump_enable:
|
||||
# Since AtraceProcessDumpAgent was enabled it's unnecessary to collect ps
|
||||
# data because each process memory dump updates information about processes
|
||||
# and their threads. It's more complete data than two ps snapshots for an
|
||||
# entire trace. However, that agent isn't enabled by default.
|
||||
return None
|
||||
return AndroidProcessDataAgent()
|
||||
|
||||
def get_config(options):
|
||||
return options
|
||||
|
||||
class AndroidProcessDataAgent(tracing_agents.TracingAgent):
|
||||
def __init__(self):
|
||||
super(AndroidProcessDataAgent, self).__init__()
|
||||
self._trace_data = ""
|
||||
self._device = None
|
||||
|
||||
def __repr__(self):
|
||||
return 'android_process_data'
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
self._device = device_utils.DeviceUtils(config.device_serial_number)
|
||||
self._trace_data += self._get_process_snapshot()
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
self._trace_data += self._get_process_snapshot()
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
||||
def GetResults(self, timeout=None):
|
||||
result = TRACE_HEADER + self._trace_data
|
||||
return trace_result.TraceResult('androidProcessDump', result)
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
return False
|
||||
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
||||
pass
|
||||
|
||||
def _get_process_snapshot(self):
|
||||
use_legacy = False
|
||||
try:
|
||||
dump = self._device.RunShellCommand( \
|
||||
PS_COMMAND_PROC, check_return=True, as_root=True, shell=True)
|
||||
except AdbShellCommandFailedError:
|
||||
use_legacy = True
|
||||
|
||||
# Check length of 2 as we execute two commands, which in case of failure
|
||||
# on old devices output 1 line each.
|
||||
if use_legacy or len(dump) == 2:
|
||||
logging.debug('Couldn\'t parse ps dump, trying legacy method ...')
|
||||
dump = self._device.RunShellCommand( \
|
||||
PS_COMMAND_PROC_LEGACY, check_return=True, as_root=True, shell=True)
|
||||
if len(dump) == 2:
|
||||
logging.error('Unable to extract process data!')
|
||||
return ""
|
||||
|
||||
return '\n'.join(dump) + '\n'
|
||||
@@ -0,0 +1,472 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
import optparse
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
import threading
|
||||
import zlib
|
||||
|
||||
import py_utils
|
||||
|
||||
from devil.android import device_utils
|
||||
from devil.android.sdk import version_codes
|
||||
from py_trace_event import trace_time as trace_time_module
|
||||
from systrace import trace_result
|
||||
from systrace import tracing_agents
|
||||
from systrace import util
|
||||
|
||||
# Text that ADB sends, but does not need to be displayed to the user.
|
||||
ADB_IGNORE_REGEXP = r'^capturing trace\.\.\. done|^capturing trace\.\.\.'
|
||||
# The number of seconds to wait on output from ADB.
|
||||
ADB_STDOUT_READ_TIMEOUT = 0.2
|
||||
# The number of seconds to wait for large output from ADB.
|
||||
ADB_LARGE_OUTPUT_TIMEOUT = 600
|
||||
# The adb shell command to initiate a trace.
|
||||
ATRACE_BASE_ARGS = ['atrace']
|
||||
# If a custom list of categories is not specified, traces will include
|
||||
# these categories (if available on the device).
|
||||
DEFAULT_CATEGORIES = 'sched,freq,gfx,view,dalvik,webview,'\
|
||||
'input,disk,am,wm,rs,binder_driver'
|
||||
# The command to list trace categories.
|
||||
LIST_CATEGORIES_ARGS = ATRACE_BASE_ARGS + ['--list_categories']
|
||||
# Minimum number of seconds between displaying status updates.
|
||||
MIN_TIME_BETWEEN_STATUS_UPDATES = 0.2
|
||||
# ADB sends this text to indicate the beginning of the trace data.
|
||||
TRACE_START_REGEXP = r'TRACE\:'
|
||||
# Plain-text trace data should always start with this string.
|
||||
TRACE_TEXT_HEADER = '# tracer'
|
||||
_FIX_MISSING_TGIDS = True
|
||||
_FIX_CIRCULAR_TRACES = True
|
||||
|
||||
|
||||
def list_categories(config):
|
||||
"""List the possible trace event categories.
|
||||
|
||||
This function needs the tracing config since it needs to get the serial
|
||||
number of the device to send a command to.
|
||||
|
||||
Args:
|
||||
config: Tracing config.
|
||||
"""
|
||||
devutils = device_utils.DeviceUtils(config.device_serial_number)
|
||||
categories = devutils.RunShellCommand(
|
||||
LIST_CATEGORIES_ARGS, check_return=True)
|
||||
|
||||
device_sdk_version = util.get_device_sdk_version()
|
||||
if device_sdk_version < version_codes.MARSHMALLOW:
|
||||
# work around platform bug where rs tag would corrupt trace until M(Api23)
|
||||
categories = [c for c in categories if not re.match(r'^\s*rs\s*-', c)]
|
||||
|
||||
print '\n'.join(categories)
|
||||
if not devutils.HasRoot():
|
||||
print '\nNOTE: more categories may be available with adb root\n'
|
||||
|
||||
|
||||
def get_available_categories(config, device_sdk_version):
|
||||
"""Gets the list of atrace categories available for tracing.
|
||||
Args:
|
||||
config: Tracing config.
|
||||
device_sdk_version: Sdk version int of device to be queried.
|
||||
"""
|
||||
devutils = device_utils.DeviceUtils(config.device_serial_number)
|
||||
categories_output = devutils.RunShellCommand(
|
||||
LIST_CATEGORIES_ARGS, check_return=True)
|
||||
categories = [c.split('-')[0].strip() for c in categories_output]
|
||||
|
||||
if device_sdk_version < version_codes.MARSHMALLOW:
|
||||
# work around platform bug where rs tag would corrupt trace until M(Api23)
|
||||
categories = [c for c in categories if c != 'rs']
|
||||
return categories
|
||||
|
||||
|
||||
def try_create_agent(config):
|
||||
"""Create an Atrace agent.
|
||||
|
||||
Args:
|
||||
config: Command line config.
|
||||
"""
|
||||
if config.target != 'android':
|
||||
return None
|
||||
if config.from_file is not None:
|
||||
return None
|
||||
|
||||
if not config.atrace_categories:
|
||||
return None
|
||||
|
||||
# Check device SDK version.
|
||||
device_sdk_version = util.get_device_sdk_version()
|
||||
if device_sdk_version < version_codes.JELLY_BEAN_MR2:
|
||||
print ('Device SDK versions < 18 (Jellybean MR2) not supported.\n'
|
||||
'Your device SDK version is %d.' % device_sdk_version)
|
||||
return None
|
||||
|
||||
return AtraceAgent(device_sdk_version)
|
||||
|
||||
def _construct_extra_atrace_args(config, categories):
|
||||
"""Construct extra arguments (-a, -k, categories) for atrace command.
|
||||
|
||||
Args:
|
||||
config: Tracing config.
|
||||
"""
|
||||
extra_args = []
|
||||
|
||||
if config.app_name is not None:
|
||||
extra_args.extend(['-a', config.app_name])
|
||||
|
||||
if config.kfuncs is not None:
|
||||
extra_args.extend(['-k', config.kfuncs])
|
||||
|
||||
extra_args.extend(categories)
|
||||
return extra_args
|
||||
|
||||
def _construct_atrace_args(config, categories):
|
||||
"""Builds the command used to invoke a trace process.
|
||||
Returns:
|
||||
A tuple where the first element is an array of command arguments, and
|
||||
the second element is a boolean which will be true if the command will
|
||||
stream trace data.
|
||||
"""
|
||||
atrace_args = ATRACE_BASE_ARGS[:]
|
||||
|
||||
if config.compress_trace_data:
|
||||
atrace_args.extend(['-z'])
|
||||
|
||||
if (config.trace_time is not None) and (config.trace_time > 0):
|
||||
atrace_args.extend(['-t', str(config.trace_time)])
|
||||
|
||||
if (config.trace_buf_size is not None) and (config.trace_buf_size > 0):
|
||||
atrace_args.extend(['-b', str(config.trace_buf_size)])
|
||||
|
||||
elif 'webview' in categories and 'sched' in categories:
|
||||
# https://crbug.com/814330: webview_startup sometimes exceeds the buffer
|
||||
# limit, so doubling this.
|
||||
atrace_args.extend(['-b', '8192'])
|
||||
|
||||
elif 'sched' in categories:
|
||||
# 'sched' is a high-volume tag, double the default buffer size
|
||||
# to accommodate that
|
||||
atrace_args.extend(['-b', '4096'])
|
||||
extra_args = _construct_extra_atrace_args(config, categories)
|
||||
|
||||
atrace_args.extend(extra_args)
|
||||
return atrace_args
|
||||
|
||||
|
||||
class AtraceAgent(tracing_agents.TracingAgent):
|
||||
|
||||
def __init__(self, device_sdk_version):
|
||||
super(AtraceAgent, self).__init__()
|
||||
self._device_sdk_version = device_sdk_version
|
||||
self._adb = None
|
||||
self._trace_data = None
|
||||
self._tracer_args = None
|
||||
self._collection_thread = None
|
||||
self._device_utils = None
|
||||
self._device_serial_number = None
|
||||
self._config = None
|
||||
self._categories = None
|
||||
|
||||
def __repr__(self):
|
||||
return 'atrace'
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
assert config.atrace_categories, 'Atrace categories are missing!'
|
||||
self._config = config
|
||||
self._categories = config.atrace_categories
|
||||
if isinstance(self._categories, list):
|
||||
self._categories = ','.join(self._categories)
|
||||
avail_cats = get_available_categories(config, self._device_sdk_version)
|
||||
unavailable = [x for x in self._categories.split(',') if
|
||||
x not in avail_cats]
|
||||
self._categories = [x for x in self._categories.split(',') if
|
||||
x in avail_cats]
|
||||
if unavailable:
|
||||
print 'These categories are unavailable: ' + ' '.join(unavailable)
|
||||
self._device_utils = device_utils.DeviceUtils(config.device_serial_number)
|
||||
self._device_serial_number = config.device_serial_number
|
||||
self._tracer_args = _construct_atrace_args(config,
|
||||
self._categories)
|
||||
self._device_utils.RunShellCommand(
|
||||
self._tracer_args + ['--async_start'], check_return=True)
|
||||
return True
|
||||
|
||||
def _collect_and_preprocess(self):
|
||||
"""Collects and preprocesses trace data.
|
||||
|
||||
Stores results in self._trace_data.
|
||||
"""
|
||||
trace_data = self._collect_trace_data()
|
||||
self._trace_data = self._preprocess_trace_data(trace_data)
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
"""Stops tracing and starts collecting results.
|
||||
|
||||
To synchronously retrieve the results after calling this function,
|
||||
call GetResults().
|
||||
"""
|
||||
self._collection_thread = threading.Thread(
|
||||
target=self._collect_and_preprocess)
|
||||
self._collection_thread.start()
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
||||
def GetResults(self, timeout=None):
|
||||
"""Waits for collection thread to finish and returns trace results."""
|
||||
self._collection_thread.join()
|
||||
self._collection_thread = None
|
||||
return trace_result.TraceResult('systemTraceEvents', self._trace_data)
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
return True
|
||||
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
||||
"""Records a clock sync marker.
|
||||
|
||||
Args:
|
||||
sync_id: ID string for clock sync marker.
|
||||
"""
|
||||
cmd = 'echo trace_event_clock_sync: name=%s >' \
|
||||
' /sys/kernel/debug/tracing/trace_marker' % sync_id
|
||||
with self._device_utils.adb.PersistentShell(
|
||||
self._device_serial_number) as shell:
|
||||
t1 = trace_time_module.Now()
|
||||
shell.RunCommand(cmd, close=True)
|
||||
did_record_sync_marker_callback(t1, sync_id)
|
||||
|
||||
def _stop_collect_trace(self):
|
||||
"""Stops atrace.
|
||||
|
||||
Note that prior to Api 23, --async-stop isn't working correctly. It
|
||||
doesn't stop tracing and clears trace buffer before dumping it rendering
|
||||
results unusable."""
|
||||
if self._device_sdk_version < version_codes.MARSHMALLOW:
|
||||
is_trace_enabled_file = '/sys/kernel/debug/tracing/tracing_on'
|
||||
# Stop tracing first so new data won't arrive while dump is performed (it
|
||||
# may take a non-trivial time and tracing buffer may overflow).
|
||||
self._device_utils.WriteFile(is_trace_enabled_file, '0')
|
||||
result = self._device_utils.RunShellCommand(
|
||||
self._tracer_args + ['--async_dump'], raw_output=True,
|
||||
large_output=True, check_return=True,
|
||||
timeout=ADB_LARGE_OUTPUT_TIMEOUT)
|
||||
# Run synchronous tracing for 0 seconds to stop tracing, clear buffers
|
||||
# and other state.
|
||||
self._device_utils.RunShellCommand(
|
||||
self._tracer_args + ['-t 0'], check_return=True)
|
||||
else:
|
||||
# On M+ --async_stop does everything necessary
|
||||
result = self._device_utils.RunShellCommand(
|
||||
self._tracer_args + ['--async_stop'], raw_output=True,
|
||||
large_output=True, check_return=True,
|
||||
timeout=ADB_LARGE_OUTPUT_TIMEOUT)
|
||||
|
||||
return result
|
||||
|
||||
def _collect_trace_data(self):
|
||||
"""Reads the output from atrace and stops the trace."""
|
||||
result = self._stop_collect_trace()
|
||||
|
||||
data_start = re.search(TRACE_START_REGEXP, result)
|
||||
if data_start:
|
||||
data_start = data_start.end(0)
|
||||
else:
|
||||
raise IOError('Unable to get atrace data. Did you forget adb root?')
|
||||
output = re.sub(ADB_IGNORE_REGEXP, '', result[data_start:])
|
||||
return output
|
||||
|
||||
def _preprocess_trace_data(self, trace_data):
|
||||
"""Performs various processing on atrace data.
|
||||
|
||||
Args:
|
||||
trace_data: The raw trace data.
|
||||
Returns:
|
||||
The processed trace data.
|
||||
"""
|
||||
if trace_data:
|
||||
trace_data = strip_and_decompress_trace(trace_data)
|
||||
|
||||
if not trace_data:
|
||||
print >> sys.stderr, ('No data was captured. Output file was not '
|
||||
'written.')
|
||||
sys.exit(1)
|
||||
|
||||
if _FIX_MISSING_TGIDS:
|
||||
# Gather proc data from device and patch tgids
|
||||
procfs_dump = self._device_utils.RunShellCommand(
|
||||
'echo -n /proc/[0-9]*/task/[0-9]*',
|
||||
shell=True, check_return=True)[0].split(' ')
|
||||
pid2_tgid = extract_tgids(procfs_dump)
|
||||
trace_data = fix_missing_tgids(trace_data, pid2_tgid)
|
||||
|
||||
if _FIX_CIRCULAR_TRACES:
|
||||
trace_data = fix_circular_traces(trace_data)
|
||||
|
||||
return trace_data
|
||||
|
||||
def extract_tgids(trace_lines):
|
||||
"""Removes the procfs dump from the given trace text
|
||||
|
||||
Args:
|
||||
trace_lines: The text portion of the trace
|
||||
|
||||
Returns:
|
||||
a map of pids to their tgid.
|
||||
"""
|
||||
tgid_2pid = {}
|
||||
for line in trace_lines:
|
||||
result = re.match('^/proc/([0-9]+)/task/([0-9]+)', line)
|
||||
if result:
|
||||
parent_pid, tgid = result.group(1, 2)
|
||||
tgid_2pid[tgid] = parent_pid
|
||||
|
||||
return tgid_2pid
|
||||
|
||||
|
||||
def strip_and_decompress_trace(trace_data):
|
||||
"""Fixes new-lines and decompresses trace data.
|
||||
|
||||
Args:
|
||||
trace_data: The trace data returned by atrace.
|
||||
Returns:
|
||||
The decompressed trace data.
|
||||
"""
|
||||
# Collapse CRLFs that are added by adb shell.
|
||||
if trace_data.startswith('\r\n'):
|
||||
trace_data = trace_data.replace('\r\n', '\n')
|
||||
elif trace_data.startswith('\r\r\n'):
|
||||
# On windows, adb adds an extra '\r' character for each line.
|
||||
trace_data = trace_data.replace('\r\r\n', '\n')
|
||||
|
||||
# Skip the initial newline.
|
||||
if trace_data[0] == '\n':
|
||||
trace_data = trace_data[1:]
|
||||
|
||||
if not trace_data.startswith(TRACE_TEXT_HEADER):
|
||||
# No header found, so assume the data is compressed.
|
||||
trace_data = zlib.decompress(trace_data)
|
||||
|
||||
# Enforce Unix line-endings.
|
||||
trace_data = trace_data.replace('\r', '')
|
||||
|
||||
# Skip any initial newlines.
|
||||
while trace_data and trace_data[0] == '\n':
|
||||
trace_data = trace_data[1:]
|
||||
|
||||
return trace_data
|
||||
|
||||
|
||||
def fix_missing_tgids(trace_data, pid2_tgid):
|
||||
"""Replaces missing TGIDs from the trace data with those found in procfs
|
||||
|
||||
Args:
|
||||
trace_data: the atrace data
|
||||
|
||||
Returns:
|
||||
The updated trace data with missing TGIDs replaced with the correct TGID
|
||||
"""
|
||||
|
||||
def repl(m):
|
||||
tid = m.group(2)
|
||||
if (int(tid) > 0 and m.group(1) != '<idle>' and m.group(3) == '(-----)'
|
||||
and tid in pid2_tgid):
|
||||
# returns Proc_name-PID (TGID)
|
||||
# Binder_2-381 (-----) becomes Binder_2-381 (128)
|
||||
return m.group(1) + '-' + m.group(2) + ' ( ' + pid2_tgid[tid] + ')'
|
||||
|
||||
return m.group(0)
|
||||
|
||||
# matches something like:
|
||||
# Binder_2-895 (-----)
|
||||
trace_data = re.sub(r'^\s*(\S+)-(\d+)\s+(\(\S+\))', repl, trace_data,
|
||||
flags=re.MULTILINE)
|
||||
return trace_data
|
||||
|
||||
|
||||
def fix_circular_traces(out):
|
||||
"""Fix inconsistentcies in traces due to circular buffering.
|
||||
|
||||
The circular buffers are kept per CPU, so it is not guaranteed that the
|
||||
beginning of a slice is overwritten before the end. To work around this, we
|
||||
throw away the prefix of the trace where not all CPUs have events yet.
|
||||
|
||||
Args:
|
||||
out: The data to fix.
|
||||
Returns:
|
||||
The updated trace data.
|
||||
"""
|
||||
# If any of the CPU's buffers have filled up and
|
||||
# older events have been dropped, the kernel
|
||||
# emits markers of the form '##### CPU 2 buffer started ####' on
|
||||
# the line before the first event in the trace on that CPU.
|
||||
#
|
||||
# No such headers are emitted if there were no overflows or the trace
|
||||
# was captured with non-circular buffers.
|
||||
buffer_start_re = re.compile(r'^#+ CPU \d+ buffer started', re.MULTILINE)
|
||||
|
||||
start_of_full_trace = 0
|
||||
|
||||
while True:
|
||||
result = buffer_start_re.search(out, start_of_full_trace + 1)
|
||||
if result:
|
||||
start_of_full_trace = result.start()
|
||||
else:
|
||||
break
|
||||
|
||||
if start_of_full_trace > 0:
|
||||
# Need to keep the header intact to make the importer happy.
|
||||
end_of_header = re.search(r'^[^#]', out, re.MULTILINE).start()
|
||||
out = out[:end_of_header] + out[start_of_full_trace:]
|
||||
return out
|
||||
|
||||
|
||||
class AtraceConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self, atrace_categories, trace_buf_size, kfuncs,
|
||||
app_name, compress_trace_data, from_file,
|
||||
device_serial_number, trace_time, target):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
self.atrace_categories = atrace_categories
|
||||
self.trace_buf_size = trace_buf_size
|
||||
self.kfuncs = kfuncs
|
||||
self.app_name = app_name
|
||||
# Trace compression is broken on Windows.
|
||||
# TODO: Fix https://crbug.com/739751.
|
||||
self.compress_trace_data = \
|
||||
compress_trace_data and platform.system() != 'Windows'
|
||||
self.from_file = from_file
|
||||
self.device_serial_number = device_serial_number
|
||||
self.trace_time = trace_time
|
||||
self.target = target
|
||||
|
||||
|
||||
def add_options(parser):
|
||||
options = optparse.OptionGroup(parser, 'Atrace options')
|
||||
options.add_option('--atrace-categories', dest='atrace_categories',
|
||||
help='Select atrace categories with a comma-delimited '
|
||||
'list, e.g. --atrace-categories=cat1,cat2,cat3')
|
||||
options.add_option('-k', '--ktrace', dest='kfuncs', action='store',
|
||||
help='specify a comma-separated list of kernel functions '
|
||||
'to trace')
|
||||
options.add_option('--no-compress', dest='compress_trace_data',
|
||||
default=True, action='store_false',
|
||||
help='Tell the device not to send the trace data in '
|
||||
'compressed form.')
|
||||
options.add_option('-a', '--app', dest='app_name', default=None,
|
||||
type='string', action='store',
|
||||
help='enable application-level tracing for '
|
||||
'comma-separated list of app cmdlines')
|
||||
options.add_option('--from-file', dest='from_file',
|
||||
action='store', help='read the trace from a '
|
||||
'file (compressed) rather than running a '
|
||||
'live trace')
|
||||
return options
|
||||
|
||||
def get_config(options):
|
||||
return AtraceConfig(options.atrace_categories,
|
||||
options.trace_buf_size, options.kfuncs,
|
||||
options.app_name, options.compress_trace_data,
|
||||
options.from_file, options.device_serial_number,
|
||||
options.trace_time, options.target)
|
||||
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) 2015 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.
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from systrace import decorators
|
||||
from systrace import run_systrace
|
||||
from systrace import util
|
||||
from systrace.tracing_agents import atrace_agent
|
||||
|
||||
from devil.android import device_utils
|
||||
from devil.android.sdk import intent
|
||||
from py_utils import tempfile_ext
|
||||
|
||||
|
||||
DEVICE_SERIAL = 'AG8404EC0444AGC'
|
||||
ATRACE_ARGS = ['atrace', '-z', '-t', '10', '-b', '4096']
|
||||
CATEGORIES = ['sched', 'gfx', 'view', 'wm']
|
||||
ADB_SHELL = ['adb', '-s', DEVICE_SERIAL, 'shell']
|
||||
|
||||
SYSTRACE_CMD = ['./run_systrace.py', '--time', '10', '-o', 'out.html', '-e',
|
||||
DEVICE_SERIAL] + CATEGORIES
|
||||
TRACE_ARGS = (ATRACE_ARGS + CATEGORIES)
|
||||
|
||||
TEST_DIR = os.path.join(os.path.dirname(__file__), os.pardir, 'test_data')
|
||||
ATRACE_DATA = os.path.join(TEST_DIR, 'atrace_data')
|
||||
ATRACE_DATA_RAW = os.path.join(TEST_DIR, 'atrace_data_raw')
|
||||
ATRACE_DATA_STRIPPED = os.path.join(TEST_DIR, 'atrace_data_stripped')
|
||||
ATRACE_PROCFS_DUMP = os.path.join(TEST_DIR, 'atrace_procfs_dump')
|
||||
ATRACE_EXTRACTED_TGIDS = os.path.join(TEST_DIR, 'atrace_extracted_tgids')
|
||||
ATRACE_MISSING_TGIDS = os.path.join(TEST_DIR, 'atrace_missing_tgids')
|
||||
ATRACE_FIXED_TGIDS = os.path.join(TEST_DIR, 'atrace_fixed_tgids')
|
||||
|
||||
|
||||
class AtraceAgentTest(unittest.TestCase):
|
||||
|
||||
# TODO(washingtonp): These end-to-end tests do not work on the Trybot server
|
||||
# because adb cannot be found on the Trybot servers. Figure out what the
|
||||
# issue is and update this test.
|
||||
@decorators.Disabled
|
||||
def test_tracing(self):
|
||||
TRACE_BUFFER_SIZE = '16384'
|
||||
TRACE_TIME = '5'
|
||||
|
||||
devices = device_utils.DeviceUtils.HealthyDevices()
|
||||
package_info = util.get_supported_browsers()['stable']
|
||||
device = devices[0]
|
||||
with tempfile_ext.TemporaryFileName() as output_file_name:
|
||||
# Launch the browser before tracing.
|
||||
device.StartActivity(
|
||||
intent.Intent(activity=package_info.activity,
|
||||
package=package_info.package,
|
||||
data='about:blank',
|
||||
extras={'create_new_tab': True}),
|
||||
blocking=True, force_stop=True)
|
||||
|
||||
# Run atrace agent.
|
||||
run_systrace.main_impl(['./run_systrace.py',
|
||||
'-b',
|
||||
TRACE_BUFFER_SIZE,
|
||||
'-t',
|
||||
TRACE_TIME,
|
||||
'-o',
|
||||
output_file_name,
|
||||
'-e',
|
||||
str(device),
|
||||
'--atrace-categories=gfx,input,view'])
|
||||
|
||||
# Verify results.
|
||||
with open(output_file_name, 'r') as f:
|
||||
full_trace = f.read()
|
||||
self.assertTrue('CPU#' in full_trace)
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_construct_atrace_args(self):
|
||||
options, categories = run_systrace.parse_options(SYSTRACE_CMD)
|
||||
options.atrace_categories = categories
|
||||
tracer_args = atrace_agent._construct_atrace_args(options, categories)
|
||||
self.assertEqual(' '.join(TRACE_ARGS), ' '.join(tracer_args))
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_strip_and_decompress_trace(self):
|
||||
with contextlib.nested(open(ATRACE_DATA_RAW, 'r'),
|
||||
open(ATRACE_DATA_STRIPPED, 'r')) as (f1, f2):
|
||||
atrace_data_raw = f1.read()
|
||||
atrace_data_stripped = f2.read()
|
||||
|
||||
trace_data = atrace_agent.strip_and_decompress_trace(atrace_data_raw)
|
||||
self.assertEqual(atrace_data_stripped, trace_data)
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_extract_tgids(self):
|
||||
with contextlib.nested(open(ATRACE_PROCFS_DUMP, 'r'),
|
||||
open(ATRACE_EXTRACTED_TGIDS, 'r')) as (f1, f2):
|
||||
|
||||
atrace_procfs_dump = f1.read()
|
||||
atrace_procfs_extracted = f2.read()
|
||||
|
||||
tgids = eval(atrace_procfs_extracted)
|
||||
result = atrace_agent.extract_tgids(atrace_procfs_dump.splitlines())
|
||||
|
||||
self.assertEqual(result, tgids)
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_fix_missing_tgids(self):
|
||||
with contextlib.nested(open(ATRACE_EXTRACTED_TGIDS, 'r'),
|
||||
open(ATRACE_MISSING_TGIDS, 'r'),
|
||||
open(ATRACE_FIXED_TGIDS, 'r')) as (f1, f2, f3):
|
||||
|
||||
atrace_data = f2.read()
|
||||
tgid_map = eval(f1.read())
|
||||
fixed = f3.read()
|
||||
|
||||
res = atrace_agent.fix_missing_tgids(atrace_data, tgid_map)
|
||||
self.assertEqual(res, fixed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
unittest.main(verbosity=2)
|
||||
@@ -0,0 +1,130 @@
|
||||
# Copyright 2016 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.
|
||||
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib2
|
||||
|
||||
import py_utils
|
||||
|
||||
from systrace import trace_result
|
||||
from systrace import tracing_agents
|
||||
from systrace.tracing_agents import atrace_agent
|
||||
|
||||
|
||||
# ADB sends this text to indicate the beginning of the trace data.
|
||||
TRACE_START_REGEXP = r'TRACE\:'
|
||||
# Text that ADB sends, but does not need to be displayed to the user.
|
||||
ADB_IGNORE_REGEXP = r'^capturing trace\.\.\. done|^capturing trace\.\.\.'
|
||||
|
||||
T2T_OUTPUT = 'trace.systrace'
|
||||
|
||||
def try_create_agent(options):
|
||||
if options.from_file is not None:
|
||||
with open(options.from_file, 'rb') as f_in:
|
||||
if is_perfetto(f_in):
|
||||
if convert_perfetto_trace(options.from_file):
|
||||
options.from_file = T2T_OUTPUT
|
||||
else:
|
||||
print ('Perfetto trace file: ' + options.from_file +
|
||||
' could not be converted.')
|
||||
sys.exit(1)
|
||||
return AtraceFromFileAgent(options)
|
||||
else:
|
||||
return False
|
||||
|
||||
def convert_perfetto_trace(in_file):
|
||||
traceconv_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'../traceconv'))
|
||||
try:
|
||||
traceconv = urllib2.urlopen('https://get.perfetto.dev/traceconv')
|
||||
with open(traceconv_path, 'w') as out:
|
||||
out.write(traceconv.read())
|
||||
except urllib2.URLError:
|
||||
print 'Could not download traceconv to convert the Perfetto trace.'
|
||||
sys.exit(1)
|
||||
os.chmod(traceconv_path, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
|
||||
return subprocess.call([traceconv_path, 'systrace', in_file, T2T_OUTPUT]) == 0
|
||||
|
||||
def is_perfetto(from_file):
|
||||
# Starts with a preamble for field ID=1 (TracePacket)
|
||||
if ord(from_file.read(1)) != 0x0a:
|
||||
return False
|
||||
for _ in range(10): # Check the first 10 packets are structured correctly
|
||||
# Then a var int that specifies field size
|
||||
field_size = 0
|
||||
shift = 0
|
||||
while True:
|
||||
c = ord(from_file.read(1))
|
||||
field_size |= (c & 0x7f) << shift
|
||||
shift += 7
|
||||
if not c & 0x80:
|
||||
break
|
||||
# The packet itself
|
||||
from_file.seek(field_size, os.SEEK_CUR)
|
||||
# The preamble for the next field ID=1 (TracePacket)
|
||||
if ord(from_file.read(1)) != 0x0a:
|
||||
return False
|
||||
# Go back to the beginning of the file
|
||||
from_file.seek(0)
|
||||
return True
|
||||
|
||||
class AtraceFromFileConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self, from_file):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
self.fix_circular = True
|
||||
self.from_file = from_file
|
||||
|
||||
def add_options(parser): # pylint: disable=unused-argument
|
||||
# The atrace_from_file_agent is not currently used, so don't display
|
||||
# any options.
|
||||
return None
|
||||
|
||||
def get_config(options):
|
||||
return AtraceFromFileConfig(options.from_file)
|
||||
|
||||
|
||||
class AtraceFromFileAgent(tracing_agents.TracingAgent):
|
||||
def __init__(self, options):
|
||||
super(AtraceFromFileAgent, self).__init__()
|
||||
self._filename = os.path.expanduser(options.from_file)
|
||||
self._trace_data = False
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
# pylint: disable=unused-argument
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
self._trace_data = self._read_trace_data()
|
||||
return True
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
return False
|
||||
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_clock_sync_callback):
|
||||
raise NotImplementedError
|
||||
|
||||
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
||||
def GetResults(self, timeout=None):
|
||||
return trace_result.TraceResult('trace-data', self._trace_data)
|
||||
|
||||
def _read_trace_data(self):
|
||||
with open(self._filename, 'rb') as f:
|
||||
result = f.read()
|
||||
data_start = re.search(TRACE_START_REGEXP, result).end(0)
|
||||
data = re.sub(ADB_IGNORE_REGEXP, '', result[data_start:])
|
||||
return self._preprocess_data(data)
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def _preprocess_data(self, data):
|
||||
# TODO: add fix_threads and fix_tgids options back in here
|
||||
# once we embed the dump data in the file (b/27504068)
|
||||
data = atrace_agent.strip_and_decompress_trace(data)
|
||||
data = atrace_agent.fix_circular_traces(data)
|
||||
return data
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) 2016 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.
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from py_utils import tempfile_ext
|
||||
from systrace import decorators
|
||||
from systrace import run_systrace
|
||||
from systrace import update_systrace_trace_viewer
|
||||
|
||||
|
||||
TEST_DIR = os.path.join(os.path.dirname(__file__), '..', 'test_data')
|
||||
|
||||
COMPRESSED_ATRACE_DATA = os.path.join(TEST_DIR, 'compressed_atrace_data.txt')
|
||||
DECOMPRESSED_ATRACE_DATA = os.path.join(TEST_DIR,
|
||||
'decompressed_atrace_data.txt')
|
||||
NON_EXISTENT_DATA = os.path.join(TEST_DIR, 'THIS_FILE_DOES_NOT_EXIST.txt')
|
||||
|
||||
|
||||
class AtraceFromFileAgentTest(unittest.TestCase):
|
||||
@decorators.HostOnlyTest
|
||||
def test_from_file(self):
|
||||
update_systrace_trace_viewer.update(force_update=True)
|
||||
self.assertTrue(os.path.exists(
|
||||
update_systrace_trace_viewer.SYSTRACE_TRACE_VIEWER_HTML_FILE))
|
||||
try:
|
||||
with tempfile_ext.TemporaryFileName() as output_file_name:
|
||||
# use from-file to create a specific expected output
|
||||
run_systrace.main_impl(['./run_systrace.py',
|
||||
'--from-file',
|
||||
COMPRESSED_ATRACE_DATA,
|
||||
'-o',
|
||||
output_file_name])
|
||||
# and verify file contents
|
||||
with contextlib.nested(open(output_file_name, 'r'),
|
||||
open(DECOMPRESSED_ATRACE_DATA, 'r')) as (f1, f2):
|
||||
full_trace = f1.read()
|
||||
expected_contents = f2.read()
|
||||
self.assertTrue(expected_contents in full_trace)
|
||||
finally:
|
||||
os.remove(update_systrace_trace_viewer.SYSTRACE_TRACE_VIEWER_HTML_FILE)
|
||||
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_default_output_filename(self):
|
||||
update_systrace_trace_viewer.update(force_update=True)
|
||||
self.assertTrue(os.path.exists(
|
||||
update_systrace_trace_viewer.SYSTRACE_TRACE_VIEWER_HTML_FILE))
|
||||
output_file_name = os.path.join(TEST_DIR, 'compressed_atrace_data.html')
|
||||
try:
|
||||
# use from-file to create a specific expected output
|
||||
run_systrace.main_impl(['./run_systrace.py',
|
||||
'--from-file',
|
||||
COMPRESSED_ATRACE_DATA])
|
||||
# and verify file contents
|
||||
with contextlib.nested(open(output_file_name, 'r'),
|
||||
open(DECOMPRESSED_ATRACE_DATA, 'r')) as (f1, f2):
|
||||
full_trace = f1.read()
|
||||
expected_contents = f2.read()
|
||||
self.assertTrue(expected_contents in full_trace)
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
os.remove(update_systrace_trace_viewer.SYSTRACE_TRACE_VIEWER_HTML_FILE)
|
||||
if os.path.exists(output_file_name):
|
||||
os.remove(output_file_name)
|
||||
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_missing_file(self):
|
||||
try:
|
||||
run_systrace.main_impl(['./run_systrace.py',
|
||||
'--from-file',
|
||||
NON_EXISTENT_DATA])
|
||||
self.fail('should not get here')
|
||||
except IOError:
|
||||
pass
|
||||
@@ -0,0 +1,128 @@
|
||||
# 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.
|
||||
|
||||
# Tracing agent that captures periodic per-process memory dumps and other
|
||||
# useful information from ProcFS like utime, stime, OOM stats, etc.
|
||||
|
||||
import json
|
||||
import logging
|
||||
import optparse
|
||||
import py_utils
|
||||
|
||||
from devil.android import device_utils
|
||||
from devil.android.device_errors import AdbShellCommandFailedError
|
||||
from py_trace_event import trace_time as trace_time_module
|
||||
from systrace import tracing_agents
|
||||
from systrace import trace_result
|
||||
|
||||
TRACE_HEADER = 'ATRACE_PROCESS_DUMP'
|
||||
TRACE_RESULT_NAME = 'atraceProcessDump'
|
||||
|
||||
HELPER_COMMAND = '/data/local/tmp/atrace_helper'
|
||||
HELPER_STOP_COMMAND = 'kill -TERM `pidof atrace_helper`'
|
||||
HELPER_DUMP_JSON = '/data/local/tmp/procdump.json'
|
||||
|
||||
|
||||
class AtraceProcessDumpAgent(tracing_agents.TracingAgent):
|
||||
def __init__(self):
|
||||
super(AtraceProcessDumpAgent, self).__init__()
|
||||
self._device = None
|
||||
self._dump = None
|
||||
self._clock_sync_markers = {}
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
self._device = device_utils.DeviceUtils(config.device_serial_number)
|
||||
cmd = [HELPER_COMMAND, '-b', '-g',
|
||||
'-t', str(config.dump_interval_ms),
|
||||
'-o', HELPER_DUMP_JSON]
|
||||
if config.full_dump_config:
|
||||
cmd += ['-m', config.full_dump_config]
|
||||
if config.enable_mmaps:
|
||||
cmd += ['-s']
|
||||
self._device.RunShellCommand(cmd, check_return=True, as_root=True)
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
self._device.RunShellCommand(
|
||||
HELPER_STOP_COMMAND,
|
||||
shell=True, check_return=True, as_root=True)
|
||||
try:
|
||||
self._device.RunShellCommand(['test', '-f', HELPER_DUMP_JSON],
|
||||
check_return=True, as_root=True)
|
||||
self._dump = self._device.ReadFile(HELPER_DUMP_JSON, force_pull=True)
|
||||
self._device.RunShellCommand(['rm', HELPER_DUMP_JSON],
|
||||
check_return=True, as_root=True)
|
||||
except AdbShellCommandFailedError:
|
||||
logging.error('AtraceProcessDumpAgent failed to pull data. Check device storage.')
|
||||
return False
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
||||
def GetResults(self, timeout=None):
|
||||
result = TRACE_HEADER + '\n' + self._dump
|
||||
cs = json.dumps(self._clock_sync_markers)
|
||||
result = TRACE_HEADER + \
|
||||
'\n{\"clock_sync_markers\":' + cs + ',\n\"dump\":' + self._dump + '}'
|
||||
return trace_result.TraceResult(TRACE_RESULT_NAME, result)
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
return True
|
||||
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
||||
with self._device.adb.PersistentShell(self._device.serial) as shell:
|
||||
ts_in_controller_domain = trace_time_module.Now()
|
||||
output = shell.RunCommand(HELPER_COMMAND + ' --echo-ts', close=True)
|
||||
ts_in_agent_domain = int(output[0][0])
|
||||
self._clock_sync_markers[sync_id] = ts_in_agent_domain
|
||||
did_record_sync_marker_callback(ts_in_controller_domain, sync_id)
|
||||
|
||||
|
||||
class AtraceProcessDumpConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self, enabled, device_serial_number,
|
||||
dump_interval_ms, full_dump_config, enable_mmaps):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
self.enabled = enabled
|
||||
self.device_serial_number = device_serial_number
|
||||
self.dump_interval_ms = dump_interval_ms
|
||||
self.full_dump_config = full_dump_config
|
||||
self.enable_mmaps = enable_mmaps
|
||||
|
||||
|
||||
def add_options(parser):
|
||||
options = optparse.OptionGroup(parser, 'Atrace process dump options')
|
||||
options.add_option('--process-dump', dest='process_dump_enable',
|
||||
default=False, action='store_true',
|
||||
help='Capture periodic per-process memory dumps.')
|
||||
options.add_option('--process-dump-interval', dest='process_dump_interval_ms',
|
||||
default=5000,
|
||||
help='Interval between memory dumps in milliseconds.')
|
||||
options.add_option('--process-dump-full', dest='process_dump_full_config',
|
||||
default=None,
|
||||
help='Capture full memory dumps for some processes.\n' \
|
||||
'Value: all, apps or comma-separated process names.')
|
||||
options.add_option('--process-dump-mmaps', dest='process_dump_mmaps',
|
||||
default=False, action='store_true',
|
||||
help='Capture VM regions and memory-mapped files.\n' \
|
||||
'It increases dump size dramatically, hence only ' \
|
||||
'has effect if --process-dump-full is a whitelist.')
|
||||
return options
|
||||
|
||||
|
||||
def get_config(options):
|
||||
can_enable = (options.target == 'android') and (not options.from_file)
|
||||
return AtraceProcessDumpConfig(
|
||||
enabled=(options.process_dump_enable and can_enable),
|
||||
device_serial_number=options.device_serial_number,
|
||||
dump_interval_ms=options.process_dump_interval_ms,
|
||||
full_dump_config=options.process_dump_full_config,
|
||||
enable_mmaps=options.process_dump_mmaps
|
||||
)
|
||||
|
||||
|
||||
def try_create_agent(config):
|
||||
if config.enabled:
|
||||
return AtraceProcessDumpAgent()
|
||||
return None
|
||||
@@ -0,0 +1,258 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import py_utils
|
||||
|
||||
from systrace import trace_result
|
||||
from systrace import tracing_agents
|
||||
|
||||
|
||||
class FtraceAgentIo(object):
|
||||
@staticmethod
|
||||
def writeFile(path, data):
|
||||
if FtraceAgentIo.haveWritePermissions(path):
|
||||
with open(path, 'w') as f:
|
||||
f.write(data)
|
||||
else:
|
||||
raise IOError('Cannot write to %s; did you forget sudo/root?' % path)
|
||||
|
||||
@staticmethod
|
||||
def readFile(path):
|
||||
with open(path, 'r') as f:
|
||||
return f.read()
|
||||
|
||||
@staticmethod
|
||||
def haveWritePermissions(path):
|
||||
return os.access(path, os.W_OK)
|
||||
|
||||
|
||||
FT_DIR = "/sys/kernel/debug/tracing/"
|
||||
FT_CLOCK = FT_DIR + "trace_clock"
|
||||
FT_BUFFER_SIZE = FT_DIR + "buffer_size_kb"
|
||||
FT_TRACER = FT_DIR + "current_tracer"
|
||||
FT_PRINT_TGID = FT_DIR + "options/print-tgid"
|
||||
FT_TRACE_ON = FT_DIR + "tracing_on"
|
||||
FT_TRACE = FT_DIR + "trace"
|
||||
FT_TRACE_MARKER = FT_DIR + "trace_marker"
|
||||
FT_OVERWRITE = FT_DIR + "options/overwrite"
|
||||
|
||||
all_categories = {
|
||||
"sched": {
|
||||
"desc": "CPU Scheduling",
|
||||
"req": ["sched/sched_switch/", "sched/sched_wakeup/"]
|
||||
},
|
||||
"freq": {
|
||||
"desc": "CPU Frequency",
|
||||
"req": ["power/cpu_frequency/"],
|
||||
"opt": ["power/clock_set_rate/", "clk/clk_set_rate/"]
|
||||
},
|
||||
"irq": {
|
||||
"desc": "CPU IRQS and IPIS",
|
||||
"req": ["irq/"],
|
||||
"opt": ["ipi/"]
|
||||
},
|
||||
"workq": {
|
||||
"desc": "Kernel workqueues",
|
||||
"req": ["workqueue/"]
|
||||
},
|
||||
"memreclaim": {
|
||||
"desc": "Kernel Memory Reclaim",
|
||||
"req": ["vmscan/mm_vmscan_direct_reclaim_begin/",
|
||||
"vmscan/mm_vmscan_direct_reclaim_end/",
|
||||
"vmscan/mm_vmscan_kswapd_wake/",
|
||||
"vmscan/mm_vmscan_kswapd_sleep/"]
|
||||
},
|
||||
"idle": {
|
||||
"desc": "CPU Idle",
|
||||
"req": ["power/cpu_idle/"]
|
||||
},
|
||||
"regulators": {
|
||||
"desc": "Voltage and Current Regulators",
|
||||
"req": ["regulator/"]
|
||||
},
|
||||
"disk": {
|
||||
"desc": "Disk I/O",
|
||||
"req": ["block/block_rq_issue/",
|
||||
"block/block_rq_complete/"],
|
||||
"opt": ["f2fs/f2fs_sync_file_enter/",
|
||||
"f2fs/f2fs_sync_file_exit/",
|
||||
"f2fs/f2fs_write_begin/",
|
||||
"f2fs/f2fs_write_end/",
|
||||
"ext4/ext4_da_write_begin/",
|
||||
"ext4/ext4_da_write_end/",
|
||||
"ext4/ext4_sync_file_enter/",
|
||||
"ext4/ext4_sync_file_exit/"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def try_create_agent(config):
|
||||
if config.target != 'linux':
|
||||
return None
|
||||
return FtraceAgent(FtraceAgentIo)
|
||||
|
||||
|
||||
def list_categories(_):
|
||||
agent = FtraceAgent(FtraceAgentIo)
|
||||
agent._print_avail_categories()
|
||||
|
||||
|
||||
class FtraceConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self, ftrace_categories, target, trace_buf_size):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
self.ftrace_categories = ftrace_categories
|
||||
self.target = target
|
||||
self.trace_buf_size = trace_buf_size
|
||||
|
||||
|
||||
def add_options(parser):
|
||||
options = optparse.OptionGroup(parser, 'Ftrace options')
|
||||
options.add_option('--ftrace-categories', dest='ftrace_categories',
|
||||
help='Select ftrace categories with a comma-delimited '
|
||||
'list, e.g. --ftrace-categories=cat1,cat2,cat3')
|
||||
return options
|
||||
|
||||
|
||||
def get_config(options):
|
||||
return FtraceConfig(options.ftrace_categories, options.target,
|
||||
options.trace_buf_size)
|
||||
|
||||
|
||||
class FtraceAgent(tracing_agents.TracingAgent):
|
||||
|
||||
def __init__(self, fio=FtraceAgentIo):
|
||||
"""Initialize a systrace agent.
|
||||
|
||||
Args:
|
||||
config: The command-line config.
|
||||
categories: The trace categories to capture.
|
||||
"""
|
||||
super(FtraceAgent, self).__init__()
|
||||
self._fio = fio
|
||||
self._config = None
|
||||
self._categories = None
|
||||
|
||||
def _get_trace_buffer_size(self):
|
||||
buffer_size = 4096
|
||||
if ((self._config.trace_buf_size is not None)
|
||||
and (self._config.trace_buf_size > 0)):
|
||||
buffer_size = self._config.trace_buf_size
|
||||
return buffer_size
|
||||
|
||||
def _fix_categories(self, categories):
|
||||
"""
|
||||
Applies the default category (sched) if there are no categories
|
||||
in the list and removes unavailable categories from the list.
|
||||
Args:
|
||||
categories: List of categories.
|
||||
"""
|
||||
if not categories:
|
||||
categories = ["sched"]
|
||||
return [x for x in categories
|
||||
if self._is_category_available(x)]
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
"""Start tracing.
|
||||
"""
|
||||
self._config = config
|
||||
categories = self._fix_categories(config.ftrace_categories)
|
||||
self._fio.writeFile(FT_BUFFER_SIZE,
|
||||
str(self._get_trace_buffer_size()))
|
||||
self._fio.writeFile(FT_CLOCK, 'global')
|
||||
self._fio.writeFile(FT_TRACER, 'nop')
|
||||
self._fio.writeFile(FT_OVERWRITE, "0")
|
||||
|
||||
# TODO: riandrews to push necessary patches for TGID option to upstream
|
||||
# linux kernel
|
||||
# self._fio.writeFile(FT_PRINT_TGID, '1')
|
||||
|
||||
for category in categories:
|
||||
self._category_enable(category)
|
||||
|
||||
self._categories = categories # need to store list of categories to disable
|
||||
print 'starting tracing.'
|
||||
|
||||
self._fio.writeFile(FT_TRACE, '')
|
||||
self._fio.writeFile(FT_TRACE_ON, '1')
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
"""Collect the result of tracing.
|
||||
|
||||
This function will block while collecting the result. For sync mode, it
|
||||
reads the data, e.g., from stdout, until it finishes. For async mode, it
|
||||
blocks until the agent is stopped and the data is ready.
|
||||
"""
|
||||
self._fio.writeFile(FT_TRACE_ON, '0')
|
||||
for category in self._categories:
|
||||
self._category_disable(category)
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
||||
def GetResults(self, timeout=None):
|
||||
# get the output
|
||||
d = self._fio.readFile(FT_TRACE)
|
||||
self._fio.writeFile(FT_BUFFER_SIZE, "1")
|
||||
return trace_result.TraceResult('trace-data', d)
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
return False
|
||||
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
||||
# No implementation, but need to have this to support the API
|
||||
# pylint: disable=unused-argument
|
||||
return False
|
||||
|
||||
def _is_category_available(self, category):
|
||||
if category not in all_categories:
|
||||
return False
|
||||
events_dir = FT_DIR + "events/"
|
||||
req_events = all_categories[category]["req"]
|
||||
for event in req_events:
|
||||
event_full_path = events_dir + event + "enable"
|
||||
if not self._fio.haveWritePermissions(event_full_path):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _avail_categories(self):
|
||||
ret = []
|
||||
for event in all_categories:
|
||||
if self._is_category_available(event):
|
||||
ret.append(event)
|
||||
return ret
|
||||
|
||||
def _print_avail_categories(self):
|
||||
avail = self._avail_categories()
|
||||
if len(avail):
|
||||
print "tracing config:"
|
||||
for category in self._avail_categories():
|
||||
desc = all_categories[category]["desc"]
|
||||
print "{0: <16}".format(category), ": ", desc
|
||||
else:
|
||||
print "No tracing categories available - perhaps you need root?"
|
||||
|
||||
def _category_enable_paths(self, category):
|
||||
events_dir = FT_DIR + "events/"
|
||||
req_events = all_categories[category]["req"]
|
||||
for event in req_events:
|
||||
event_full_path = events_dir + event + "enable"
|
||||
yield event_full_path
|
||||
if "opt" in all_categories[category]:
|
||||
opt_events = all_categories[category]["opt"]
|
||||
for event in opt_events:
|
||||
event_full_path = events_dir + event + "enable"
|
||||
if self._fio.haveWritePermissions(event_full_path):
|
||||
yield event_full_path
|
||||
|
||||
def _category_enable(self, category):
|
||||
for path in self._category_enable_paths(category):
|
||||
self._fio.writeFile(path, "1")
|
||||
|
||||
def _category_disable(self, category):
|
||||
for path in self._category_enable_paths(category):
|
||||
self._fio.writeFile(path, "0")
|
||||
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2015 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.
|
||||
|
||||
import unittest
|
||||
import logging
|
||||
|
||||
from systrace import decorators
|
||||
from systrace import run_systrace
|
||||
from systrace.tracing_agents import ftrace_agent
|
||||
|
||||
|
||||
SYSTRACE_HOST_CMD_DEFAULT = ['./systrace.py', '--target=linux']
|
||||
FT_DIR = "/sys/kernel/debug/tracing/"
|
||||
FT_EVENT_DIR = FT_DIR + "events/"
|
||||
FT_TRACE_ON = FT_DIR + "tracing_on"
|
||||
FT_TRACE = FT_DIR + "trace"
|
||||
FT_BUFFER_SIZE = FT_DIR + "buffer_size_kb"
|
||||
|
||||
|
||||
def make_test_io_interface(permitted_files):
|
||||
class TestIoImpl(object):
|
||||
|
||||
@staticmethod
|
||||
def writeFile(path, data):
|
||||
permitted_files[path] = data
|
||||
|
||||
@staticmethod
|
||||
def readFile(path):
|
||||
if path in permitted_files:
|
||||
return permitted_files[path]
|
||||
else:
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def haveWritePermissions(path):
|
||||
return path in permitted_files
|
||||
|
||||
return TestIoImpl
|
||||
|
||||
|
||||
class FtraceAgentTest(unittest.TestCase):
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_avail_categories(self):
|
||||
# sched only has required events
|
||||
permitted_files = {
|
||||
FT_EVENT_DIR + "sched/sched_switch/enable": "0",
|
||||
FT_EVENT_DIR + "sched/sched_wakeup/enable": "0"
|
||||
}
|
||||
io_interface = make_test_io_interface(permitted_files)
|
||||
agent = ftrace_agent.FtraceAgent(io_interface)
|
||||
self.assertEqual(['sched'], agent._avail_categories())
|
||||
|
||||
# check for no available categories
|
||||
permitted_files = {}
|
||||
io_interface = make_test_io_interface(permitted_files)
|
||||
agent = ftrace_agent.FtraceAgent(io_interface)
|
||||
self.assertEqual([], agent._avail_categories())
|
||||
|
||||
# block has some required, some optional events
|
||||
permitted_files = {
|
||||
FT_EVENT_DIR + "block/block_rq_complete/enable": "0",
|
||||
FT_EVENT_DIR + "block/block_rq_issue/enable": "0"
|
||||
}
|
||||
io_interface = make_test_io_interface(permitted_files)
|
||||
agent = ftrace_agent.FtraceAgent(io_interface)
|
||||
self.assertEqual(['disk'], agent._avail_categories())
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_tracing_bootstrap(self):
|
||||
workq_event_path = FT_EVENT_DIR + "workqueue/enable"
|
||||
permitted_files = {
|
||||
workq_event_path: "0",
|
||||
FT_TRACE: "x"
|
||||
}
|
||||
io_interface = make_test_io_interface(permitted_files)
|
||||
systrace_cmd = SYSTRACE_HOST_CMD_DEFAULT + ["workq"]
|
||||
options, categories = run_systrace.parse_options(systrace_cmd)
|
||||
agent = ftrace_agent.FtraceAgent(io_interface)
|
||||
self.assertEqual(['workq'], agent._avail_categories())
|
||||
|
||||
# confirm tracing is enabled, buffer is cleared
|
||||
agent.StartAgentTracing(options, categories)
|
||||
self.assertEqual(permitted_files[FT_TRACE_ON], "1")
|
||||
self.assertEqual(permitted_files[FT_TRACE], "")
|
||||
|
||||
# fill in file with dummy contents
|
||||
dummy_trace = "trace_contents"
|
||||
permitted_files[FT_TRACE] = dummy_trace
|
||||
|
||||
# confirm tracing is disabled
|
||||
agent.StopAgentTracing()
|
||||
agent.GetResults()
|
||||
self.assertEqual(permitted_files[FT_TRACE_ON], "0")
|
||||
|
||||
# confirm trace is expected, and read from fs
|
||||
self.assertEqual(agent.GetResults().raw_data, dummy_trace)
|
||||
|
||||
# confirm buffer size is reset to 1
|
||||
self.assertEqual(permitted_files[FT_BUFFER_SIZE], "1")
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_tracing_event_enable_disable(self):
|
||||
# turn on irq tracing
|
||||
ipi_event_path = FT_EVENT_DIR + "ipi/enable"
|
||||
irq_event_path = FT_EVENT_DIR + "irq/enable"
|
||||
permitted_files = {
|
||||
ipi_event_path: "0",
|
||||
irq_event_path: "0"
|
||||
}
|
||||
io_interface = make_test_io_interface(permitted_files)
|
||||
systrace_cmd = SYSTRACE_HOST_CMD_DEFAULT + ["irq"]
|
||||
options, categories = run_systrace.parse_options(systrace_cmd)
|
||||
options.ftrace_categories = categories
|
||||
agent = ftrace_agent.FtraceAgent(io_interface)
|
||||
self.assertEqual(['irq'], agent._avail_categories())
|
||||
|
||||
# confirm all the event nodes are turned on during tracing
|
||||
agent.StartAgentTracing(options)
|
||||
self.assertEqual(permitted_files[irq_event_path], "1")
|
||||
self.assertEqual(permitted_files[ipi_event_path], "1")
|
||||
|
||||
# and then turned off when completed.
|
||||
agent.StopAgentTracing()
|
||||
agent.GetResults()
|
||||
self.assertEqual(permitted_files[irq_event_path], "0")
|
||||
self.assertEqual(permitted_files[ipi_event_path], "0")
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_buffer_size(self):
|
||||
systrace_cmd = SYSTRACE_HOST_CMD_DEFAULT + ['-b', '16000']
|
||||
options, categories = run_systrace.parse_options(systrace_cmd)
|
||||
agent = ftrace_agent.FtraceAgent()
|
||||
agent._config = options
|
||||
agent._config.atrace_categories = categories
|
||||
self.assertEqual(agent._get_trace_buffer_size(), 16000)
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
unittest.main(verbosity=2)
|
||||
@@ -0,0 +1,121 @@
|
||||
# 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.
|
||||
|
||||
import optparse
|
||||
import threading
|
||||
|
||||
import py_utils
|
||||
|
||||
from devil.android import device_utils
|
||||
from systrace import trace_result
|
||||
from systrace import tracing_agents
|
||||
from py_trace_event import trace_time as trace_time_module
|
||||
|
||||
TRACE_FILE_PATH = \
|
||||
'/sdcard/Android/data/org.chromium.latency.walt/files/trace.txt'
|
||||
|
||||
CLOCK_DOMAIN_MARKER = '# clock_type=LINUX_CLOCK_MONOTONIC\n'
|
||||
|
||||
|
||||
def try_create_agent(options):
|
||||
if options.is_walt_enabled:
|
||||
return WaltAgent()
|
||||
return None
|
||||
|
||||
|
||||
class WaltConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self, device_serial_number, is_walt_enabled):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
self.device_serial_number = device_serial_number
|
||||
self.is_walt_enabled = is_walt_enabled
|
||||
|
||||
|
||||
def add_options(parser):
|
||||
options = optparse.OptionGroup(parser, 'WALT trace options')
|
||||
options.add_option('--walt', dest='is_walt_enabled', default=False,
|
||||
action='store_true', help='Use the WALT tracing agent. '
|
||||
'WALT is a device for measuring latency of physical '
|
||||
'sensors on phones and computers. '
|
||||
'See https://github.com/google/walt')
|
||||
return options
|
||||
|
||||
|
||||
def get_config(options):
|
||||
return WaltConfig(options.device_serial_number, options.is_walt_enabled)
|
||||
|
||||
|
||||
class WaltAgent(tracing_agents.TracingAgent):
|
||||
"""
|
||||
This tracing agent requires the WALT app to be installed on the Android phone,
|
||||
and requires the WALT device to be attached to the phone. WALT is a device
|
||||
for measuring latency of physical sensors and outputs on phones and
|
||||
computers. For more information, visit https://github.com/google/walt
|
||||
"""
|
||||
def __init__(self):
|
||||
super(WaltAgent, self).__init__()
|
||||
self._trace_contents = None
|
||||
self._config = None
|
||||
self._device_utils = None
|
||||
self._clock_sync_marker = None
|
||||
self._collection_thread = None
|
||||
|
||||
def __repr__(self):
|
||||
return 'WaltAgent'
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
del timeout # unused
|
||||
self._config = config
|
||||
self._device_utils = device_utils.DeviceUtils(
|
||||
self._config.device_serial_number)
|
||||
if self._device_utils.PathExists(TRACE_FILE_PATH):
|
||||
# clear old trace events so they are not included in the current trace
|
||||
self._device_utils.WriteFile(TRACE_FILE_PATH, '')
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
"""Stops tracing and starts collecting results.
|
||||
|
||||
To synchronously retrieve the results after calling this function,
|
||||
call GetResults().
|
||||
"""
|
||||
del timeout # unused
|
||||
self._collection_thread = threading.Thread(
|
||||
target=self._collect_trace_data)
|
||||
self._collection_thread.start()
|
||||
return True
|
||||
|
||||
def _collect_trace_data(self):
|
||||
self._trace_contents = self._device_utils.ReadFile(TRACE_FILE_PATH)
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
return True
|
||||
|
||||
def RecordClockSyncMarker(self, sync_id, did_record_clock_sync_callback):
|
||||
cmd = 'cat /proc/timer_list | grep now'
|
||||
t1 = trace_time_module.Now()
|
||||
command_result = self._device_utils.RunShellCommand(cmd, shell=True)
|
||||
nsec = command_result[0].split()[2]
|
||||
self._clock_sync_marker = format_clock_sync_marker(sync_id, nsec)
|
||||
did_record_clock_sync_callback(t1, sync_id)
|
||||
|
||||
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
||||
def GetResults(self, timeout=None):
|
||||
del timeout # unused
|
||||
self._collection_thread.join()
|
||||
self._collection_thread = None
|
||||
return trace_result.TraceResult('waltTrace', self._get_trace_result())
|
||||
|
||||
def _get_trace_result(self):
|
||||
result = '# tracer: \n' + CLOCK_DOMAIN_MARKER + self._trace_contents
|
||||
if self._clock_sync_marker is not None:
|
||||
result += self._clock_sync_marker
|
||||
return result
|
||||
|
||||
|
||||
def format_clock_sync_marker(sync_id, nanosec_time):
|
||||
return ('<0>-0 (-----) [001] ...1 ' + str(float(nanosec_time) / 1e9)
|
||||
+ ': tracing_mark_write: trace_event_clock_sync: name='
|
||||
+ sync_id + '\n')
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) 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.
|
||||
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
from systrace import decorators
|
||||
from systrace import run_systrace
|
||||
from systrace.tracing_agents import walt_agent
|
||||
|
||||
|
||||
class WaltAgentTest(unittest.TestCase):
|
||||
"""
|
||||
The WALT agent pulls the trace log from the Android phone, and does not
|
||||
communicate with the WALT device directly. This makes the agent similar
|
||||
to atrace. Since the host only connects to the Android phone, more exhaustive
|
||||
testing would require mocking DeviceUtils.
|
||||
"""
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_construct_walt_args(self):
|
||||
options, _ = run_systrace.parse_options(['./run_systrace.py',
|
||||
'--walt'])
|
||||
self.assertTrue(walt_agent.get_config(options).is_walt_enabled)
|
||||
options, _ = run_systrace.parse_options(['./run_systrace.py'])
|
||||
self.assertFalse(walt_agent.get_config(options).is_walt_enabled)
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_format_clock_sync_marker(self):
|
||||
actual_marker = walt_agent.format_clock_sync_marker(
|
||||
'some_sync_id', 12345678901234)
|
||||
expected_marker = ('<0>-0 (-----) [001] ...1 12345.6789012: ' +
|
||||
'tracing_mark_write: trace_event_clock_sync: ' +
|
||||
'name=some_sync_id\n')
|
||||
self.assertEqual(actual_marker, expected_marker)
|
||||
|
||||
@decorators.HostOnlyTest
|
||||
def test_get_results_string(self):
|
||||
agent = walt_agent.WaltAgent()
|
||||
agent._trace_contents = '<trace contents here>\n'
|
||||
agent._clock_sync_marker = '<clock sync marker here>\n'
|
||||
result = agent._get_trace_result()
|
||||
self.assertEquals(result, '# tracer: \n# clock_type=LINUX_CLOCK_MONOTONIC\n'
|
||||
'<trace contents here>\n<clock sync marker here>\n')
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
unittest.main(verbosity=2)
|
||||
@@ -0,0 +1,309 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2016 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.
|
||||
|
||||
'''Tracing controller class. This class manages
|
||||
multiple tracing agents and collects data from all of them. It also
|
||||
manages the clock sync process.
|
||||
'''
|
||||
|
||||
import ast
|
||||
import json
|
||||
import sys
|
||||
import tempfile
|
||||
import uuid
|
||||
|
||||
import py_utils
|
||||
|
||||
from systrace import trace_result
|
||||
from systrace import tracing_agents
|
||||
from py_trace_event import trace_event
|
||||
|
||||
|
||||
TRACE_DATA_CONTROLLER_NAME = 'systraceController'
|
||||
|
||||
|
||||
def ControllerAgentClockSync(issue_ts, name):
|
||||
"""Record the clock sync marker for controller tracing agent.
|
||||
|
||||
Unlike with the other tracing agents, the tracing controller should not
|
||||
call this directly. Rather, it is called via callback from the other
|
||||
tracing agents when they write a trace.
|
||||
"""
|
||||
trace_event.clock_sync(name, issue_ts=issue_ts)
|
||||
|
||||
|
||||
class TracingControllerAgent(tracing_agents.TracingAgent):
|
||||
def __init__(self):
|
||||
super(TracingControllerAgent, self).__init__()
|
||||
self._log_path = None
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StartAgentTracing(self, config, timeout=None):
|
||||
"""Start tracing for the controller tracing agent.
|
||||
|
||||
Start tracing for the controller tracing agent. Note that
|
||||
the tracing controller records the "controller side"
|
||||
of the clock sync records, and nothing else.
|
||||
"""
|
||||
del config
|
||||
if not trace_event.trace_can_enable():
|
||||
raise RuntimeError, ('Cannot enable trace_event;'
|
||||
' ensure py_utils is in PYTHONPATH')
|
||||
|
||||
controller_log_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
self._log_path = controller_log_file.name
|
||||
controller_log_file.close()
|
||||
trace_event.trace_enable(self._log_path)
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
||||
def StopAgentTracing(self, timeout=None):
|
||||
"""Stops tracing for the controller tracing agent.
|
||||
"""
|
||||
# pylint: disable=no-self-use
|
||||
# This function doesn't use self, but making it a member function
|
||||
# for consistency with the other TracingAgents
|
||||
trace_event.trace_disable()
|
||||
return True
|
||||
|
||||
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
||||
def GetResults(self, timeout=None):
|
||||
"""Gets the log output from the controller tracing agent.
|
||||
|
||||
This output only contains the "controller side" of the clock sync records.
|
||||
"""
|
||||
with open(self._log_path, 'r') as outfile:
|
||||
data = ast.literal_eval(outfile.read() + ']')
|
||||
# Explicitly set its own clock domain. This will stop the Systrace clock
|
||||
# domain from incorrectly being collapsed into the on device clock domain.
|
||||
formatted_data = {
|
||||
'traceEvents': data,
|
||||
'metadata': {
|
||||
'clock-domain': 'SYSTRACE',
|
||||
}
|
||||
}
|
||||
return trace_result.TraceResult(TRACE_DATA_CONTROLLER_NAME,
|
||||
json.dumps(formatted_data))
|
||||
|
||||
def SupportsExplicitClockSync(self):
|
||||
"""Returns whether this supports explicit clock sync.
|
||||
Although the tracing controller conceptually supports explicit clock
|
||||
sync, it is not an agent controlled by other controllers so it does not
|
||||
define RecordClockSyncMarker (rather, the recording of the "controller
|
||||
side" of the clock sync marker is done in _IssueClockSyncMarker). Thus,
|
||||
SupportsExplicitClockSync must return false.
|
||||
"""
|
||||
return False
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def RecordClockSyncMarker(self, sync_id, callback):
|
||||
raise NotImplementedError
|
||||
|
||||
class TracingController(object):
|
||||
def __init__(self, agents_with_config, controller_config):
|
||||
"""Create tracing controller.
|
||||
|
||||
Create a tracing controller object. Note that the tracing
|
||||
controller is also a tracing agent.
|
||||
|
||||
Args:
|
||||
agents_with_config: List of tracing agents for this controller with the
|
||||
corresponding tracing configuration objects.
|
||||
controller_config: Configuration options for the tracing controller.
|
||||
"""
|
||||
self._child_agents = None
|
||||
self._child_agents_with_config = agents_with_config
|
||||
self._controller_agent = TracingControllerAgent()
|
||||
self._controller_config = controller_config
|
||||
self._trace_in_progress = False
|
||||
self.all_results = None
|
||||
|
||||
@property
|
||||
def get_child_agents(self):
|
||||
return self._child_agents
|
||||
|
||||
def StartTracing(self):
|
||||
"""Start tracing for all tracing agents.
|
||||
|
||||
This function starts tracing for both the controller tracing agent
|
||||
and the child tracing agents.
|
||||
|
||||
Returns:
|
||||
Boolean indicating whether or not the start tracing succeeded.
|
||||
Start tracing is considered successful if at least the
|
||||
controller tracing agent was started.
|
||||
"""
|
||||
assert not self._trace_in_progress, 'Trace already in progress.'
|
||||
self._trace_in_progress = True
|
||||
|
||||
# Start the controller tracing agents. Controller tracing agent
|
||||
# must be started successfully to proceed.
|
||||
if not self._controller_agent.StartAgentTracing(
|
||||
self._controller_config,
|
||||
timeout=self._controller_config.timeout):
|
||||
print 'Unable to start controller tracing agent.'
|
||||
return False
|
||||
|
||||
# Start the child tracing agents.
|
||||
succ_agents = []
|
||||
for agent_and_config in self._child_agents_with_config:
|
||||
agent = agent_and_config.agent
|
||||
config = agent_and_config.config
|
||||
if agent.StartAgentTracing(config,
|
||||
timeout=self._controller_config.timeout):
|
||||
succ_agents.append(agent)
|
||||
else:
|
||||
print 'Agent %s not started.' % str(agent)
|
||||
|
||||
# Print warning if all agents not started.
|
||||
na = len(self._child_agents_with_config)
|
||||
ns = len(succ_agents)
|
||||
if ns < na:
|
||||
print 'Warning: Only %d of %d tracing agents started.' % (ns, na)
|
||||
self._child_agents = succ_agents
|
||||
return True
|
||||
|
||||
def StopTracing(self):
|
||||
"""Issue clock sync marker and stop tracing for all tracing agents.
|
||||
|
||||
This function stops both the controller tracing agent
|
||||
and the child tracing agents. It issues a clock sync marker prior
|
||||
to stopping tracing.
|
||||
|
||||
Returns:
|
||||
Boolean indicating whether or not the stop tracing succeeded
|
||||
for all agents.
|
||||
"""
|
||||
assert self._trace_in_progress, 'No trace in progress.'
|
||||
self._trace_in_progress = False
|
||||
|
||||
# Issue the clock sync marker and stop the child tracing agents.
|
||||
self._IssueClockSyncMarker()
|
||||
succ_agents = []
|
||||
for agent in self._child_agents:
|
||||
if agent.StopAgentTracing(timeout=self._controller_config.timeout):
|
||||
succ_agents.append(agent)
|
||||
else:
|
||||
print 'Agent %s not stopped.' % str(agent)
|
||||
|
||||
# Stop the controller tracing agent. Controller tracing agent
|
||||
# must be stopped successfully to proceed.
|
||||
if not self._controller_agent.StopAgentTracing(
|
||||
timeout=self._controller_config.timeout):
|
||||
print 'Unable to stop controller tracing agent.'
|
||||
return False
|
||||
|
||||
# Print warning if all agents not stopped.
|
||||
na = len(self._child_agents)
|
||||
ns = len(succ_agents)
|
||||
if ns < na:
|
||||
print 'Warning: Only %d of %d tracing agents stopped.' % (ns, na)
|
||||
self._child_agents = succ_agents
|
||||
|
||||
# Collect the results from all the stopped tracing agents.
|
||||
all_results = []
|
||||
for agent in self._child_agents + [self._controller_agent]:
|
||||
try:
|
||||
result = agent.GetResults(
|
||||
timeout=self._controller_config.collection_timeout)
|
||||
if not result:
|
||||
print 'Warning: Timeout when getting results from %s.' % str(agent)
|
||||
continue
|
||||
if result.source_name in [r.source_name for r in all_results]:
|
||||
print ('Warning: Duplicate tracing agents named %s.' %
|
||||
result.source_name)
|
||||
all_results.append(result)
|
||||
# Check for exceptions. If any exceptions are seen, reraise and abort.
|
||||
# Note that a timeout exception will be swalloed by the timeout
|
||||
# mechanism and will not get to that point (it will return False instead
|
||||
# of the trace result, which will be dealt with above)
|
||||
except:
|
||||
print 'Warning: Exception getting results from %s:' % str(agent)
|
||||
print sys.exc_info()[0]
|
||||
raise
|
||||
self.all_results = all_results
|
||||
return all_results
|
||||
|
||||
def GetTraceType(self):
|
||||
"""Return a string representing the child agents that are being traced."""
|
||||
sorted_agents = sorted(map(str, self._child_agents))
|
||||
return ' + '.join(sorted_agents)
|
||||
|
||||
def _IssueClockSyncMarker(self):
|
||||
"""Issue clock sync markers to all the child tracing agents."""
|
||||
for agent in self._child_agents:
|
||||
if agent.SupportsExplicitClockSync():
|
||||
sync_id = GetUniqueSyncID()
|
||||
agent.RecordClockSyncMarker(sync_id, ControllerAgentClockSync)
|
||||
|
||||
def GetUniqueSyncID():
|
||||
"""Get a unique sync ID.
|
||||
|
||||
Gets a unique sync ID by generating a UUID and converting it to a string
|
||||
(since UUIDs are not JSON serializable)
|
||||
"""
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
class AgentWithConfig(object):
|
||||
def __init__(self, agent, config):
|
||||
self.agent = agent
|
||||
self.config = config
|
||||
|
||||
|
||||
def CreateAgentsWithConfig(options, modules):
|
||||
"""Create tracing agents.
|
||||
|
||||
This function will determine which tracing agents are valid given the
|
||||
options and create those agents along with their corresponding configuration
|
||||
object.
|
||||
Args:
|
||||
options: The command-line options.
|
||||
modules: The modules for either Systrace or profile_chrome.
|
||||
TODO(washingtonp): After all profile_chrome agents are in
|
||||
Systrace, this parameter will no longer be valid.
|
||||
Returns:
|
||||
A list of AgentWithConfig options containing agents and their corresponding
|
||||
configuration object.
|
||||
"""
|
||||
result = []
|
||||
for module in modules:
|
||||
config = module.get_config(options)
|
||||
agent = module.try_create_agent(config)
|
||||
if agent and config:
|
||||
result.append(AgentWithConfig(agent, config))
|
||||
return [x for x in result if x and x.agent]
|
||||
|
||||
|
||||
class TracingControllerConfig(tracing_agents.TracingConfig):
|
||||
def __init__(self, output_file, trace_time, write_json,
|
||||
link_assets, asset_dir, timeout, collection_timeout,
|
||||
device_serial_number, target, trace_buf_size):
|
||||
tracing_agents.TracingConfig.__init__(self)
|
||||
self.output_file = output_file
|
||||
self.trace_time = trace_time
|
||||
self.write_json = write_json
|
||||
self.link_assets = link_assets
|
||||
self.asset_dir = asset_dir
|
||||
self.timeout = timeout
|
||||
self.collection_timeout = collection_timeout
|
||||
self.device_serial_number = device_serial_number
|
||||
self.target = target
|
||||
self.trace_buf_size = trace_buf_size
|
||||
|
||||
|
||||
def GetControllerConfig(options):
|
||||
return TracingControllerConfig(options.output_file, options.trace_time,
|
||||
options.write_json,
|
||||
options.link_assets, options.asset_dir,
|
||||
options.timeout, options.collection_timeout,
|
||||
options.device_serial_number, options.target,
|
||||
options.trace_buf_size)
|
||||
|
||||
def GetChromeStartupControllerConfig(options):
|
||||
return TracingControllerConfig(None, options.trace_time,
|
||||
options.write_json, None, None, None, None,
|
||||
None, None, options.trace_buf_size)
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2015 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.
|
||||
|
||||
import codecs
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
_CATAPULT_PATH = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir))
|
||||
sys.path.append(os.path.join(_CATAPULT_PATH, 'tracing'))
|
||||
|
||||
# this import needs to be after the change to sys.path above
|
||||
#pylint: disable=wrong-import-position
|
||||
from tracing_build import vulcanize_trace_viewer
|
||||
|
||||
|
||||
SYSTRACE_TRACE_VIEWER_HTML_FILE = os.path.join(
|
||||
os.path.abspath(os.path.dirname(__file__)),
|
||||
'systrace_trace_viewer.html')
|
||||
CATAPULT_REV_ = 'CATAPULT_REV'
|
||||
NO_AUTO_UPDATE_ = 'NO_AUTO_UPDATE'
|
||||
UNKNOWN_REVISION_ = 'UNKNOWN'
|
||||
|
||||
|
||||
def create_catapult_rev_str_(revision):
|
||||
return '<!--' + CATAPULT_REV_ + '=' + str(revision) + '-->'
|
||||
|
||||
|
||||
def get_catapult_rev_in_file_(html_file):
|
||||
assert os.path.exists(html_file)
|
||||
rev = ''
|
||||
with open(html_file, 'r') as f:
|
||||
lines = f.readlines()
|
||||
for line in lines[::-1]:
|
||||
if CATAPULT_REV_ in line:
|
||||
tokens = line.split(CATAPULT_REV_)
|
||||
rev = re.sub(r'[=\->]', '', tokens[1]).strip()
|
||||
break
|
||||
return rev
|
||||
|
||||
|
||||
def get_catapult_rev_in_git_():
|
||||
try:
|
||||
catapult_rev = subprocess.check_output(
|
||||
'git rev-parse HEAD',
|
||||
shell=True, # Needed by Windows
|
||||
cwd=os.path.dirname(os.path.abspath(__file__))).strip()
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
return None
|
||||
if not catapult_rev:
|
||||
return None
|
||||
return catapult_rev
|
||||
|
||||
|
||||
def update(no_auto_update=False, no_min=False, force_update=False):
|
||||
"""Update the systrace trace viewer html file.
|
||||
|
||||
When the html file exists, do not update the file if
|
||||
1. the revision is NO_AUTO_UPDATE_;
|
||||
2. or the revision is not changed.
|
||||
|
||||
Args:
|
||||
no_auto_update: If true, force updating the file with revision
|
||||
NO_AUTO_UPDATE_. Future auto-updates will be skipped.
|
||||
no_min: If true, skip minification when updating the file.
|
||||
force_update: If true, update the systrace trace viewer file no matter
|
||||
what.
|
||||
"""
|
||||
if no_auto_update:
|
||||
new_rev = NO_AUTO_UPDATE_
|
||||
else:
|
||||
new_rev = get_catapult_rev_in_git_()
|
||||
if not new_rev:
|
||||
# Source tree could be missing git metadata.
|
||||
print >> sys.stderr, 'Warning: Couldn\'t determine current git revision.'
|
||||
new_rev = UNKNOWN_REVISION_
|
||||
|
||||
need_update = False
|
||||
if force_update:
|
||||
need_update = True
|
||||
elif no_auto_update:
|
||||
need_update = True
|
||||
elif not os.path.exists(SYSTRACE_TRACE_VIEWER_HTML_FILE):
|
||||
need_update = True
|
||||
else:
|
||||
old_rev = get_catapult_rev_in_file_(SYSTRACE_TRACE_VIEWER_HTML_FILE)
|
||||
if not old_rev or old_rev == UNKNOWN_REVISION_:
|
||||
need_update = True
|
||||
# If old_rev was set to NO_AUTO_UPDATE_ it should be skipped, since forced
|
||||
# update cases have been already handled above.
|
||||
if old_rev != new_rev and old_rev != NO_AUTO_UPDATE_:
|
||||
need_update = True
|
||||
|
||||
if not need_update:
|
||||
print 'Update skipped.'
|
||||
return
|
||||
|
||||
print 'Generating viewer file %s with revision %s.' % (
|
||||
SYSTRACE_TRACE_VIEWER_HTML_FILE, new_rev)
|
||||
|
||||
# Generate the vulcanized result.
|
||||
with codecs.open(SYSTRACE_TRACE_VIEWER_HTML_FILE,
|
||||
encoding='utf-8', mode='w') as f:
|
||||
vulcanize_trace_viewer.WriteTraceViewer(
|
||||
f,
|
||||
config_name='full',
|
||||
minify=(not no_min),
|
||||
output_html_head_and_body=False)
|
||||
if not force_update:
|
||||
f.write(create_catapult_rev_str_(new_rev))
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('--force-update', dest='force_update',
|
||||
default=False, action='store_true', help='force update the '
|
||||
'systrace trace viewer html file')
|
||||
parser.add_option('--no-auto-update', dest='no_auto_update',
|
||||
default=False, action='store_true', help='force update the '
|
||||
'systrace trace viewer html file and disable auto-updates, '
|
||||
'delete \'systrace_trace_viewer.html\' to re-enable '
|
||||
'auto-updates')
|
||||
parser.add_option('--no-min', dest='no_min', default=False,
|
||||
action='store_true', help='skip minification')
|
||||
# pylint: disable=unused-variable
|
||||
options, unused_args = parser.parse_args(sys.argv[1:])
|
||||
|
||||
update(no_auto_update=options.no_auto_update,
|
||||
no_min=options.no_min,
|
||||
force_update=options.force_update)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
149
tools/adb/systrace/catapult/systrace/systrace/util.py
Normal file
149
tools/adb/systrace/catapult/systrace/systrace/util.py
Normal file
@@ -0,0 +1,149 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from devil.android.constants import chrome
|
||||
from devil.android import device_utils, device_errors
|
||||
|
||||
class OptionParserIgnoreErrors(optparse.OptionParser):
|
||||
"""Wrapper for OptionParser that ignores errors and produces no output."""
|
||||
|
||||
def error(self, msg):
|
||||
pass
|
||||
|
||||
def exit(self, status=0, msg=None):
|
||||
pass
|
||||
|
||||
def print_usage(self, out_file=None):
|
||||
pass
|
||||
|
||||
def print_help(self, out_file=None):
|
||||
pass
|
||||
|
||||
def print_version(self, out_file=None):
|
||||
pass
|
||||
|
||||
|
||||
def run_adb_shell(shell_args, device_serial):
|
||||
"""Runs "adb shell" with the given arguments.
|
||||
|
||||
Args:
|
||||
shell_args: array of arguments to pass to adb shell.
|
||||
device_serial: if not empty, will add the appropriate command-line
|
||||
parameters so that adb targets the given device.
|
||||
Returns:
|
||||
A tuple containing the adb output (stdout & stderr) and the return code
|
||||
from adb. Will exit if adb fails to start.
|
||||
"""
|
||||
adb_output = []
|
||||
adb_return_code = 0
|
||||
device = device_utils.DeviceUtils.HealthyDevices(device_arg=device_serial)[0]
|
||||
try:
|
||||
adb_output = device.RunShellCommand(shell_args, shell=False,
|
||||
check_return=True, raw_output=True)
|
||||
except device_errors.AdbShellCommandFailedError as error:
|
||||
adb_return_code = error.status
|
||||
adb_output = error.output
|
||||
|
||||
return (adb_output, adb_return_code)
|
||||
|
||||
|
||||
def get_device_sdk_version():
|
||||
"""Uses adb to attempt to determine the SDK version of a running device."""
|
||||
|
||||
getprop_args = ['getprop', 'ro.build.version.sdk']
|
||||
|
||||
# get_device_sdk_version() is called before we even parse our command-line
|
||||
# args. Therefore, parse just the device serial number part of the
|
||||
# command-line so we can send the adb command to the correct device.
|
||||
parser = OptionParserIgnoreErrors()
|
||||
parser.add_option('-e', '--serial', dest='device_serial', type='string')
|
||||
options, unused_args = parser.parse_args() # pylint: disable=unused-variable
|
||||
|
||||
success = False
|
||||
|
||||
adb_output, adb_return_code = run_adb_shell(getprop_args,
|
||||
options.device_serial)
|
||||
|
||||
if adb_return_code == 0:
|
||||
# ADB may print output other than the version number (e.g. it chould
|
||||
# print a message about starting the ADB server).
|
||||
# Break the ADB output into white-space delimited segments.
|
||||
parsed_output = str.split(adb_output)
|
||||
if parsed_output:
|
||||
# Assume that the version number is the last thing printed by ADB.
|
||||
version_string = parsed_output[-1]
|
||||
if version_string:
|
||||
try:
|
||||
# Try to convert the text into an integer.
|
||||
version = int(version_string)
|
||||
except ValueError:
|
||||
version = -1
|
||||
else:
|
||||
success = True
|
||||
|
||||
if not success:
|
||||
print >> sys.stderr, adb_output
|
||||
raise Exception("Failed to get device sdk version")
|
||||
|
||||
return version
|
||||
|
||||
|
||||
def get_supported_browsers():
|
||||
"""Returns the package names of all supported browsers."""
|
||||
# Add aliases for backwards compatibility.
|
||||
supported_browsers = {
|
||||
'stable': chrome.PACKAGE_INFO['chrome_stable'],
|
||||
'beta': chrome.PACKAGE_INFO['chrome_beta'],
|
||||
'dev': chrome.PACKAGE_INFO['chrome_dev'],
|
||||
'build': chrome.PACKAGE_INFO['chrome'],
|
||||
}
|
||||
supported_browsers.update(chrome.PACKAGE_INFO)
|
||||
return supported_browsers
|
||||
|
||||
|
||||
def get_default_serial():
|
||||
if 'ANDROID_SERIAL' in os.environ:
|
||||
return os.environ['ANDROID_SERIAL']
|
||||
return None
|
||||
|
||||
|
||||
def get_main_options(parser):
|
||||
parser.add_option('-o', dest='output_file', help='write trace output to FILE',
|
||||
default=None, metavar='FILE')
|
||||
parser.add_option('-t', '--time', dest='trace_time', type='int',
|
||||
help='trace for N seconds', metavar='N')
|
||||
parser.add_option('-j', '--json', dest='write_json',
|
||||
default=False, action='store_true',
|
||||
help='write a JSON file')
|
||||
parser.add_option('--link-assets', dest='link_assets', default=False,
|
||||
action='store_true',
|
||||
help='(deprecated)')
|
||||
parser.add_option('--from-file', dest='from_file', action='store',
|
||||
help='read the trace from a file (compressed) rather than'
|
||||
'running a live trace')
|
||||
parser.add_option('--asset-dir', dest='asset_dir', default='trace-viewer',
|
||||
type='string', help='(deprecated)')
|
||||
parser.add_option('-e', '--serial', dest='device_serial_number',
|
||||
default=get_default_serial(),
|
||||
type='string', help='adb device serial number')
|
||||
parser.add_option('--target', dest='target', default='android', type='string',
|
||||
help='choose tracing target (android or linux)')
|
||||
parser.add_option('--timeout', dest='timeout', type='int',
|
||||
help='timeout for start and stop tracing (seconds)')
|
||||
parser.add_option('--collection-timeout', dest='collection_timeout',
|
||||
type='int', help='timeout for data collection (seconds)')
|
||||
parser.add_option('-a', '--app', dest='app_name', default=None,
|
||||
type='string', action='store',
|
||||
help='enable application-level tracing for '
|
||||
'comma-separated list of app cmdlines')
|
||||
parser.add_option('-t', '--time', dest='trace_time', type='int',
|
||||
help='trace for N seconds', metavar='N')
|
||||
parser.add_option('-b', '--buf-size', dest='trace_buf_size',
|
||||
type='int', help='use a trace buffer size '
|
||||
' of N KB', metavar='N')
|
||||
return parser
|
||||
Reference in New Issue
Block a user