|  | /* | 
|  | * 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/sqlite/sqlite_engine.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <optional> | 
|  | #include <unordered_set> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "perfetto/base/status.h" | 
|  | #include "perfetto/ext/base/string_utils.h" | 
|  | #include "perfetto/public/compiler.h" | 
|  | #include "src/trace_processor/sqlite/db_sqlite_table.h" | 
|  | #include "src/trace_processor/sqlite/query_cache.h" | 
|  | #include "src/trace_processor/sqlite/scoped_db.h" | 
|  | #include "src/trace_processor/sqlite/sql_source.h" | 
|  | #include "src/trace_processor/sqlite/sqlite_table.h" | 
|  | #include "src/trace_processor/sqlite/sqlite_utils.h" | 
|  |  | 
|  | // In Android and Chromium tree builds, we don't have the percentile module. | 
|  | // Just don't include it. | 
|  | #if PERFETTO_BUILDFLAG(PERFETTO_TP_PERCENTILE) | 
|  | // defined in sqlite_src/ext/misc/percentile.c | 
|  | extern "C" int sqlite3_percentile_init(sqlite3* db, | 
|  | char** error, | 
|  | const sqlite3_api_routines* api); | 
|  | #endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_PERCENTILE) | 
|  |  | 
|  | namespace perfetto { | 
|  | namespace trace_processor { | 
|  | namespace { | 
|  |  | 
|  | void EnsureSqliteInitialized() { | 
|  | // sqlite3_initialize isn't actually thread-safe despite being documented | 
|  | // as such; we need to make sure multiple TraceProcessorImpl instances don't | 
|  | // call it concurrently and only gets called once per process, instead. | 
|  | static bool init_once = [] { return sqlite3_initialize() == SQLITE_OK; }(); | 
|  | PERFETTO_CHECK(init_once); | 
|  | } | 
|  |  | 
|  | void InitializeSqlite(sqlite3* db) { | 
|  | char* error = nullptr; | 
|  | sqlite3_exec(db, "PRAGMA temp_store=2", nullptr, nullptr, &error); | 
|  | if (error) { | 
|  | PERFETTO_FATAL("Error setting pragma temp_store: %s", error); | 
|  | } | 
|  | // In Android tree builds, we don't have the percentile module. | 
|  | // Just don't include it. | 
|  | #if PERFETTO_BUILDFLAG(PERFETTO_TP_PERCENTILE) | 
|  | sqlite3_percentile_init(db, &error, nullptr); | 
|  | if (error) { | 
|  | PERFETTO_ELOG("Error initializing: %s", error); | 
|  | sqlite3_free(error); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | std::optional<uint32_t> GetErrorOffsetDb(sqlite3* db) { | 
|  | int offset = sqlite3_error_offset(db); | 
|  | return offset == -1 ? std::nullopt | 
|  | : std::make_optional(static_cast<uint32_t>(offset)); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | SqliteEngine::SqliteEngine() { | 
|  | sqlite3* db = nullptr; | 
|  | EnsureSqliteInitialized(); | 
|  |  | 
|  | // Ensure that we open the database with mutexes disabled: this is because | 
|  | // trace processor as a whole cannot be used from multiple threads so there is | 
|  | // no point paying the (potentially significant) cost of mutexes at the SQLite | 
|  | // level. | 
|  | static constexpr int kSqliteOpenFlags = | 
|  | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; | 
|  | PERFETTO_CHECK(sqlite3_open_v2(":memory:", &db, kSqliteOpenFlags, nullptr) == | 
|  | SQLITE_OK); | 
|  | InitializeSqlite(db); | 
|  | db_.reset(std::move(db)); | 
|  | } | 
|  |  | 
|  | SqliteEngine::~SqliteEngine() { | 
|  | // IMPORTANT: the order of operations in this destructor is very sensitive and | 
|  | // should not be changed without careful consideration of the consequences. | 
|  | // Thankfully, because we are very aggressive with PERFETTO_CHECK, mistakes | 
|  | // will usually manifest as crashes, but this is not guaranteed. | 
|  |  | 
|  | // Drop any explicitly created virtual tables before destroying the database | 
|  | // so that any prepared statements are correctly finalized. Note that we need | 
|  | // to do this in two steps (first create all the SQLs before then executing | 
|  | // them) because |OnSqliteTableDestroyed| will be called as each DROP is | 
|  | // executed. | 
|  | std::vector<std::string> drop_stmts; | 
|  | std::unordered_set<std::string> dropped_tables; | 
|  | for (auto it = all_created_sqlite_tables_.rbegin(); | 
|  | it != all_created_sqlite_tables_.rend(); it++) { | 
|  | if (auto* type = sqlite_tables_.Find(*it); | 
|  | !type || *type != SqliteTable::TableType::kExplicitCreate) { | 
|  | continue; | 
|  | } | 
|  | if (auto it_and_ins = dropped_tables.insert(*it); !it_and_ins.second) { | 
|  | continue; | 
|  | } | 
|  | base::StackString<1024> drop("DROP TABLE %s", it->c_str()); | 
|  | drop_stmts.emplace_back(drop.ToStdString()); | 
|  | } | 
|  | for (const auto& drop : drop_stmts) { | 
|  | int ret = sqlite3_exec(db(), drop.c_str(), nullptr, nullptr, nullptr); | 
|  | if (PERFETTO_UNLIKELY(ret != SQLITE_OK)) { | 
|  | PERFETTO_FATAL("Failed to execute statement: '%s'", drop.c_str()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // It is important to unregister any functions that have been registered with | 
|  | // the database before destroying it. This is because functions can hold onto | 
|  | // prepared statements, which must be finalized before database destruction. | 
|  | for (auto it = fn_ctx_.GetIterator(); it; ++it) { | 
|  | int ret = sqlite3_create_function_v2(db_.get(), it.key().first.c_str(), | 
|  | it.key().second, SQLITE_UTF8, nullptr, | 
|  | nullptr, nullptr, nullptr, nullptr); | 
|  | if (PERFETTO_UNLIKELY(ret != SQLITE_OK)) { | 
|  | PERFETTO_FATAL("Failed to drop function: '%s'", it.key().first.c_str()); | 
|  | } | 
|  | } | 
|  | fn_ctx_.Clear(); | 
|  |  | 
|  | // Reset the database itself. | 
|  | db_.reset(); | 
|  |  | 
|  | // SQLite is not guaranteed to pick saved tables back up when destroyed as | 
|  | // from it's perspective, it has called xDisconnect. Make sure to do that | 
|  | // ourselves. | 
|  | saved_tables_.Clear(); | 
|  |  | 
|  | // The above operations should have cleared all the tables. | 
|  | if (PERFETTO_UNLIKELY(sqlite_tables_.size() != 0)) { | 
|  | std::vector<std::string> tables; | 
|  | for (auto it = sqlite_tables_.GetIterator(); it; ++it) { | 
|  | tables.push_back(it.key()); | 
|  | } | 
|  | std::string joined = base::Join(tables, ","); | 
|  | PERFETTO_FATAL( | 
|  | "SqliteTable instances still exist: count='%zu', tables='[%s]'", | 
|  | sqlite_tables_.size(), joined.c_str()); | 
|  | } | 
|  | } | 
|  |  | 
|  | SqliteEngine::PreparedStatement SqliteEngine::PrepareStatement(SqlSource sql) { | 
|  | PERFETTO_TP_TRACE(metatrace::Category::QUERY_DETAILED, "QUERY_PREPARE"); | 
|  | sqlite3_stmt* raw_stmt = nullptr; | 
|  | int err = | 
|  | sqlite3_prepare_v2(db_.get(), sql.sql().c_str(), -1, &raw_stmt, nullptr); | 
|  | PreparedStatement statement{ScopedStmt(raw_stmt), std::move(sql)}; | 
|  | if (err != SQLITE_OK) { | 
|  | const char* errmsg = sqlite3_errmsg(db_.get()); | 
|  | std::string frame = | 
|  | statement.sql_source_.AsTracebackForSqliteOffset(GetErrorOffset()); | 
|  | base::Status status = base::ErrStatus("%s%s", frame.c_str(), errmsg); | 
|  | status.SetPayload("perfetto.dev/has_traceback", "true"); | 
|  |  | 
|  | statement.status_ = std::move(status); | 
|  | return statement; | 
|  | } | 
|  | if (!raw_stmt) { | 
|  | statement.status_ = base::ErrStatus("No SQL to execute"); | 
|  | } | 
|  | return statement; | 
|  | } | 
|  |  | 
|  | base::Status SqliteEngine::RegisterFunction(const char* name, | 
|  | int argc, | 
|  | Fn* fn, | 
|  | void* ctx, | 
|  | FnCtxDestructor* destructor, | 
|  | bool deterministic) { | 
|  | int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0); | 
|  | int ret = | 
|  | sqlite3_create_function_v2(db_.get(), name, static_cast<int>(argc), flags, | 
|  | ctx, fn, nullptr, nullptr, destructor); | 
|  | if (ret != SQLITE_OK) { | 
|  | return base::ErrStatus("Unable to register function with name %s", name); | 
|  | } | 
|  | *fn_ctx_.Insert(std::make_pair(name, argc), ctx).first = ctx; | 
|  | return base::OkStatus(); | 
|  | } | 
|  |  | 
|  | base::Status SqliteEngine::UnregisterFunction(const char* name, int argc) { | 
|  | int ret = sqlite3_create_function_v2(db_.get(), name, static_cast<int>(argc), | 
|  | SQLITE_UTF8, nullptr, nullptr, nullptr, | 
|  | nullptr, nullptr); | 
|  | if (ret != SQLITE_OK) { | 
|  | return base::ErrStatus("Unable to unregister function with name %s", name); | 
|  | } | 
|  | fn_ctx_.Erase({name, argc}); | 
|  | return base::OkStatus(); | 
|  | } | 
|  |  | 
|  | base::Status SqliteEngine::DeclareVirtualTable(const std::string& create_stmt) { | 
|  | int res = sqlite3_declare_vtab(db_.get(), create_stmt.c_str()); | 
|  | if (res != SQLITE_OK) { | 
|  | return base::ErrStatus("Declare vtab failed: %s", | 
|  | sqlite3_errmsg(db_.get())); | 
|  | } | 
|  | return base::OkStatus(); | 
|  | } | 
|  |  | 
|  | base::Status SqliteEngine::SaveSqliteTable(const std::string& table_name, | 
|  | std::unique_ptr<SqliteTable> table) { | 
|  | auto res = saved_tables_.Insert(table_name, {}); | 
|  | if (!res.second) { | 
|  | return base::ErrStatus("Table with name %s already is saved", | 
|  | table_name.c_str()); | 
|  | } | 
|  | *res.first = std::move(table); | 
|  | return base::OkStatus(); | 
|  | } | 
|  |  | 
|  | base::StatusOr<std::unique_ptr<SqliteTable>> SqliteEngine::RestoreSqliteTable( | 
|  | const std::string& table_name) { | 
|  | auto* res = saved_tables_.Find(table_name); | 
|  | if (!res) { | 
|  | return base::ErrStatus("Table with name %s does not exist in saved state", | 
|  | table_name.c_str()); | 
|  | } | 
|  | std::unique_ptr<SqliteTable> table = std::move(*res); | 
|  | PERFETTO_CHECK(saved_tables_.Erase(table_name)); | 
|  | return std::move(table); | 
|  | } | 
|  |  | 
|  | void* SqliteEngine::GetFunctionContext(const std::string& name, int argc) { | 
|  | auto* res = fn_ctx_.Find(std::make_pair(name, argc)); | 
|  | return res ? *res : nullptr; | 
|  | } | 
|  |  | 
|  | std::optional<uint32_t> SqliteEngine::GetErrorOffset() const { | 
|  | return GetErrorOffsetDb(db_.get()); | 
|  | } | 
|  |  | 
|  | void SqliteEngine::OnSqliteTableCreated(const std::string& name, | 
|  | SqliteTable::TableType type) { | 
|  | auto it_and_inserted = sqlite_tables_.Insert(name, type); | 
|  | PERFETTO_CHECK(it_and_inserted.second); | 
|  | all_created_sqlite_tables_.push_back(name); | 
|  | } | 
|  |  | 
|  | void SqliteEngine::OnSqliteTableDestroyed(const std::string& name) { | 
|  | PERFETTO_CHECK(sqlite_tables_.Erase(name)); | 
|  | } | 
|  |  | 
|  | SqliteEngine::PreparedStatement::PreparedStatement(ScopedStmt stmt, | 
|  | SqlSource source) | 
|  | : stmt_(std::move(stmt)), | 
|  | expanded_sql_(sqlite3_expanded_sql(stmt_.get())), | 
|  | sql_source_(std::move(source)) {} | 
|  |  | 
|  | bool SqliteEngine::PreparedStatement::Step() { | 
|  | PERFETTO_TP_TRACE(metatrace::Category::QUERY_DETAILED, "STMT_STEP", | 
|  | [this](metatrace::Record* record) { | 
|  | record->AddArg("Original SQL", original_sql()); | 
|  | record->AddArg("Executed SQL", sql()); | 
|  | }); | 
|  |  | 
|  | // Now step once into |cur_stmt| so that when we prepare the next statment | 
|  | // we will have executed any dependent bytecode in this one. | 
|  | int err = sqlite3_step(stmt_.get()); | 
|  | if (err == SQLITE_ROW) { | 
|  | return true; | 
|  | } | 
|  | if (err == SQLITE_DONE) { | 
|  | return false; | 
|  | } | 
|  | sqlite3* db = sqlite3_db_handle(stmt_.get()); | 
|  | std::string frame = | 
|  | sql_source_.AsTracebackForSqliteOffset(GetErrorOffsetDb(db)); | 
|  | const char* errmsg = sqlite3_errmsg(db); | 
|  | status_ = base::ErrStatus("%s%s", frame.c_str(), errmsg); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool SqliteEngine::PreparedStatement::IsDone() const { | 
|  | return !sqlite3_stmt_busy(stmt_.get()); | 
|  | } | 
|  |  | 
|  | const char* SqliteEngine::PreparedStatement::original_sql() const { | 
|  | return sql_source_.original_sql().c_str(); | 
|  | } | 
|  |  | 
|  | const char* SqliteEngine::PreparedStatement::sql() const { | 
|  | return expanded_sql_.get(); | 
|  | } | 
|  |  | 
|  | }  // namespace trace_processor | 
|  | }  // namespace perfetto |