• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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_SQLITE_SQLITE_ENGINE_H_
18 #define SRC_TRACE_PROCESSOR_SQLITE_SQLITE_ENGINE_H_
19 
20 #include <sqlite3.h>
21 #include <stdint.h>
22 #include <functional>
23 #include <memory>
24 #include <type_traits>
25 
26 #include "perfetto/base/status.h"
27 #include "perfetto/ext/base/flat_hash_map.h"
28 #include "perfetto/ext/base/hash.h"
29 #include "src/trace_processor/db/table.h"
30 #include "src/trace_processor/prelude/functions/sql_function.h"
31 #include "src/trace_processor/prelude/table_functions/table_function.h"
32 #include "src/trace_processor/sqlite/query_cache.h"
33 #include "src/trace_processor/sqlite/scoped_db.h"
34 #include "src/trace_processor/sqlite/sqlite_table.h"
35 #include "src/trace_processor/sqlite/sqlite_utils.h"
36 
37 namespace perfetto {
38 namespace trace_processor {
39 
40 // Wrapper class around SQLite C API.
41 //
42 // The goal of this class is to provide a one-stop-shop mechanism to use SQLite.
43 // Benefits of this include:
44 // 1) It allows us to add code which intercepts registration of functions
45 //    and tables and keeps track of this for later lookup.
46 // 2) Allows easily auditing the SQLite APIs we use making it easy to determine
47 //    what functionality we rely on.
48 class SqliteEngine {
49  public:
50   SqliteEngine();
51   ~SqliteEngine();
52 
53   // Registers a trace processor C++ table with SQLite with an SQL name of
54   // |name|.
55   void RegisterTable(const Table& table, const std::string& name);
56 
57   // Registers a trace processor C++ function to be runnable from SQL.
58   //
59   // The format of the function is given by the |SqlFunction|.
60   //
61   // |db|:          sqlite3 database object
62   // |name|:        name of the function in SQL
63   // |argc|:        number of arguments for this function. This can be -1 if
64   //                the number of arguments is variable.
65   // |ctx|:         context object for the function (see SqlFunction::Run
66   // above);
67   //                this object *must* outlive the function so should likely be
68   //                either static or scoped to the lifetime of TraceProcessor.
69   // |determistic|: whether this function has deterministic output given the
70   //                same set of arguments.
71   template <typename Function = SqlFunction>
72   base::Status RegisterSqlFunction(const char* name,
73                                    int argc,
74                                    typename Function::Context* ctx,
75                                    bool deterministic = true);
76 
77   // Registers a trace processor C++ function to be runnable from SQL.
78   //
79   // This function is the same as the above except allows a unique_ptr to be
80   // passed for the context; this allows for SQLite to manage the lifetime of
81   // this pointer instead of the essentially static requirement of the context
82   // pointer above.
83   template <typename Function>
84   base::Status RegisterSqlFunction(
85       const char* name,
86       int argc,
87       std::unique_ptr<typename Function::Context> ctx,
88       bool deterministic = true);
89 
90   // Registers a trace processor C++ table function with SQLite.
91   void RegisterTableFunction(std::unique_ptr<TableFunction> fn);
92 
93   // Registers a SQLite virtual table module with the given name.
94   //
95   // This API only exists for internal/legacy use: most callers should use
96   // one of the RegisterTable* APIs above.
97   template <typename Vtab, typename Context>
98   void RegisterVirtualTableModule(const std::string& module_name,
99                                   Context ctx,
100                                   SqliteTable::TableType table_type,
101                                   bool updatable);
102 
103   // Declares a virtual table with SQLite.
104   //
105   // This API only exists for internal use. Most callers should never call this
106   // directly: instead use one of the RegisterTable* APIs above.
107   base::Status DeclareVirtualTable(const std::string& create_stmt);
108 
109   // Saves a SQLite table across a pair of xDisconnect/xConnect callbacks.
110   //
111   // This API only exists for internal use. Most callers should never call this
112   // directly.
113   base::Status SaveSqliteTable(const std::string& table_name,
114                                std::unique_ptr<SqliteTable>);
115 
116   // Restores a SQLite table across a pair of xDisconnect/xConnect callbacks.
117   //
118   // This API only exists for internal use. Most callers should never call this
119   // directly.
120   base::StatusOr<std::unique_ptr<SqliteTable>> RestoreSqliteTable(
121       const std::string& table_name);
122 
123   // Gets the context for a registered SQL function.
124   //
125   // This API only exists for internal use. Most callers should never call this
126   // directly.
127   void* GetFunctionContext(const std::string& name, int argc);
128 
db()129   sqlite3* db() const { return db_.get(); }
130 
131  private:
132   struct FnHasher {
operatorFnHasher133     size_t operator()(const std::pair<std::string, int>& x) const {
134       base::Hasher hasher;
135       hasher.Update(x.first);
136       hasher.Update(x.second);
137       return static_cast<size_t>(hasher.digest());
138     }
139   };
140 
141   std::unique_ptr<QueryCache> query_cache_;
142   base::FlatHashMap<std::string, std::unique_ptr<SqliteTable>> saved_tables_;
143   base::FlatHashMap<std::pair<std::string, int>, void*, FnHasher> fn_ctx_;
144 
145   ScopedDb db_;
146 };
147 
148 }  // namespace trace_processor
149 }  // namespace perfetto
150 
151 // The rest of this file is just implementation details which we need
152 // in the header file because it is templated code. We separate it out
153 // like this to keep the API people actually care about easy to read.
154 
155 namespace perfetto {
156 namespace trace_processor {
157 namespace sqlite_internal {
158 
159 // RAII type to call Function::Cleanup when destroyed.
160 template <typename Function>
161 struct ScopedCleanup {
162   typename Function::Context* ctx;
~ScopedCleanupScopedCleanup163   ~ScopedCleanup() { Function::Cleanup(ctx); }
164 };
165 
166 template <typename Function>
WrapSqlFunction(sqlite3_context * ctx,int argc,sqlite3_value ** argv)167 void WrapSqlFunction(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
168   using Context = typename Function::Context;
169   Context* ud = static_cast<Context*>(sqlite3_user_data(ctx));
170 
171   ScopedCleanup<Function> scoped_cleanup{ud};
172   SqlValue value{};
173   SqlFunction::Destructors destructors{};
174   base::Status status =
175       Function::Run(ud, static_cast<size_t>(argc), argv, value, destructors);
176   if (!status.ok()) {
177     sqlite3_result_error(ctx, status.c_message(), -1);
178     return;
179   }
180 
181   if (Function::kVoidReturn) {
182     if (!value.is_null()) {
183       sqlite3_result_error(ctx, "void SQL function returned value", -1);
184       return;
185     }
186 
187     // If the function doesn't want to return anything, set the "VOID"
188     // pointer type to a non-null value. Note that because of the weird
189     // way |sqlite3_value_pointer| works, we need to set some value even
190     // if we don't actually read it - just set it to a pointer to an empty
191     // string for this reason.
192     static char kVoidValue[] = "";
193     sqlite3_result_pointer(ctx, kVoidValue, "VOID", nullptr);
194   } else {
195     sqlite_utils::ReportSqlValue(ctx, value, destructors.string_destructor,
196                                  destructors.bytes_destructor);
197   }
198 
199   status = Function::VerifyPostConditions(ud);
200   if (!status.ok()) {
201     sqlite3_result_error(ctx, status.c_message(), -1);
202     return;
203   }
204 }
205 
206 }  // namespace sqlite_internal
207 
208 template <typename Function>
RegisterSqlFunction(const char * name,int argc,typename Function::Context * ctx,bool deterministic)209 base::Status SqliteEngine::RegisterSqlFunction(const char* name,
210                                                int argc,
211                                                typename Function::Context* ctx,
212                                                bool deterministic) {
213   int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
214   int ret = sqlite3_create_function_v2(
215       db_.get(), name, static_cast<int>(argc), flags, ctx,
216       sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr, nullptr);
217   if (ret != SQLITE_OK) {
218     return base::ErrStatus("Unable to register function with name %s", name);
219   }
220   *fn_ctx_.Insert(std::make_pair(name, argc), ctx).first = ctx;
221   return base::OkStatus();
222 }
223 
224 template <typename Function>
RegisterSqlFunction(const char * name,int argc,std::unique_ptr<typename Function::Context> user_data,bool deterministic)225 base::Status SqliteEngine::RegisterSqlFunction(
226     const char* name,
227     int argc,
228     std::unique_ptr<typename Function::Context> user_data,
229     bool deterministic) {
230   int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
231   void* fn_ctx = user_data.get();
232   int ret = sqlite3_create_function_v2(
233       db_.get(), name, static_cast<int>(argc), flags, user_data.release(),
234       sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr,
235       [](void* ptr) { delete static_cast<typename Function::Context*>(ptr); });
236   if (ret != SQLITE_OK) {
237     return base::ErrStatus("Unable to register function with name %s", name);
238   }
239   *fn_ctx_.Insert(std::make_pair(name, argc), fn_ctx).first = fn_ctx;
240   return base::OkStatus();
241 }
242 
243 template <typename Vtab, typename Context>
RegisterVirtualTableModule(const std::string & module_name,Context ctx,SqliteTable::TableType table_type,bool updatable)244 void SqliteEngine::RegisterVirtualTableModule(const std::string& module_name,
245                                               Context ctx,
246                                               SqliteTable::TableType table_type,
247                                               bool updatable) {
248   static_assert(std::is_base_of_v<SqliteTable, Vtab>,
249                 "Must subclass TypedSqliteTable");
250 
251   auto module_arg =
252       Vtab::CreateModuleArg(this, std::move(ctx), table_type, updatable);
253   sqlite3_module* module = &module_arg->module;
254   int res = sqlite3_create_module_v2(
255       db_.get(), module_name.c_str(), module, module_arg.release(),
256       [](void* arg) { delete static_cast<typename Vtab::ModuleArg*>(arg); });
257   PERFETTO_CHECK(res == SQLITE_OK);
258 }
259 
260 }  // namespace trace_processor
261 }  // namespace perfetto
262 
263 #endif  // SRC_TRACE_PROCESSOR_SQLITE_SQLITE_ENGINE_H_
264