Add interactive trace_processor_shell + fix mac build

Adds an interactive shell (ninja target: trace_processor_shell)
to test the trace processor on the host.
Also this CL fixes a small build issue on mac (unix_socket.cc)

Change-Id: I4923f51a706818bd5cf689b035a2075fa6c595a8
diff --git a/BUILD.gn b/BUILD.gn
index 3133792..31f6c70 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -50,7 +50,10 @@
       "tools/proto_to_cpp",
     ]
     if (!build_with_android) {
-      deps += [ "tools/trace_to_text" ]
+      deps += [
+        ":trace_processor",
+        "tools/trace_to_text",
+      ]
     }
     if (is_linux || is_android) {
       deps += [ "tools/skippy" ]
@@ -80,6 +83,14 @@
       "ui",
     ]
   }
+
+  # The trace processor shell executable. An interactive shell that allows to
+  # make queries on the trace using the terminal.
+  group("trace_processor") {
+    deps = [
+      "src/trace_processor:trace_processor_shell",
+    ]
+  }
 }
 
 executable("perfetto_unittests") {
@@ -215,14 +226,6 @@
         "protos/perfetto/trace:lite",
       ]
     }
-  } else {
-    # The library which eases processing of Perfetto traces by exposing reading
-    # friendly APIs.
-    static_library("trace_processor") {
-      deps = [
-        "src/trace_processor:lib",
-      ]
-    }
   }
 }
 
diff --git a/gn/wasm.gni b/gn/wasm.gni
index c47439e..32dd089 100644
--- a/gn/wasm.gni
+++ b/gn/wasm.gni
@@ -19,4 +19,11 @@
   import("//gn/standalone/wasm.gni")
 } else {
   is_wasm = false  # The WASM toolchain is supported only in standalone builds.
+
+  # Create a dummy template to avoid GN warnings in non-standalone builds.
+  template("wasm_lib") {
+    source_set("${target_name}_unused") {
+      forward_variables_from(invoker, "*")
+    }
+  }
 }
diff --git a/protos/perfetto/trace_processor/raw_query.proto b/protos/perfetto/trace_processor/raw_query.proto
index 9c5eaf0..673994f 100644
--- a/protos/perfetto/trace_processor/raw_query.proto
+++ b/protos/perfetto/trace_processor/raw_query.proto
@@ -44,4 +44,5 @@
   repeated ColumnDesc column_descriptors = 1;
   optional uint64 num_records = 2;
   repeated ColumnValues columns = 3;
+  optional string error = 4;
 }
diff --git a/src/ipc/unix_socket.cc b/src/ipc/unix_socket.cc
index d8dad3b..b53bbde 100644
--- a/src/ipc/unix_socket.cc
+++ b/src/ipc/unix_socket.cc
@@ -343,7 +343,7 @@
     struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg_hdr);
     cmsg->cmsg_level = SOL_SOCKET;
     cmsg->cmsg_type = SCM_RIGHTS;
-    cmsg->cmsg_len = CMSG_LEN(num_fds * sizeof(int));
+    cmsg->cmsg_len = static_cast<CBufLenType>(CMSG_LEN(num_fds * sizeof(int)));
     memcpy(CMSG_DATA(cmsg), send_fds, num_fds * sizeof(int));
     msg_hdr.msg_controllen = cmsg->cmsg_len;
   }
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index f560a99..c0e0aca 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -36,6 +36,8 @@
 source_set("lib") {
   sources = [
     "blob_reader.h",
+    "file_reader.cc",
+    "file_reader.h",
     "process_table.cc",
     "process_table.h",
     "process_tracker.cc",
@@ -72,6 +74,18 @@
   ]
 }
 
