tp: perfetto_table_info

Bug:317966255
Change-Id: If1b5b4cd54e5c5963970bdb102111183dc2ae4bc
diff --git a/Android.bp b/Android.bp
index c9ef938..b81feff 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11721,6 +11721,7 @@
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.cc",
+        "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/view.cc",
     ],
 }
diff --git a/BUILD b/BUILD
index 2c2dbbe..5cebd65 100644
--- a/BUILD
+++ b/BUILD
@@ -2180,6 +2180,8 @@
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.h",
+        "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc",
+        "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/view.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/view.h",
     ],
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index 54e66a1..e660046 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -355,6 +355,9 @@
   // Public for testing.
   bool IsDummy() const { return type_ == ColumnType::kDummy; }
 
+  // Returns true if this column is a hidden column.
+  bool IsHidden() const { return (flags_ & Flag::kHidden) != 0; }
+
   // Returns the index of the RowMap in the containing table.
   uint32_t overlay_index() const { return overlay_index_; }
 
@@ -435,9 +438,6 @@
     return static_cast<ColumnStorage<stored_type<T>>*>(storage_);
   }
 
-  // Returns true if this column is a hidden column.
-  bool IsHidden() const { return (flags_ & Flag::kHidden) != 0; }
-
   const StringPool& string_pool() const { return *string_pool_; }
 
   // Returns the type of this Column in terms of SqlValue::Type.
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
index 4337b56..c236a2f 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
@@ -184,6 +184,7 @@
                                             const std::string& table_name) {
   auto context =
       std::make_unique<DbSqliteTable::Context>(query_cache_.get(), &table);
+  static_tables_.Insert(table_name, &table);
   engine_->RegisterVirtualTableModule<DbSqliteTable>(
       table_name, std::move(context), SqliteTable::kEponymousOnly, false);
 
@@ -833,5 +834,23 @@
       base::Join(columns_missing_from_schema, ", ").c_str());
 }
 
+const RuntimeTable* PerfettoSqlEngine::GetRuntimeTableOrNull(
+    std::string_view name) const {
+  auto table_ptr = runtime_tables_.Find(name.data());
+  if (!table_ptr) {
+    return nullptr;
+  }
+  return table_ptr->get();
+}
+
+const Table* PerfettoSqlEngine::GetStaticTableOrNull(
+    std::string_view name) const {
+  auto table_ptr = static_tables_.Find(name.data());
+  if (!table_ptr) {
+    return nullptr;
+  }
+  return *table_ptr;
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h
index 404d1c0..5d47e50 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h
@@ -19,7 +19,9 @@
 
 #include <cstdint>
 #include <memory>
+#include <optional>
 
+#include "perfetto/base/logging.h"
 #include "perfetto/base/status.h"
 #include "perfetto/ext/base/flat_hash_map.h"
 #include "perfetto/ext/base/status_or.h"
@@ -116,7 +118,7 @@
 
   // Registers a trace processor C++ table with SQLite with an SQL name of
   // |name|.
-  void RegisterStaticTable(const Table& table, const std::string& name);
+  void RegisterStaticTable(const Table&, const std::string& name);
 
   // Registers a trace processor C++ table function with SQLite.
   void RegisterStaticTableFunction(std::unique_ptr<StaticTableFunction> fn);
@@ -164,6 +166,12 @@
            macros_.size();
   }
 
+  // Find RuntimeTable registered with engine with provided name.
+  const RuntimeTable* GetRuntimeTableOrNull(std::string_view) const;
+
+  // Find static table registered with engine with provided name.
+  const Table* GetStaticTableOrNull(std::string_view) const;
+
  private:
   base::StatusOr<SqlSource> ExecuteCreateFunction(
       const PerfettoSqlParser::CreateFunction&,
@@ -221,6 +229,7 @@
 
   base::FlatHashMap<std::string, std::unique_ptr<RuntimeTableFunction::State>>
       runtime_table_fn_states_;
+  base::FlatHashMap<std::string, const Table*> static_tables_;
   base::FlatHashMap<std::string, std::unique_ptr<RuntimeTable>> runtime_tables_;
   base::FlatHashMap<std::string, sql_modules::RegisteredModule> modules_;
   base::FlatHashMap<std::string, PerfettoSqlPreprocessor::Macro> macros_;
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn b/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
index 1375d9e..cbe3513 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
@@ -39,6 +39,8 @@
     "experimental_slice_layout.h",
     "flamegraph_construction_algorithms.cc",
     "flamegraph_construction_algorithms.h",
+    "table_info.cc",
+    "table_info.h",
     "view.cc",
     "view.h",
   ]
@@ -56,6 +58,7 @@
     "../../../tables",
     "../../../types",
     "../../../util",
