tp: add support for dynamic tables and migrate flamegraph/counter dur
This CL introduces proper support for dynamic tables in SqliteDbTable
instead of the hacky thing we were doing before this point which was
subclassing and using parts of SqliteDbTable in arbitrary ways (e.g.
passing nullptr as the table and then overriding all methods, creating
a cursor but not using any of the rest of the code etc).
This prepares the way for describe_event in go/perfetto-analysis-design
which will be implemented as a dynamic table similar to the flamegraph
and counter-with-dur ones.
Change-Id: I8cff6554a2f54afa92dbf64c3eac7e6f70fbbdba
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index a98e85d..d4094a9 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table.cc
@@ -86,7 +86,9 @@
DbSqliteTable::DbSqliteTable(sqlite3*, Context context)
: cache_(context.cache),
schema_(std::move(context.schema)),
- table_(context.table) {}
+ computation_(context.computation),
+ static_table_(context.static_table),
+ generator_(std::move(context.generator)) {}
DbSqliteTable::~DbSqliteTable() = default;
void DbSqliteTable::RegisterTable(sqlite3* db,
@@ -94,8 +96,19 @@
Table::Schema schema,
const Table* table,
const std::string& name) {
- SqliteTable::Register<DbSqliteTable, Context>(
- db, Context{cache, schema, table}, name);
+ Context context{cache, schema, TableComputation::kStatic, table, nullptr};
+ SqliteTable::Register<DbSqliteTable, Context>(db, std::move(context), name);
+}
+
+void DbSqliteTable::RegisterTable(
+ sqlite3* db,
+ QueryCache* cache,
+ std::unique_ptr<DynamicTableGenerator> generator) {
+ Table::Schema schema = generator->CreateSchema();
+ std::string name = generator->TableName();
+ Context context{cache, std::move(schema), TableComputation::kDynamic, nullptr,
+ std::move(generator)};
+ SqliteTable::Register<DbSqliteTable, Context>(db, std::move(context), name);
}
util::Status DbSqliteTable::Init(int, const char* const*, Schema* schema) {
@@ -108,7 +121,7 @@
std::vector<SqliteTable::Column> schema_cols;
for (uint32_t i = 0; i < schema.columns.size(); ++i) {
const auto& col = schema.columns[i];
- schema_cols.emplace_back(i, col.name, col.type);
+ schema_cols.emplace_back(i, col.name, col.type, col.is_hidden);
}
// TODO(lalitm): this is hardcoded to be the id column but change this to be
@@ -129,7 +142,17 @@
}
int DbSqliteTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
- BestIndex(schema_, table_->row_count(), qc, info);
+ switch (computation_) {
+ case TableComputation::kStatic:
+ BestIndex(schema_, static_table_->row_count(), qc, info);
+ break;
+ case TableComputation::kDynamic:
+ util::Status status = generator_->ValidateConstraints(qc);
+ if (!status.ok())
+ return SQLITE_CONSTRAINT;
+ BestIndex(schema_, generator_->EstimateRowCount(), qc, info);
+ break;
+ }
return SQLITE_OK;
}
@@ -291,15 +314,13 @@
}
std::unique_ptr<SqliteTable::Cursor> DbSqliteTable::CreateCursor() {
- return std::unique_ptr<Cursor>(new Cursor(this, cache_, table_));
+ return std::unique_ptr<Cursor>(new Cursor(this, cache_));
}
-DbSqliteTable::Cursor::Cursor(SqliteTable* sqlite_table,
- QueryCache* cache,
- const Table* table)
+DbSqliteTable::Cursor::Cursor(DbSqliteTable* sqlite_table, QueryCache* cache)
: SqliteTable::Cursor(sqlite_table),
- cache_(cache),
- initial_db_table_(table) {}
+ db_sqlite_table_(sqlite_table),
+ cache_(cache) {}
void DbSqliteTable::Cursor::TryCacheCreateSortedTable(
const QueryConstraints& qc,
@@ -314,7 +335,7 @@
// Check if the new constraint set is cached by another cursor.
sorted_cache_table_ =
- cache_->GetIfCached(initial_db_table_, qc.constraints());
+ cache_->GetIfCached(upstream_table_, qc.constraints());
return;
}
@@ -343,13 +364,13 @@
// If the column is already sorted, we don't need to cache at all.
uint32_t col = static_cast<uint32_t>(c.column);
- if (initial_db_table_->GetColumn(col).IsSorted())
+ if (upstream_table_->GetColumn(col).IsSorted())
return;
// Try again to get the result or start caching it.
sorted_cache_table_ =
- cache_->GetOrCache(initial_db_table_, qc.constraints(), [this, col]() {
- return initial_db_table_->Sort({Order{col, false}});
+ cache_->GetOrCache(upstream_table_, qc.constraints(), [this, col]() {
+ return upstream_table_->Sort({Order{col, false}});
});
}
@@ -386,6 +407,23 @@
orders_[i] = Order{col, static_cast<bool>(ob.desc)};
}
+ // Setup the upstream table based on the computation state.
+ switch (db_sqlite_table_->computation_) {
+ case TableComputation::kStatic:
+ // If we have a static table, just set the upstream table to be the static
+ // table.
+ upstream_table_ = db_sqlite_table_->static_table_;
+ break;
+ case TableComputation::kDynamic:
+ // If we have a dynamically created table, regenerate the table based on
+ // the new constraints.
+ upstream_table_ =
+ db_sqlite_table_->generator_->ComputeTable(constraints_, orders_);
+ if (!upstream_table_)
+ return SQLITE_CONSTRAINT;
+ break;
+ }
+
// Tries to create a sorted cached table which can be used to speed up
// filters below.
TryCacheCreateSortedTable(qc, history);
@@ -482,5 +520,7 @@
return SQLITE_OK;
}
+DbSqliteTable::DynamicTableGenerator::~DynamicTableGenerator() = default;
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h
index a020008..cfb5003 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.h
+++ b/src/trace_processor/sqlite/db_sqlite_table.h
@@ -27,9 +27,54 @@
// Implements the SQLite table interface for db tables.
class DbSqliteTable : public SqliteTable {
public:
+ enum class TableComputation {
+ // Mode when the table is static (i.e. passed in at construction
+ // time).
+ kStatic,
+
+ // Mode when table is dynamically computed at filter time.
+ kDynamic,
+ };
+
+ // Interface which can be subclassed to allow generation of tables dynamically
+ // at filter time.
+ // This class is used to implement table-valued functions and other similar
+ // tables.
+ class DynamicTableGenerator {
+ public:
+ virtual ~DynamicTableGenerator();
+
+ // Returns the schema of the table that will be returned by ComputeTable.
+ virtual Table::Schema CreateSchema() = 0;
+
+ // Returns the name of the dynamic table.
+ // This will be used to register the table with SQLite.
+ virtual std::string TableName() = 0;
+
+ // Returns the estimated number of rows the table would generate.
+ virtual uint32_t EstimateRowCount() = 0;
+
+ // Checks that the constraint set is valid.
+ //
+ // Returning util::OkStatus means that the required constraints are present
+ // in |qc| for dynamically computing the table (e.g. any required
+ // constraints on hidden columns for table-valued functions are present).
+ virtual util::Status ValidateConstraints(const QueryConstraints& qc) = 0;
+
+ // Dynamically computes the table given the constraints and order by
+ // vectors.
+ //
+ // Implementations should store the generated table inside a stable pointer
+ // (e.g. unique_ptr, shared_ptr or similar) and return the pointer to that
+ // object. The pointer should not be freed until a successive call to
+ // |ComputeTable|.
+ virtual Table* ComputeTable(const std::vector<Constraint>& cs,
+ const std::vector<Order>& ob) = 0;
+ };
+
class Cursor : public SqliteTable::Cursor {
public:
- Cursor(SqliteTable*, QueryCache*, const Table*);
+ Cursor(DbSqliteTable*, QueryCache*);
Cursor(Cursor&&) noexcept = default;
Cursor& operator=(Cursor&&) = default;
@@ -42,12 +87,6 @@
int Eof() override;
int Column(sqlite3_context*, int N) override;
- protected:
- // Sets the table this class uses as the reference for all filter
- // operations. Should be immediately followed by a call to Filter with
- // |FilterHistory::kDifferent|.
- void set_table(const Table* table) { initial_db_table_ = table; }
-
private:
enum class Mode {
kSingleRow,
@@ -61,14 +100,16 @@
const Table* SourceTable() const {
// Try and use the sorted cache table (if it exists) to speed up the
// sorting. Otherwise, just use the original table.
- return sorted_cache_table_ ? &*sorted_cache_table_ : initial_db_table_;
+ return sorted_cache_table_ ? &*sorted_cache_table_ : upstream_table_;
}
Cursor(const Cursor&) = delete;
Cursor& operator=(const Cursor&) = delete;
+ DbSqliteTable* db_sqlite_table_ = nullptr;
QueryCache* cache_ = nullptr;
- const Table* initial_db_table_ = nullptr;
+
+ const Table* upstream_table_ = nullptr;
// Only valid for Mode::kSingleRow.
base::Optional<uint32_t> single_row_;
@@ -100,7 +141,13 @@
struct Context {
QueryCache* cache;
Table::Schema schema;
- const Table* table;
+ TableComputation computation;
+
+ // Only valid when computation == TableComputation::kStatic.
+ const Table* static_table;
+
+ // Only valid when computation == TableComputation::kDynamic.
+ std::unique_ptr<DynamicTableGenerator> generator;
};
static void RegisterTable(sqlite3* db,
@@ -109,6 +156,10 @@
const Table* table,
const std::string& name);
+ static void RegisterTable(sqlite3* db,
+ QueryCache* cache,
+ std::unique_ptr<DynamicTableGenerator> generator);
+
DbSqliteTable(sqlite3*, Context context);
virtual ~DbSqliteTable() override;
@@ -138,7 +189,14 @@
private:
QueryCache* cache_ = nullptr;
Table::Schema schema_;
- const Table* table_ = nullptr;
+
+ TableComputation computation_ = TableComputation::kStatic;
+
+ // Only valid when computation_ == TableComputation::kStatic.
+ const Table* static_table_ = nullptr;
+
+ // Only valid when computation_ == TableComputation::kDynamic.
+ std::unique_ptr<DynamicTableGenerator> generator_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/sqlite/db_sqlite_table_unittest.cc b/src/trace_processor/sqlite/db_sqlite_table_unittest.cc
index ace779f..78b6961 100644
--- a/src/trace_processor/sqlite/db_sqlite_table_unittest.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table_unittest.cc
@@ -1,3 +1,4 @@
+
/*
* Copyright (C) 2019 The Android Open Source Project
*
@@ -24,16 +25,16 @@
Table::Schema CreateSchema() {
Table::Schema schema;
- schema.columns.push_back(
- {"id", SqlValue::Type::kLong, true /* is_id */, true /* is_sorted */});
+ schema.columns.push_back({"id", SqlValue::Type::kLong, true /* is_id */,
+ true /* is_sorted */, false /* is_hidden */});
schema.columns.push_back({"type", SqlValue::Type::kLong, false /* is_id */,
- false /* is_sorted */});
+ false /* is_sorted */, false /* is_hidden */});
schema.columns.push_back({"test1", SqlValue::Type::kLong, false /* is_id */,
- true /* is_sorted */});
+ true /* is_sorted */, false /* is_hidden */});
schema.columns.push_back({"test2", SqlValue::Type::kLong, false /* is_id */,
- false /* is_sorted */});
+ false /* is_sorted */, false /* is_hidden */});
schema.columns.push_back({"test3", SqlValue::Type::kLong, false /* is_id */,
- false /* is_sorted */});
+ false /* is_sorted */, false /* is_hidden */});
return schema;
}
diff --git a/src/trace_processor/sqlite/sqlite_table.h b/src/trace_processor/sqlite/sqlite_table.h
index 2cb7a0f..a6a038f 100644
--- a/src/trace_processor/sqlite/sqlite_table.h
+++ b/src/trace_processor/sqlite/sqlite_table.h
@@ -20,6 +20,7 @@
#include <sqlite3.h>
#include <functional>
+#include <limits>
#include <memory>
#include <string>
#include <vector>
@@ -212,7 +213,7 @@
auto create_fn = [](sqlite3* xdb, void* arg, int argc,
const char* const* argv, sqlite3_vtab** tab,
char** pzErr) {
- const auto* xdesc = static_cast<const TableDescriptor<Context>*>(arg);
+ auto* xdesc = static_cast<TableDescriptor<Context>*>(arg);
auto table = xdesc->factory(xdb, std::move(xdesc->context));
table->name_ = xdesc->name;