// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <inttypes.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory>
#include <set>
#include <string>
#include <sstream>
#include "atrace_process_dump.h"
#include "logging.h"
namespace {
std::unique_ptr<AtraceProcessDump> g_prog;
void ParseFullDumpConfig(const std::string& config, AtraceProcessDump* prog) {
using FullDumpMode = AtraceProcessDump::FullDumpMode;
if (config == "all") {
prog->set_full_dump_mode(FullDumpMode::kAllProcesses);
} else if (config == "apps") {
prog->set_full_dump_mode(FullDumpMode::kAllJavaApps);
} else {
std::set<std::string> whitelist;
std::istringstream ss(config);
std::string entry;
while (std::getline(ss, entry, ',')) {
whitelist.insert(entry);
}
if (whitelist.empty())
return;
prog->set_full_dump_mode(FullDumpMode::kOnlyWhitelisted);
prog->set_full_dump_whitelist(whitelist);
}
}
} // namespace
int main(int argc, char** argv) {
if (argc == 2 && !strcmp(argv[1], "--echo-ts")) {
// Used by clock sync marker to correct the difference between
// Linux monotonic clocks on the device and host.
printf("%" PRIu64 "\n", time_utils::GetTimestamp());
return 0;
}
bool background = false;
int dump_interval_ms = 5000;
char out_file[PATH_MAX] = {};
bool dump_to_file = false;
int count = -1;
AtraceProcessDump* prog = new AtraceProcessDump();
g_prog = std::unique_ptr<AtraceProcessDump>(prog);
if (geteuid()) {
fprintf(stderr, "Must run as root\n");
exit(EXIT_FAILURE);
}
int opt;
while ((opt = getopt(argc, argv, "bm:gst:o:c:")) != -1) {
switch (opt) {
case 'b':
background = true;
break;
case 'm':
ParseFullDumpConfig(optarg, prog);
break;
case 'g':
prog->enable_graphics_stats();
break;
case 's':
prog->enable_print_smaps();
break;
case 't':
dump_interval_ms = atoi(optarg);
CHECK(dump_interval_ms > 0);
break;
case 'c':
count = atoi(optarg);
CHECK(count > 0);
break;
case 'o':
strncpy(out_file, optarg, sizeof(out_file));
dump_to_file = true;
break;
default:
fprintf(stderr,
"Usage: %s [-b] [-m full_dump_filter] [-g] [-s] "
"[-t dump_interval_ms] "
"[-c dumps_count] [-o out.json]\n",
argv[0]);
exit(EXIT_FAILURE);
}
}
prog->set_dump_count(count);
prog->SetDumpInterval(dump_interval_ms);
FILE* out_stream = stdout;
char tmp_file[PATH_MAX];
if (dump_to_file) {
unlink(out_file);
sprintf(tmp_file, "%s.tmp", out_file);
out_stream = fopen(tmp_file, "w");
CHECK(out_stream);
}
if (background) {
if (!dump_to_file) {
fprintf(stderr, "-b requires -o for output dump path.\n");
exit(EXIT_FAILURE);
}
printf("Continuing in background. kill -TERM to terminate the daemon.\n");
CHECK(daemon(0 /* nochdir */, 0 /* noclose */) == 0);
}
auto on_exit = [](int) { g_prog->Stop(); };
signal(SIGINT, on_exit);
signal(SIGTERM, on_exit);
prog->RunAndPrintJson(out_stream);
fclose(out_stream);
if (dump_to_file)
rename(tmp_file, out_file);
}