FOREACH(hfile ${HEADERS_ttrace})
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/${hfile} DESTINATION ${INCLUDEDIR})
ENDFOREACH(hfile)
+
#################################################################
-# Build atrace Library (KK)
+# Build atrace / atrace-helper
# ------------------------------
-SET(ATRACE "atrace")
-SET(SRCS_atrace src/atrace/atrace.cpp)
-SET(HEADERS_atrace src/ttrace/ttrace.h)
-
-INCLUDE(FindPkgConfig)
-pkg_check_modules(pkg_atrace REQUIRED zlib libsmack)
-FOREACH(flag ${pkg_atrace_CXXFLAGS})
- SET(EXTRA_CXXFLAGS_common "${EXTRA_CXXFLAGS_common} ${flag}")
-ENDFOREACH(flag)
-SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS} ${EXTRA_CXXFLAGS_common} -std=c++11")
-MESSAGE("CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")
-
-# Build atrace-helper
-SET(ATRACE_HELPER "atrace-helper")
-SET(ATRACE_HELPER_PATH src/atrace_helper)
-SET(SRCS_atrace_helper
- ${ATRACE_HELPER_PATH}/atrace_process_dump.cc
- ${ATRACE_HELPER_PATH}/file_utils.cc
- ${ATRACE_HELPER_PATH}/main.cc
- ${ATRACE_HELPER_PATH}/process_memory_stats.cc
- ${ATRACE_HELPER_PATH}/procfs_utils.cc
- ${ATRACE_HELPER_PATH}/time_utils.cc
- )
-SET(HEADER_atrace_helper
- ${ATRACE_HELPER_PATH}/atrace_process_dump.h
- ${ATRACE_HELPER_PATH}/file_utils.h
- ${ATRACE_HELPER_PATH}/logging.h
- ${ATRACE_HELPER_PATH}/process_info.h
- ${ATRACE_HELPER_PATH}/process_memory_stats.h
- ${ATRACE_HELPER_PATH}/procfs_utils.h
- ${ATRACE_HELPER_PATH}/tile_utils.h
- )
-
-ADD_EXECUTABLE(${ATRACE} ${SRCS_atrace})
-#SET_TARGET_PROPERTIES(${ATRACE} PROPERTIES COMPILE_FLAGS ${EXTRA_CFLAGS_common})
-TARGET_LINK_LIBRARIES(${ATRACE} ${pkg_atrace_LDFLAGS} "-ldl")
-
-ADD_EXECUTABLE(${ATRACE_HELPER} ${SRCS_atrace_helper})
-
-CONFIGURE_FILE(${ATRACE}.pc.in ${ATRACE}.pc @ONLY)
-
-INSTALL(TARGETS ${ATRACE} DESTINATION bin)
-INSTALL(TARGETS ${ATRACE_HELPER} DESTINATION bin)
-INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${ATRACE}.pc DESTINATION ${LIBDIR}/pkgconfig)
+ADD_SUBDIRECTORY(atrace)
+ADD_SUBDIRECTORY(atrace-helper)
--- /dev/null
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(atrace-helper CXX)
+
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+SET(EXEC_PREFIX "\${prefix}")
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} -std=c++11")
+SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
+SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} -std=c++11")
+SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
+SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES})
+
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES VERSION ${ATRACE_HELPER_VERSION})
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin)
--- /dev/null
+// 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.
--- /dev/null
+// 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());
+ dump_count_ = 0;
+ out_ = nullptr;
+ snapshot_timestamp_ = 0;
+ dump_interval_in_timer_ticks_ = 0;
+}
+
+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);
+ }
+ });
+}
+
+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());
+ }
+
+ // 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;
+}
--- /dev/null
+// 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_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_
--- /dev/null
+// 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
--- /dev/null
+// 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_
--- /dev/null
+// 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 <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; \
+ fprintf(stderr, "\n" CHECK_ARGS(x, e)); \
+ fflush(stderr); \
+ abort(); \
+ } \
+ } while (0)
+
+inline void LogError(const char* message) {
+ fprintf(stderr, "\n%s\n", message);
+ fflush(stderr);
+}
+
+#endif // LOGGING_H_
--- /dev/null
+// 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"
+
+#define PATH_MAX 256
+
+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:st:o:c:")) != -1) {
+ switch (opt) {
+ case 'b':
+ background = true;
+ break;
+ case 'm':
+ ParseFullDumpConfig(optarg, prog);
+ 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));
+ out_file[PATH_MAX - 1] = '\0';
+ dump_to_file = true;
+ break;
+ default:
+ fprintf(stderr,
+ "Usage: %s [-b] [-m full_dump_filter] [-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 + 4];
+ if (dump_to_file) {
+ unlink(out_file);
+ snprintf(tmp_file, PATH_MAX + 4, "%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);
+}
--- /dev/null
+// 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_
--- /dev/null
+// 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 "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_ = (uint64_t)rss_pages * kKbPerPage;
+ virt_kb_ = (uint64_t)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 %*x %*[: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;
+}
--- /dev/null
+// 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);
+
+ // 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_; }
+
+ 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;
+
+ std::vector<std::unique_ptr<const MmapInfo>> mmaps_;
+};
+
+#endif // PROCESS_MEMORY_STATS_H_
--- /dev/null
+// 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];
+ snprintf(exe_path, sizeof(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];
+ snprintf(tasks_path, sizeof(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];
+ snprintf(task_comm, sizeof(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 %*s %*c %*d %*d %*d %*d %*d %*u %lu %*u %lu %*u %lu %lu",
+ &snapshot->minor_faults, &snapshot->major_faults,
+ &snapshot->utime, &snapshot->stime);
+ printf("ret is %d[%s]\n", ret, buf);
+ 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
--- /dev/null
+// 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_
--- /dev/null
+// 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 ((uint64_t)ts.tv_sec * 1000) + ((uint64_t)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
--- /dev/null
+// 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_
+++ /dev/null
-# Package Information for pkg-config
-
-prefix=@PREFIX@
-exec_prefix=@EXEC_PREFIX@
-libdir=@LIB_INSTALL_DIR@
-includedir=@INCLUDEDIR@
-
-Name: atrace
-Description: atrace for tizen
-Version: @VERSION@
-Requires: zlib
-Libs: -L${libdir}
-Cflags: -I${includedir}
--- /dev/null
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(atrace C)
+
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+SET(EXEC_PREFIX "\${prefix}")
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} -std=c++11")
+SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
+SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
+
+AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkg_atrace REQUIRED zlib libsmack)
+FOREACH(flag ${pkg_atrace_CXXFLAGS})
+ SET(EXTRA_CXXFLAGS "${EXTRA_CXXFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkg_atrace_LDFLAGS} "-ldl")
+
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES VERSION ${ATRACE_VERSION})
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin)
--- /dev/null
+
+ Copyright (c) 2012, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
--- /dev/null
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/sendfile.h>
+#include <time.h>
+#include <zlib.h>
+#include <stdint.h>
+#include <strings.h>
+#include <string.h>
+#include <grp.h>
+#include <sys/mman.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/smack.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <memory>
+#include <fstream>
+
+#include "ttrace.h"
+
+using std::string;
+
+#define TTRACE_TAG_NONE 9999
+#define TAG_NONE_IDX 0
+
+#define BACKUP_TRACE "/tmp/trace.backup"
+#define BOOTUP_TRACE "/etc/ttrace.conf"
+#define DEF_GR_SIZE 1024
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+enum { MAX_SYS_FILES = 11 };
+
+const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
+const char* k_traceAppCmdlineProperty = "debug.atrace.app_cmdlines";
+
+typedef enum { OPT, REQ } requiredness ;
+
+char str_error[256] = "";
+
+struct TracingCategory {
+ // The name identifying the category.
+ const char* name;
+
+ // A longer description of the category.
+ const char* longname;
+
+ // The userland tracing tags that the category enables.
+ uint64_t tags;
+
+ // The fname==nullptr terminated list of /sys/ files that the category
+ // enables.
+ struct {
+ // Whether the file must be writable in order to enable the tracing
+ // category.
+ requiredness required;
+
+ // The path to the enable file.
+ const char* path;
+ } sysfiles[MAX_SYS_FILES];
+};
+
+/* Tracing categories */
+static const TracingCategory k_categories[] = {
+ { "none", "None", TTRACE_TAG_NONE, { } }, //do not change "none" option's index
+ { "gfx", "Graphics", TTRACE_TAG_GRAPHICS, { } },
+ { "input", "Input", TTRACE_TAG_INPUT, { } },
+ { "view", "View System", TTRACE_TAG_VIEW, { } },
+ { "web", "Web", TTRACE_TAG_WEB, { } },
+ { "wm", "Window Manager", TTRACE_TAG_WINDOW_MANAGER, { } },
+ { "am", "Application Manager", TTRACE_TAG_APPLICATION_MANAGER, { } },
+ { "image", "Image", TTRACE_TAG_IMAGE, { } },
+ { "audio", "Audio", TTRACE_TAG_AUDIO, { } },
+ { "video", "Video", TTRACE_TAG_VIDEO, { } },
+ { "camera", "Camera", TTRACE_TAG_CAMERA, { } },
+ { "hal", "Hardware Modules", TTRACE_TAG_HAL, { } },
+ { "mc", "Multimedia content", TTRACE_TAG_MEDIA_CONTENT, { } },
+ { "mdb", "Multimedia database", TTRACE_TAG_MEDIA_DB, { } },
+ { "scmirroring", "Screen mirroring", TTRACE_TAG_SCREEN_MIRRORING, { } },
+ { "efl", "EFL", TTRACE_TAG_EFL, { } },
+ { "app", "Application", TTRACE_TAG_APP, { } },
+ { "sched", "CPU Scheduling", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/sched/sched_waking/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/sched/sched_pi_setprio/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/sched/sched_process_exit/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/cgroup/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/oom/oom_score_adj_update/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/task/task_rename/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/task/task_newtask/enable" },
+ } },
+ { "irq", "IRQ Events", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/irq/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/ipi/enable" },
+ } },
+ { "irqoff", "IRQ-disabled code section tracing", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/preemptirq/irq_enable/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/preemptirq/irq_disable/enable" },
+ } },
+ { "i2c", "I2C Events", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/i2c/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_read/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_write/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_result/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_reply/enable" },
+ } },
+ { "freq", "CPU Frequency", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/power/clock_disable/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/power/clock_enable/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/clk/clk_set_rate/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/clk/clk_disable/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/clk/clk_enable/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/power/suspend_resume/enable" },
+ } },
+ { "membus", "Memory Bus Utilization", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/memory_bus/enable" },
+ } },
+ { "idle", "CPU Idle", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/power/cpu_idle/enable" },
+ } },
+ { "disk", "Disk I/O", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable" },
+ } },
+ { "mmc", "eMMC commands", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/mmc/enable" },
+ } },
+ { "load", "CPU Load", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable" },
+ } },
+ { "sync", "Synchronization", 0, {
+ // linux kernel < 4.9
+ { OPT, "/sys/kernel/debug/tracing/events/sync/enable" },
+ // linux kernel == 4.9.x
+ { OPT, "/sys/kernel/debug/tracing/events/fence/enable" },
+ // linux kernel > 4.9
+ { OPT, "/sys/kernel/debug/tracing/events/dma_fence/enable" },
+ } },
+ { "workq", "Kernel Workqueues", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/workqueue/enable" },
+ } },
+ { "regulators", "Voltage and Current Regulators", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/regulator/enable" },
+ } },
+ { "pagecache", "Page cache", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/filemap/enable" },
+ } },
+#ifdef TTRACE_PROFILE_MOBILE
+#elif defined TTRACE_PROFILE_TV
+ { "system", "System", TTRACE_TAG_SYSTEM, { } },
+ { "perftest", "For Performance test", TTRACE_TAG_PERFTEST, { } },
+#elif defined TTRACE_PROFILE_WEARABLE
+#endif
+};
+
+/* Command line options */
+static int g_traceDurationSeconds = 5;
+static bool g_traceOverwrite = false;
+static int g_traceBufferSizeKB = 2048;
+static bool g_compress = false;
+static bool g_nohup = false;
+static int g_initialSleepSecs = 0;
+static const char* g_kernelTraceFuncs = nullptr;
+static const char* g_debugAppCmdLine = "";
+
+/* Global state */
+static bool g_traceAborted = false;
+static bool g_categoryEnables[NELEM(k_categories)] = {};
+static bool g_append_trace = false;
+static bool g_backup_trace = false;
+
+/* Save excluded tags list */
+uint64_t excludedTags;
+
+static bool setCategoryEnable(const char* name, bool enable);
+
+/* Sys file paths */
+static const char* k_traceClockPath =
+ "/sys/kernel/debug/tracing/trace_clock";
+
+static const char* k_traceBufferSizePath =
+ "/sys/kernel/debug/tracing/buffer_size_kb";
+
+static const char* k_tracingOverwriteEnablePath =
+ "/sys/kernel/debug/tracing/options/overwrite";
+
+static const char* k_currentTracerPath =
+ "/sys/kernel/debug/tracing/current_tracer";
+
+static const char* k_printTgidPath =
+ "/sys/kernel/debug/tracing/options/print-tgid";
+
+static const char* k_funcgraphAbsTimePath =
+ "/sys/kernel/debug/tracing/options/funcgraph-abstime";
+
+static const char* k_funcgraphCpuPath =
+ "/sys/kernel/debug/tracing/options/funcgraph-cpu";
+
+static const char* k_funcgraphProcPath =
+ "/sys/kernel/debug/tracing/options/funcgraph-proc";
+
+static const char* k_funcgraphFlatPath =
+ "/sys/kernel/debug/tracing/options/funcgraph-flat";
+
+static const char* k_funcgraphDurationPath =
+ "/sys/kernel/debug/tracing/options/funcgraph-duration";
+
+static const char* k_ftraceFilterPath =
+ "/sys/kernel/debug/tracing/set_ftrace_filter";
+
+static const char* k_tracingOnPath =
+ "/sys/kernel/debug/tracing/tracing_on";
+
+static const char* k_tracePath =
+ "/sys/kernel/debug/tracing/trace";
+
+// Check whether a file exists.
+static bool fileExists(const char* filename) {
+ return access(filename, F_OK) != -1;
+}
+
+// Check whether a file is writable.
+static bool fileIsWritable(const char* filename) {
+ return access(filename, W_OK) != -1;
+}
+
+// Truncate a file.
+static bool truncateFile(const char* path)
+{
+ // This uses creat rather than truncate because some of the debug kernel
+ // device nodes (e.g. k_ftraceFilterPath) currently aren't changed by
+ // calls to truncate, but they are cleared by calls to creat.
+ int traceFD = creat(path, 0);
+ if (traceFD == -1) {
+ fprintf(stderr, "error truncating %s: %s (%d)\n", path,
+ strerror_r(errno, str_error, sizeof(str_error)), errno);
+ return false;
+ }
+
+ close(traceFD);
+
+ return true;
+}
+
+static bool _writeStr(const char* filename, const char* str, int flags)
+{
+ int fd = open(filename, flags);
+ if (fd == -1) {
+ fprintf(stderr, "error opening %s: %s (%d)\n", filename,
+ strerror_r(errno, str_error, sizeof(str_error)), errno);
+ return false;
+ }
+
+ bool ok = true;
+ ssize_t len = strlen(str);
+ if (write(fd, str, len) != len) {
+ fprintf(stderr, "error writing to %s: %s (%d)\n", filename,
+ strerror_r(errno, str_error, sizeof(str_error)), errno);
+ ok = false;
+ }
+
+ close(fd);
+
+ return ok;
+}
+
+// Write a string to a file, returning true if the write was successful.
+static bool writeStr(const char* filename, const char* str)
+{
+ return _writeStr(filename, str, O_WRONLY);
+}
+
+// Append a string to a file, returning true if the write was successful.
+static bool appendStr(const char* filename, const char* str)
+{
+ return _writeStr(filename, str, O_APPEND|O_WRONLY);
+}
+
+// Enable or disable a kernel option by writing a "1" or a "0" into a /sys
+// file.
+static bool setKernelOptionEnable(const char* filename, bool enable)
+{
+ return writeStr(filename, enable ? "1" : "0");
+}
+
+// Check whether the category is supported on the device with the current
+// rootness. A category is supported only if all its required /sys/ files are
+// writable and if enabling the category will enable one or more tracing tags
+// or /sys/ files.
+static bool isCategorySupported(const TracingCategory& category)
+{
+ bool ok = category.tags != 0;
+ for (int i = 0; i < MAX_SYS_FILES; i++) {
+ const char* path = category.sysfiles[i].path;
+ bool req = category.sysfiles[i].required == REQ;
+ if (path != nullptr) {
+ if (req) {
+ if (!fileIsWritable(path)) {
+ return false;
+ } else {
+ ok = true;
+ }
+ } else {
+ ok |= fileIsWritable(path);
+ }
+ }
+ }
+ return ok;
+}
+
+// Check whether the category would be supported on the device if the user
+// were root. This function assumes that root is able to write to any file
+// that exists. It performs the same logic as isCategorySupported, but it
+// uses file existance rather than writability in the /sys/ file checks.
+static bool isCategorySupportedForRoot(const TracingCategory& category)
+{
+ bool ok = category.tags != 0;
+ for (int i = 0; i < MAX_SYS_FILES; i++) {
+ const char* path = category.sysfiles[i].path;
+ bool req = category.sysfiles[i].required == REQ;
+ if (path != nullptr) {
+ if (req) {
+ if (!fileExists(path)) {
+ return false;
+ } else {
+ ok = true;
+ }
+ } else {
+ ok |= fileExists(path);
+ }
+ }
+ }
+ return ok;
+}
+
+// Enable or disable overwriting of the kernel trace buffers. Disabling this
+// will cause tracing to stop once the trace buffers have filled up.
+static bool setTraceOverwriteEnable(bool enable)
+{
+ return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable);
+}
+
+// Enable or disable kernel tracing.
+static bool setTracingEnabled(bool enable)
+{
+ return setKernelOptionEnable(k_tracingOnPath, enable);
+}
+
+// Clear the contents of the kernel trace.
+static bool clearKernelTrace()
+{
+ return truncateFile(k_tracePath);
+}
+
+// Set the size of the kernel's trace buffer in kilobytes.
+static bool setTraceBufferSizeKB(int size)
+{
+ char str[32] = "1";
+ if (size < 1) {
+ size = 1;
+ }
+ snprintf(str, 32, "%d", size);
+ return writeStr(k_traceBufferSizePath, str);
+}
+
+// Set the clock to the best available option while tracing. Use 'boot' if it's
+// available; otherwise, use 'mono'. If neither are available use 'global'.
+// Any write to the trace_clock sysfs file will reset the buffer, so only
+// update it if the requested value is not the current value.
+static bool setBestClock(bool enable)
+{
+ std::ifstream clockFile(k_traceClockPath, std::ios::in);
+ std::string clockStr((std::istreambuf_iterator<char>(clockFile)),
+ std::istreambuf_iterator<char>());
+ std::string newClock;
+
+ if (enable == false)
+ return writeStr(k_traceClockPath, "local");
+
+ if (clockStr.find("boot") != std::string::npos)
+ newClock = "boot";
+ else if (clockStr.find("mono") != std::string::npos)
+ newClock = "mono";
+ else
+ newClock = "global";
+
+ size_t begin = clockStr.find('[') + 1;
+ size_t end = clockStr.find(']');
+ if (newClock.compare(0, std::string::npos, clockStr, begin, end-begin) == 0)
+ return true;
+
+ return writeStr(k_traceClockPath, newClock.c_str());
+}
+
+static bool setPrintTgidEnableIfPresent(bool enable)
+{
+ if (fileExists(k_printTgidPath)) {
+ return setKernelOptionEnable(k_printTgidPath, enable);
+ }
+ return true;
+}
+
+static bool getBootupTagStr(char* bootupTagStr, int strSize)
+{
+ if(fileExists(BOOTUP_TRACE)) {
+ FILE *bootupTagFile = nullptr;
+
+ bootupTagFile = fopen(BOOTUP_TRACE, "r");
+ if (bootupTagFile == nullptr) {
+ return false;
+ }
+ if (fgets(bootupTagStr, strSize, bootupTagFile) == NULL) {
+ fclose(bootupTagFile);
+ return false;
+ }
+ fclose(bootupTagFile);
+ fprintf(stderr, "[Info] Loaded tags: %s, bufsize: %d\n",\
+ bootupTagStr, strSize);
+ return true;
+ }
+ return false;
+}
+
+static void setBootupTags(char* bootupTagStr)
+{
+ char* tagPtr;
+ char* nextTagPtr;
+ tagPtr = strtok_r(bootupTagStr, " \n", &nextTagPtr);
+ while (tagPtr != nullptr) {
+ setCategoryEnable(tagPtr, true);
+ fprintf(stderr, "[Info] Tag %s enabled\n", tagPtr);
+ tagPtr = strtok_r(nullptr, " \n", &nextTagPtr);
+ }
+ return;
+}
+
+static bool initEnabledTagFile()
+{
+ uint64_t *sm_for_enabled_tag = nullptr;
+ int fd = -1;
+
+ if(fileExists(ENABLED_TAG_FILE)) {
+ fprintf(stderr, "[Info] T-trace has been already initailized\n");
+ return true; //atrace has been already initailized.
+ }
+
+ fd = open("/tmp/tmp_tag", O_CREAT | O_RDWR | O_CLOEXEC, 0600);
+ if(fd < 0){
+ fprintf(stderr, "Fail to open enabled_tag file: %s(%d)\n", strerror_r(errno, str_error, sizeof(str_error)), errno);
+ return false;
+ }
+
+ if (ftruncate(fd, sizeof(uint64_t)) < 0) {
+ fprintf(stderr, "ftruncate() failed(%s)\n", strerror_r(errno, str_error, sizeof(str_error)));
+ close(fd);
+ return false;
+ }
+ sm_for_enabled_tag = (uint64_t*)mmap(nullptr, sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if(sm_for_enabled_tag == MAP_FAILED) {
+ fprintf(stderr, "mmap() failed(%s)\n", strerror_r(errno, str_error, sizeof(str_error)));
+ close(fd);
+ return false;
+ }
+
+ memset(sm_for_enabled_tag, 0, sizeof(uint64_t));
+ if(-1 == rename("/tmp/tmp_tag", ENABLED_TAG_FILE)) {
+ fprintf(stderr, "Fail to rename enabled_tag file: %s(%d)\n", strerror_r(errno, str_error, sizeof(str_error)), errno);
+ }
+
+ munmap(sm_for_enabled_tag, sizeof(uint64_t));
+ close(fd);
+ return true;
+}
+
+// Set the trace tags that userland tracing uses, and poke the running
+// processes to pick up the new value.
+static bool setTagsProperty(uint64_t tags)
+{
+ uint64_t *sm_for_enabled_tag = nullptr;
+ int fd = -1;
+
+ // atrace normal mode
+ fd = open(ENABLED_TAG_FILE, O_RDWR | O_CLOEXEC, 0600);
+ if(fd < 0){
+ fprintf(stderr, "Fail to open enabled_tag file: %s(%d)\n", strerror_r(errno, str_error, sizeof(str_error)), errno);
+ return false;
+ }
+ sm_for_enabled_tag = (uint64_t*)mmap(nullptr, sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if(sm_for_enabled_tag == MAP_FAILED) {
+ fprintf(stderr, "mmap() failed(%s)\n", strerror_r(errno, str_error, sizeof(str_error)));
+ close(fd);
+ return false;
+ }
+ *sm_for_enabled_tag = tags;
+
+ // For debug
+ // fprintf(stderr, "Enabled TAGs: %u\n", (uint32_t)*sm_for_enabled_tag);
+ //
+ munmap(sm_for_enabled_tag, sizeof(uint64_t));
+ close(fd);
+ return true;
+}
+
+// Disable all /sys/ enable files.
+static bool disableKernelTraceEvents() {
+ bool ok = true;
+ for (int i = 0; i < NELEM(k_categories); i++) {
+ const TracingCategory &c = k_categories[i];
+ for (int j = 0; j < MAX_SYS_FILES; j++) {
+ const char* path = c.sysfiles[j].path;
+ if (path != nullptr && fileIsWritable(path)) {
+ ok &= setKernelOptionEnable(path, false);
+ }
+ }
+ }
+ return ok;
+}
+
+// Set the comma separated list of functions that the kernel is to trace.
+static bool setKernelTraceFuncs(const char* funcs)
+{
+ bool ok = true;
+ char *ptr[2];
+
+ if (funcs == nullptr || funcs[0] == '\0') {
+ // Disable kernel function tracing.
+ if (fileIsWritable(k_currentTracerPath)) {
+ ok &= writeStr(k_currentTracerPath, "nop");
+ }
+ if (fileIsWritable(k_ftraceFilterPath)) {
+ ok &= truncateFile(k_ftraceFilterPath);
+ }
+ } else {
+ // Enable kernel function tracing.
+ ok &= writeStr(k_currentTracerPath, "function_graph");
+ ok &= setKernelOptionEnable(k_funcgraphAbsTimePath, true);
+ ok &= setKernelOptionEnable(k_funcgraphCpuPath, true);
+ ok &= setKernelOptionEnable(k_funcgraphProcPath, true);
+ ok &= setKernelOptionEnable(k_funcgraphFlatPath, true);
+ ok &= setKernelOptionEnable(k_funcgraphDurationPath, true);
+
+ // Set the requested filter functions.
+ ok &= truncateFile(k_ftraceFilterPath);
+ char* myFuncs = strdup(funcs);
+ char* func = strtok_r(myFuncs, ",", &ptr[0]);
+ while (func) {
+ ok &= appendStr(k_ftraceFilterPath, func);
+ func = strtok_r(NULL, ",", &ptr[1]);
+ }
+ free(myFuncs);
+ }
+
+ return ok;
+}
+
+// Enable tracing in the kernel.
+static bool startTrace()
+{
+ return setTracingEnabled(true);
+}
+
+// Set all the kernel tracing settings to the desired state for this trace
+// capture.
+static bool setUpTrace()
+{
+ bool ok = true;
+
+ // Set up the tracing options.
+ ok &= setTraceOverwriteEnable(g_traceOverwrite);
+ if(!g_append_trace) {
+ ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
+ ok &= setBestClock(true);
+ ok &= setKernelTraceFuncs(g_kernelTraceFuncs);
+ }
+ ok &= setPrintTgidEnableIfPresent(true);
+
+ // Set up the tags property.
+ uint64_t tags = 0;
+ if (g_categoryEnables[TAG_NONE_IDX]) tags = TTRACE_TAG_NEVER;
+ else {
+ for (int i = 0; i < NELEM(k_categories); i++) {
+ if (g_categoryEnables[i]) {
+ const TracingCategory &c = k_categories[i];
+ if ((c.tags & excludedTags) == 0)
+ tags |= c.tags;
+ }
+ }
+ if (tags == 0 && excludedTags == 0) {
+ tags |= TTRACE_TAG_ALWAYS;
+ }
+ else if (tags == 0 && excludedTags != 0) {
+ excludedTags |= TTRACE_TAG_ALWAYS;
+ tags |= ~excludedTags;
+ }
+ else {
+ excludedTags |= TTRACE_TAG_ALWAYS;
+ tags &= ~excludedTags;
+ }
+ printf("Tags: 0x%" PRIx64 " excludedTags: 0x%" PRIx64 "\n", tags, excludedTags);
+ }
+
+ ok &= startTrace();
+ if(!g_append_trace) {
+ // Clear Kernel Trace
+ ok &= clearKernelTrace();
+
+ // Disable all the sysfs enables. This is done as a separate loop from
+ // the enables to allow the same enable to exist in multiple categories.
+ ok &= disableKernelTraceEvents();
+ }
+ ok &= setTagsProperty(tags);
+
+ // Enable all the sysfs enables that are in an enabled category.
+ for (int i = 0; i < NELEM(k_categories); i++) {
+ if (g_categoryEnables[i]) {
+ const TracingCategory &c = k_categories[i];
+ for (int j = 0; j < MAX_SYS_FILES; j++) {
+ const char* path = c.sysfiles[j].path;
+ bool required = c.sysfiles[j].required == REQ;
+ if (path != nullptr) {
+ if (fileIsWritable(path)) {
+ ok &= setKernelOptionEnable(path, true);
+ } else if (required) {
+ fprintf(stderr, "error writing file %s\n", path);
+ ok = false;
+ }
+ }
+ }
+ }
+ }
+
+ return ok;
+}
+
+// Reset all the kernel tracing settings to their default state.
+static void cleanUpTrace()
+{
+ // Disable all tracing that we're able to.
+ disableKernelTraceEvents();
+
+ // Set the options back to their defaults.
+ setTraceOverwriteEnable(true);
+ setTraceBufferSizeKB(1);
+ setBestClock(false);
+ setPrintTgidEnableIfPresent(false);
+ setKernelTraceFuncs(nullptr);
+}
+
+// Disable tracing in the kernel.
+static void stopTrace()
+{
+ setTagsProperty(0);
+ setTracingEnabled(false);
+}
+
+// Read the current kernel trace and write it to stdout.
+static void dumpTrace(bool startup)
+{
+ int backup_fd = -1;
+ int traceFD = -1;
+
+ traceFD = open(k_tracePath, O_RDWR);
+ if (traceFD == -1) {
+ fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath,
+ strerror_r(errno, str_error, sizeof(str_error)), errno);
+ return;
+ }
+
+ if(startup) {
+ backup_fd = open(BACKUP_TRACE, O_CREAT|O_RDWR|O_TRUNC, 0600);
+ if (backup_fd == -1) {
+ fprintf(stderr, "error opening %s: %s (%d)\n", BACKUP_TRACE,
+ strerror_r(errno, str_error, sizeof(str_error)), errno);
+ close(traceFD);
+ return;
+ }
+ }
+
+ if (g_compress) {
+ z_stream zs;
+ int result, flush;
+
+ bzero(&zs, sizeof(zs));
+ result = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
+ if (result != Z_OK) {
+ fprintf(stderr, "error initializing zlib: %d\n", result);
+ close(traceFD);
+ if (backup_fd > -1)
+ close(backup_fd);
+ return;
+ }
+
+ const size_t bufSize = 64*1024;
+ std::unique_ptr<uint8_t[]> in(new uint8_t[bufSize]);
+ std::unique_ptr<uint8_t[]> out(new uint8_t[bufSize]);
+ if ((in == nullptr) || (out == nullptr)) {
+ fprintf(stderr, "Could not allocate memory");
+ close(traceFD);
+ if (backup_fd > -1)
+ close(backup_fd);
+ return;
+ }
+ flush = Z_NO_FLUSH;
+
+ zs.next_out = reinterpret_cast<Bytef*>(out.get());
+ zs.avail_out = bufSize;
+
+ do {
+
+ if (zs.avail_in == 0) {
+ // More input is needed.
+ result = read(traceFD, in.get(), bufSize);
+ if (result < 0) {
+ fprintf(stderr, "error reading trace: %s (%d)\n",
+ strerror_r(errno, str_error, sizeof(str_error)), errno);
+ result = Z_STREAM_END;
+ break;
+ } else if (result == 0) {
+ flush = Z_FINISH;
+ } else {
+ zs.next_in = reinterpret_cast<Bytef*>(in.get());
+ zs.avail_in = result;
+ }
+ }
+
+ if (zs.avail_out == 0) {
+ // Need to write the output.
+ if(startup)
+ result = write(backup_fd, out.get(), bufSize);
+ else
+ result = write(STDOUT_FILENO, out.get(), bufSize);
+
+ if ((size_t)result < bufSize) {
+ fprintf(stderr, "error writing deflated trace: %s (%d)\n",
+ strerror_r(errno, str_error, sizeof(str_error)), errno);
+ result = Z_STREAM_END; // skip deflate error message
+ zs.avail_out = bufSize; // skip the final write
+ break;
+ }
+ zs.next_out = reinterpret_cast<Bytef*>(out.get());
+ zs.avail_out = bufSize;
+ }
+
+ } while ((result = deflate(&zs, flush)) == Z_OK);
+
+ if (result != Z_STREAM_END) {
+ fprintf(stderr, "error deflating trace: %s\n", zs.msg);
+ }
+
+ if (zs.avail_out < bufSize) {
+ size_t bytes = bufSize - zs.avail_out;
+ if(startup)
+ result = write(backup_fd, out.get(), bytes);
+ else
+ result = write(STDOUT_FILENO, out.get(), bytes);
+
+ if ((size_t)result < bytes) {
+ fprintf(stderr, "error writing deflated trace: %s (%d)\n",
+ strerror_r(errno, str_error, sizeof(str_error)), errno);
+ }
+ }
+
+ result = deflateEnd(&zs);
+ if (result != Z_OK) {
+ fprintf(stderr, "error cleaning up zlib: %d\n", result);
+ }
+ } else {
+ ssize_t sent = 0;
+ if (startup)
+ while ((sent = sendfile(backup_fd, traceFD, nullptr, 64*1024*1024)) > 0);
+ else
+ while ((sent = sendfile(STDOUT_FILENO, traceFD, nullptr, 64*1024*1024)) > 0);
+
+ if (sent == -1) {
+ fprintf(stderr, "error dumping trace: %s (%d)\n", strerror_r(errno, str_error, sizeof(str_error)),
+ errno);
+ }
+ }
+
+ if (backup_fd > -1)
+ close(backup_fd);
+ close(traceFD);
+}
+
+static void handleSignal(int signo)
+{
+ if (!g_nohup) {
+ g_traceAborted = true;
+ }
+}
+
+static void registerSigHandler()
+{
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = handleSignal;
+ sigaction(SIGHUP, &sa, nullptr);
+ sigaction(SIGINT, &sa, nullptr);
+ sigaction(SIGQUIT, &sa, nullptr);
+ sigaction(SIGTERM, &sa, nullptr);
+}
+
+static bool setCategoryEnable(const char* name, bool enable)
+{
+ for (int i = 0; i < NELEM(k_categories); i++) {
+ const TracingCategory& c = k_categories[i];
+ if (strcmp(name, c.name) == 0) {
+ if (isCategorySupported(c)) {
+ g_categoryEnables[i] = enable;
+ return true;
+ } else {
+ if (isCategorySupportedForRoot(c)) {
+ fprintf(stderr, "error: category \"%s\" requires root "
+ "privileges.\n", name);
+ } else {
+ fprintf(stderr, "error: category \"%s\" is not supported "
+ "on this device.\n", name);
+ }
+ return false;
+ }
+ }
+ }
+ fprintf(stderr, "error: unknown tracing category \"%s\"\n", name);
+ return false;
+}
+
+static void listSupportedCategories()
+{
+ for (int i = 0; i < NELEM(k_categories); i++) {
+ if(i==TAG_NONE_IDX) continue;
+ const TracingCategory& c = k_categories[i];
+ if (isCategorySupported(c)) {
+ printf(" %10s - %s\n", c.name, c.longname);
+ }
+ }
+}
+
+// Print the command usage help to stderr.
+static void showHelp(const char *cmd)
+{
+ fprintf(stderr, "usage: %s [options] [categories...]\n", cmd);
+ fprintf(stderr, "options include:\n"
+ " -b N use a trace buffer size of N KB\n"
+ " -c trace into a circular buffer\n"
+ " -e category specify a category to exclude [default None]\n"
+ " example, $ atrace -e wm -e am -e gfx\n"
+ " -k fname,... trace the listed kernel functions\n"
+ " -n ignore signals\n"
+ " -s N sleep for N seconds before tracing [default 0]\n"
+ " -t N trace for N seconds [defualt 5]\n"
+ " -z compress the trace dump\n"
+ " --async_start start circular trace and return immediatly\n"
+ " --async_dump dump the current contents of circular trace buffer\n"
+ " --async_stop stop tracing and dump the current contents of circular\n"
+ " trace buffer\n"
+ " --append append traces to the existing traces. do not clear the trace buffer \n"
+ " and kernel trace events set\n"
+ " --backup back up the existing traces to /tmp/trace.backup and then clear the trace buffer\n"
+ " --list_categories\n"
+ " list the available tracing categories\n"
+ );
+}
+
+int main(int argc, char **argv)
+{
+ bool async = false;
+ bool traceStart = true;
+ bool traceStop = true;
+ bool traceDump = true;
+
+ char strBuf[128];
+
+ // Global Variable Initialization
+ excludedTags = 0ULL;
+
+ if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
+ showHelp(argv[0]);
+ exit(0);
+ }
+
+ for (;;) {
+ int ret;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"async_start", no_argument, nullptr, 0 },
+ {"async_stop", no_argument, nullptr, 0 },
+ {"async_dump", no_argument, nullptr, 0 },
+ {"list_categories", no_argument, nullptr, 0 },
+ {"init_exec", no_argument, nullptr, 0 },
+ {"append", no_argument, nullptr, 0 },
+ {"backup", no_argument, nullptr, 0 },
+ {0, 0, nullptr, 0 }
+ };
+ ret = getopt_long(argc, argv, "b:ck:e:ns:t:z",
+ long_options, &option_index);
+ if (ret < 0) {
+ for (int i = optind; i < argc; i++) {
+ if (!setCategoryEnable(argv[i], true)) {
+ fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]);
+ exit(1);
+ }
+ }
+ break;
+ }
+
+ switch(ret) {
+ case 'a':
+ g_debugAppCmdLine = optarg;
+ break;
+
+ case 'b':
+ g_traceBufferSizeKB = atoi(optarg);
+ break;
+
+ case 'c':
+ g_traceOverwrite = true;
+ break;
+
+ case 'e':
+ setCategoryEnable(optarg, true);
+ for (int i = 0; i < NELEM(k_categories); i++) {
+ const TracingCategory &c = k_categories[i];
+ if (!strcmp(c.name, optarg)) {
+ excludedTags |= c.tags;
+ }
+ }
+ break;
+
+ case 'k':
+ g_kernelTraceFuncs = optarg;
+ break;
+
+ case 'n':
+ g_nohup = true;
+ break;
+
+ case 's':
+ g_initialSleepSecs = atoi(optarg);
+ break;
+
+ case 't':
+ g_traceDurationSeconds = atoi(optarg);
+ break;
+
+ case 'z':
+ g_compress = true;
+ break;
+
+ case 0:
+ if (!strcmp(long_options[option_index].name, "list_categories")) {
+ listSupportedCategories();
+ exit(0);
+ }
+ if (!strcmp(long_options[option_index].name, "async_start")) {
+ async = true;
+ traceStop = false;
+ traceDump = false;
+ g_traceOverwrite = true;
+ } else if (!strcmp(long_options[option_index].name, "async_stop")) {
+ async = true;
+ traceStart = false;
+ traceStop = true;
+ } else if (!strcmp(long_options[option_index].name, "async_dump")) {
+ async = true;
+ traceStart = false;
+ traceStop = false;
+ } else if (!strcmp(long_options[option_index].name, "list_categories")) {
+ listSupportedCategories();
+ exit(0);
+ } else if (!strcmp(long_options[option_index].name, "init_exec")) {
+ fprintf(stderr, "[Info] Initailize T-trace\n");
+ if(!initEnabledTagFile()) {
+ exit(-1);
+ }
+ if (getBootupTagStr(strBuf, sizeof(strBuf))) {
+ async = true;
+ traceStop = false;
+ traceDump = false;
+ g_traceOverwrite = true;
+ setBootupTags(strBuf);
+ }
+ else {
+ exit(0);
+ }
+ } else if (!strcmp(long_options[option_index].name, "append")) {
+ g_append_trace = true;
+ } else if (!strcmp(long_options[option_index].name, "backup")) {
+ g_backup_trace = true;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "\n");
+ showHelp(argv[0]);
+ exit(0);
+ break;
+ }
+ }
+
+ registerSigHandler();
+
+ if (g_initialSleepSecs > 0) {
+ sleep(g_initialSleepSecs);
+ }
+
+ bool ok = true;
+ if(traceStart && g_backup_trace) {
+ //before start tracing by atrace, backup existig traces
+ stopTrace();
+ dumpTrace(true);
+ }
+ if (!(async && !g_traceOverwrite)) {
+ ok &= setUpTrace();
+ }
+ if (ok && traceStart) {
+ // For debug
+ // printf("capturing trace...");
+ //
+ fflush(stdout);
+
+ // We clear the trace after starting it because tracing gets enabled for
+ // each CPU individually in the kernel. Having the beginning of the trace
+ // contain entries from only one CPU can cause "begin" entries without a
+ // matching "end" entry to show up if a task gets migrated from one CPU to
+ // another.
+ if (ok && !async) {
+ // Sleep to allow the trace to be captured.
+ struct timespec timeLeft;
+ timeLeft.tv_sec = g_traceDurationSeconds;
+ timeLeft.tv_nsec = 0;
+ do {
+ if (g_traceAborted) {
+ break;
+ }
+ } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR);
+ }
+ }
+
+ // Stop the trace and restore the default settings.
+ if (traceStop)
+ stopTrace();
+
+ if (ok && traceDump) {
+ if (!g_traceAborted) {
+ printf(" done\nTRACE:\n");
+ fflush(stdout);
+ dumpTrace(false);
+ } else {
+ printf("\ntrace aborted.\n");
+ fflush(stdout);
+ }
+ clearKernelTrace();
+ } else if (!ok) {
+ fprintf(stderr, "unable to start tracing\n");
+ }
+
+ // Reset the trace buffer size to 1.
+ if (traceStop)
+ cleanUpTrace();
+
+ return g_traceAborted ? 1 : 0;
+}
%define TTRACE_TIZEN_VERSION_MAJOR 3
%endif
+%define ATRACE_VERSION 1.1
+%define ATRACE_HELPER_VERSION 1.0
+
%description
T-trace library
MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'`
%cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DLIBDIR=%{_libdir} -DINCLUDEDIR=%{_includedir} \
-DTTRACE_PROFILE=%{TTRACE_PROFILE} -DTTRACE_TIZEN_VERSION_MAJOR=%{TTRACE_TIZEN_VERSION_MAJOR} \
- -DMAJORVER=${MAJORVER} -DFULLVER=%{version}
+ -DMAJORVER=${MAJORVER} -DFULLVER=%{version} \
+ -DATRACE_VERSION=%{ATRACE_VERSION} -DATRACE_HELPER_VERSION=%{ATRACE_HELPER_VERSION}
make %{?jobs:-j%jobs}
%if 0%{?gcov:1}
%{_unitdir}/ttrace-marker.service
%{_unitdir}/sys-kernel-debug-tracing.mount
%attr(755,root,users) %{_bindir}/atrace
+%attr(755,root,users) %{_bindir}/atrace-%{ATRACE_VERSION}
%attr(755,root,users) %{_bindir}/atrace-helper
+%attr(755,root,users) %{_bindir}/atrace-helper-%{ATRACE_HELPER_VERSION}
%{_unitdir}/sysinit.target.wants/ttrace-marker.service
%{_unitdir}/sysinit.target.wants/sys-kernel-debug-tracing.mount
%attr(755,root,root) %{_bindir}/atrace-bootup.sh
+++ /dev/null
-
- Copyright (c) 2012, The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
+++ /dev/null
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/sendfile.h>
-#include <time.h>
-#include <zlib.h>
-#include <stdint.h>
-#include <strings.h>
-#include <string.h>
-#include <grp.h>
-#include <sys/mman.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/smack.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <inttypes.h>
-
-#include <memory>
-#include <fstream>
-
-#include "ttrace.h"
-
-using std::string;
-
-#define TTRACE_TAG_NONE 9999
-#define TAG_NONE_IDX 0
-
-#define BACKUP_TRACE "/tmp/trace.backup"
-#define BOOTUP_TRACE "/etc/ttrace.conf"
-#define DEF_GR_SIZE 1024
-#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
-
-enum { MAX_SYS_FILES = 11 };
-
-const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
-const char* k_traceAppCmdlineProperty = "debug.atrace.app_cmdlines";
-
-typedef enum { OPT, REQ } requiredness ;
-
-char str_error[256] = "";
-
-struct TracingCategory {
- // The name identifying the category.
- const char* name;
-
- // A longer description of the category.
- const char* longname;
-
- // The userland tracing tags that the category enables.
- uint64_t tags;
-
- // The fname==nullptr terminated list of /sys/ files that the category
- // enables.
- struct {
- // Whether the file must be writable in order to enable the tracing
- // category.
- requiredness required;
-
- // The path to the enable file.
- const char* path;
- } sysfiles[MAX_SYS_FILES];
-};
-
-/* Tracing categories */
-static const TracingCategory k_categories[] = {
- { "none", "None", TTRACE_TAG_NONE, { } }, //do not change "none" option's index
- { "gfx", "Graphics", TTRACE_TAG_GRAPHICS, { } },
- { "input", "Input", TTRACE_TAG_INPUT, { } },
- { "view", "View System", TTRACE_TAG_VIEW, { } },
- { "web", "Web", TTRACE_TAG_WEB, { } },
- { "wm", "Window Manager", TTRACE_TAG_WINDOW_MANAGER, { } },
- { "am", "Application Manager", TTRACE_TAG_APPLICATION_MANAGER, { } },
- { "image", "Image", TTRACE_TAG_IMAGE, { } },
- { "audio", "Audio", TTRACE_TAG_AUDIO, { } },
- { "video", "Video", TTRACE_TAG_VIDEO, { } },
- { "camera", "Camera", TTRACE_TAG_CAMERA, { } },
- { "hal", "Hardware Modules", TTRACE_TAG_HAL, { } },
- { "mc", "Multimedia content", TTRACE_TAG_MEDIA_CONTENT, { } },
- { "mdb", "Multimedia database", TTRACE_TAG_MEDIA_DB, { } },
- { "scmirroring", "Screen mirroring", TTRACE_TAG_SCREEN_MIRRORING, { } },
- { "efl", "EFL", TTRACE_TAG_EFL, { } },
- { "app", "Application", TTRACE_TAG_APP, { } },
- { "sched", "CPU Scheduling", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/sched/sched_waking/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/sched/sched_pi_setprio/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/sched/sched_process_exit/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/cgroup/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/oom/oom_score_adj_update/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/task/task_rename/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/task/task_newtask/enable" },
- } },
- { "irq", "IRQ Events", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/irq/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/ipi/enable" },
- } },
- { "irqoff", "IRQ-disabled code section tracing", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/preemptirq/irq_enable/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/preemptirq/irq_disable/enable" },
- } },
- { "i2c", "I2C Events", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/i2c/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_read/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_write/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_result/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/i2c/i2c_reply/enable" },
- } },
- { "freq", "CPU Frequency", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/power/clock_disable/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/power/clock_enable/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/clk/clk_set_rate/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/clk/clk_disable/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/clk/clk_enable/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/power/suspend_resume/enable" },
- } },
- { "membus", "Memory Bus Utilization", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/memory_bus/enable" },
- } },
- { "idle", "CPU Idle", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/power/cpu_idle/enable" },
- } },
- { "disk", "Disk I/O", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable" },
- { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable" },
- { OPT, "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable" },
- } },
- { "mmc", "eMMC commands", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/mmc/enable" },
- } },
- { "load", "CPU Load", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable" },
- } },
- { "sync", "Synchronization", 0, {
- // linux kernel < 4.9
- { OPT, "/sys/kernel/debug/tracing/events/sync/enable" },
- // linux kernel == 4.9.x
- { OPT, "/sys/kernel/debug/tracing/events/fence/enable" },
- // linux kernel > 4.9
- { OPT, "/sys/kernel/debug/tracing/events/dma_fence/enable" },
- } },
- { "workq", "Kernel Workqueues", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/workqueue/enable" },
- } },
- { "regulators", "Voltage and Current Regulators", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/regulator/enable" },
- } },
- { "pagecache", "Page cache", 0, {
- { REQ, "/sys/kernel/debug/tracing/events/filemap/enable" },
- } },
-#ifdef TTRACE_PROFILE_MOBILE
-#elif defined TTRACE_PROFILE_TV
- { "system", "System", TTRACE_TAG_SYSTEM, { } },
- { "perftest", "For Performance test", TTRACE_TAG_PERFTEST, { } },
-#elif defined TTRACE_PROFILE_WEARABLE
-#endif
-};
-
-/* Command line options */
-static int g_traceDurationSeconds = 5;
-static bool g_traceOverwrite = false;
-static int g_traceBufferSizeKB = 2048;
-static bool g_compress = false;
-static bool g_nohup = false;
-static int g_initialSleepSecs = 0;
-static const char* g_kernelTraceFuncs = nullptr;
-static const char* g_debugAppCmdLine = "";
-
-/* Global state */
-static bool g_traceAborted = false;
-static bool g_categoryEnables[NELEM(k_categories)] = {};
-static bool g_append_trace = false;
-static bool g_backup_trace = false;
-
-/* Save excluded tags list */
-uint64_t excludedTags;
-
-static bool setCategoryEnable(const char* name, bool enable);
-
-/* Sys file paths */
-static const char* k_traceClockPath =
- "/sys/kernel/debug/tracing/trace_clock";
-
-static const char* k_traceBufferSizePath =
- "/sys/kernel/debug/tracing/buffer_size_kb";
-
-static const char* k_tracingOverwriteEnablePath =
- "/sys/kernel/debug/tracing/options/overwrite";
-
-static const char* k_currentTracerPath =
- "/sys/kernel/debug/tracing/current_tracer";
-
-static const char* k_printTgidPath =
- "/sys/kernel/debug/tracing/options/print-tgid";
-
-static const char* k_funcgraphAbsTimePath =
- "/sys/kernel/debug/tracing/options/funcgraph-abstime";
-
-static const char* k_funcgraphCpuPath =
- "/sys/kernel/debug/tracing/options/funcgraph-cpu";
-
-static const char* k_funcgraphProcPath =
- "/sys/kernel/debug/tracing/options/funcgraph-proc";
-
-static const char* k_funcgraphFlatPath =
- "/sys/kernel/debug/tracing/options/funcgraph-flat";
-
-static const char* k_funcgraphDurationPath =
- "/sys/kernel/debug/tracing/options/funcgraph-duration";
-
-static const char* k_ftraceFilterPath =
- "/sys/kernel/debug/tracing/set_ftrace_filter";
-
-static const char* k_tracingOnPath =
- "/sys/kernel/debug/tracing/tracing_on";
-
-static const char* k_tracePath =
- "/sys/kernel/debug/tracing/trace";
-
-// Check whether a file exists.
-static bool fileExists(const char* filename) {
- return access(filename, F_OK) != -1;
-}
-
-// Check whether a file is writable.
-static bool fileIsWritable(const char* filename) {
- return access(filename, W_OK) != -1;
-}
-
-// Truncate a file.
-static bool truncateFile(const char* path)
-{
- // This uses creat rather than truncate because some of the debug kernel
- // device nodes (e.g. k_ftraceFilterPath) currently aren't changed by
- // calls to truncate, but they are cleared by calls to creat.
- int traceFD = creat(path, 0);
- if (traceFD == -1) {
- fprintf(stderr, "error truncating %s: %s (%d)\n", path,
- strerror_r(errno, str_error, sizeof(str_error)), errno);
- return false;
- }
-
- close(traceFD);
-
- return true;
-}
-
-static bool _writeStr(const char* filename, const char* str, int flags)
-{
- int fd = open(filename, flags);
- if (fd == -1) {
- fprintf(stderr, "error opening %s: %s (%d)\n", filename,
- strerror_r(errno, str_error, sizeof(str_error)), errno);
- return false;
- }
-
- bool ok = true;
- ssize_t len = strlen(str);
- if (write(fd, str, len) != len) {
- fprintf(stderr, "error writing to %s: %s (%d)\n", filename,
- strerror_r(errno, str_error, sizeof(str_error)), errno);
- ok = false;
- }
-
- close(fd);
-
- return ok;
-}
-
-// Write a string to a file, returning true if the write was successful.
-static bool writeStr(const char* filename, const char* str)
-{
- return _writeStr(filename, str, O_WRONLY);
-}
-
-// Append a string to a file, returning true if the write was successful.
-static bool appendStr(const char* filename, const char* str)
-{
- return _writeStr(filename, str, O_APPEND|O_WRONLY);
-}
-
-// Enable or disable a kernel option by writing a "1" or a "0" into a /sys
-// file.
-static bool setKernelOptionEnable(const char* filename, bool enable)
-{
- return writeStr(filename, enable ? "1" : "0");
-}
-
-// Check whether the category is supported on the device with the current
-// rootness. A category is supported only if all its required /sys/ files are
-// writable and if enabling the category will enable one or more tracing tags
-// or /sys/ files.
-static bool isCategorySupported(const TracingCategory& category)
-{
- bool ok = category.tags != 0;
- for (int i = 0; i < MAX_SYS_FILES; i++) {
- const char* path = category.sysfiles[i].path;
- bool req = category.sysfiles[i].required == REQ;
- if (path != nullptr) {
- if (req) {
- if (!fileIsWritable(path)) {
- return false;
- } else {
- ok = true;
- }
- } else {
- ok |= fileIsWritable(path);
- }
- }
- }
- return ok;
-}
-
-// Check whether the category would be supported on the device if the user
-// were root. This function assumes that root is able to write to any file
-// that exists. It performs the same logic as isCategorySupported, but it
-// uses file existance rather than writability in the /sys/ file checks.
-static bool isCategorySupportedForRoot(const TracingCategory& category)
-{
- bool ok = category.tags != 0;
- for (int i = 0; i < MAX_SYS_FILES; i++) {
- const char* path = category.sysfiles[i].path;
- bool req = category.sysfiles[i].required == REQ;
- if (path != nullptr) {
- if (req) {
- if (!fileExists(path)) {
- return false;
- } else {
- ok = true;
- }
- } else {
- ok |= fileExists(path);
- }
- }
- }
- return ok;
-}
-
-// Enable or disable overwriting of the kernel trace buffers. Disabling this
-// will cause tracing to stop once the trace buffers have filled up.
-static bool setTraceOverwriteEnable(bool enable)
-{
- return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable);
-}
-
-// Enable or disable kernel tracing.
-static bool setTracingEnabled(bool enable)
-{
- return setKernelOptionEnable(k_tracingOnPath, enable);
-}
-
-// Clear the contents of the kernel trace.
-static bool clearKernelTrace()
-{
- return truncateFile(k_tracePath);
-}
-
-// Set the size of the kernel's trace buffer in kilobytes.
-static bool setTraceBufferSizeKB(int size)
-{
- char str[32] = "1";
- if (size < 1) {
- size = 1;
- }
- snprintf(str, 32, "%d", size);
- return writeStr(k_traceBufferSizePath, str);
-}
-
-// Set the clock to the best available option while tracing. Use 'boot' if it's
-// available; otherwise, use 'mono'. If neither are available use 'global'.
-// Any write to the trace_clock sysfs file will reset the buffer, so only
-// update it if the requested value is not the current value.
-static bool setBestClock(bool enable)
-{
- std::ifstream clockFile(k_traceClockPath, std::ios::in);
- std::string clockStr((std::istreambuf_iterator<char>(clockFile)),
- std::istreambuf_iterator<char>());
- std::string newClock;
-
- if (enable == false)
- return writeStr(k_traceClockPath, "local");
-
- if (clockStr.find("boot") != std::string::npos)
- newClock = "boot";
- else if (clockStr.find("mono") != std::string::npos)
- newClock = "mono";
- else
- newClock = "global";
-
- size_t begin = clockStr.find('[') + 1;
- size_t end = clockStr.find(']');
- if (newClock.compare(0, std::string::npos, clockStr, begin, end-begin) == 0)
- return true;
-
- return writeStr(k_traceClockPath, newClock.c_str());
-}
-
-static bool setPrintTgidEnableIfPresent(bool enable)
-{
- if (fileExists(k_printTgidPath)) {
- return setKernelOptionEnable(k_printTgidPath, enable);
- }
- return true;
-}
-
-static bool getBootupTagStr(char* bootupTagStr, int strSize)
-{
- if(fileExists(BOOTUP_TRACE)) {
- FILE *bootupTagFile = nullptr;
-
- bootupTagFile = fopen(BOOTUP_TRACE, "r");
- if (bootupTagFile == nullptr) {
- return false;
- }
- if (fgets(bootupTagStr, strSize, bootupTagFile) == NULL) {
- fclose(bootupTagFile);
- return false;
- }
- fclose(bootupTagFile);
- fprintf(stderr, "[Info] Loaded tags: %s, bufsize: %d\n",\
- bootupTagStr, strSize);
- return true;
- }
- return false;
-}
-
-static void setBootupTags(char* bootupTagStr)
-{
- char* tagPtr;
- char* nextTagPtr;
- tagPtr = strtok_r(bootupTagStr, " \n", &nextTagPtr);
- while (tagPtr != nullptr) {
- setCategoryEnable(tagPtr, true);
- fprintf(stderr, "[Info] Tag %s enabled\n", tagPtr);
- tagPtr = strtok_r(nullptr, " \n", &nextTagPtr);
- }
- return;
-}
-
-static bool initEnabledTagFile()
-{
- uint64_t *sm_for_enabled_tag = nullptr;
- int fd = -1;
-
- if(fileExists(ENABLED_TAG_FILE)) {
- fprintf(stderr, "[Info] T-trace has been already initailized\n");
- return true; //atrace has been already initailized.
- }
-
- fd = open("/tmp/tmp_tag", O_CREAT | O_RDWR | O_CLOEXEC, 0600);
- if(fd < 0){
- fprintf(stderr, "Fail to open enabled_tag file: %s(%d)\n", strerror_r(errno, str_error, sizeof(str_error)), errno);
- return false;
- }
-
- if (ftruncate(fd, sizeof(uint64_t)) < 0) {
- fprintf(stderr, "ftruncate() failed(%s)\n", strerror_r(errno, str_error, sizeof(str_error)));
- close(fd);
- return false;
- }
- sm_for_enabled_tag = (uint64_t*)mmap(nullptr, sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-
- if(sm_for_enabled_tag == MAP_FAILED) {
- fprintf(stderr, "mmap() failed(%s)\n", strerror_r(errno, str_error, sizeof(str_error)));
- close(fd);
- return false;
- }
-
- memset(sm_for_enabled_tag, 0, sizeof(uint64_t));
- if(-1 == rename("/tmp/tmp_tag", ENABLED_TAG_FILE)) {
- fprintf(stderr, "Fail to rename enabled_tag file: %s(%d)\n", strerror_r(errno, str_error, sizeof(str_error)), errno);
- }
-
- munmap(sm_for_enabled_tag, sizeof(uint64_t));
- close(fd);
- return true;
-}
-
-// Set the trace tags that userland tracing uses, and poke the running
-// processes to pick up the new value.
-static bool setTagsProperty(uint64_t tags)
-{
- uint64_t *sm_for_enabled_tag = nullptr;
- int fd = -1;
-
- // atrace normal mode
- fd = open(ENABLED_TAG_FILE, O_RDWR | O_CLOEXEC, 0600);
- if(fd < 0){
- fprintf(stderr, "Fail to open enabled_tag file: %s(%d)\n", strerror_r(errno, str_error, sizeof(str_error)), errno);
- return false;
- }
- sm_for_enabled_tag = (uint64_t*)mmap(nullptr, sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if(sm_for_enabled_tag == MAP_FAILED) {
- fprintf(stderr, "mmap() failed(%s)\n", strerror_r(errno, str_error, sizeof(str_error)));
- close(fd);
- return false;
- }
- *sm_for_enabled_tag = tags;
-
- // For debug
- // fprintf(stderr, "Enabled TAGs: %u\n", (uint32_t)*sm_for_enabled_tag);
- //
- munmap(sm_for_enabled_tag, sizeof(uint64_t));
- close(fd);
- return true;
-}
-
-// Disable all /sys/ enable files.
-static bool disableKernelTraceEvents() {
- bool ok = true;
- for (int i = 0; i < NELEM(k_categories); i++) {
- const TracingCategory &c = k_categories[i];
- for (int j = 0; j < MAX_SYS_FILES; j++) {
- const char* path = c.sysfiles[j].path;
- if (path != nullptr && fileIsWritable(path)) {
- ok &= setKernelOptionEnable(path, false);
- }
- }
- }
- return ok;
-}
-
-// Set the comma separated list of functions that the kernel is to trace.
-static bool setKernelTraceFuncs(const char* funcs)
-{
- bool ok = true;
- char *ptr[2];
-
- if (funcs == nullptr || funcs[0] == '\0') {
- // Disable kernel function tracing.
- if (fileIsWritable(k_currentTracerPath)) {
- ok &= writeStr(k_currentTracerPath, "nop");
- }
- if (fileIsWritable(k_ftraceFilterPath)) {
- ok &= truncateFile(k_ftraceFilterPath);
- }
- } else {
- // Enable kernel function tracing.
- ok &= writeStr(k_currentTracerPath, "function_graph");
- ok &= setKernelOptionEnable(k_funcgraphAbsTimePath, true);
- ok &= setKernelOptionEnable(k_funcgraphCpuPath, true);
- ok &= setKernelOptionEnable(k_funcgraphProcPath, true);
- ok &= setKernelOptionEnable(k_funcgraphFlatPath, true);
- ok &= setKernelOptionEnable(k_funcgraphDurationPath, true);
-
- // Set the requested filter functions.
- ok &= truncateFile(k_ftraceFilterPath);
- char* myFuncs = strdup(funcs);
- char* func = strtok_r(myFuncs, ",", &ptr[0]);
- while (func) {
- ok &= appendStr(k_ftraceFilterPath, func);
- func = strtok_r(NULL, ",", &ptr[1]);
- }
- free(myFuncs);
- }
-
- return ok;
-}
-
-// Enable tracing in the kernel.
-static bool startTrace()
-{
- return setTracingEnabled(true);
-}
-
-// Set all the kernel tracing settings to the desired state for this trace
-// capture.
-static bool setUpTrace()
-{
- bool ok = true;
-
- // Set up the tracing options.
- ok &= setTraceOverwriteEnable(g_traceOverwrite);
- if(!g_append_trace) {
- ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
- ok &= setBestClock(true);
- ok &= setKernelTraceFuncs(g_kernelTraceFuncs);
- }
- ok &= setPrintTgidEnableIfPresent(true);
-
- // Set up the tags property.
- uint64_t tags = 0;
- if (g_categoryEnables[TAG_NONE_IDX]) tags = TTRACE_TAG_NEVER;
- else {
- for (int i = 0; i < NELEM(k_categories); i++) {
- if (g_categoryEnables[i]) {
- const TracingCategory &c = k_categories[i];
- if ((c.tags & excludedTags) == 0)
- tags |= c.tags;
- }
- }
- if (tags == 0 && excludedTags == 0) {
- tags |= TTRACE_TAG_ALWAYS;
- }
- else if (tags == 0 && excludedTags != 0) {
- excludedTags |= TTRACE_TAG_ALWAYS;
- tags |= ~excludedTags;
- }
- else {
- excludedTags |= TTRACE_TAG_ALWAYS;
- tags &= ~excludedTags;
- }
- printf("Tags: 0x%" PRIx64 " excludedTags: 0x%" PRIx64 "\n", tags, excludedTags);
- }
-
- ok &= startTrace();
- if(!g_append_trace) {
- // Clear Kernel Trace
- ok &= clearKernelTrace();
-
- // Disable all the sysfs enables. This is done as a separate loop from
- // the enables to allow the same enable to exist in multiple categories.
- ok &= disableKernelTraceEvents();
- }
- ok &= setTagsProperty(tags);
-
- // Enable all the sysfs enables that are in an enabled category.
- for (int i = 0; i < NELEM(k_categories); i++) {
- if (g_categoryEnables[i]) {
- const TracingCategory &c = k_categories[i];
- for (int j = 0; j < MAX_SYS_FILES; j++) {
- const char* path = c.sysfiles[j].path;
- bool required = c.sysfiles[j].required == REQ;
- if (path != nullptr) {
- if (fileIsWritable(path)) {
- ok &= setKernelOptionEnable(path, true);
- } else if (required) {
- fprintf(stderr, "error writing file %s\n", path);
- ok = false;
- }
- }
- }
- }
- }
-
- return ok;
-}
-
-// Reset all the kernel tracing settings to their default state.
-static void cleanUpTrace()
-{
- // Disable all tracing that we're able to.
- disableKernelTraceEvents();
-
- // Set the options back to their defaults.
- setTraceOverwriteEnable(true);
- setTraceBufferSizeKB(1);
- setBestClock(false);
- setPrintTgidEnableIfPresent(false);
- setKernelTraceFuncs(nullptr);
-}
-
-// Disable tracing in the kernel.
-static void stopTrace()
-{
- setTagsProperty(0);
- setTracingEnabled(false);
-}
-
-// Read the current kernel trace and write it to stdout.
-static void dumpTrace(bool startup)
-{
- int backup_fd = -1;
- int traceFD = -1;
-
- traceFD = open(k_tracePath, O_RDWR);
- if (traceFD == -1) {
- fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath,
- strerror_r(errno, str_error, sizeof(str_error)), errno);
- return;
- }
-
- if(startup) {
- backup_fd = open(BACKUP_TRACE, O_CREAT|O_RDWR|O_TRUNC, 0600);
- if (backup_fd == -1) {
- fprintf(stderr, "error opening %s: %s (%d)\n", BACKUP_TRACE,
- strerror_r(errno, str_error, sizeof(str_error)), errno);
- close(traceFD);
- return;
- }
- }
-
- if (g_compress) {
- z_stream zs;
- int result, flush;
-
- bzero(&zs, sizeof(zs));
- result = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
- if (result != Z_OK) {
- fprintf(stderr, "error initializing zlib: %d\n", result);
- close(traceFD);
- if (backup_fd > -1)
- close(backup_fd);
- return;
- }
-
- const size_t bufSize = 64*1024;
- std::unique_ptr<uint8_t[]> in(new uint8_t[bufSize]);
- std::unique_ptr<uint8_t[]> out(new uint8_t[bufSize]);
- if ((in == nullptr) || (out == nullptr)) {
- fprintf(stderr, "Could not allocate memory");
- close(traceFD);
- if (backup_fd > -1)
- close(backup_fd);
- return;
- }
- flush = Z_NO_FLUSH;
-
- zs.next_out = reinterpret_cast<Bytef*>(out.get());
- zs.avail_out = bufSize;
-
- do {
-
- if (zs.avail_in == 0) {
- // More input is needed.
- result = read(traceFD, in.get(), bufSize);
- if (result < 0) {
- fprintf(stderr, "error reading trace: %s (%d)\n",
- strerror_r(errno, str_error, sizeof(str_error)), errno);
- result = Z_STREAM_END;
- break;
- } else if (result == 0) {
- flush = Z_FINISH;
- } else {
- zs.next_in = reinterpret_cast<Bytef*>(in.get());
- zs.avail_in = result;
- }
- }
-
- if (zs.avail_out == 0) {
- // Need to write the output.
- if(startup)
- result = write(backup_fd, out.get(), bufSize);
- else
- result = write(STDOUT_FILENO, out.get(), bufSize);
-
- if ((size_t)result < bufSize) {
- fprintf(stderr, "error writing deflated trace: %s (%d)\n",
- strerror_r(errno, str_error, sizeof(str_error)), errno);
- result = Z_STREAM_END; // skip deflate error message
- zs.avail_out = bufSize; // skip the final write
- break;
- }
- zs.next_out = reinterpret_cast<Bytef*>(out.get());
- zs.avail_out = bufSize;
- }
-
- } while ((result = deflate(&zs, flush)) == Z_OK);
-
- if (result != Z_STREAM_END) {
- fprintf(stderr, "error deflating trace: %s\n", zs.msg);
- }
-
- if (zs.avail_out < bufSize) {
- size_t bytes = bufSize - zs.avail_out;
- if(startup)
- result = write(backup_fd, out.get(), bytes);
- else
- result = write(STDOUT_FILENO, out.get(), bytes);
-
- if ((size_t)result < bytes) {
- fprintf(stderr, "error writing deflated trace: %s (%d)\n",
- strerror_r(errno, str_error, sizeof(str_error)), errno);
- }
- }
-
- result = deflateEnd(&zs);
- if (result != Z_OK) {
- fprintf(stderr, "error cleaning up zlib: %d\n", result);
- }
- } else {
- ssize_t sent = 0;
- if (startup)
- while ((sent = sendfile(backup_fd, traceFD, nullptr, 64*1024*1024)) > 0);
- else
- while ((sent = sendfile(STDOUT_FILENO, traceFD, nullptr, 64*1024*1024)) > 0);
-
- if (sent == -1) {
- fprintf(stderr, "error dumping trace: %s (%d)\n", strerror_r(errno, str_error, sizeof(str_error)),
- errno);
- }
- }
-
- if (backup_fd > -1)
- close(backup_fd);
- close(traceFD);
-}
-
-static void handleSignal(int signo)
-{
- if (!g_nohup) {
- g_traceAborted = true;
- }
-}
-
-static void registerSigHandler()
-{
- struct sigaction sa;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
- sa.sa_handler = handleSignal;
- sigaction(SIGHUP, &sa, nullptr);
- sigaction(SIGINT, &sa, nullptr);
- sigaction(SIGQUIT, &sa, nullptr);
- sigaction(SIGTERM, &sa, nullptr);
-}
-
-static bool setCategoryEnable(const char* name, bool enable)
-{
- for (int i = 0; i < NELEM(k_categories); i++) {
- const TracingCategory& c = k_categories[i];
- if (strcmp(name, c.name) == 0) {
- if (isCategorySupported(c)) {
- g_categoryEnables[i] = enable;
- return true;
- } else {
- if (isCategorySupportedForRoot(c)) {
- fprintf(stderr, "error: category \"%s\" requires root "
- "privileges.\n", name);
- } else {
- fprintf(stderr, "error: category \"%s\" is not supported "
- "on this device.\n", name);
- }
- return false;
- }
- }
- }
- fprintf(stderr, "error: unknown tracing category \"%s\"\n", name);
- return false;
-}
-
-static void listSupportedCategories()
-{
- for (int i = 0; i < NELEM(k_categories); i++) {
- if(i==TAG_NONE_IDX) continue;
- const TracingCategory& c = k_categories[i];
- if (isCategorySupported(c)) {
- printf(" %10s - %s\n", c.name, c.longname);
- }
- }
-}
-
-// Print the command usage help to stderr.
-static void showHelp(const char *cmd)
-{
- fprintf(stderr, "usage: %s [options] [categories...]\n", cmd);
- fprintf(stderr, "options include:\n"
- " -b N use a trace buffer size of N KB\n"
- " -c trace into a circular buffer\n"
- " -e category specify a category to exclude [default None]\n"
- " example, $ atrace -e wm -e am -e gfx\n"
- " -k fname,... trace the listed kernel functions\n"
- " -n ignore signals\n"
- " -s N sleep for N seconds before tracing [default 0]\n"
- " -t N trace for N seconds [defualt 5]\n"
- " -z compress the trace dump\n"
- " --async_start start circular trace and return immediatly\n"
- " --async_dump dump the current contents of circular trace buffer\n"
- " --async_stop stop tracing and dump the current contents of circular\n"
- " trace buffer\n"
- " --append append traces to the existing traces. do not clear the trace buffer \n"
- " and kernel trace events set\n"
- " --backup back up the existing traces to /tmp/trace.backup and then clear the trace buffer\n"
- " --list_categories\n"
- " list the available tracing categories\n"
- );
-}
-
-int main(int argc, char **argv)
-{
- bool async = false;
- bool traceStart = true;
- bool traceStop = true;
- bool traceDump = true;
-
- char strBuf[128];
-
- // Global Variable Initialization
- excludedTags = 0ULL;
-
- if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
- showHelp(argv[0]);
- exit(0);
- }
-
- for (;;) {
- int ret;
- int option_index = 0;
- static struct option long_options[] = {
- {"async_start", no_argument, nullptr, 0 },
- {"async_stop", no_argument, nullptr, 0 },
- {"async_dump", no_argument, nullptr, 0 },
- {"list_categories", no_argument, nullptr, 0 },
- {"init_exec", no_argument, nullptr, 0 },
- {"append", no_argument, nullptr, 0 },
- {"backup", no_argument, nullptr, 0 },
- {0, 0, nullptr, 0 }
- };
- ret = getopt_long(argc, argv, "b:ck:e:ns:t:z",
- long_options, &option_index);
- if (ret < 0) {
- for (int i = optind; i < argc; i++) {
- if (!setCategoryEnable(argv[i], true)) {
- fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]);
- exit(1);
- }
- }
- break;
- }
-
- switch(ret) {
- case 'a':
- g_debugAppCmdLine = optarg;
- break;
-
- case 'b':
- g_traceBufferSizeKB = atoi(optarg);
- break;
-
- case 'c':
- g_traceOverwrite = true;
- break;
-
- case 'e':
- setCategoryEnable(optarg, true);
- for (int i = 0; i < NELEM(k_categories); i++) {
- const TracingCategory &c = k_categories[i];
- if (!strcmp(c.name, optarg)) {
- excludedTags |= c.tags;
- }
- }
- break;
-
- case 'k':
- g_kernelTraceFuncs = optarg;
- break;
-
- case 'n':
- g_nohup = true;
- break;
-
- case 's':
- g_initialSleepSecs = atoi(optarg);
- break;
-
- case 't':
- g_traceDurationSeconds = atoi(optarg);
- break;
-
- case 'z':
- g_compress = true;
- break;
-
- case 0:
- if (!strcmp(long_options[option_index].name, "list_categories")) {
- listSupportedCategories();
- exit(0);
- }
- if (!strcmp(long_options[option_index].name, "async_start")) {
- async = true;
- traceStop = false;
- traceDump = false;
- g_traceOverwrite = true;
- } else if (!strcmp(long_options[option_index].name, "async_stop")) {
- async = true;
- traceStart = false;
- traceStop = true;
- } else if (!strcmp(long_options[option_index].name, "async_dump")) {
- async = true;
- traceStart = false;
- traceStop = false;
- } else if (!strcmp(long_options[option_index].name, "list_categories")) {
- listSupportedCategories();
- exit(0);
- } else if (!strcmp(long_options[option_index].name, "init_exec")) {
- fprintf(stderr, "[Info] Initailize T-trace\n");
- if(!initEnabledTagFile()) {
- exit(-1);
- }
- if (getBootupTagStr(strBuf, sizeof(strBuf))) {
- async = true;
- traceStop = false;
- traceDump = false;
- g_traceOverwrite = true;
- setBootupTags(strBuf);
- }
- else {
- exit(0);
- }
- } else if (!strcmp(long_options[option_index].name, "append")) {
- g_append_trace = true;
- } else if (!strcmp(long_options[option_index].name, "backup")) {
- g_backup_trace = true;
- }
- break;
-
- default:
- fprintf(stderr, "\n");
- showHelp(argv[0]);
- exit(0);
- break;
- }
- }
-
- registerSigHandler();
-
- if (g_initialSleepSecs > 0) {
- sleep(g_initialSleepSecs);
- }
-
- bool ok = true;
- if(traceStart && g_backup_trace) {
- //before start tracing by atrace, backup existig traces
- stopTrace();
- dumpTrace(true);
- }
- if (!(async && !g_traceOverwrite)) {
- ok &= setUpTrace();
- }
- if (ok && traceStart) {
- // For debug
- // printf("capturing trace...");
- //
- fflush(stdout);
-
- // We clear the trace after starting it because tracing gets enabled for
- // each CPU individually in the kernel. Having the beginning of the trace
- // contain entries from only one CPU can cause "begin" entries without a
- // matching "end" entry to show up if a task gets migrated from one CPU to
- // another.
- if (ok && !async) {
- // Sleep to allow the trace to be captured.
- struct timespec timeLeft;
- timeLeft.tv_sec = g_traceDurationSeconds;
- timeLeft.tv_nsec = 0;
- do {
- if (g_traceAborted) {
- break;
- }
- } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR);
- }
- }
-
- // Stop the trace and restore the default settings.
- if (traceStop)
- stopTrace();
-
- if (ok && traceDump) {
- if (!g_traceAborted) {
- printf(" done\nTRACE:\n");
- fflush(stdout);
- dumpTrace(false);
- } else {
- printf("\ntrace aborted.\n");
- fflush(stdout);
- }
- clearKernelTrace();
- } else if (!ok) {
- fprintf(stderr, "unable to start tracing\n");
- }
-
- // Reset the trace buffer size to 1.
- if (traceStop)
- cleanUpTrace();
-
- return g_traceAborted ? 1 : 0;
-}
+++ /dev/null
-// 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.
+++ /dev/null
-// 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());
- dump_count_ = 0;
- out_ = nullptr;
- snapshot_timestamp_ = 0;
- dump_interval_in_timer_ticks_ = 0;
-}
-
-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);
- }
- });
-}
-
-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());
- }
-
- // 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;
-}
+++ /dev/null
-// 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_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_
+++ /dev/null
-// 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
+++ /dev/null
-// 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_
+++ /dev/null
-// 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 <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; \
- fprintf(stderr, "\n" CHECK_ARGS(x, e)); \
- fflush(stderr); \
- abort(); \
- } \
- } while (0)
-
-inline void LogError(const char* message) {
- fprintf(stderr, "\n%s\n", message);
- fflush(stderr);
-}
-
-#endif // LOGGING_H_
+++ /dev/null
-// 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"
-
-#define PATH_MAX 256
-
-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:st:o:c:")) != -1) {
- switch (opt) {
- case 'b':
- background = true;
- break;
- case 'm':
- ParseFullDumpConfig(optarg, prog);
- 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));
- out_file[PATH_MAX - 1] = '\0';
- dump_to_file = true;
- break;
- default:
- fprintf(stderr,
- "Usage: %s [-b] [-m full_dump_filter] [-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 + 4];
- if (dump_to_file) {
- unlink(out_file);
- snprintf(tmp_file, PATH_MAX + 4, "%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);
-}
+++ /dev/null
-// 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_
+++ /dev/null
-// 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 "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_ = (uint64_t)rss_pages * kKbPerPage;
- virt_kb_ = (uint64_t)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 %*x %*[: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;
-}
+++ /dev/null
-// 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);
-
- // 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_; }
-
- 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;
-
- std::vector<std::unique_ptr<const MmapInfo>> mmaps_;
-};
-
-#endif // PROCESS_MEMORY_STATS_H_
+++ /dev/null
-// 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];
- snprintf(exe_path, sizeof(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];
- snprintf(tasks_path, sizeof(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];
- snprintf(task_comm, sizeof(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 %*s %*c %*d %*d %*d %*d %*d %*u %lu %*u %lu %*u %lu %lu",
- &snapshot->minor_faults, &snapshot->major_faults,
- &snapshot->utime, &snapshot->stime);
- printf("ret is %d[%s]\n", ret, buf);
- 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
+++ /dev/null
-// 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_
+++ /dev/null
-// 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 ((uint64_t)ts.tv_sec * 1000) + ((uint64_t)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
+++ /dev/null
-// 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_