summaryrefslogtreecommitdiff
path: root/src/compiler/main.cc
blob: 3036936ca8c11133a6da23340a32d636244b658f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// Copyright (C) 2019 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 "common/cmd_utils.h"
#include "common/debug.h"
#include "compiler/compiler.h"
#include "inode2filename/inode_resolver.h"

#include <android-base/parseint.h>
#include <android-base/logging.h>

#include <iostream>
#include <limits>
#include <optional>
#include <string>

#if defined(IORAP_COMPILER_MAIN)

namespace iorap::compiler {

// Log everything to stderr.
// Log errors and higher to logd.
class StderrAndLogdErrorLogger {
 public:
  explicit StderrAndLogdErrorLogger(android::base::LogId default_log_id = android::base::MAIN)
#ifdef __ANDROID__
      : logd_(default_log_id)
#endif
  {
  }

  void operator()(::android::base::LogId id,
                  ::android::base::LogSeverity sev,
                  const char* tag,
                  const char* file,
                  unsigned int line,
                  const char* message) {
#ifdef __ANDROID__
    if (static_cast<int>(sev) >= static_cast<int>(::android::base::ERROR)) {
      logd_(id, sev, tag, file, line, message);
    }
#endif
    StderrLogger(id, sev, tag, file, line, message);
  }