+executable("trace_processor_shell") {
+  deps = [
+    ":lib",
+    "../../gn:default_deps",
+    "../../protos/perfetto/trace_processor:lite",
+    "../base",
+  ]
+  sources = [
+    "trace_processor_shell.cc",
+  ]
+}
+
 source_set("unittests") {
   testonly = true
   sources = [
diff --git a/src/trace_processor/file_reader.cc b/src/trace_processor/file_reader.cc
new file mode 100644
index 0000000..d140dc3
--- /dev/null
+++ b/src/trace_processor/file_reader.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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 "src/trace_processor/file_reader.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+namespace perfetto {
+namespace trace_processor {
+
+FileReader::FileReader(const char* path) {
+  fd_.reset(open(path, O_RDONLY));
+  if (!fd_)
+    PERFETTO_FATAL("Could not open %s", path);
+  struct stat stat_buf {};
+  PERFETTO_CHECK(fstat(*fd_, &stat_buf) == 0);
+  file_size_ = static_cast<uint64_t>(stat_buf.st_size);
+}
+
+FileReader::~FileReader() = default;
+
+uint32_t FileReader::Read(uint64_t offset, uint32_t len, uint8_t* dst) {
+  ssize_t res = pread(*fd_, dst, len, static_cast<off_t>(offset));
+  return res > 0 ? static_cast<uint32_t>(res) : 0;
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/file_reader.h b/src/trace_processor/file_reader.h
new file mode 100644
index 0000000..e3970e0
--- /dev/null
+++ b/src/trace_processor/file_reader.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_FILE_READER_H_
+#define SRC_TRACE_PROCESSOR_FILE_READER_H_
+
+#include <stdint.h>
+
+#include "perfetto/base/scoped_file.h"
+#include "src/trace_processor/blob_reader.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class FileReader : public BlobReader {
+ public:
+  explicit FileReader(const char* path);
+  ~FileReader() override;
+
+  uint32_t Read(uint64_t offset, uint32_t len, uint8_t* dst) override;
+  uint64_t file_size() const { return file_size_; }
+
+ private:
+  FileReader(const FileReader&) = delete;
+  FileReader& operator=(const FileReader&) = delete;
+
+  base::ScopedFile fd_;
+  uint64_t file_size_ = 0;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_FILE_READER_H_
diff --git a/src/trace_processor/process_tracker_unittest.cc b/src/trace_processor/process_tracker_unittest.cc
index 24c4777..29f77dd 100644
--- a/src/trace_processor/process_tracker_unittest.cc
+++ b/src/trace_processor/process_tracker_unittest.cc
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/trace_processor.h"
-
+#include "src/trace_processor/process_tracker.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "src/trace_processor/sched_tracker.h"
+#include "src/trace_processor/trace_parser.h"
+#include "src/trace_processor/trace_processor.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/sched_tracker_unittest.cc b/src/trace_processor/sched_tracker_unittest.cc
index 3a115df..cf930b0 100644
--- a/src/trace_processor/sched_tracker_unittest.cc
+++ b/src/trace_processor/sched_tracker_unittest.cc
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/trace_processor.h"
-
+#include "src/trace_processor/sched_tracker.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "src/trace_processor/process_tracker.h"
+#include "src/trace_processor/trace_parser.h"
+#include "src/trace_processor/trace_processor.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/scoped_db.h b/src/trace_processor/scoped_db.h
index 372c771..75c156a 100644
--- a/src/trace_processor/scoped_db.h
+++ b/src/trace_processor/scoped_db.h
@@ -17,10 +17,15 @@
 #ifndef SRC_TRACE_PROCESSOR_SCOPED_DB_H_
 #define SRC_TRACE_PROCESSOR_SCOPED_DB_H_
 
-#include <sqlite3.h>
-
 #include "perfetto/base/scoped_file.h"
 
+extern "C" {
+struct sqlite3;
+struct sqlite3_stmt;
+extern int sqlite3_close(sqlite3*);
+extern int sqlite3_finalize(sqlite3_stmt* pStmt);
+}
+
 namespace perfetto {
 namespace trace_processor {
 
diff --git a/src/trace_processor/trace_processor.cc b/src/trace_processor/trace_processor.cc
index d1f3220..04977d8 100644
--- a/src/trace_processor/trace_processor.cc
+++ b/src/trace_processor/trace_processor.cc
@@ -16,8 +16,19 @@
 
 #include "src/trace_processor/trace_processor.h"
 
+#include <sqlite3.h>
 #include <functional>
 
+#include "perfetto/base/task_runner.h"
+#include "src/trace_processor/process_table.h"
+#include "src/trace_processor/process_tracker.h"
+#include "src/trace_processor/sched_slice_table.h"
+#include "src/trace_processor/sched_tracker.h"
+#include "src/trace_processor/thread_table.h"
+#include "src/trace_processor/trace_parser.h"
+
+#include "perfetto/trace_processor/raw_query.pb.h"
+
 namespace perfetto {
 namespace trace_processor {
 namespace {
@@ -39,6 +50,8 @@
   ThreadTable::RegisterTable(*db_, context_.storage.get());
 }
 
+TraceProcessor::~TraceProcessor() = default;
+
 void TraceProcessor::LoadTrace(BlobReader* reader,
                                std::function<void()> callback) {
   // Reset storage.
@@ -50,7 +63,7 @@
 
 void TraceProcessor::ExecuteQuery(
     const protos::RawQueryArgs& args,
-    std::function<void(protos::RawQueryResult)> callback) {
+    std::function<void(const protos::RawQueryResult&)> callback) {
   protos::RawQueryResult proto;
 
   const auto& sql = args.sql_query();
@@ -59,6 +72,7 @@
                                &raw_stmt, nullptr);
   ScopedStmt stmt(std::move(raw_stmt));
   if (err) {
+    proto.set_error(sqlite3_errmsg(*db_));
     callback(std::move(proto));
     return;
   }
@@ -109,7 +123,7 @@
   }
   proto.set_num_records(static_cast<uint64_t>(row_count));
 
-  callback(std::move(proto));
+  callback(proto);
 }
 
 void TraceProcessor::LoadTraceChunk(std::function<void()> callback) {
diff --git a/src/trace_processor/trace_processor.h b/src/trace_processor/trace_processor.h
index ffe26b9..c0ec406 100644
--- a/src/trace_processor/trace_processor.h
+++ b/src/trace_processor/trace_processor.h
@@ -17,26 +17,28 @@
 #ifndef SRC_TRACE_PROCESSOR_TRACE_PROCESSOR_H_
 #define SRC_TRACE_PROCESSOR_TRACE_PROCESSOR_H_
 
-#include <sqlite3.h>
+#include <functional>
 #include <memory>
 
-#include "perfetto/base/task_runner.h"
 #include "perfetto/base/weak_ptr.h"
-#include "perfetto/trace_processor/raw_query.pb.h"
-#include "src/trace_processor/blob_reader.h"
-#include "src/trace_processor/process_table.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/sched_slice_table.h"
-#include "src/trace_processor/sched_tracker.h"
 #include "src/trace_processor/scoped_db.h"
-#include "src/trace_processor/thread_table.h"
-#include "src/trace_processor/trace_parser.h"
 #include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
 
 namespace perfetto {
+
+namespace base {
+class TaskRunner;
+}
+
+namespace protos {
+class RawQueryArgs;
+class RawQueryResult;
+}  // namespace protos
+
 namespace trace_processor {
 
+class BlobReader;
+
 // Coordinates the loading of traces from an arbitary source and allows
 // execution of SQL queries on the events in these traces.
 class TraceProcessor {
@@ -51,7 +53,7 @@
   // Executes a SQLite query on the loaded portion of the trace. |result| will
   // be invoked once after the result of the query is available.
   void ExecuteQuery(const protos::RawQueryArgs&,
-                    std::function<void(protos::RawQueryResult)>);
+                    std::function<void(const protos::RawQueryResult&)>);
 
  private:
   void LoadTraceChunk(std::function<void()> callback);
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
new file mode 100644
index 0000000..eda0116
--- /dev/null
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2018 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 <unistd.h>
+
+#include <functional>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/time.h"
+#include "perfetto/base/unix_task_runner.h"
+#include "src/trace_processor/file_reader.h"
+#include "src/trace_processor/trace_processor.h"
+
+#include "perfetto/trace_processor/raw_query.pb.h"
+
+using namespace perfetto;
+using namespace perfetto::trace_processor;
+
+namespace {
+void PrintPrompt() {
+  printf("\r%80s\r> ", "");
+  fflush(stdout);
+}
+
+void OnQueryResult(base::TimeNanos t_start, const protos::RawQueryResult& res) {
+  PERFETTO_CHECK(res.columns_size() == res.column_descriptors_size());
+  if (res.has_error()) {
+    PERFETTO_ELOG("SQLite error: %s", res.error().c_str());
+    return;
+  }
+
+  base::TimeNanos t_end = base::GetWallTimeNs();
+
+  for (int r = 0; r < static_cast<int>(res.num_records()); r++) {
+    if (r % 32 == 0) {
+      if (r > 0) {
+        fprintf(stderr, "...\nType 'q' to stop, Enter for more records: ");
+        fflush(stderr);
+        char input[32];
+        if (!fgets(input, sizeof(input) - 1, stdin))
+          exit(0);
+        if (input[0] == 'q')
+          break;
+      }
+      for (const auto& col : res.column_descriptors())
+        printf("%20s ", col.name().c_str());
+      printf("\n");
+
+      for (int i = 0; i < res.columns_size(); i++)
+        printf("%20s ", "--------------------");
+      printf("\n");
+    }
+
+    for (int c = 0; c < res.columns_size(); c++) {
+      switch (res.column_descriptors(c).type()) {
+        case protos::RawQueryResult_ColumnDesc_Type_STRING:
+          printf("%20s ", res.columns(c).string_values(r).c_str());
+          break;
+        case protos::RawQueryResult_ColumnDesc_Type_DOUBLE:
+          printf("%20f ", res.columns(c).double_values(r));
+          break;
+        case protos::RawQueryResult_ColumnDesc_Type_LONG:
+          printf("%20lld ", res.columns(c).long_values(r));
+          break;
+      }
+    }
+    printf("\n");
+  }
+  printf("\nQuery executed in %.3f ms\n\n", (t_end - t_start).count() / 1E6);
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  if (argc < 2) {
+    PERFETTO_ELOG("Usage: %s trace_file.proto", argv[0]);
+    return 1;
+  }
+
+  base::UnixTaskRunner task_runner;
+  FileReader reader(argv[1]);
+  TraceProcessor tp(&task_runner);
+
+  task_runner.PostTask([&tp, &reader]() {
+    auto t_start = base::GetWallTimeMs();
+    auto on_trace_loaded = [t_start, &reader] {
+      double s = (base::GetWallTimeMs() - t_start).count() / 1000.0;
+      double size_mb = reader.file_size() / 1000000.0;
+      PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb, size_mb / s);
+      PrintPrompt();
+    };
+    tp.LoadTrace(&reader, on_trace_loaded);
+  });
+
+  task_runner.AddFileDescriptorWatch(STDIN_FILENO, [&tp, &task_runner] {
+    char line[1024];
+    if (!fgets(line, sizeof(line) - 1, stdin)) {
+      task_runner.Quit();
+      return;
+    }
+    protos::RawQueryArgs query;
+    query.set_sql_query(line);
+    base::TimeNanos t_start = base::GetWallTimeNs();
+    tp.ExecuteQuery(query, [t_start](const protos::RawQueryResult& res) {
+      OnQueryResult(t_start, res);
+    });
+    PrintPrompt();
+  });
+
+  task_runner.Run();
+  return 0;
+}
diff --git a/src/trace_processor/wasm_bridge.cc b/src/trace_processor/wasm_bridge.cc
index 3cb997d..f85cb69 100644
--- a/src/trace_processor/wasm_bridge.cc
+++ b/src/trace_processor/wasm_bridge.cc
@@ -19,11 +19,13 @@
 #include <string>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/trace_processor/raw_query.pb.h"
-#include "perfetto/trace_processor/sched.pb.h"
+#include "src/trace_processor/blob_reader.h"
 #include "src/trace_processor/emscripten_task_runner.h"
 #include "src/trace_processor/trace_processor.h"
 
+#include "perfetto/trace_processor/raw_query.pb.h"
+#include "perfetto/trace_processor/sched.pb.h"
+
 namespace perfetto {
 namespace trace_processor {