Expose trace_processor_shell target to build from chrome checkout

Makes trace_processor shell target buildable from chrome checkout.
The windows build of the shell has the following limitations:
1) Of all the commandline options, only -q is supported.
2) The trace file is read synchronously.

Bug: 133746356
Change-Id: Ice28d6abf902c3b922b15cb1d848f2d7b05485e5
diff --git a/BUILD.gn b/BUILD.gn
index 367b8b6..812ddb8 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -48,6 +48,9 @@
     ":perfetto_unittests",
     "src/protozero/protoc_plugin($host_toolchain)",
   ]
+  if (perfetto_build_standalone || build_with_chromium) {
+    deps += [ ":trace_processor" ]
+  }
   if (perfetto_build_standalone || perfetto_build_with_android) {
     deps += [
       ":perfetto",
@@ -64,7 +67,6 @@
     if (perfetto_build_standalone) {
       deps += [
         ":perfetto_benchmarks",
-        ":trace_processor",
         "src/profiling/memory:ring_buffer",
         "src/tracing:consumer_api_test",
         "test/configs",
@@ -87,13 +89,6 @@
       }
     }
   }
-
-  # TODO(khokhlov): Remove this once the chrome dep is added. This is a
-  # temporary target to make sure that we don't regress trace processor support
-  # for the Windows build.
-  if (build_with_chromium) {
-    deps += [ "src/trace_processor:lib" ]
-  }
 }
 
 # TODO(primiano): temporary workaround to:
@@ -106,15 +101,9 @@
   ]
 }
 
