Merge "perfetto: replace remaining gtest_prod.h header usages"
diff --git a/src/profiling/memory/README.md b/src/profiling/memory/README.md
index a9a5b9c..602fa1d 100644
--- a/src/profiling/memory/README.md
+++ b/src/profiling/memory/README.md
@@ -14,24 +14,33 @@
 ```bash
 INTERVAL=128000
 
-adb root
-adb shell setenforce 0
-adb shell heapprofd -s -i ${INTERVAL} &
-adb shell kill -36 ${PID} # Start profiling the process.
+adb shell su root setenforce 0
+adb shell su root start heapprofd
+
+echo '
+buffers {
+  size_kb: 100024
+}
+
+data_sources {
+  config {
+    name: "android.heapprofd"
+    target_buffer: 0
+    heapprofd_config {
+      sampling_interval_bytes: '${INTERVAL}'
+      pid: '${PID}'
+      continuous_dump_config {
+        dump_phase_ms: 10000
+        dump_interval_ms: 1000
+      }
+    }
+  }
+}
+
+duration_ms: 20000
+' | adb shell perfetto -t -c - -o /data/misc/perfetto-traces/trace
+
+adb pull /data/misc/perfetto-traces/trace /tmp/trace
 ```
 
-To obtain heap dumps for all profiled processes, send `SIGUSR1` to heapprofd
-which produces heap dumps in /data/local/tmp.
-
-```bash
-adb shell killall -USR1 heapprofd
-adb pull /data/local/tmp/heap_dump.${PID}
-```
-
-This file can then be converted to a flamegraph using Brendan Gregg's
-[`flamegraph.pl`](
-  https://github.com/brendangregg/FlameGraph/blob/master/flamegraph.pl).
-
-```bash
-flamegraph.pl heap_dump.${PID} > heap_dump.${PID}.svg
-```
+TODO(fmayer): Add instructions how to visualize the trace.
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 7f5fbca..132565b 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -176,6 +176,10 @@
     "../../protos/perfetto/trace:lite",
     "../base",
   ]