+    "../../engine",
   ]
   public_deps = [ ":interface" ]
 }
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc b/src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc
new file mode 100644
index 0000000..fe17ac7
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2023 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/perfetto_sql/intrinsics/table_functions/table_info.h"
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/db/runtime_table.h"
+#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/tables_py.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace tables {
+
+PerfettoTableInfoTable::~PerfettoTableInfoTable() = default;
+
+}  // namespace tables
+
+namespace {
+
+using TableInfoTable = tables::PerfettoTableInfoTable;
+
+std::vector<TableInfoTable::Row> GetColInfoRows(const std::vector<Column>& cols,
+                                                StringPool* pool) {
+  std::vector<TableInfoTable::Row> rows;
+  for (const Column& col : cols) {
+    if (col.IsHidden()) {
+      continue;
+    }
+    TableInfoTable::Row row;
+    row.name = pool->InternString(col.name());
+    switch (col.col_type()) {
+      case ColumnType::kString:
+        row.col_type = pool->InternString("string");
+        break;
+      case ColumnType::kInt64:
+        row.col_type = pool->InternString("int64");
+        break;
+      case ColumnType::kInt32:
+        row.col_type = pool->InternString("int32");
+        break;
+      case ColumnType::kUint32:
+        row.col_type = pool->InternString("uint32");
+        break;
+      case ColumnType::kDouble:
+        row.col_type = pool->InternString("double");
+        break;
+      case ColumnType::kId:
+        row.col_type = pool->InternString("id");
+        break;
+      case ColumnType::kDummy:
+        row.col_type = pool->InternString("dummy");
+        break;
+    }
+    if (col.IsSetId()) {
+      row.col_type = pool->InternString("set id");
+    }
+    row.nullable = col.IsNullable();
+    row.sorted = col.IsSorted();
+    rows.push_back(row);
+  }
+  return rows;
+}
+
+}  // namespace
+
+TableInfo::TableInfo(StringPool* string_pool, const PerfettoSqlEngine* engine)
+    : string_pool_(string_pool), engine_(engine) {}
+
+base::Status TableInfo::ValidateConstraints(const QueryConstraints& qc) {
+  const auto& cs = qc.constraints();
+
+  int column = static_cast<int>(TableInfoTable::ColumnIndex::table_name);
+  auto id_fn = [column](const QueryConstraints::Constraint& c) {
+    return c.column == column && sqlite_utils::IsOpEq(c.op);
+  };
+  bool has_id_cs = std::find_if(cs.begin(), cs.end(), id_fn) != cs.end();
+  return has_id_cs ? base::OkStatus()
+                   : base::ErrStatus("Failed to find required constraints");
+}
+
+base::Status TableInfo::ComputeTable(const std::vector<Constraint>& cs,
+                                     const std::vector<Order>&,
+                                     const BitVector&,
+                                     std::unique_ptr<Table>& table_return) {
+  uint32_t column = TableInfoTable::ColumnIndex::table_name;
+  auto constraint_it =
+      std::find_if(cs.begin(), cs.end(), [column](const Constraint& c) {
+        return c.col_idx == column && c.op == FilterOp::kEq;
+      });
+  if (constraint_it == cs.end()) {
+    return base::ErrStatus("Failed to find required constraints");
+  }
+
+  if (constraint_it->value.type != SqlValue::kString) {
+    return base::ErrStatus("perfetto_table_info takes table name as a string.");
+  }
+
+  std::string table_name = constraint_it->value.AsString();
+  auto table = std::make_unique<TableInfoTable>(string_pool_);
+  auto table_name_id = string_pool_->InternString(table_name.c_str());
+
+  // Find static table
+  const Table* static_table = engine_->GetStaticTableOrNull(table_name);
+  if (static_table) {
+    for (auto& row : GetColInfoRows(static_table->columns(), string_pool_)) {
+      row.table_name = table_name_id;
+      table->Insert(row);
+    }
+    table_return = std::move(table);
+    return base::OkStatus();
+  }
+
+  // Find runtime table
+  const RuntimeTable* runtime_table =
+      engine_->GetRuntimeTableOrNull(table_name);
+  if (runtime_table) {
+    for (auto& row : GetColInfoRows(runtime_table->columns(), string_pool_)) {
+      row.table_name = table_name_id;
+      table->Insert(row);
+    }
+    table_return = std::move(table);
+    return base::OkStatus();
+  }
+
+  return base::ErrStatus("Perfetto table '%s' not found.", table_name.c_str());
+}
+
+Table::Schema TableInfo::CreateSchema() {
+  return TableInfoTable::ComputeStaticSchema();
+}
+
+std::string TableInfo::TableName() {
+  return TableInfoTable::Name();
+}
+
+uint32_t TableInfo::EstimateRowCount() {
+  return 1;
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h
new file mode 100644
index 0000000..bec9897
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 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_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_TABLE_INFO_H_
+#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_TABLE_INFO_H_
+
+#include <optional>
+
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class TableInfo : public StaticTableFunction {
+ public:
+  explicit TableInfo(StringPool*, const PerfettoSqlEngine*);
+
+  Table::Schema CreateSchema() override;
+  std::string TableName() override;
+  uint32_t EstimateRowCount() override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>&,
+                            const std::vector<Order>&,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
+
+ private:
+  StringPool* string_pool_ = nullptr;
+  const PerfettoSqlEngine* engine_ = nullptr;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_TABLE_INFO_H_
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py b/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
index 32e4550..b022676 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
@@ -30,6 +30,18 @@
 from src.trace_processor.tables.slice_tables import SLICE_TABLE
 from src.trace_processor.tables.sched_tables import SCHED_SLICE_TABLE
 
+TABLE_INFO_TABLE = Table(
+    python_module=__file__,
+    class_name="PerfettoTableInfoTable",
+    sql_name="perfetto_table_info",
+    columns=[
+        C("table_name", CppString(), flags=ColumnFlag.HIDDEN),
+        C('name', CppString()),
+        C('col_type', CppString()),
+        C('nullable', CppInt64()),
+        C('sorted', CppInt64()),
+    ])
+
 ANCESTOR_SLICE_TABLE = Table(
     python_module=__file__,
     class_name="AncestorSliceTable",
@@ -98,30 +110,6 @@
     ],
     parent=STACK_PROFILE_CALLSITE_TABLE)
 
-EXPERIMENTAL_ANNOTATED_CALLSTACK_TABLE = Table(
-    python_module=__file__,
-    class_name="ExperimentalAnnotatedCallstackTable",
-    sql_name="experimental_annotated_callstack",
-    columns=[
-        C("annotation", CppString()),
-        C("start_id",
-          CppTableId(STACK_PROFILE_CALLSITE_TABLE),
-          flags=ColumnFlag.HIDDEN),
-    ],
-    parent=STACK_PROFILE_CALLSITE_TABLE)
-
-EXPERIMENTAL_ANNOTATED_CALLSTACK_TABLE = Table(
-    python_module=__file__,
-    class_name="ExperimentalAnnotatedCallstackTable",
-    sql_name="experimental_annotated_callstack",
-    columns=[
-        C("annotation", CppString()),
-        C("start_id",
-          CppTableId(STACK_PROFILE_CALLSITE_TABLE),
-          flags=ColumnFlag.HIDDEN),
-    ],
-    parent=STACK_PROFILE_CALLSITE_TABLE)
-
 EXPERIMENTAL_COUNTER_DUR_TABLE = Table(
     python_module=__file__,
     class_name="ExperimentalCounterDurTable",
@@ -163,4 +151,5 @@
     EXPERIMENTAL_COUNTER_DUR_TABLE,
     EXPERIMENTAL_SCHED_UPID_TABLE,
     EXPERIMENTAL_SLICE_LAYOUT_TABLE,
+    TABLE_INFO_TABLE,
 ]
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 514b92f..e777d6e 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -78,6 +78,7 @@
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.h"
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h"
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h"
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/view.h"
 #include "src/trace_processor/perfetto_sql/prelude/tables_views.h"
 #include "src/trace_processor/perfetto_sql/stdlib/stdlib.h"
@@ -862,6 +863,8 @@
   engine_->RegisterStaticTableFunction(std::unique_ptr<ExperimentalSliceLayout>(
       new ExperimentalSliceLayout(context_.storage.get()->mutable_string_pool(),
                                   &storage->slice_table())));
+  engine_->RegisterStaticTableFunction(std::unique_ptr<TableInfo>(new TableInfo(
+      context_.storage.get()->mutable_string_pool(), engine_.get())));
   engine_->RegisterStaticTableFunction(std::unique_ptr<Ancestor>(
       new Ancestor(Ancestor::Type::kSlice, context_.storage.get())));
   engine_->RegisterStaticTableFunction(std::unique_ptr<Ancestor>(new Ancestor(
diff --git a/test/trace_processor/diff_tests/syntax/table_tests.py b/test/trace_processor/diff_tests/syntax/table_tests.py
index f675760..1833f59 100644
--- a/test/trace_processor/diff_tests/syntax/table_tests.py
+++ b/test/trace_processor/diff_tests/syntax/table_tests.py
@@ -63,3 +63,37 @@
         1,"big"
         2,"bigger"
         """))
+
+  def test_perfetto_table_info_static_table(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_boot.pftrace'),
+        query="""
+        SELECT * FROM perfetto_table_info('counter');
+        """,
+        out=Csv("""
+        "id","type","name","col_type","nullable","sorted"
+        0,"perfetto_table_info","id","id",0,1
+        1,"perfetto_table_info","type","string",0,0
+        2,"perfetto_table_info","ts","int64",0,1
+        3,"perfetto_table_info","track_id","uint32",0,0
+        4,"perfetto_table_info","value","double",0,0
+        5,"perfetto_table_info","arg_set_id","uint32",1,0
+        """))
+
+  def test_perfetto_table_info_runtime_table(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_boot.pftrace'),
+        query="""
+        CREATE PERFETTO TABLE foo AS
+        SELECT 2 AS col
+        UNION
+        SELECT 1 AS col
+        UNION
+        SELECT 0 AS col;
+
+        SELECT * FROM perfetto_table_info('foo');
+        """,
+        out=Csv("""
+        "id","type","name","col_type","nullable","sorted"
+        0,"perfetto_table_info","col","int64",1,0
+        """))