-if (perfetto_build_standalone) {
-  group("ui") {
-    deps = [
-      "ui",
-    ]
-  }
-
-  # The trace processor shell executable. An interactive shell that allows to
-  # make queries on the trace using the terminal.
+# The trace processor shell executable. An interactive shell that allows to
+# make queries on the trace using the terminal.
+if (perfetto_build_standalone || build_with_chromium) {
   group("trace_processor") {
     deps = [
       "src/trace_processor:trace_processor_shell",
@@ -123,6 +112,14 @@
   }
 }
 
+if (perfetto_build_standalone) {
+  group("ui") {
+    deps = [
+      "ui",
+    ]
+  }
+}
+
 test("perfetto_unittests") {
   deps = [
     "gn:default_deps",
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index e5d33e2..67f8bbf 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -203,7 +203,7 @@
   }
 }
 
-if (!perfetto_build_with_embedder && current_toolchain == host_toolchain) {
+if (current_toolchain == host_toolchain) {
   executable("trace_processor_shell_host") {
     testonly = true  # We need this for proto full.
     deps = [
@@ -226,20 +226,24 @@
   }
 }
 
-if (!perfetto_build_with_embedder) {
+if (perfetto_build_standalone || build_with_chromium) {
   copy("trace_processor_shell") {
     testonly = true  # We need this for proto full.
     host_out_dir_ =
         get_label_info(":trace_processor_shell_host($host_toolchain)",
                        "root_out_dir")
+    extension_ = ""
+    if (is_win) {
+      extension_ = ".exe"
+    }
     deps = [
       ":trace_processor_shell_host($host_toolchain)",
     ]
     sources = [
-      "${host_out_dir_}/trace_processor_shell_host",
+      "${host_out_dir_}/trace_processor_shell_host${extension_}",
     ]
     outputs = [
-      "${root_out_dir}/trace_processor_shell",
+      "${root_out_dir}/trace_processor_shell${extension_}",
     ]
   }
 }
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 9e34dd3..02a0210 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -77,9 +77,9 @@
 #include "src/trace_processor/json_trace_tokenizer.h"
 #endif
 
-// In Android tree builds, we don't have the percentile module.
+// In Android and Chromium tree builds, we don't have the percentile module.
 // Just don't include it.
-#if !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
 // defined in sqlite_src/ext/misc/percentile.c
 extern "C" int sqlite3_percentile_init(sqlite3* db,
                                        char** error,
@@ -105,7 +105,7 @@
   sqlite3_str_split_init(db);
 // In Android tree builds, we don't have the percentile module.
 // Just don't include it.
-#if !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
   sqlite3_percentile_init(db, &error, nullptr);
   if (error) {
     PERFETTO_ELOG("Error initializing: %s", error);
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 96b15f5..415956e 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-#include <aio.h>
 #include <fcntl.h>
-#include <getopt.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <sys/stat.h>
-#include <unistd.h>
 
 #include <functional>
 #include <iostream>
@@ -61,6 +58,13 @@
 #include <signal.h>
 #endif
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#define ftruncate _chsize
+#else
+#include <aio.h>
+#include <getopt.h>
+#endif
+
 namespace perfetto {
 namespace trace_processor {
 
@@ -579,6 +583,72 @@
   return 0;
 }
 
+struct CommandLineOptions {
+  std::string perf_file_path;
+  std::string query_file_path;
+  std::string sqlite_file_path;
+  std::string metric_names;
+  std::string metric_output;
+  std::string trace_file_path;
+  bool launch_shell = false;
+};
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+void PrintUsage(char** argv) {
+  PERFETTO_ELOG(
+      "Interactive trace processor shell.\n"
+      "Usage: %s [-q query_file] trace_file.pb",
+      argv[0]);
+}
+
+CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
+  CommandLineOptions command_line_options;
+
+  if (argc == 2) {
+    command_line_options.trace_file_path = argv[1];
+  } else if (argc == 4) {
+    if (strcmp(argv[1], "-q") != 0) {
+      PrintUsage(argv);
+      exit(1);
+    }
+    command_line_options.query_file_path = argv[2];
+    command_line_options.trace_file_path = argv[3];
+  } else {
+    PrintUsage(argv);
+    exit(1);
+  }
+
+  return command_line_options;
+}
+
+uint64_t ReadTrace(TraceProcessor* tp, int file_descriptor) {
+  // Load the trace in chunks using ordinary read().
+  // This version is used on Windows, since there's no aio library.
+
+  constexpr size_t kChunkSize = 1024 * 1024;
+  uint64_t file_size = 0;
+
+  for (int i = 0;; i++) {
+    if (i % 128 == 0)
+      fprintf(stderr, "\rLoading trace: %.2f MB\r", file_size / 1E6);
+
+    std::unique_ptr<uint8_t[]> buf(new uint8_t[kChunkSize]);
+    auto rsize = read(file_descriptor, buf.get(), kChunkSize);
+    if (rsize <= 0)
+      break;
+    file_size += static_cast<uint64_t>(rsize);
+
+    util::Status status = tp->Parse(std::move(buf), static_cast<size_t>(rsize));
+    if (PERFETTO_UNLIKELY(!status.ok())) {
+      PERFETTO_ELOG("Fatal error while parsing trace: %s", status.c_message());
+      exit(1);
+    }
+  }
+  tp->NotifyEndOfFile();
+  return file_size;
+}
+
+#else  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 void PrintUsage(char** argv) {
   PERFETTO_ELOG(
       "Interactive trace processor shell.\n"
@@ -608,135 +678,30 @@
       argv[0]);
 }
 
-int TraceProcessorMain(int argc, char** argv) {
-  enum LongOption {
-    OPT_RUN_METRICS = 1000,
-    OPT_METRICS_OUTPUT,
-  };
-
-  static const struct option long_options[] = {
-      {"help", no_argument, nullptr, 'h'},
-      {"version", no_argument, nullptr, 'v'},
-      {"interactive", no_argument, nullptr, 'i'},
-      {"debug", no_argument, nullptr, 'd'},
-      {"perf-file", required_argument, nullptr, 'p'},
-      {"query-file", required_argument, nullptr, 'q'},
-      {"export", required_argument, nullptr, 'e'},
-      {"run-metrics", required_argument, nullptr, OPT_RUN_METRICS},
-      {"metrics-output", required_argument, nullptr, OPT_METRICS_OUTPUT},
-      {nullptr, 0, nullptr, 0}};
-
-  std::string perf_file_path;
-  std::string query_file_path;
-  std::string sqlite_file_path;
-  std::string metric_names;
-  std::string metric_output;
-  bool explicit_interactive = false;
-  int option_index = 0;
-  for (;;) {
-    int option =
-        getopt_long(argc, argv, "hvidp:q:e:", long_options, &option_index);
-
-    if (option == -1)
-      break;  // EOF.
-
-    if (option == 'v') {
-      printf("%s\n", PERFETTO_GET_GIT_REVISION());
-      return 0;
-    }
-
-    if (option == 'i') {
-      explicit_interactive = true;
-      continue;
-    }
-
-    if (option == 'd') {
-      EnableSQLiteVtableDebugging();
-      continue;
-    }
-
-    if (option == 'p') {
-      perf_file_path = optarg;
-      continue;
-    }
-
-    if (option == 'q') {
-      query_file_path = optarg;
-      continue;
-    }
-
-    if (option == 'e') {
-      sqlite_file_path = optarg;
-      continue;
-    }
-
-    if (option == OPT_RUN_METRICS) {
-      metric_names = optarg;
-      continue;
-    }
-
-    if (option == OPT_METRICS_OUTPUT) {
-      metric_output = optarg;
-      continue;
-    }
-
-    PrintUsage(argv);
-    return option == 'h' ? 0 : 1;
-  }
-
-  // Ensure that we have the tracefile argument only at the end.
-  if (optind != argc - 1) {
-    PrintUsage(argv);
-    return 1;
-  }
-
-  const char* trace_file_path = argv[optind];
-  if (trace_file_path == nullptr) {
-    PrintUsage(argv);
-    return 1;
-  }
-
-  bool launch_shell =
-      explicit_interactive || (metric_names.empty() && query_file_path.empty());
-
-  // Only allow non-interactive queries to emit perf data.
-  if (!perf_file_path.empty() && launch_shell) {
-    PrintUsage(argv);
-    return 1;
-  }
-
-  // Load the trace file into the trace processor.
-  Config config;
-  std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
-  base::ScopedFile fd(base::OpenFile(trace_file_path, O_RDONLY));
-  if (!fd) {
-    PERFETTO_ELOG("Could not open trace file (path: %s)", trace_file_path);
-    return 1;
-  }
-
+uint64_t ReadTrace(TraceProcessor* tp, int file_descriptor) {
   // Load the trace in chunks using async IO. We create a simple pipeline where,
   // at each iteration, we parse the current chunk and asynchronously start
   // reading the next chunk.
 
   // 1MB chunk size seems the best tradeoff on a MacBook Pro 2013 - i7 2.8 GHz.
   constexpr size_t kChunkSize = 1024 * 1024;
+  uint64_t file_size = 0;
+
   struct aiocb cb {};
   cb.aio_nbytes = kChunkSize;
-  cb.aio_fildes = *fd;
+  cb.aio_fildes = file_descriptor;
 
   std::unique_ptr<uint8_t[]> aio_buf(new uint8_t[kChunkSize]);
 #if defined(MEMORY_SANITIZER)
   // Just initialize the memory to make the memory sanitizer happy as it
   // cannot track aio calls below.
   memset(aio_buf.get(), 0, kChunkSize);
-#endif
+#endif  // defined(MEMORY_SANITIZER)
   cb.aio_buf = aio_buf.get();
 
   PERFETTO_CHECK(aio_read(&cb) == 0);
   struct aiocb* aio_list[1] = {&cb};
 
-  uint64_t file_size = 0;
-  auto t_load_start = base::GetWallTimeNs();
   for (int i = 0;; i++) {
     if (i % 128 == 0)
       fprintf(stderr, "\rLoading trace: %.2f MB\r", file_size / 1E6);
@@ -756,7 +721,7 @@
     // Just initialize the memory to make the memory sanitizer happy as it
     // cannot track aio calls below.
     memset(aio_buf.get(), 0, kChunkSize);
-#endif
+#endif  // defined(MEMORY_SANITIZER)
     cb.aio_buf = aio_buf.get();
     cb.aio_offset += rsize;
     PERFETTO_CHECK(aio_read(&cb) == 0);
@@ -765,11 +730,123 @@
     util::Status status = tp->Parse(std::move(buf), static_cast<size_t>(rsize));
     if (PERFETTO_UNLIKELY(!status.ok())) {
       PERFETTO_ELOG("Fatal error while parsing trace: %s", status.c_message());
-      return 1;
+      exit(1);
     }
   }
   tp->NotifyEndOfFile();
+  return file_size;
+}
 
+CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
+  CommandLineOptions command_line_options;
+  enum LongOption {
+    OPT_RUN_METRICS = 1000,
+    OPT_METRICS_OUTPUT,
+  };
+
+  static const struct option long_options[] = {
+      {"help", no_argument, nullptr, 'h'},
+      {"version", no_argument, nullptr, 'v'},
+      {"interactive", no_argument, nullptr, 'i'},
+      {"debug", no_argument, nullptr, 'd'},
+      {"perf-file", required_argument, nullptr, 'p'},
+      {"query-file", required_argument, nullptr, 'q'},
+      {"export", required_argument, nullptr, 'e'},
+      {"run-metrics", required_argument, nullptr, OPT_RUN_METRICS},
+      {"metrics-output", required_argument, nullptr, OPT_METRICS_OUTPUT},
+      {nullptr, 0, nullptr, 0}};
+
+  bool explicit_interactive = false;
+  int option_index = 0;
+  for (;;) {
+    int option =
+        getopt_long(argc, argv, "hvidp:q:e:", long_options, &option_index);
+
+    if (option == -1)
+      break;  // EOF.
+
+    if (option == 'v') {
+      printf("%s\n", PERFETTO_GET_GIT_REVISION());
+      exit(0);
+    }
+
+    if (option == 'i') {
+      explicit_interactive = true;
+      continue;
+    }
+
+    if (option == 'd') {
+      EnableSQLiteVtableDebugging();
+      continue;
+    }
+
+    if (option == 'p') {
+      command_line_options.perf_file_path = optarg;
+      continue;
+    }
+
+    if (option == 'q') {
+      command_line_options.query_file_path = optarg;
+      continue;
+    }
+
+    if (option == 'e') {
+      command_line_options.sqlite_file_path = optarg;
+      continue;
+    }
+
+    if (option == OPT_RUN_METRICS) {
+      command_line_options.metric_names = optarg;
+      continue;
+    }
+
+    if (option == OPT_METRICS_OUTPUT) {
+      command_line_options.metric_output = optarg;
+      continue;
+    }
+
+    PrintUsage(argv);
+    exit(option == 'h' ? 0 : 1);
+  }
+
+  command_line_options.launch_shell =
+      explicit_interactive || (command_line_options.metric_names.empty() &&
+                               command_line_options.query_file_path.empty());
+
+  // Only allow non-interactive queries to emit perf data.
+  if (!command_line_options.perf_file_path.empty() &&
+      command_line_options.launch_shell) {
+    PrintUsage(argv);
+    exit(1);
+  }
+
+  // Ensure that we have the tracefile argument only at the end.
+  if (optind != argc - 1 || argv[optind] == nullptr) {
+    PrintUsage(argv);
+    exit(1);
+  }
+
+  command_line_options.trace_file_path = argv[optind];
+
+  return command_line_options;
+}
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+int TraceProcessorMain(int argc, char** argv) {
+  CommandLineOptions options = ParseCommandLineOptions(argc, argv);
+
+  // Load the trace file into the trace processor.
+  Config config;
+  std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
+  base::ScopedFile fd(base::OpenFile(options.trace_file_path, O_RDONLY));
+  if (!fd) {
+    PERFETTO_ELOG("Could not open trace file (path: %s)",
+                  options.trace_file_path.c_str());
+    return 1;
+  }
+
+  auto t_load_start = base::GetWallTimeNs();
+  uint64_t file_size = ReadTrace(tp.get(), *fd);
   auto t_load = base::GetWallTimeNs() - t_load_start;
   double t_load_s = t_load.count() / 1E9;
   double size_mb = file_size / 1E6;
@@ -797,9 +874,9 @@
     pool.BuildFile(desc);
   }
 
-  if (!metric_names.empty()) {
+  if (!options.metric_names.empty()) {
     std::vector<std::string> metrics;
-    for (base::StringSplitter ss(metric_names, ','); ss.Next();) {
+    for (base::StringSplitter ss(options.metric_names, ','); ss.Next();) {
       metrics.emplace_back(ss.cur_token());
     }
 
@@ -835,22 +912,22 @@
       metrics[i] = basename;
     }
 
-    bool metrics_textproto = metric_output != "binary";
+    bool metrics_textproto = options.metric_output != "binary";
     int ret = RunMetrics(std::move(metrics), metrics_textproto, pool);
     if (!ret) {
       auto t_query = base::GetWallTimeNs() - t_run_start;
-      ret = MaybePrintPerfFile(perf_file_path, t_load, t_query);
+      ret = MaybePrintPerfFile(options.perf_file_path, t_load, t_query);
     }
     if (ret)
       return ret;
   } else {
     // If we were given a query file, load contents
     std::vector<std::string> queries;
-    if (!query_file_path.empty()) {
-      base::ScopedFstream file(fopen(query_file_path.c_str(), "r"));
+    if (!options.query_file_path.empty()) {
+      base::ScopedFstream file(fopen(options.query_file_path.c_str(), "r"));
       if (!file) {
         PERFETTO_ELOG("Could not open query file (path: %s)",
-                      query_file_path.c_str());
+                      options.query_file_path.c_str());
         return 1;
       }
       if (!LoadQueries(file.get(), &queries)) {
@@ -863,13 +940,13 @@
     }
   }
 
-  if (!sqlite_file_path.empty()) {
-    return ExportTraceToDatabase(sqlite_file_path);
+  if (!options.sqlite_file_path.empty()) {
+    return ExportTraceToDatabase(options.sqlite_file_path);
   }
 
-  if (!launch_shell) {
+  if (!options.launch_shell) {
     auto t_query = base::GetWallTimeNs() - t_run_start;
-    return MaybePrintPerfFile(perf_file_path, t_load, t_query);
+    return MaybePrintPerfFile(options.perf_file_path, t_load, t_query);
   }
 
   return StartInteractiveShell();