+  if (perfetto_build_standalone) {
+    sources += [ "json_trace_parser_unittest.cc" ]
+    deps += [ "../../gn:jsoncpp_deps" ]
+  }
 }
 
 source_set("integrationtests") {
diff --git a/src/trace_processor/json_trace_parser.cc b/src/trace_processor/json_trace_parser.cc
index 245348c..5bb3b28 100644
--- a/src/trace_processor/json_trace_parser.cc
+++ b/src/trace_processor/json_trace_parser.cc
@@ -16,6 +16,7 @@
 
 #include "src/trace_processor/json_trace_parser.h"
 
+#include <inttypes.h>
 #include <json/reader.h>
 #include <json/value.h>
 
@@ -36,7 +37,6 @@
 
 namespace perfetto {
 namespace trace_processor {
-
 namespace {
 
 enum ReadDictRes { kFoundDict, kNeedsMoreData, kEndOfTrace, kFatalError };
@@ -82,6 +82,31 @@
 
 }  // namespace
 
+bool CoerceToUint64(const Json::Value& value, uint64_t* integer_ptr) {
+  switch (static_cast<size_t>(value.type())) {
+    case Json::uintValue:
+    case Json::intValue:
+      *integer_ptr = value.asUInt64();
+      return true;
+    case Json::stringValue: {
+      std::string s = value.asString();
+      char* end;
+      *integer_ptr = strtoull(s.c_str(), &end, 10);
+      return end == s.data() + s.size();
+    }
+    default:
+      return false;
+  }
+}
+
+bool CoerceToUint32(const Json::Value& value, uint32_t* integer_ptr) {
+  uint64_t n = 0;
+  if (!CoerceToUint64(value, &n))
+    return false;
+  *integer_ptr = static_cast<uint32_t>(n);
+  return true;
+}
+
 JsonTraceParser::JsonTraceParser(TraceProcessorContext* context)
     : context_(context) {}
 
@@ -124,9 +149,13 @@
     if (!ph.isString())
       continue;
     char phase = *ph.asCString();
-    uint32_t tid = value["tid"].asUInt();
-    uint32_t pid = value["pid"].asUInt();
-    uint64_t ts = value["ts"].asLargestUInt() * 1000;
+    uint32_t tid = 0;
+    PERFETTO_CHECK(CoerceToUint32(value["tid"], &tid));
+    uint32_t pid = 0;
+    PERFETTO_CHECK(CoerceToUint32(value["pid"], &pid));
+    uint64_t ts = 0;
+    PERFETTO_CHECK(CoerceToUint64(value["ts"], &ts));
+    ts *= 1000;
     const char* cat = value.isMember("cat") ? value["cat"].asCString() : "";
     const char* name = value.isMember("name") ? value["name"].asCString() : "";
     StringId cat_id = storage->InternString(cat);
@@ -143,7 +172,9 @@
         break;
       }
       case 'X': {  // TRACE_EVENT (scoped event).
-        uint64_t duration = value["dur"].asUInt() * 1000;
+        uint64_t duration = 0;
+        PERFETTO_CHECK(CoerceToUint64(value["ts"], &duration));
+        duration *= 1000;
         slice_tracker->Scoped(ts, utid, cat_id, name_id, duration);
         break;
       }
diff --git a/src/trace_processor/json_trace_parser.h b/src/trace_processor/json_trace_parser.h
index bdd9378..36f001d 100644
--- a/src/trace_processor/json_trace_parser.h
+++ b/src/trace_processor/json_trace_parser.h
@@ -26,11 +26,19 @@
 #include "src/trace_processor/chunked_trace_reader.h"
 #include "src/trace_processor/trace_storage.h"
 
+namespace Json {
+class Value;
+}
+
 namespace perfetto {
 namespace trace_processor {
 
 class TraceProcessorContext;
 
+// TODO(hjd): Make optional.
+bool CoerceToUint64(const Json::Value& value, uint64_t* integer_ptr);
+bool CoerceToUint32(const Json::Value& value, uint32_t* integer_ptr);
+
 // Parses legacy chrome JSON traces. The support for now is extremely rough
 // and supports only explicit TRACE_EVENT_BEGIN/END events.
 class JsonTraceParser : public ChunkedTraceReader {
diff --git a/src/trace_processor/json_trace_parser_unittest.cc b/src/trace_processor/json_trace_parser_unittest.cc
new file mode 100644
index 0000000..be23d7a
--- /dev/null
+++ b/src/trace_processor/json_trace_parser_unittest.cc
@@ -0,0 +1,53 @@
+/*
+ * 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/json_trace_parser.h"
+
+#include <json/value.h>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+TEST(JsonTraceParserTest, CoerceToUint32) {
+  uint32_t n = 0;
+
+  ASSERT_TRUE(CoerceToUint32(Json::Value(42), &n));
+  EXPECT_EQ(n, 42);
+
+  ASSERT_TRUE(CoerceToUint32(Json::Value("42"), &n));
+  EXPECT_EQ(n, 42);
+}
+
+TEST(JsonTraceParserTest, CoerceToUint64) {
+  uint64_t n = 0;
+
+  ASSERT_TRUE(CoerceToUint64(Json::Value(42), &n));
+  EXPECT_EQ(n, 42);
+
+  ASSERT_TRUE(CoerceToUint64(Json::Value("42"), &n));
+  EXPECT_EQ(n, 42);
+
+  ASSERT_FALSE(CoerceToUint64(Json::Value("foo"), &n));
+  ASSERT_FALSE(CoerceToUint64(Json::Value("1234!"), &n));
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/span_join_operator_table.cc b/src/trace_processor/span_join_operator_table.cc
index db8bb78..de06fbf 100644
--- a/src/trace_processor/span_join_operator_table.cc
+++ b/src/trace_processor/span_join_operator_table.cc
@@ -41,7 +41,9 @@
 
 void SpanJoinOperatorTable::RegisterTable(sqlite3* db,
                                           const TraceStorage* storage) {
-  Table::Register<SpanJoinOperatorTable>(db, storage, "span_join");
+  Table::Register<SpanJoinOperatorTable>(db, storage, "span_join",
+                                         /* read_write */ false,
+                                         /* requires_args */ true);
 }
 
 Table::Schema SpanJoinOperatorTable::CreateSchema(int argc,
diff --git a/src/trace_processor/sqlite_utils.h b/src/trace_processor/sqlite_utils.h
index 4c700f9..0d3dc94 100644
--- a/src/trace_processor/sqlite_utils.h
+++ b/src/trace_processor/sqlite_utils.h
@@ -95,29 +95,29 @@
 template <>
 inline uint8_t ExtractSqliteValue(sqlite3_value* value) {
   auto type = sqlite3_value_type(value);
-  PERFETTO_DCHECK(type == SQLITE_INTEGER || type == SQLITE_FLOAT);
+  PERFETTO_DCHECK(type == SQLITE_INTEGER);
   return static_cast<uint8_t>(sqlite3_value_int(value));
 }
 
 template <>
 inline uint32_t ExtractSqliteValue(sqlite3_value* value) {
   auto type = sqlite3_value_type(value);
-  PERFETTO_DCHECK(type == SQLITE_INTEGER || type == SQLITE_FLOAT);
-  return static_cast<uint32_t>(sqlite3_value_int(value));
+  PERFETTO_DCHECK(type == SQLITE_INTEGER);
+  return static_cast<uint32_t>(sqlite3_value_int64(value));
 }
 
 template <>
 inline uint64_t ExtractSqliteValue(sqlite3_value* value) {
   auto type = sqlite3_value_type(value);
-  PERFETTO_DCHECK(type == SQLITE_INTEGER || type == SQLITE_FLOAT);
-  return static_cast<uint64_t>(sqlite3_value_int(value));
+  PERFETTO_DCHECK(type == SQLITE_INTEGER);
+  return static_cast<uint64_t>(sqlite3_value_int64(value));
 }
 
 template <>
 inline int64_t ExtractSqliteValue(sqlite3_value* value) {
   auto type = sqlite3_value_type(value);
-  PERFETTO_DCHECK(type == SQLITE_INTEGER || type == SQLITE_FLOAT);
-  return static_cast<int64_t>(sqlite3_value_int(value));
+  PERFETTO_DCHECK(type == SQLITE_INTEGER);
+  return static_cast<int64_t>(sqlite3_value_int64(value));
 }
 
 template <>
diff --git a/src/trace_processor/storage_schema.h b/src/trace_processor/storage_schema.h
index d50efd4..ebca90f 100644
--- a/src/trace_processor/storage_schema.h
+++ b/src/trace_processor/storage_schema.h
@@ -131,11 +131,14 @@
     }
 
     Predicate Filter(int op, sqlite3_value* value) const override {
-      auto binary_op = sqlite_utils::GetPredicateForOp<T>(op);
-      T extracted = sqlite_utils::ExtractSqliteValue<T>(value);
-      return [this, binary_op, extracted](uint32_t idx) {
-        return binary_op((*deque_)[idx], extracted);
-      };
+      auto type = sqlite3_value_type(value);
+      if (type == SQLITE_INTEGER && std::is_integral<T>::value) {
+        return FilterWithCast<int64_t>(op, value);
+      } else if (type == SQLITE_INTEGER || type == SQLITE_FLOAT) {
+        return FilterWithCast<double>(op, value);
+      } else {
+        PERFETTO_FATAL("Unexpected sqlite value to compare against");
+      }
     }
 
     Comparator Sort(const QueryConstraints::OrderBy& ob) const override {
@@ -172,6 +175,16 @@
     }
 
    private:
+    template <typename C>
+    Predicate FilterWithCast(int op, sqlite3_value* value) const {
+      auto binary_op = sqlite_utils::GetPredicateForOp<C>(op);
+      C extracted = sqlite_utils::ExtractSqliteValue<C>(value);
+      return [this, binary_op, extracted](uint32_t idx) {
+        auto val = static_cast<C>((*deque_)[idx]);
+        return binary_op(val, extracted);
+      };
+    }
+
     const std::deque<T>* deque_ = nullptr;
     bool is_naturally_ordered_ = false;
   };
diff --git a/src/trace_processor/table.cc b/src/trace_processor/table.cc
index ceca4b6..8ded39a 100644
--- a/src/trace_processor/table.cc
+++ b/src/trace_processor/table.cc
@@ -71,6 +71,7 @@
                              const TraceStorage* storage,
                              const std::string& table_name,
                              bool read_write,
+                             bool requires_args,
                              Factory factory) {
   std::unique_ptr<TableDescriptor> desc(new TableDescriptor());
   desc->storage = storage;
@@ -86,7 +87,7 @@
 
     auto schema = table->CreateSchema(argc, argv);
     auto create_stmt = schema.ToCreateTableStmt();
-    PERFETTO_DLOG("Create table statment: %s", create_stmt.c_str());
+    PERFETTO_DLOG("Create table statement: %s", create_stmt.c_str());
 
     int res = sqlite3_declare_vtab(xdb, create_stmt.c_str());
     if (res != SQLITE_OK)
@@ -156,6 +157,22 @@
       db, table_name.c_str(), module, desc.release(),
       [](void* arg) { delete static_cast<TableDescriptor*>(arg); });
   PERFETTO_CHECK(res == SQLITE_OK);
+
+  // Register virtual tables into an internal 'perfetto_tables' table. This is
+  // used for iterating through all the tables during a database export. Note
+  // that virtual tables requiring arguments aren't registered because they
+  // can't be automatically instantiated for exporting.
+  if (!requires_args) {
+    char* insert_sql = sqlite3_mprintf(
+        "INSERT INTO perfetto_tables(name) VALUES('%q')", table_name.c_str());
+    char* error = nullptr;
+    sqlite3_exec(db, insert_sql, 0, 0, &error);
+    sqlite3_free(insert_sql);
+    if (error) {
+      PERFETTO_ELOG("Error registering table: %s", error);
+      sqlite3_free(error);
+    }
+  }
 }
 
 int Table::OpenInternal(sqlite3_vtab_cursor** ppCursor) {
diff --git a/src/trace_processor/table.h b/src/trace_processor/table.h
index bccf710..f0b4cac 100644
--- a/src/trace_processor/table.h
+++ b/src/trace_processor/table.h
@@ -138,12 +138,17 @@
   Table();
 
   // Called by derived classes to register themselves with the SQLite db.
+  // |read_write| specifies whether the table can also be written to.
+  // |requires_args| should be true if the table requires arguments in order to
+  // be instantiated.
   template <typename T>
   static void Register(sqlite3* db,
                        const TraceStorage* storage,
                        const std::string& name,
-                       bool read_write = false) {
-    RegisterInternal(db, storage, name, read_write, GetFactory<T>());
+                       bool read_write = false,
+                       bool requires_args = false) {
+    RegisterInternal(db, storage, name, read_write, requires_args,
+                     GetFactory<T>());
   }
 
   // Methods to be implemented by derived table classes.
@@ -178,6 +183,7 @@
                                const TraceStorage*,
                                const std::string& name,
                                bool read_write,
+                               bool requires_args,
                                Factory);
 
   // Overriden functions from sqlite3_vtab.
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 614cb21..297944b 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -52,8 +52,18 @@
 void InitializeSqliteModules(sqlite3* db) {
   char* error = nullptr;
   sqlite3_percentile_init(db, &error, nullptr);
-  if (error != nullptr) {
+  if (error) {
     PERFETTO_ELOG("Error initializing: %s", error);
+    sqlite3_free(error);
+  }
+}
+
+void CreateBuiltinTables(sqlite3* db) {
+  char* error = nullptr;
+  sqlite3_exec(db, "CREATE TABLE perfetto_tables(name STRING)", 0, 0, &error);
+  if (error) {
+    PERFETTO_ELOG("Error initializing: %s", error);
+    sqlite3_free(error);
   }
 }
 }  // namespace
