Trace Processor: Add RestoreInitialTables() method

Deletes all tables and views created by the user/UI
after loadinig the trace. Will be used by the UI with
the RPC+HTTP mode to reuse the same trace processor
instance.

Bug: 143074239
Change-Id: I4cefb0f8cbd7e6bec9124b0208a128d568698484
diff --git a/include/perfetto/trace_processor/trace_processor.h b/include/perfetto/trace_processor/trace_processor.h
index f6f5089..13f45c8 100644
--- a/include/perfetto/trace_processor/trace_processor.h
+++ b/include/perfetto/trace_processor/trace_processor.h
@@ -117,6 +117,11 @@
 
   // Interrupts the current query. Typically used by Ctrl-C handler.
   virtual void InterruptQuery() = 0;
+
+  // Deletes all tables and view that have been create (by the UI or user) after
+  // the trace was loaded. It preserves the built-in tables/view created by the
+  // loading process. Returns the number of table/views deleted.
+  virtual size_t RestoreInitialTables() = 0;
 };
 
 // When set, logs SQLite actions on the console.
diff --git a/src/trace_processor/trace_database_integrationtest.cc b/src/trace_processor/trace_database_integrationtest.cc
index 0392c85..4d7d007 100644
--- a/src/trace_processor/trace_database_integrationtest.cc
+++ b/src/trace_processor/trace_database_integrationtest.cc
@@ -56,6 +56,8 @@
     return processor_->ExecuteQuery(query.c_str());
   }
 
+  size_t RestoreInitialTables() { return processor_->RestoreInitialTables(); }
+
  private:
   std::unique_ptr<TraceProcessor> processor_;
 };
@@ -179,6 +181,28 @@
   ASSERT_TRUE(LoadTrace("clusterfuzz_17805", 4096).ok());
 }
 
+TEST_F(TraceProcessorIntegrationTest, RestoreInitialTables) {
+  ASSERT_TRUE(LoadTrace("android_sched_and_ps.pb").ok());
+
+  for (int repeat = 0; repeat < 3; repeat++) {
+    ASSERT_EQ(RestoreInitialTables(), 0u);
+
+    auto it = Query("CREATE TABLE user1(unused text);");
+    it.Next();
+    ASSERT_TRUE(it.Status().ok());
+
+    it = Query("CREATE TEMPORARY TABLE user2(unused text);");
+    it.Next();
+    ASSERT_TRUE(it.Status().ok());
+
+    it = Query("CREATE VIEW user3 AS SELECT * FROM stats;");
+    it.Next();
+    ASSERT_TRUE(it.Status().ok());
+
+    ASSERT_EQ(RestoreInitialTables(), 3u);
+  }
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index f6a9dd3..0944d19 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -97,6 +97,10 @@
 namespace trace_processor {
 namespace {
 
+const char kAllTablesQuery[] =
+    "SELECT tbl_name, type FROM (SELECT * FROM sqlite_master UNION ALL SELECT "
+    "* FROM sqlite_temp_master)";
+
 void InitializeSqlite(sqlite3* db) {
   char* error = nullptr;
   sqlite3_exec(db, "PRAGMA temp_store=2", 0, 0, &error);
@@ -440,6 +444,41 @@
   context_.event_tracker->FlushPendingEvents();
   context_.slice_tracker->FlushPendingSlices();
   BuildBoundsTable(*db_, context_.storage->GetTraceTimestampBoundsNs());
+
+  // Create a snapshot of all tables and views created so far. This is so later
+  // we can drop all extra tables created by the UI and reset to the original
+  // state (see RestoreInitialTables).
+  initial_tables_.clear();
+  auto it = ExecuteQuery(kAllTablesQuery);
+  while (it.Next()) {
+    auto value = it.Get(0);
+    PERFETTO_CHECK(value.type == SqlValue::Type::kString);
+    initial_tables_.push_back(value.string_value);
+  }
+}
+
+size_t TraceProcessorImpl::RestoreInitialTables() {
+  std::vector<std::pair<std::string, std::string>> deletion_list;
+  std::string msg = "Resetting DB to initial state, deleting table/views:";
+  for (auto it = ExecuteQuery(kAllTablesQuery); it.Next();) {
+    std::string name(it.Get(0).string_value);
+    std::string type(it.Get(1).string_value);
+    if (std::find(initial_tables_.begin(), initial_tables_.end(), name) ==
+        initial_tables_.end()) {
+      msg += " " + name;
+      deletion_list.push_back(std::make_pair(type, name));
+    }
+  }
+
+  PERFETTO_LOG("%s", msg.c_str());
+  for (const auto& tn : deletion_list) {
+    std::string query = "DROP " + tn.first + " " + tn.second;
+    auto it = ExecuteQuery(query);
+    while (it.Next()) {
+    }
+    PERFETTO_CHECK(it.Status().ok());
+  }
+  return deletion_list.size();
 }
 
 TraceProcessor::Iterator TraceProcessorImpl::ExecuteQuery(
diff --git a/src/trace_processor/trace_processor_impl.h b/src/trace_processor/trace_processor_impl.h
index 0f61c2b..44c3335 100644
--- a/src/trace_processor/trace_processor_impl.h
+++ b/src/trace_processor/trace_processor_impl.h
@@ -22,6 +22,7 @@
 #include <atomic>
 #include <functional>
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "perfetto/ext/base/string_view.h"
@@ -67,6 +68,8 @@
 
   void InterruptQuery() override;
 
+  size_t RestoreInitialTables() override;
+
   TraceProcessorContext* context() { return &context_; }
 
  private:
@@ -87,6 +90,11 @@
   // This is atomic because it is set by the CTRL-C signal handler and we need
   // to prevent single-flow compiler optimizations in ExecuteQuery().
   std::atomic<bool> query_interrupted_{false};
+
+  // Keeps track of the tables created by the ingestion process. This is used
+  // by RestoreInitialTables() to delete all the tables/view that have been
+  // created after that point.
+  std::vector<std::string> initial_tables_;
 };
 
 // The pointer implementation of TraceProcessor::Iterator.
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 25ffb6f..3745634 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -444,7 +444,8 @@
       "Available commands:\n"
       ".quit, .q    Exit the shell.\n"
       ".help        This text.\n"
-      ".dump FILE   Export the trace as a sqlite database.\n");
+      ".dump FILE   Export the trace as a sqlite database.\n"
+      ".reset       Destroys all tables/view created by the user.\n");
 }
 
 int StartInteractiveShell(uint32_t column_width) {
@@ -467,6 +468,8 @@
       } else if (strcmp(command, "dump") == 0 && strlen(arg)) {
         if (ExportTraceToDatabase(arg) != 0)
           PERFETTO_ELOG("Database export failed");
+      } else if (strcmp(command, "reset") == 0) {
+        g_tp->RestoreInitialTables();
       } else {
         PrintShellUsage();
       }