1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <inttypes.h>
6 #include <signal.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include <memory>
12 #include <set>
13 #include <string>
14 #include <sstream>
15 
16 #include "atrace_process_dump.h"
17 #include "logging.h"
18 
19 namespace {
20 
21 std::unique_ptr<AtraceProcessDump> g_prog;
22 
ParseFullDumpConfig(const std::string & config,AtraceProcessDump * prog)23 void ParseFullDumpConfig(const std::string& config, AtraceProcessDump* prog) {
24   using FullDumpMode = AtraceProcessDump::FullDumpMode;
25   if (config == "all") {
26     prog->set_full_dump_mode(FullDumpMode::kAllProcesses);
27   } else if (config == "apps") {
28     prog->set_full_dump_mode(FullDumpMode::kAllJavaApps);
29   } else {
30     std::set<std::string> whitelist;
31     std::istringstream ss(config);
32     std::string entry;
33     while (std::getline(ss, entry, ',')) {
34       whitelist.insert(entry);
35     }
36     if (whitelist.empty())
37       return;
38     prog->set_full_dump_mode(FullDumpMode::kOnlyWhitelisted);
39     prog->set_full_dump_whitelist(whitelist);
40   }
41 }
42 
43 }  // namespace
44 
main(int argc,char ** argv)45 int main(int argc, char** argv) {
46   if (argc == 2 && !strcmp(argv[1], "--echo-ts")) {
47     // Used by clock sync marker to correct the difference between
48     // Linux monotonic clocks on the device and host.
49     printf("%" PRIu64 "\n", time_utils::GetTimestamp());
50     return 0;
51   }
52 
53   bool background = false;
54   int dump_interval_ms = 5000;
55   char out_file[PATH_MAX] = {};
56   bool dump_to_file = false;
57   int count = -1;
58 
59   AtraceProcessDump* prog = new AtraceProcessDump();
60   g_prog = std::unique_ptr<AtraceProcessDump>(prog);
61 
62   if (geteuid()) {
63     fprintf(stderr, "Must run as root\n");
64     exit(EXIT_FAILURE);
65   }
66 
67   int opt;
68   while ((opt = getopt(argc, argv, "bm:gst:o:c:")) != -1) {
69     switch (opt) {
70       case 'b':
71         background = true;
72         break;
73       case 'm':
74         ParseFullDumpConfig(optarg, prog);
75         break;
76       case 'g':
77         prog->enable_graphics_stats();
78         break;
79       case 's':
80         prog->enable_print_smaps();
81         break;
82       case 't':
83         dump_interval_ms = atoi(optarg);
84         CHECK(dump_interval_ms > 0);
85         break;
86       case 'c':
87         count = atoi(optarg);
88         CHECK(count > 0);
89         break;
90       case 'o':
91         strncpy(out_file, optarg, sizeof(out_file));
92         dump_to_file = true;
93         break;
94       default:
95         fprintf(stderr,
96                 "Usage: %s [-b] [-m full_dump_filter] [-g] [-s] "
97                 "[-t dump_interval_ms] "
98                 "[-c dumps_count] [-o out.json]\n",
99                 argv[0]);
100         exit(EXIT_FAILURE);
101     }
102   }
103 
104   prog->set_dump_count(count);
105   prog->SetDumpInterval(dump_interval_ms);
106 
107   FILE* out_stream = stdout;
108   char tmp_file[PATH_MAX];
109   if (dump_to_file) {
110     unlink(out_file);
111     sprintf(tmp_file, "%s.tmp", out_file);
112     out_stream = fopen(tmp_file, "w");
113     CHECK(out_stream);
114   }
115 
116   if (background) {
117     if (!dump_to_file) {
118       fprintf(stderr, "-b requires -o for output dump path.\n");
119       exit(EXIT_FAILURE);
120     }
121     printf("Continuing in background. kill -TERM to terminate the daemon.\n");
122     CHECK(daemon(0 /* nochdir */, 0 /* noclose */) == 0);
123   }
124 
125   auto on_exit = [](int) { g_prog->Stop(); };
126   signal(SIGINT, on_exit);
127   signal(SIGTERM, on_exit);
128 
129   prog->RunAndPrintJson(out_stream);
130   fclose(out_stream);
131 
132   if (dump_to_file)
133     rename(tmp_file, out_file);
134 }
135