 private:
#ifdef __ANDROID__
  ::android::base::LogdLogger logd_;
#endif
};

void Usage(char** argv) {
  std::cerr << "Usage: " << argv[0] << " [--output-proto=output.pb] input1.pb [input2.pb ...]" << std::endl;
  std::cerr << "" << std::endl;
  std::cerr << "  Request a compilation of multiple inputs (format: PerfettoTraceProto)." << std::endl;
  std::cerr << "  The result is a TraceFile, representing a merged compiled trace with inodes resolved." << std::endl;
  std::cerr << "" << std::endl;
  std::cerr << "  Optional flags:" << std::endl;
  std::cerr << "    --help,-h                  Print this Usage." << std::endl;
  std::cerr << "    --denylist-filter,-df     Specify regex acting as a denylist filter."
            << std::endl;
  std::cerr << "                               Filepaths matching this regex are removed from the output file." << std::endl;
  std::cerr << "    --output-text,-ot          Output ascii text instead of protobuf (default off)." << std::endl;
  std::cerr << "    --output-proto $,-op $     TraceFile tracebuffer output file (default stdout)." << std::endl;
  std::cerr << "    --inode-textcache $,-it $  Resolve inode->filename from textcache (disables diskscan)." << std::endl;
  std::cerr << "    --verbose,-v               Set verbosity (default off)." << std::endl;
  std::cerr << "    --wait,-w                  Wait for key stroke before continuing (default off)." << std::endl;
  std::cerr << "    --pid,-p                   Set the pid for the compiled trace" << std::endl;
  std::cerr << "    --timestamp_limit_ns,-tl   Set the limit timestamp in nanoseconds for the compiled trace. "
                                              "The order and size of the timestamp should match that of "
                                              "the input trace files. If not specified at all, All of"
                                              "the timestamps are set to max."<< std::endl;
  exit(1);
}

int Main(int argc, char** argv) {
  android::base::InitLogging(argv);
  android::base::SetLogger(StderrAndLogdErrorLogger{});

  bool wait_for_keystroke = false;
  bool enable_verbose = false;

  std::optional<std::string> arg_blacklist_filter;
  std::string arg_output_proto;
  bool arg_output_text = false;
  std::optional<std::string> arg_inode_textcache;

  std::vector<uint64_t> timestamp_limit_ns;
  std::vector<int32_t> pids;

  if (argc == 1) {
    // Need at least 1 input file to do anything.
    Usage(argv);
  }

  std::vector<std::string> arg_input_filenames;

  for (int arg = 1; arg < argc; ++arg) {
    std::string argstr = argv[arg];
    bool has_arg_next = (arg+1)<argc;
    std::string arg_next = has_arg_next ? argv[arg+1] : "";

    if (argstr == "--help" || argstr == "-h") {
      Usage(argv);
    } else if (argstr == "--output-proto" || argstr == "-op") {
      if (!has_arg_next) {
        std::cerr << "Missing --output-proto <value>" << std::endl;
        return 1;
      }
      arg_output_proto = arg_next;
      ++arg;
    } else if (argstr == "--output-text" || argstr == "-ot") {
      arg_output_text = true;
    } else if (argstr == "--inode-textcache" || argstr == "-it") {
      if (!has_arg_next) {
        std::cerr << "Missing --inode-textcache <value>" << std::endl;
        return 1;
      }
      arg_inode_textcache = arg_next;
      ++arg;
    } else if (argstr == "--denylist-filter" || argstr == "-df") {
      if (!has_arg_next) {
        std::cerr << "Missing --denylist-filter <value>" << std::endl;
        return 1;
      }
      arg_blacklist_filter = arg_next;
      ++arg;
    }
    else if (argstr == "--verbose" || argstr == "-v") {
      enable_verbose = true;
    } else if (argstr == "--wait" || argstr == "-w") {
      wait_for_keystroke = true;
    } else if (argstr == "--pid" || argstr == "-p") {
      if (!has_arg_next) {
        std::cerr << "Missing --pid <value>" << std::endl;
        return 1;
      }
      int32_t pid;
      if (!::android::base::ParseInt<int32_t>(arg_next, &pid)) {
        std::cerr << "Invalid --pid "<< arg_next << std::endl;
        return 1;
      }
      pids.push_back(pid);
      ++arg;
    } else if (argstr == "--timestamp_limit_ns" || argstr == "-tl") {
      if (!has_arg_next) {
        std::cerr << "Missing --timestamp_limit_ns <value>" << std::endl;
        return 1;
      }
      uint64_t timestamp;
      if (!::android::base::ParseUint<uint64_t>(arg_next, &timestamp)) {
        std::cerr << "Invalid --timestamp-limit-ns "<< arg_next << std::endl;
        return 1;
      }
      timestamp_limit_ns.push_back(timestamp);
      ++arg;
    } else {
      arg_input_filenames.push_back(argstr);
    }
  }

  if (!timestamp_limit_ns.empty() &&
      timestamp_limit_ns.size() != arg_input_filenames.size()) {
    std::cerr << "The size of timestamp limits doesn't match the size of input files."
              << std::endl;
    return 1;
  }

  if (!pids.empty() && pids.size() != arg_input_filenames.size()) {
    std::cerr << "The size of pids doesn't match the size of input files."
              << std::endl;
    return 1;
  }
  if (enable_verbose) {
    android::base::SetMinimumLogSeverity(android::base::VERBOSE);

    LOG(VERBOSE) << "Verbose check";
    LOG(VERBOSE) << "Debug check: " << ::iorap::kIsDebugBuild;
  } else {
    android::base::SetMinimumLogSeverity(android::base::DEBUG);
  }

  // Useful to attach a debugger...
  // 1) $> iorap.cmd.compiler -w <args>
  // 2) $> gdbclient <pid>
  if (wait_for_keystroke) {
    LOG(INFO) << "Self pid: " << getpid();
    LOG(INFO) << "Press any key to continue...";
    std::cin >> wait_for_keystroke;
  }

  auto system_call = std::make_unique<SystemCallImpl>();

  using namespace inode2filename;

  InodeResolverDependencies ir_dependencies;
  // Passed from command-line.
  if (arg_inode_textcache) {
    ir_dependencies.data_source = DataSourceKind::kTextCache;
    ir_dependencies.text_cache_filename = arg_inode_textcache;
    ir_dependencies.verify = VerifyKind::kNone;  // required for determinism.
  } else {
    ir_dependencies.data_source = DataSourceKind::kDiskScan;
    LOG(WARNING) << "--inode-textcache unspecified. "
                 << "Inodes will be resolved by scanning the disk, which makes compilation "
                 << "non-deterministic.";
  }
  // TODO: add command line.
  ir_dependencies.root_directories.push_back("/system");
  ir_dependencies.root_directories.push_back("/apex");
  ir_dependencies.root_directories.push_back("/data");
  ir_dependencies.root_directories.push_back("/vendor");
  ir_dependencies.root_directories.push_back("/product");
  ir_dependencies.root_directories.push_back("/metadata");
  // Hardcoded.
  if (iorap::common::GetBoolEnvOrProperty("iorap.inode2filename.out_of_process", true)) {
    ir_dependencies.process_mode = ProcessMode::kOutOfProcessIpc;
  } else {
    ir_dependencies.process_mode = ProcessMode::kInProcessDirect;
  }
  ir_dependencies.system_call = /*borrowed*/system_call.get();

  int return_code = 0;
  std::vector<CompilationInput> perfetto_traces =
      MakeCompilationInputs(arg_input_filenames, timestamp_limit_ns, pids);
  return_code =
      !PerformCompilation(std::move(perfetto_traces),
                          std::move(arg_output_proto),
                          !arg_output_text,
                          arg_blacklist_filter,
                          std::move(ir_dependencies));

  // Uncomment this if we want to leave the process around to inspect it from adb shell.
  // sleep(100000);

  // 0 -> successfully wrote the proto out to file.
  // 1 -> failed along the way (#on_error and also see the error logs).
  return return_code;
}

}  // namespace iorap::compiler

int main(int argc, char** argv) {
  return ::iorap::compiler::Main(argc, argv);
}

#endif  // IORAP_COMPILER_MAIN