Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #ifndef SRC_TRACE_PROCESSOR_TABLE_H_ |
| 18 | #define SRC_TRACE_PROCESSOR_TABLE_H_ |
| 19 | |
| 20 | #include <sqlite3.h> |
Primiano Tucci | 0e9bca4 | 2018-08-31 19:20:58 +0200 | [diff] [blame] | 21 | |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 22 | #include <functional> |
| 23 | #include <memory> |
| 24 | #include <string> |
| 25 | #include <vector> |
| 26 | |
Lalit Maganti | f45f8ad | 2018-12-06 12:58:15 +0000 | [diff] [blame] | 27 | #include "perfetto/base/optional.h" |
Lalit Maganti | 7ee6abb | 2019-05-09 17:57:32 +0100 | [diff] [blame^] | 28 | #include "perfetto/trace_processor/basic_types.h" |
Primiano Tucci | 0e9bca4 | 2018-08-31 19:20:58 +0200 | [diff] [blame] | 29 | #include "src/trace_processor/query_constraints.h" |
| 30 | |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 31 | namespace perfetto { |
| 32 | namespace trace_processor { |
| 33 | |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 34 | class TraceStorage; |
| 35 | |
| 36 | // Abstract base class representing a SQLite virtual table. Implements the |
| 37 | // common bookeeping required across all tables and allows subclasses to |
| 38 | // implement a friendlier API than that required by SQLite. |
| 39 | class Table : public sqlite3_vtab { |
| 40 | public: |
Primiano Tucci | b2ea4d4 | 2018-08-21 15:05:13 +0200 | [diff] [blame] | 41 | using Factory = |
Lalit Maganti | 6003ae5 | 2018-09-18 14:36:02 +0100 | [diff] [blame] | 42 | std::function<std::unique_ptr<Table>(sqlite3*, const TraceStorage*)>; |
Primiano Tucci | b2ea4d4 | 2018-08-21 15:05:13 +0200 | [diff] [blame] | 43 | |
Lalit Maganti | a270b65 | 2018-09-26 11:54:33 +0100 | [diff] [blame] | 44 | // Allowed types for columns in a table. |
| 45 | enum ColumnType { |
| 46 | kString = 1, |
Lalit Maganti | d632ab4 | 2018-12-12 21:07:57 +0000 | [diff] [blame] | 47 | kUint = 2, |
| 48 | kLong = 3, |
| 49 | kInt = 4, |
| 50 | kDouble = 5, |
| 51 | kUnknown = 6, |
Lalit Maganti | a270b65 | 2018-09-26 11:54:33 +0100 | [diff] [blame] | 52 | }; |
| 53 | |
| 54 | // Describes a column of this table. |
| 55 | class Column { |
| 56 | public: |
Lalit Maganti | acda68b | 2018-10-29 15:23:25 +0000 | [diff] [blame] | 57 | Column(size_t idx, std::string name, ColumnType type, bool hidden = false); |
Lalit Maganti | a270b65 | 2018-09-26 11:54:33 +0100 | [diff] [blame] | 58 | |
| 59 | size_t index() const { return index_; } |
| 60 | const std::string& name() const { return name_; } |
| 61 | ColumnType type() const { return type_; } |
| 62 | bool hidden() const { return hidden_; } |
| 63 | |
| 64 | private: |
| 65 | size_t index_ = 0; |
| 66 | std::string name_; |
| 67 | ColumnType type_ = ColumnType::kString; |
| 68 | bool hidden_ = false; |
| 69 | }; |
| 70 | |
Primiano Tucci | b2ea4d4 | 2018-08-21 15:05:13 +0200 | [diff] [blame] | 71 | // When set it logs all BestIndex and Filter actions on the console. |
| 72 | static bool debug; |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 73 | |
| 74 | // Public for unique_ptr destructor calls. |
| 75 | virtual ~Table(); |
| 76 | |
| 77 | // Abstract base class representing an SQLite Cursor. Presents a friendlier |
| 78 | // API for subclasses to implement. |
| 79 | class Cursor : public sqlite3_vtab_cursor { |
| 80 | public: |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 81 | Cursor(Table* table); |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 82 | virtual ~Cursor(); |
| 83 | |
| 84 | // Methods to be implemented by derived table classes. |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 85 | |
| 86 | // Called to intialise the cursor with the constraints of the query. |
| 87 | virtual int Filter(const QueryConstraints& qc, sqlite3_value**) = 0; |
| 88 | |
| 89 | // Called to forward the cursor to the next row in the table. |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 90 | virtual int Next() = 0; |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 91 | |
| 92 | // Called to check if the cursor has reached eof. Column will be called iff |
| 93 | // this method returns true. |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 94 | virtual int Eof() = 0; |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 95 | |
| 96 | // Used to extract the value from the column at index |N|. |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 97 | virtual int Column(sqlite3_context* context, int N) = 0; |
| 98 | |
Lalit Maganti | f4588fc | 2018-09-24 14:14:49 +0100 | [diff] [blame] | 99 | // Optional methods to implement. |
| 100 | virtual int RowId(sqlite3_int64*); |
Lalit Maganti | f0b4e17 | 2018-09-27 13:43:26 +0100 | [diff] [blame] | 101 | |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 102 | protected: |
| 103 | Cursor(Cursor&) = delete; |
| 104 | Cursor& operator=(const Cursor&) = delete; |
Lalit Maganti | f0b4e17 | 2018-09-27 13:43:26 +0100 | [diff] [blame] | 105 | |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 106 | Cursor(Cursor&&) noexcept = default; |
| 107 | Cursor& operator=(Cursor&&) = default; |
Lalit Maganti | f4588fc | 2018-09-24 14:14:49 +0100 | [diff] [blame] | 108 | |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 109 | private: |
| 110 | friend class Table; |
| 111 | |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 112 | Table* table_ = nullptr; |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 113 | }; |
| 114 | |
Lalit Maganti | a270b65 | 2018-09-26 11:54:33 +0100 | [diff] [blame] | 115 | // The schema of the table. Created by subclasses to allow the table class to |
| 116 | // do filtering and inform SQLite about the CREATE table statement. |
| 117 | class Schema { |
| 118 | public: |
| 119 | Schema(); |
| 120 | Schema(std::vector<Column>, std::vector<size_t> primary_keys); |
| 121 | |
| 122 | // This class is explicitly copiable. |
Lalit Maganti | 1ebebf1 | 2018-10-15 17:24:04 +0100 | [diff] [blame] | 123 | Schema(const Schema&); |
Lalit Maganti | a270b65 | 2018-09-26 11:54:33 +0100 | [diff] [blame] | 124 | Schema& operator=(const Schema& t); |
| 125 | |
Lalit Maganti | f45f8ad | 2018-12-06 12:58:15 +0000 | [diff] [blame] | 126 | std::string ToCreateTableStmt() const; |
Lalit Maganti | a270b65 | 2018-09-26 11:54:33 +0100 | [diff] [blame] | 127 | |
Lalit Maganti | acda68b | 2018-10-29 15:23:25 +0000 | [diff] [blame] | 128 | const std::vector<Column>& columns() const { return columns_; } |
Lalit Maganti | a270b65 | 2018-09-26 11:54:33 +0100 | [diff] [blame] | 129 | const std::vector<size_t> primary_keys() { return primary_keys_; } |
| 130 | |
| 131 | private: |
| 132 | // The names and types of the columns of the table. |
| 133 | std::vector<Column> columns_; |
| 134 | |
| 135 | // The primary keys of the table given by an offset into |columns|. |
| 136 | std::vector<size_t> primary_keys_; |
| 137 | }; |
| 138 | |
Lalit Maganti | acda68b | 2018-10-29 15:23:25 +0000 | [diff] [blame] | 139 | protected: |
| 140 | // Populated by a BestIndex call to allow subclasses to tweak SQLite's |
| 141 | // handling of sets of constraints. |
| 142 | struct BestIndexInfo { |
| 143 | bool order_by_consumed = false; |
| 144 | uint32_t estimated_cost = 0; |
| 145 | std::vector<bool> omit; |
| 146 | }; |
| 147 | |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 148 | struct TableDescriptor { |
| 149 | Table::Factory factory; |
| 150 | const TraceStorage* storage = nullptr; |
| 151 | std::string name; |
| 152 | sqlite3_module module = {}; |
| 153 | }; |
| 154 | |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 155 | Table(); |
| 156 | |
| 157 | // Called by derived classes to register themselves with the SQLite db. |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 158 | // |read_write| specifies whether the table can also be written to. |
| 159 | // |requires_args| should be true if the table requires arguments in order to |
| 160 | // be instantiated. |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 161 | // Note: this function is inlined here because we use the TTable template to |
| 162 | // devirtualise the function calls. |
| 163 | template <typename TTable> |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 164 | static void Register(sqlite3* db, |
| 165 | const TraceStorage* storage, |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 166 | const std::string& table_name, |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 167 | bool read_write = false, |
| 168 | bool requires_args = false) { |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 169 | using TCursor = typename TTable::Cursor; |
| 170 | |
| 171 | std::unique_ptr<TableDescriptor> desc(new TableDescriptor()); |
| 172 | desc->storage = storage; |
| 173 | desc->factory = GetFactory<TTable>(); |
| 174 | desc->name = table_name; |
| 175 | sqlite3_module* module = &desc->module; |
| 176 | memset(module, 0, sizeof(*module)); |
| 177 | |
| 178 | auto create_fn = [](sqlite3* xdb, void* arg, int argc, |
Lalit Maganti | 7ee6abb | 2019-05-09 17:57:32 +0100 | [diff] [blame^] | 179 | const char* const* argv, sqlite3_vtab** tab, |
| 180 | char** pzErr) { |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 181 | const TableDescriptor* xdesc = static_cast<const TableDescriptor*>(arg); |
| 182 | auto table = xdesc->factory(xdb, xdesc->storage); |
| 183 | table->name_ = xdesc->name; |
| 184 | |
Lalit Maganti | 7ee6abb | 2019-05-09 17:57:32 +0100 | [diff] [blame^] | 185 | Schema schema; |
| 186 | util::Status status = table->Init(argc, argv, &schema); |
| 187 | if (!status.ok()) { |
| 188 | *pzErr = sqlite3_mprintf("%s", status.c_message()); |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 189 | return SQLITE_ERROR; |
| 190 | } |
| 191 | |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 192 | auto create_stmt = schema.ToCreateTableStmt(); |
| 193 | PERFETTO_DLOG("Create table statement: %s", create_stmt.c_str()); |
| 194 | |
| 195 | int res = sqlite3_declare_vtab(xdb, create_stmt.c_str()); |
| 196 | if (res != SQLITE_OK) |
| 197 | return res; |
| 198 | |
| 199 | // Freed in xDisconnect(). |
| 200 | table->schema_ = std::move(schema); |
| 201 | *tab = table.release(); |
| 202 | |
| 203 | return SQLITE_OK; |
| 204 | }; |
| 205 | auto destroy_fn = [](sqlite3_vtab* t) { |
| 206 | delete static_cast<TTable*>(t); |
| 207 | return SQLITE_OK; |
| 208 | }; |
| 209 | |
| 210 | module->xCreate = create_fn; |
| 211 | module->xConnect = create_fn; |
| 212 | module->xDisconnect = destroy_fn; |
| 213 | module->xDestroy = destroy_fn; |
| 214 | module->xOpen = [](sqlite3_vtab* t, sqlite3_vtab_cursor** c) { |
| 215 | return static_cast<TTable*>(t)->OpenInternal(c); |
| 216 | }; |
| 217 | module->xClose = [](sqlite3_vtab_cursor* c) { |
| 218 | delete static_cast<TCursor*>(c); |
| 219 | return SQLITE_OK; |
| 220 | }; |
| 221 | module->xBestIndex = [](sqlite3_vtab* t, sqlite3_index_info* i) { |
| 222 | return static_cast<TTable*>(t)->BestIndexInternal(i); |
| 223 | }; |
| 224 | module->xFilter = [](sqlite3_vtab_cursor* c, int i, const char* s, int a, |
| 225 | sqlite3_value** v) { |
| 226 | const auto& qc = |
| 227 | static_cast<Cursor*>(c)->table_->ParseConstraints(i, s, a); |
| 228 | return static_cast<TCursor*>(c)->Filter(qc, v); |
| 229 | }; |
| 230 | module->xNext = [](sqlite3_vtab_cursor* c) { |
| 231 | return static_cast<TCursor*>(c)->Next(); |
| 232 | }; |
| 233 | module->xEof = [](sqlite3_vtab_cursor* c) { |
| 234 | return static_cast<TCursor*>(c)->Eof(); |
| 235 | }; |
| 236 | module->xColumn = [](sqlite3_vtab_cursor* c, sqlite3_context* a, int b) { |
| 237 | return static_cast<TCursor*>(c)->Column(a, b); |
| 238 | }; |
| 239 | module->xRowid = [](sqlite3_vtab_cursor* c, sqlite3_int64* r) { |
| 240 | return static_cast<TCursor*>(c)->RowId(r); |
| 241 | }; |
| 242 | module->xFindFunction = |
| 243 | [](sqlite3_vtab* t, int, const char* name, |
| 244 | void (**fn)(sqlite3_context*, int, sqlite3_value**), void** args) { |
| 245 | return static_cast<TTable*>(t)->FindFunction(name, fn, args); |
| 246 | }; |
| 247 | |
| 248 | if (read_write) { |
| 249 | module->xUpdate = [](sqlite3_vtab* t, int a, sqlite3_value** v, |
| 250 | sqlite3_int64* r) { |
| 251 | return static_cast<TTable*>(t)->Update(a, v, r); |
| 252 | }; |
| 253 | } |
| 254 | |
| 255 | int res = sqlite3_create_module_v2( |
| 256 | db, table_name.c_str(), module, desc.release(), |
| 257 | [](void* arg) { delete static_cast<TableDescriptor*>(arg); }); |
| 258 | PERFETTO_CHECK(res == SQLITE_OK); |
| 259 | |
| 260 | // Register virtual tables into an internal 'perfetto_tables' table. This is |
| 261 | // used for iterating through all the tables during a database export. Note |
| 262 | // that virtual tables requiring arguments aren't registered because they |
| 263 | // can't be automatically instantiated for exporting. |
| 264 | if (!requires_args) { |
| 265 | char* insert_sql = sqlite3_mprintf( |
| 266 | "INSERT INTO perfetto_tables(name) VALUES('%q')", table_name.c_str()); |
| 267 | char* error = nullptr; |
| 268 | sqlite3_exec(db, insert_sql, 0, 0, &error); |
| 269 | sqlite3_free(insert_sql); |
| 270 | if (error) { |
| 271 | PERFETTO_ELOG("Error registering table: %s", error); |
| 272 | sqlite3_free(error); |
| 273 | } |
| 274 | } |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 275 | } |
| 276 | |
| 277 | // Methods to be implemented by derived table classes. |
Lalit Maganti | 7ee6abb | 2019-05-09 17:57:32 +0100 | [diff] [blame^] | 278 | virtual util::Status Init(int argc, const char* const* argv, Schema*) = 0; |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 279 | virtual std::unique_ptr<Cursor> CreateCursor() = 0; |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 280 | virtual int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) = 0; |
| 281 | |
| 282 | // Optional metods to implement. |
| 283 | using FindFunctionFn = void (**)(sqlite3_context*, int, sqlite3_value**); |
| 284 | virtual int FindFunction(const char* name, FindFunctionFn fn, void** args); |
| 285 | |
Lalit Maganti | f4588fc | 2018-09-24 14:14:49 +0100 | [diff] [blame] | 286 | // At registration time, the function should also pass true for |read_write|. |
| 287 | virtual int Update(int, sqlite3_value**, sqlite3_int64*); |
| 288 | |
Lalit Maganti | 43d8f14 | 2018-11-12 14:58:15 +0000 | [diff] [blame] | 289 | void SetErrorMessage(char* error) { |
| 290 | sqlite3_free(zErrMsg); |
| 291 | zErrMsg = error; |
| 292 | } |
| 293 | |
Lalit Maganti | 127479a | 2019-03-13 18:52:11 +0000 | [diff] [blame] | 294 | const Schema& schema() const { return schema_; } |
| 295 | const std::string& name() const { return name_; } |
Lalit Maganti | acda68b | 2018-10-29 15:23:25 +0000 | [diff] [blame] | 296 | |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 297 | private: |
| 298 | template <typename TableType> |
| 299 | static Factory GetFactory() { |
Lalit Maganti | 6003ae5 | 2018-09-18 14:36:02 +0100 | [diff] [blame] | 300 | return [](sqlite3* db, const TraceStorage* storage) { |
| 301 | return std::unique_ptr<Table>(new TableType(db, storage)); |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 302 | }; |
| 303 | } |
| 304 | |
| 305 | static void RegisterInternal(sqlite3* db, |
| 306 | const TraceStorage*, |
Lalit Maganti | 6003ae5 | 2018-09-18 14:36:02 +0100 | [diff] [blame] | 307 | const std::string& name, |
Lalit Maganti | f4588fc | 2018-09-24 14:14:49 +0100 | [diff] [blame] | 308 | bool read_write, |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 309 | bool requires_args, |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 310 | Factory); |
| 311 | |
Lalit Maganti | f1a8d52 | 2019-04-26 11:05:15 +0100 | [diff] [blame] | 312 | const QueryConstraints& ParseConstraints(int idxNum, |
| 313 | const char* idxStr, |
| 314 | int argc); |
| 315 | |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 316 | // Overriden functions from sqlite3_vtab. |
| 317 | int OpenInternal(sqlite3_vtab_cursor**); |
| 318 | int BestIndexInternal(sqlite3_index_info*); |
| 319 | |
| 320 | Table(const Table&) = delete; |
| 321 | Table& operator=(const Table&) = delete; |
Primiano Tucci | b2ea4d4 | 2018-08-21 15:05:13 +0200 | [diff] [blame] | 322 | |
| 323 | std::string name_; |
Lalit Maganti | a270b65 | 2018-09-26 11:54:33 +0100 | [diff] [blame] | 324 | Schema schema_; |
| 325 | |
Primiano Tucci | 0e9bca4 | 2018-08-31 19:20:58 +0200 | [diff] [blame] | 326 | QueryConstraints qc_cache_; |
| 327 | int qc_hash_ = 0; |
| 328 | int best_index_num_ = 0; |
Primiano Tucci | 3bf99f3 | 2018-07-24 10:28:28 +0100 | [diff] [blame] | 329 | }; |
| 330 | |
| 331 | } // namespace trace_processor |
| 332 | } // namespace perfetto |
| 333 | |
| 334 | #endif // SRC_TRACE_PROCESSOR_TABLE_H_ |