@@ -91,6 +101,7 @@
   sqlite3* db = nullptr;
   PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
   InitializeSqliteModules(db);
+  CreateBuiltinTables(db);
   db_.reset(std::move(db));
 
   context_.storage.reset(new TraceStorage());
diff --git a/src/trace_processor/trace_processor_impl_unittest.cc b/src/trace_processor/trace_processor_impl_unittest.cc
index a901de6..9200d4e 100644
--- a/src/trace_processor/trace_processor_impl_unittest.cc
+++ b/src/trace_processor/trace_processor_impl_unittest.cc
@@ -23,27 +23,27 @@
 namespace trace_processor {
 namespace {
 
-TEST(TraceProcessorImpl, GuessTraceType_Empty) {
+TEST(TraceProcessorImplTest, GuessTraceType_Empty) {
   const uint8_t prefix[] = "";
   EXPECT_EQ(kUnknownTraceType, GuessTraceType(prefix, 0));
 }
 
-TEST(TraceProcessorImpl, GuessTraceType_Json) {
+TEST(TraceProcessorImplTest, GuessTraceType_Json) {
   const uint8_t prefix[] = "{\"traceEvents\":[";
   EXPECT_EQ(kJsonTraceType, GuessTraceType(prefix, sizeof(prefix)));
 }
 
-TEST(TraceProcessorImpl, GuessTraceType_JsonWithSpaces) {
+TEST(TraceProcessorImplTest, GuessTraceType_JsonWithSpaces) {
   const uint8_t prefix[] = "\n{ \"traceEvents\": [";
   EXPECT_EQ(kJsonTraceType, GuessTraceType(prefix, sizeof(prefix)));
 }
 
-TEST(TraceProcessorImpl, GuessTraceType_JsonMissingTraceEvents) {
+TEST(TraceProcessorImplTest, GuessTraceType_JsonMissingTraceEvents) {
   const uint8_t prefix[] = "[{";
   EXPECT_EQ(kJsonTraceType, GuessTraceType(prefix, sizeof(prefix)));
 }
 
-TEST(TraceProcessorImpl, GuessTraceType_Proto) {
+TEST(TraceProcessorImplTest, GuessTraceType_Proto) {
   const uint8_t prefix[] = {0x0a, 0x65, 0x18, 0x8f, 0x4e, 0x32, 0x60, 0x0a};
   EXPECT_EQ(kProtoTraceType, GuessTraceType(prefix, sizeof(prefix)));
 }
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 1cc5d0d..51b4579 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -135,6 +135,68 @@
 
 #endif
 
+int ExportTraceToDatabase(const std::string& output_name) {
+  PERFETTO_CHECK(output_name.find("'") == std::string::npos);
+  {
+    base::ScopedFile fd(base::OpenFile(output_name, O_CREAT | O_RDWR, 0600));
+    if (!fd) {
+      PERFETTO_PLOG("Failed to create file: %s", output_name.c_str());
+      return 1;
+    }
+    int res = ftruncate(fd.get(), 0);
+    PERFETTO_CHECK(res == 0);
+  }
+
+  // TODO(skyostil): Use synchronous queries.
+  std::string attach_sql =
+      "ATTACH DATABASE '" + output_name + "' AS perfetto_export";
+  protos::RawQueryArgs attach_query;
+  attach_query.set_sql_query(attach_sql);
+  g_tp->ExecuteQuery(
+      attach_query, [](const protos::RawQueryResult& attach_res) {
+        if (attach_res.has_error()) {
+          PERFETTO_ELOG("SQLite error: %s", attach_res.error().c_str());
+          return;
+        }
+        protos::RawQueryArgs list_query;
+        // Find all the virtual tables we have created internally as well as
+        // actual tables registered through SQL.
+        list_query.set_sql_query(
+            "SELECT name FROM perfetto_tables UNION "
+            "SELECT name FROM sqlite_master WHERE type='table'");
+        g_tp->ExecuteQuery(list_query, [](const protos::RawQueryResult& res) {
+          if (res.has_error()) {
+            PERFETTO_ELOG("SQLite error: %s", res.error().c_str());
+            return;
+          }
+          PERFETTO_CHECK(res.columns_size() == 1);
+          for (int r = 0; r < static_cast<int>(res.num_records()); r++) {
+            std::string table_name = res.columns(0).string_values(r);
+            PERFETTO_CHECK(table_name.find("'") == std::string::npos);
+            std::string export_sql = "CREATE TABLE perfetto_export." +
+                                     table_name + " AS SELECT * FROM " +
+                                     table_name;
+            protos::RawQueryArgs export_query;
+            export_query.set_sql_query(export_sql);
+            g_tp->ExecuteQuery(export_query,
+                               [](const protos::RawQueryResult& export_res) {
+                                 if (export_res.has_error())
+                                   PERFETTO_ELOG("SQLite error: %s",
+                                                 export_res.error().c_str());
+                               });
+          }
+        });
+      });
+
+  protos::RawQueryArgs detach_query;
+  detach_query.set_sql_query("DETACH DATABASE perfetto_export");
+  g_tp->ExecuteQuery(detach_query, [](const protos::RawQueryResult& res) {
+    if (res.has_error())
+      PERFETTO_ELOG("SQLite error: %s", res.error().c_str());
+  });
+  return 0;
+}
+
 void PrintQueryResultInteractively(base::TimeNanos t_start,
                                    const protos::RawQueryResult& res) {
   if (res.has_error()) {
@@ -185,15 +247,40 @@
   }
   printf("\nQuery executed in %.3f ms\n\n", (t_end - t_start).count() / 1E6);
 }
+
+void PrintShellUsage() {
+  PERFETTO_ELOG(
+      "Available commands:\n"
+      ".quit, .q    Exit the shell.\n"
+      ".help        This text.\n"
+      ".dump FILE   Export the trace as a sqlite database.\n");
+}
+
 int StartInteractiveShell() {
   SetupLineEditor();
 
   for (;;) {
     char* line = GetLine("> ");
-    if (!line || strcmp(line, "q\n") == 0)
+    if (!line)
       break;
     if (strcmp(line, "") == 0)
       continue;
+    if (line[0] == '.') {
+      char command[32] = {};
+      char arg[1024] = {};
+      sscanf(line + 1, "%31s %1023s", command, arg);
+      if (strcmp(command, "quit") == 0 || strcmp(command, "q") == 0) {
+        break;
+      } else if (strcmp(command, "help") == 0) {
+        PrintShellUsage();
+      } else if (strcmp(command, "dump") == 0 && strlen(arg)) {
+        if (ExportTraceToDatabase(arg) != 0)
+          PERFETTO_ELOG("Database export failed");
+      } else {
+        PrintShellUsage();
+      }
+      continue;
+    }
     protos::RawQueryArgs query;
     query.set_sql_query(line);
     base::TimeNanos t_start = base::GetWallTimeNs();
@@ -287,7 +374,14 @@
 }
 
 void PrintUsage(char** argv) {
-  PERFETTO_ELOG("Usage: %s [-d] [-q query.sql] trace_file.pb", argv[0]);
+  PERFETTO_ELOG(
+      "Interactive trace processor shell.\n"
+      "Usage: %s [OPTIONS] trace_file.pb\n\n"
+      "Options:\n"
+      " -d        Enable virtual table debugging.\n"
+      " -q FILE   Read and execute an SQL query from a file.\n"
+      " -e FILE   Export the trace into a SQLite database.\n",
+      argv[0]);
 }
 
 int TraceProcessorMain(int argc, char** argv) {
@@ -297,6 +391,7 @@
   }
   const char* trace_file_path = nullptr;
   const char* query_file_path = nullptr;
+  const char* sqlite_file_path = nullptr;
   for (int i = 1; i < argc; i++) {
     if (strcmp(argv[i], "-d") == 0) {
       EnableSQLiteVtableDebugging();
@@ -309,6 +404,19 @@
       }
       query_file_path = argv[i];
       continue;
+    } else if (strcmp(argv[i], "-e") == 0) {
+      if (++i == argc) {
+        PrintUsage(argv);
+        return 1;
+      }
+      sqlite_file_path = argv[i];
+      continue;
+    } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
+      PrintUsage(argv);
+      return 0;
+    } else if (argv[i][0] == '-') {
+      PERFETTO_ELOG("Unknown option: %s", argv[i]);
+      return 1;
     }
     trace_file_path = argv[i];
   }
@@ -375,14 +483,26 @@
   signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
 #endif
 
-  // If there is no query file, start a shell.
-  if (query_file_path == nullptr) {
-    return StartInteractiveShell();
+  int ret = 0;
+
+  // If we were given a query file, first load and execute it.
+  if (query_file_path) {
+    base::ScopedFstream file(fopen(query_file_path, "r"));
+    ret = RunQueryAndPrintResult(file.get(), stdout);
   }
 
-  // Otherwise run the queries and print the results.
-  base::ScopedFstream file(fopen(query_file_path, "r"));
-  return RunQueryAndPrintResult(file.get(), stdout);
+  // After this we can dump the database and exit if needed.
+  if (ret == 0 && sqlite_file_path) {
+    return ExportTraceToDatabase(sqlite_file_path);
+  }
+
+  // If we ran an automated query, exit.
+  if (query_file_path) {
+    return ret;
+  }
+
+  // Otherwise start an interactive shell.
+  return StartInteractiveShell();
 }
 
 }  // namespace
diff --git a/src/traced/probes/ftrace/atrace_wrapper.cc b/src/traced/probes/ftrace/atrace_wrapper.cc
index db50e09..5987d46 100644
--- a/src/traced/probes/ftrace/atrace_wrapper.cc
+++ b/src/traced/probes/ftrace/atrace_wrapper.cc
@@ -44,19 +44,61 @@
     argv.push_back(const_cast<char*>(arg.c_str()));
   argv.push_back(nullptr);
 
+  // Create the pipe for the child process to return stderr.
+  int filedes[2];
+  if (pipe(filedes) == -1)
+    return false;
+
   pid_t pid = fork();
   PERFETTO_CHECK(pid >= 0);
   if (pid == 0) {
-    // Close stdin/out/err + any file descriptor that we might have mistakenly
+    // Duplicate the write end of the pipe into stderr.
+    if ((dup2(filedes[1], STDERR_FILENO) == -1)) {
+      const char kError[] = "Unable to duplicate stderr fd";
+      write(filedes[1], kError, sizeof(kError));
+      _exit(1);
+    }
+
+    // Close stdin/out + any file descriptor that we might have mistakenly
     // not marked as FD_CLOEXEC.
-    for (int i = 0; i < 128; i++)
-      close(i);
+    for (int i = 0; i < 128; i++) {
+      if (i != STDERR_FILENO)
+        close(i);
+    }
+
+    // Close the read and write end of the pipe fds.
+    close(filedes[1]);
+    close(filedes[0]);
+
     execv("/system/bin/atrace", &argv[0]);
     // Reached only if execv fails.
     _exit(1);
   }
+
+  // Close the write end of the pipe.
+  close(filedes[1]);
+
+  // Collect the output from child process.
+  std::string error;
+  char buffer[4096];
+  while (true) {
+    ssize_t count = PERFETTO_EINTR(read(filedes[0], buffer, sizeof(buffer)));
+    if (count == 0 || count == -1)
+      break;
+    error.append(buffer, static_cast<size_t>(count));
+  }
+  // Close the read end of the pipe.
+  close(filedes[0]);
+
+  // Wait until the child process exits fully.
   PERFETTO_EINTR(waitpid(pid, &status, 0));
-  return status == 0;
+
+  bool ok = WIFEXITED(status) && WEXITSTATUS(status) == 0;
+  if (!ok) {
+    // TODO(lalitm): use the stderr result from atrace.
+    base::ignore_result(error);
+  }
+  return ok;
 }
 #endif
 
diff --git a/test/trace_processor/android_sched_and_ps_b119496959.out b/test/trace_processor/android_sched_and_ps_b119496959.out
new file mode 100644
index 0000000..480ada8
--- /dev/null
+++ b/test/trace_processor/android_sched_and_ps_b119496959.out
@@ -0,0 +1,11 @@
+"ts","cpu"
+81473797824982,3
+81473797942847,3
+81473798135399,0
+81473798786857,2
+81473798875451,3
+81473799019930,2
+81473799079982,0
+81473800089357,3
+81473800144461,3
+81473800441805,3
diff --git a/test/trace_processor/b119496959.sql b/test/trace_processor/b119496959.sql
new file mode 100644
index 0000000..be4460c
--- /dev/null
+++ b/test/trace_processor/b119496959.sql
@@ -0,0 +1 @@
+select ts, cpu from sched where ts >= 81473797418963 limit 10
diff --git a/test/trace_processor/index b/test/trace_processor/index
index ea24c2d..81dbe9f 100644
--- a/test/trace_processor/index
+++ b/test/trace_processor/index
@@ -5,6 +5,7 @@
 ../data/android_sched_and_ps.pb slice_span_join_b118665515.sql android_sched_and_ps_slice_span_join_b118665515.out
 ../data/android_sched_and_ps.pb b119301023.sql android_sched_and_ps_b119301023.out
 ../data/android_sched_and_ps.pb stats.sql android_sched_and_ps_stats.out
+../data/android_sched_and_ps.pb b119496959.sql android_sched_and_ps_b119496959.out
 synth_1.py smoke.sql synth_1_smoke.out
 synth_1.py filter_sched.sql synth_1_filter_sched.out
 synth_1.py filter_counters.sql synth_1_filter_counters.out