1 // Copyright (C) 2019 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "common/cmd_utils.h"
16 #include "common/debug.h"
17 #include "compiler/compiler.h"
18 #include "inode2filename/inode_resolver.h"
19 
20 #include <android-base/parseint.h>
21 #include <android-base/logging.h>
22 
23 #include <iostream>
24 #include <limits>
25 #include <optional>
26 #include <string>
27 
28 #if defined(IORAP_COMPILER_MAIN)
29 
30 namespace iorap::compiler {
31 
32 // Log everything to stderr.
33 // Log errors and higher to logd.
34 class StderrAndLogdErrorLogger {
35  public:
StderrAndLogdErrorLogger(android::base::LogId default_log_id=android::base::MAIN)36   explicit StderrAndLogdErrorLogger(android::base::LogId default_log_id = android::base::MAIN)
37 #ifdef __ANDROID__
38       : logd_(default_log_id)
39 #endif
40   {
41   }
42 
operator ()(::android::base::LogId id,::android::base::LogSeverity sev,const char * tag,const char * file,unsigned int line,const char * message)43   void operator()(::android::base::LogId id,
44                   ::android::base::LogSeverity sev,
45                   const char* tag,
46                   const char* file,
47                   unsigned int line,
48                   const char* message) {
49 #ifdef __ANDROID__
50     if (static_cast<int>(sev) >= static_cast<int>(::android::base::ERROR)) {
51       logd_(id, sev, tag, file, line, message);
52     }
53 #endif
54     StderrLogger(id, sev, tag, file, line, message);
55   }
56 
57  private:
58 #ifdef __ANDROID__
59   ::android::base::LogdLogger logd_;
60 #endif
61 };
62 
Usage(char ** argv)63 void Usage(char** argv) {
64   std::cerr << "Usage: " << argv[0] << " [--output-proto=output.pb] input1.pb [input2.pb ...]" << std::endl;
65   std::cerr << "" << std::endl;
66   std::cerr << "  Request a compilation of multiple inputs (format: PerfettoTraceProto)." << std::endl;
67   std::cerr << "  The result is a TraceFile, representing a merged compiled trace with inodes resolved." << std::endl;
68   std::cerr << "" << std::endl;
69   std::cerr << "  Optional flags:" << std::endl;
70   std::cerr << "    --help,-h                  Print this Usage." << std::endl;
71   std::cerr << "    --denylist-filter,-df     Specify regex acting as a denylist filter."
72             << std::endl;
73   std::cerr << "                               Filepaths matching this regex are removed from the output file." << std::endl;
74   std::cerr << "    --output-text,-ot          Output ascii text instead of protobuf (default off)." << std::endl;
75   std::cerr << "    --output-proto $,-op $     TraceFile tracebuffer output file (default stdout)." << std::endl;
76   std::cerr << "    --inode-textcache $,-it $  Resolve inode->filename from textcache (disables diskscan)." << std::endl;
77   std::cerr << "    --verbose,-v               Set verbosity (default off)." << std::endl;
78   std::cerr << "    --wait,-w                  Wait for key stroke before continuing (default off)." << std::endl;
79   std::cerr << "    --pid,-p                   Set the pid for the compiled trace" << std::endl;
80   std::cerr << "    --timestamp_limit_ns,-tl   Set the limit timestamp in nanoseconds for the compiled trace. "
81                                               "The order and size of the timestamp should match that of "
82                                               "the input trace files. If not specified at all, All of"
83                                               "the timestamps are set to max."<< std::endl;
84   exit(1);
85 }
86 
Main(int argc,char ** argv)87 int Main(int argc, char** argv) {
88   android::base::InitLogging(argv);
89   android::base::SetLogger(StderrAndLogdErrorLogger{});
90 
91   bool wait_for_keystroke = false;
92   bool enable_verbose = false;
93 
94   std::optional<std::string> arg_blacklist_filter;
95   std::string arg_output_proto;
96   bool arg_output_text = false;
97   std::optional<std::string> arg_inode_textcache;
98 
99   std::vector<uint64_t> timestamp_limit_ns;
100   std::vector<int32_t> pids;
101 
102   if (argc == 1) {
103     // Need at least 1 input file to do anything.
104     Usage(argv);
105   }
106 
107   std::vector<std::string> arg_input_filenames;
108 
109   for (int arg = 1; arg < argc; ++arg) {
110     std::string argstr = argv[arg];
111     bool has_arg_next = (arg+1)<argc;
112     std::string arg_next = has_arg_next ? argv[arg+1] : "";
113 
114     if (argstr == "--help" || argstr == "-h") {
115       Usage(argv);
116     } else if (argstr == "--output-proto" || argstr == "-op") {
117       if (!has_arg_next) {
118         std::cerr << "Missing --output-proto <value>" << std::endl;
119         return 1;
120       }
121       arg_output_proto = arg_next;
122       ++arg;
123     } else if (argstr == "--output-text" || argstr == "-ot") {
124       arg_output_text = true;
125     } else if (argstr == "--inode-textcache" || argstr == "-it") {
126       if (!has_arg_next) {
127         std::cerr << "Missing --inode-textcache <value>" << std::endl;
128         return 1;
129       }
130       arg_inode_textcache = arg_next;
131       ++arg;
132     } else if (argstr == "--denylist-filter" || argstr == "-df") {
133       if (!has_arg_next) {
134         std::cerr << "Missing --denylist-filter <value>" << std::endl;
135         return 1;
136       }
137       arg_blacklist_filter = arg_next;
138       ++arg;
139     }
140     else if (argstr == "--verbose" || argstr == "-v") {
141       enable_verbose = true;
142     } else if (argstr == "--wait" || argstr == "-w") {
143       wait_for_keystroke = true;
144     } else if (argstr == "--pid" || argstr == "-p") {
145       if (!has_arg_next) {
146         std::cerr << "Missing --pid <value>" << std::endl;
147         return 1;
148       }
149       int32_t pid;
150       if (!::android::base::ParseInt<int32_t>(arg_next, &pid)) {
151         std::cerr << "Invalid --pid "<< arg_next << std::endl;
152         return 1;
153       }
154       pids.push_back(pid);
155       ++arg;
156     } else if (argstr == "--timestamp_limit_ns" || argstr == "-tl") {
157       if (!has_arg_next) {
158         std::cerr << "Missing --timestamp_limit_ns <value>" << std::endl;
159         return 1;
160       }
161       uint64_t timestamp;
162       if (!::android::base::ParseUint<uint64_t>(arg_next, &timestamp)) {
163         std::cerr << "Invalid --timestamp-limit-ns "<< arg_next << std::endl;
164         return 1;
165       }
166       timestamp_limit_ns.push_back(timestamp);
167       ++arg;
168     } else {
169       arg_input_filenames.push_back(argstr);
170     }
171   }
172 
173   if (!timestamp_limit_ns.empty() &&
174       timestamp_limit_ns.size() != arg_input_filenames.size()) {
175     std::cerr << "The size of timestamp limits doesn't match the size of input files."
176               << std::endl;
177     return 1;
178   }
179 
180   if (!pids.empty() && pids.size() != arg_input_filenames.size()) {
181     std::cerr << "The size of pids doesn't match the size of input files."
182               << std::endl;
183     return 1;
184   }
185   if (enable_verbose) {
186     android::base::SetMinimumLogSeverity(android::base::VERBOSE);
187 
188     LOG(VERBOSE) << "Verbose check";
189     LOG(VERBOSE) << "Debug check: " << ::iorap::kIsDebugBuild;
190   } else {
191     android::base::SetMinimumLogSeverity(android::base::DEBUG);
192   }
193 
194   // Useful to attach a debugger...
195   // 1) $> iorap.cmd.compiler -w <args>
196   // 2) $> gdbclient <pid>
197   if (wait_for_keystroke) {
198     LOG(INFO) << "Self pid: " << getpid();
199     LOG(INFO) << "Press any key to continue...";
200     std::cin >> wait_for_keystroke;
201   }
202 
203   auto system_call = std::make_unique<SystemCallImpl>();
204 
205   using namespace inode2filename;
206 
207   InodeResolverDependencies ir_dependencies;
208   // Passed from command-line.
209   if (arg_inode_textcache) {
210     ir_dependencies.data_source = DataSourceKind::kTextCache;
211     ir_dependencies.text_cache_filename = arg_inode_textcache;
212     ir_dependencies.verify = VerifyKind::kNone;  // required for determinism.
213   } else {
214     ir_dependencies.data_source = DataSourceKind::kDiskScan;
215     LOG(WARNING) << "--inode-textcache unspecified. "
216                  << "Inodes will be resolved by scanning the disk, which makes compilation "
217                  << "non-deterministic.";
218   }
219   // TODO: add command line.
220   ir_dependencies.root_directories.push_back("/system");
221   ir_dependencies.root_directories.push_back("/apex");
222   ir_dependencies.root_directories.push_back("/data");
223   ir_dependencies.root_directories.push_back("/vendor");
224   ir_dependencies.root_directories.push_back("/product");
225   ir_dependencies.root_directories.push_back("/metadata");
226   // Hardcoded.
227   if (iorap::common::GetBoolEnvOrProperty("iorap.inode2filename.out_of_process", true)) {
228     ir_dependencies.process_mode = ProcessMode::kOutOfProcessIpc;
229   } else {
230     ir_dependencies.process_mode = ProcessMode::kInProcessDirect;
231   }
232   ir_dependencies.system_call = /*borrowed*/system_call.get();
233 
234   int return_code = 0;
235   std::vector<CompilationInput> perfetto_traces =
236       MakeCompilationInputs(arg_input_filenames, timestamp_limit_ns, pids);
237   return_code =
238       !PerformCompilation(std::move(perfetto_traces),
239                           std::move(arg_output_proto),
240                           !arg_output_text,
241                           arg_blacklist_filter,
242                           std::move(ir_dependencies));
243 
244   // Uncomment this if we want to leave the process around to inspect it from adb shell.
245   // sleep(100000);
246 
247   // 0 -> successfully wrote the proto out to file.
248   // 1 -> failed along the way (#on_error and also see the error logs).
249   return return_code;
250 }
251 
252 }  // namespace iorap::compiler
253 
main(int argc,char ** argv)254 int main(int argc, char** argv) {
255   return ::iorap::compiler::Main(argc, argv);
256 }
257 
258 #endif  // IORAP_COMPILER_MAIN
259