Allow loading a query file and remaining in an interactive shell

This is useful e.g. to have a config file to setup views etc

Change-Id: I4a3f2916298e9525f5a8aefc69655ccc8e0db8b6
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index dc15c3d..42b7f2a 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -345,17 +345,9 @@
   }
 }
 
-int RunQueryAndPrintResult(FILE* input, FILE* output) {
+bool LoadQueries(FILE* input, std::vector<std::string>* output) {
   char buffer[4096];
-  bool is_first_query = true;
-  bool is_query_error = false;
-  bool has_output_printed = false;
-  while (!feof(input) && !ferror(input) && !is_query_error) {
-    // Add an extra newline separator between query results.
-    if (!is_first_query)
-      fprintf(output, "\n");
-    is_first_query = false;
-
+  while (!feof(input) && !ferror(input)) {
     std::string sql_query;
     while (fgets(buffer, sizeof(buffer), input)) {
       if (strncmp(buffer, "\n", sizeof(buffer)) == 0)
@@ -371,33 +363,51 @@
     if (sql_query.empty())
       continue;
 
+    output->push_back(sql_query);
+  }
+  if (ferror(input)) {
+    PERFETTO_ELOG("Error reading query file");
+    return false;
+  }
+  return true;
+}
+
+bool RunQueryAndPrintResult(const std::vector<std::string> queries,
+                            FILE* output,
+                            bool* has_output) {
+  bool is_first_query = true;
+  bool is_query_error = false;
+  *has_output = false;
+  for (const auto& sql_query : queries) {
+    // Add an extra newline separator between query results.
+    if (!is_first_query)
+      fprintf(output, "\n");
+    is_first_query = false;
+
     PERFETTO_ILOG("Executing query: %s", sql_query.c_str());
 
     protos::RawQueryArgs query;
     query.set_sql_query(sql_query);
-    g_tp->ExecuteQuery(query, [output, &is_query_error, &has_output_printed](
-                                  const protos::RawQueryResult& res) {
+    g_tp->ExecuteQuery(query, [output, &is_query_error,
+                               &has_output](const protos::RawQueryResult& res) {
       if (res.has_error()) {
         PERFETTO_ELOG("SQLite error: %s", res.error().c_str());
         is_query_error = true;
         return;
       } else if (res.num_records() != 0) {
-        if (has_output_printed) {
+        if (*has_output) {
           PERFETTO_ELOG(
               "More than one query generated result rows. This is "
               "unsupported.");
           is_query_error = true;
           return;
         }
-        has_output_printed = true;
+        *has_output = true;
       }
       PrintQueryResultAsCsv(res, output);
     });
   }
-  if (ferror(input)) {
-    PERFETTO_ELOG("Error reading query file");
-  }
-  return ferror(input) || is_query_error ? 1 : 0;
+  return !is_query_error;
 }
 
 void PrintUsage(char** argv) {
@@ -513,26 +523,32 @@
   signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
 #endif
 
-  int ret = 0;
-
-  // If we were given a query file, first load and execute it.
+  // If we were given a query file, load contents
+  std::vector<std::string> queries;
   if (query_file_path) {
     base::ScopedFstream file(fopen(query_file_path, "r"));
     if (!file) {
       PERFETTO_ELOG("Could not open query file (path: %s)", query_file_path);
       return 1;
     }
-    ret = RunQueryAndPrintResult(file.get(), stdout);
+    if (!LoadQueries(file.get(), &queries)) {
+      return 1;
+    }
+  }
+
+  bool has_csv_output;
+  if (!RunQueryAndPrintResult(queries, stdout, &has_csv_output)) {
+    return false;
   }
 
   // After this we can dump the database and exit if needed.
-  if (ret == 0 && sqlite_file_path) {
+  if (sqlite_file_path) {
     return ExportTraceToDatabase(sqlite_file_path);
   }
 
   // If we ran an automated query, exit.
-  if (query_file_path) {
-    return ret;
+  if (has_csv_output) {
+    return 0;
   }
 
   // Otherwise start an interactive shell.