tp: refactor function registration to go through SqliteEngine
This CL changes all function registration to pass through SqliteEngine
rather than using free functions. This allows us to track what
functions are being registered.
Bug: 261195778
Change-Id: I2964cffee8e1d561ad0bf075978f22413ef29226
diff --git a/src/trace_processor/metrics/metrics.h b/src/trace_processor/metrics/metrics.h
index 16e004c..2691e91 100644
--- a/src/trace_processor/metrics/metrics.h
+++ b/src/trace_processor/metrics/metrics.h
@@ -27,7 +27,7 @@
#include "perfetto/protozero/message.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "perfetto/trace_processor/trace_processor.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
#include "src/trace_processor/util/descriptors.h"
#include "protos/perfetto/trace_processor/metrics_impl.pbzero.h"
diff --git a/src/trace_processor/prelude/functions/BUILD.gn b/src/trace_processor/prelude/functions/BUILD.gn
index 61a0bce..f2f22ba 100644
--- a/src/trace_processor/prelude/functions/BUILD.gn
+++ b/src/trace_processor/prelude/functions/BUILD.gn
@@ -31,8 +31,6 @@
"layout_functions.h",
"pprof_functions.cc",
"pprof_functions.h",
- "register_function.cc",
- "register_function.h",
"sqlite3_str_split.cc",
"sqlite3_str_split.h",
"stack_functions.cc",
@@ -67,6 +65,20 @@
"../../util:sql_argument",
"../../util:stdlib",
]
+ public_deps = [ ":interface" ]
+}
+
+source_set("interface") {
+ sources = [
+ "sql_function.cc",
+ "sql_function.h",
+ ]
+ deps = [
+ "../../../../gn:default_deps",
+ "../../../../gn:sqlite",
+ "../../../../include/perfetto/trace_processor:basic_types",
+ "../../../base",
+ ]
}
perfetto_unittest_source_set("unittests") {
diff --git a/src/trace_processor/prelude/functions/clock_functions.h b/src/trace_processor/prelude/functions/clock_functions.h
index 34f23f2..aac6280 100644
--- a/src/trace_processor/prelude/functions/clock_functions.h
+++ b/src/trace_processor/prelude/functions/clock_functions.h
@@ -25,7 +25,7 @@
#include "src/trace_processor/prelude/functions/create_function_internal.h"
#include "src/trace_processor/util/status_macros.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
namespace perfetto {
namespace trace_processor {
diff --git a/src/trace_processor/prelude/functions/create_function.cc b/src/trace_processor/prelude/functions/create_function.cc
index ee2e733..979c57d 100644
--- a/src/trace_processor/prelude/functions/create_function.cc
+++ b/src/trace_processor/prelude/functions/create_function.cc
@@ -20,6 +20,7 @@
#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/prelude/functions/create_function_internal.h"
#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_engine.h"
#include "src/trace_processor/sqlite/sqlite_utils.h"
#include "src/trace_processor/tp_metatrace.h"
#include "src/trace_processor/util/status_macros.h"
@@ -31,7 +32,7 @@
struct CreatedFunction : public SqlFunction {
struct Context {
- sqlite3* db;
+ SqliteEngine* engine;
Prototype prototype;
sql_argument::Type return_type;
std::string sql;
@@ -94,7 +95,7 @@
int ret = sqlite3_step(ctx->stmt);
RETURN_IF_ERROR(
- SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
+ SqliteRetToStatus(ctx->engine->db(), ctx->prototype.function_name, ret));
if (ret == SQLITE_DONE) {
// No return value means we just return don't set |out|.
return base::OkStatus();
@@ -125,7 +126,7 @@
base::Status CreatedFunction::VerifyPostConditions(Context* ctx) {
int ret = sqlite3_step(ctx->stmt);
RETURN_IF_ERROR(
- SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
+ SqliteRetToStatus(ctx->engine->db(), ctx->prototype.function_name, ret));
if (ret == SQLITE_ROW) {
auto expanded_sql = sqlite_utils::ExpandedSqlForStmt(ctx->stmt);
return base::ErrStatus(
@@ -252,7 +253,7 @@
// Prepare the SQL definition as a statement using SQLite.
ScopedStmt stmt;
sqlite3_stmt* stmt_raw = nullptr;
- int ret = sqlite3_prepare_v2(ctx->db, sql_defn_str.data(),
+ int ret = sqlite3_prepare_v2(ctx->engine->db(), sql_defn_str.data(),
static_cast<int>(sql_defn_str.size()), &stmt_raw,
nullptr);
if (ret != SQLITE_OK) {
@@ -261,18 +262,18 @@
"statement %s",
prototype_str.ToStdString().c_str(),
sqlite_utils::FormatErrorMessage(
- stmt_raw, base::StringView(sql_defn_str), ctx->db, ret)
+ stmt_raw, base::StringView(sql_defn_str), ctx->engine->db(), ret)
.c_message());
}
stmt.reset(stmt_raw);
std::unique_ptr<CreatedFunction::Context> created(
- new CreatedFunction::Context{ctx->db, std::move(prototype),
+ new CreatedFunction::Context{ctx->engine, std::move(prototype),
*opt_return_type, std::move(sql_defn_str),
stmt.get()});
CreatedFunction::Context* created_ptr = created.get();
- RETURN_IF_ERROR(RegisterSqlFunction<CreatedFunction>(
- ctx->db, key.name.c_str(), created_argc, std::move(created)));
+ RETURN_IF_ERROR(ctx->engine->RegisterSqlFunction<CreatedFunction>(
+ key.name.c_str(), created_argc, std::move(created)));
ctx->state->emplace(key, PerFunctionState{std::move(stmt), created_ptr});
// CREATE_FUNCTION doesn't have a return value so just don't sent |out|.
diff --git a/src/trace_processor/prelude/functions/create_function.h b/src/trace_processor/prelude/functions/create_function.h
index 258c4bb..4e2138b 100644
--- a/src/trace_processor/prelude/functions/create_function.h
+++ b/src/trace_processor/prelude/functions/create_function.h
@@ -20,7 +20,9 @@
#include <sqlite3.h>
#include <unordered_map>
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
+#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_table.h"
namespace perfetto {
namespace trace_processor {
@@ -50,7 +52,7 @@
NameAndArgc::Hasher>;
struct Context {
- sqlite3* db;
+ SqliteEngine* engine;
State* state;
};
diff --git a/src/trace_processor/prelude/functions/create_view_function.h b/src/trace_processor/prelude/functions/create_view_function.h
index 56fc3ba..509ea5e 100644
--- a/src/trace_processor/prelude/functions/create_view_function.h
+++ b/src/trace_processor/prelude/functions/create_view_function.h
@@ -20,7 +20,7 @@
#include <sqlite3.h>
#include <unordered_map>
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
namespace perfetto {
namespace trace_processor {
diff --git a/src/trace_processor/prelude/functions/import.h b/src/trace_processor/prelude/functions/import.h
index 9773b5f..da40f5f 100644
--- a/src/trace_processor/prelude/functions/import.h
+++ b/src/trace_processor/prelude/functions/import.h
@@ -23,7 +23,7 @@
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/trace_processor/trace_processor.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
#include "src/trace_processor/util/sql_modules.h"
namespace perfetto {
diff --git a/src/trace_processor/prelude/functions/register_function.h b/src/trace_processor/prelude/functions/register_function.h
deleted file mode 100644
index acca3e6..0000000
--- a/src/trace_processor/prelude/functions/register_function.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2020 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_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_
-#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_
-
-#include <sqlite3.h>
-#include <memory>
-
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// Prototype for a C++ function which can be registered with SQLite.
-//
-// Usage
-//
-// Define a subclass of this struct as follows:
-// struct YourFunction : public SqlFunction {
-// // Optional if you want a custom context object (i.e. an object
-// // passed in at registration time which will be passed to Run on
-// // every invocation)
-// struct YourContext { /* define context fields here */ };
-//
-// static base::Status Run(/* see parameters below */) {
-// /* function body here */
-// }
-//
-// static base::Status Cleanup(/* see parameters below */) {
-// /* function body here */
-// }
-// }
-//
-// Then, register this function with SQLite using RegisterFunction (see below);
-// you'll likely want to do this in TraceProcessorImpl:
-// RegisterFunction<YourFunction>(/* see arguments below */)
-struct SqlFunction {
- // The type of the context object which will be passed to the function.
- // Can be redefined in any sub-classes to override the context.
- using Context = void;
-
- // Indicates whether this function is "void" (i.e. doesn't actually want
- // to return a value). While the function will still return null in SQL
- // (because SQLite does not actually allow null functions), for accounting
- // purposes, this null will be ignored when verifying whether this statement
- // has any output.
- // Can be redefined in any sub-classes to override it.
- // If this is set to true, subclasses must not modify |out| or |destructors|.
- static constexpr bool kVoidReturn = false;
-
- // Struct which holds destructors for strings/bytes returned from the
- // function. Passed as an argument to |Run| to allow implementations to
- // override the destructors.
- struct Destructors {
- sqlite3_destructor_type string_destructor = sqlite_utils::kSqliteTransient;
- sqlite3_destructor_type bytes_destructor = sqlite_utils::kSqliteTransient;
- };
-
- // The function which will be exectued with the arguments from SQL.
- //
- // Implementations MUST define this function themselves; this function is
- // declared but *not* defined so linker errors will be thrown if not defined.
- //
- // |ctx|: the context object passed at registration time.
- // |argc|: number of arguments.
- // |argv|: arguments to the function.
- // |out|: the return value of the function.
- // |destructors|: destructors for string/bytes return values.
- static base::Status Run(Context* ctx,
- size_t argc,
- sqlite3_value** argv,
- SqlValue& out,
- Destructors& destructors);
-
- // Executed after the result from |Run| is reported to SQLite.
- // Allows implementations to verify post-conditions without needing to worry
- // about overwriting return types.
- //
- // Implementations do not need to define this function; a default no-op
- // implementation will be used in this case.
- static base::Status VerifyPostConditions(Context*);
-
- // Executed after the result from |Run| is reported to SQLite.
- // Allows any pending state to be cleaned up post-copy of results by SQLite:
- // this function will be called even if |Run| or |PostRun| returned errors.
- //
- // Implementations do not need to define this function; a default no-op
- // implementation will be used in this case.
- static void Cleanup(Context*);
-};
-
-// Registers a C++ function to be runnable from SQL.
-// The format of the function is given by the |SqlFunction|; see the
-// documentaion above.
-//
-// |db|: sqlite3 database object
-// |name|: name of the function in SQL
-// |argc|: number of arguments for this function, -1 if variable
-// |ctx|: context object for the function (see SqlFunction::Run above);
-// this object *must* outlive the function so should likely be
-// either static or scoped to the lifetime of TraceProcessor.
-// |determistic|: whether this function has deterministic output given the
-// same set of arguments.
-template <typename Function>
-base::Status RegisterSqlFunction(sqlite3* db,
- const char* name,
- int argc,
- typename Function::Context* ctx,
- bool deterministic = true);
-
-// Same as above except allows a unique_ptr to be passed for the context; this
-// allows for SQLite to manage the lifetime of this pointer instead of the
-// essentially static requirement of the context pointer above.
-template <typename Function>
-base::Status RegisterSqlFunction(
- sqlite3* db,
- const char* name,
- int argc,
- std::unique_ptr<typename Function::Context> ctx,
- bool deterministic = true);
-
-} // namespace trace_processor
-} // namespace perfetto
-
-// The rest of this file is just implementation details which we need
-// in the header file because it is templated code. We separate it out
-// like this to keep the API people actually care about easy to read.
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace sqlite_internal {
-
-// RAII type to call Function::Cleanup when destroyed.
-template <typename Function>
-struct ScopedCleanup {
- typename Function::Context* ctx;
- ~ScopedCleanup() { Function::Cleanup(ctx); }
-};
-
-template <typename Function>
-void WrapSqlFunction(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
- using Context = typename Function::Context;
- Context* ud = static_cast<Context*>(sqlite3_user_data(ctx));
-
- ScopedCleanup<Function> scoped_cleanup{ud};
- SqlValue value{};
- SqlFunction::Destructors destructors{};
- base::Status status =
- Function::Run(ud, static_cast<size_t>(argc), argv, value, destructors);
- if (!status.ok()) {
- sqlite3_result_error(ctx, status.c_message(), -1);
- return;
- }
-
- if (Function::kVoidReturn) {
- if (!value.is_null()) {
- sqlite3_result_error(ctx, "void SQL function returned value", -1);
- return;
- }
-
- // If the function doesn't want to return anything, set the "VOID"
- // pointer type to a non-null value. Note that because of the weird
- // way |sqlite3_value_pointer| works, we need to set some value even
- // if we don't actually read it - just set it to a pointer to an empty
- // string for this reason.
- static char kVoidValue[] = "";
- sqlite3_result_pointer(ctx, kVoidValue, "VOID", nullptr);
- } else {
- sqlite_utils::ReportSqlValue(ctx, value, destructors.string_destructor,
- destructors.bytes_destructor);
- }
-
- status = Function::VerifyPostConditions(ud);
- if (!status.ok()) {
- sqlite3_result_error(ctx, status.c_message(), -1);
- return;
- }
-}
-} // namespace sqlite_internal
-
-template <typename Function>
-base::Status RegisterSqlFunction(sqlite3* db,
- const char* name,
- int argc,
- typename Function::Context* ctx,
- bool deterministic) {
- int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
- int ret = sqlite3_create_function_v2(
- db, name, static_cast<int>(argc), flags, ctx,
- sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr, nullptr);
- if (ret != SQLITE_OK) {
- return base::ErrStatus("Unable to register function with name %s", name);
- }
- return base::OkStatus();
-}
-
-template <typename Function>
-base::Status RegisterSqlFunction(
- sqlite3* db,
- const char* name,
- int argc,
- std::unique_ptr<typename Function::Context> user_data,
- bool deterministic) {
- int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
- int ret = sqlite3_create_function_v2(
- db, name, static_cast<int>(argc), flags, user_data.release(),
- sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr,
- [](void* ptr) { delete static_cast<typename Function::Context*>(ptr); });
- if (ret != SQLITE_OK) {
- return base::ErrStatus("Unable to register function with name %s", name);
- }
- return base::OkStatus();
-}
-
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_
diff --git a/src/trace_processor/prelude/functions/register_function.cc b/src/trace_processor/prelude/functions/sql_function.cc
similarity index 80%
rename from src/trace_processor/prelude/functions/register_function.cc
rename to src/trace_processor/prelude/functions/sql_function.cc
index ef41f1d..26bac5e 100644
--- a/src/trace_processor/prelude/functions/register_function.cc
+++ b/src/trace_processor/prelude/functions/sql_function.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-#include "src/trace_processor/prelude/functions/register_function.h"
-#include "sqlite3.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
namespace perfetto {
namespace trace_processor {
diff --git a/src/trace_processor/prelude/functions/sql_function.h b/src/trace_processor/prelude/functions/sql_function.h
new file mode 100644
index 0000000..ba7fab7
--- /dev/null
+++ b/src/trace_processor/prelude/functions/sql_function.h
@@ -0,0 +1,114 @@
+/*
+ * 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_PRELUDE_FUNCTIONS_SQL_FUNCTION_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_SQL_FUNCTION_H_
+
+#include <sqlite3.h>
+#include <memory>
+
+#include "perfetto/base/status.h"
+#include "perfetto/trace_processor/basic_types.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Prototype for a C++ function which can be registered with SQLite.
+//
+// Usage
+//
+// Define a subclass of this struct as follows:
+// struct YourFunction : public SqlFunction {
+// // Optional if you want a custom context object (i.e. an object
+// // passed in at registration time which will be passed to Run on
+// // every invocation)
+// struct YourContext { /* define context fields here */ };
+//
+// static base::Status Run(/* see parameters below */) {
+// /* function body here */
+// }
+//
+// static base::Status Cleanup(/* see parameters below */) {
+// /* function body here */
+// }
+// }
+//
+// Then, register this function with SQLite using RegisterFunction (see below);
+// you'll likely want to do this in TraceProcessorImpl:
+// RegisterFunction<YourFunction>(/* see arguments below */)
+struct SqlFunction {
+ // The type of the context object which will be passed to the function.
+ // Can be redefined in any sub-classes to override the context.
+ using Context = void;
+
+ // Indicates whether this function is "void" (i.e. doesn't actually want
+ // to return a value). While the function will still return null in SQL
+ // (because SQLite does not actually allow null functions), for accounting
+ // purposes, this null will be ignored when verifying whether this statement
+ // has any output.
+ // Can be redefined in any sub-classes to override it.
+ // If this is set to true, subclasses must not modify |out| or |destructors|.
+ static constexpr bool kVoidReturn = false;
+
+ // Struct which holds destructors for strings/bytes returned from the
+ // function. Passed as an argument to |Run| to allow implementations to
+ // override the destructors.
+ struct Destructors {
+ // This matches SQLITE_TRANSIENT constant which we cannot use because it
+ // expands to a C-style cast, causing compiler warnings.
+ sqlite3_destructor_type string_destructor =
+ reinterpret_cast<sqlite3_destructor_type>(-1);
+ sqlite3_destructor_type bytes_destructor =
+ reinterpret_cast<sqlite3_destructor_type>(-1);
+ };
+
+ // The function which will be executed with the arguments from SQL.
+ //
+ // Implementations MUST define this function themselves; this function is
+ // declared but *not* defined so linker errors will be thrown if not defined.
+ //
+ // |ctx|: the context object passed at registration time.
+ // |argc|: number of arguments.
+ // |argv|: arguments to the function.
+ // |out|: the return value of the function.
+ // |destructors|: destructors for string/bytes return values.
+ static base::Status Run(Context* ctx,
+ size_t argc,
+ sqlite3_value** argv,
+ SqlValue& out,
+ Destructors& destructors);
+
+ // Executed after the result from |Run| is reported to SQLite.
+ // Allows implementations to verify post-conditions without needing to worry
+ // about overwriting return types.
+ //
+ // Implementations do not need to define this function; a default no-op
+ // implementation will be used in this case.
+ static base::Status VerifyPostConditions(Context*);
+
+ // Executed after the result from |Run| is reported to SQLite.
+ // Allows any pending state to be cleaned up post-copy of results by SQLite:
+ // this function will be called even if |Run| or |PostRun| returned errors.
+ //
+ // Implementations do not need to define this function; a default no-op
+ // implementation will be used in this case.
+ static void Cleanup(Context*);
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_SQL_FUNCTION_H_
diff --git a/src/trace_processor/prelude/functions/stack_functions.cc b/src/trace_processor/prelude/functions/stack_functions.cc
index 2f8730b..e2c4468 100644
--- a/src/trace_processor/prelude/functions/stack_functions.cc
+++ b/src/trace_processor/prelude/functions/stack_functions.cc
@@ -31,7 +31,8 @@
#include "perfetto/trace_processor/basic_types.h"
#include "perfetto/trace_processor/status.h"
#include "protos/perfetto/trace_processor/stack.pbzero.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
+#include "src/trace_processor/sqlite/sqlite_engine.h"
#include "src/trace_processor/sqlite/sqlite_utils.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
@@ -243,15 +244,16 @@
} // namespace
-base::Status RegisterStackFunctions(sqlite3* db,
+base::Status RegisterStackFunctions(SqliteEngine* engine,
TraceProcessorContext* context) {
- RETURN_IF_ERROR(RegisterSqlFunction<CatStacksFunction>(
- db, CatStacksFunction::kFunctionName, -1, context->storage.get()));
- RETURN_IF_ERROR(RegisterSqlFunction<StackFromStackProfileFrameFunction>(
- db, StackFromStackProfileFrameFunction::kFunctionName, 1,
- context->storage.get()));
- return RegisterSqlFunction<StackFromStackProfileCallsiteFunction>(
- db, StackFromStackProfileCallsiteFunction::kFunctionName, -1,
+ RETURN_IF_ERROR(engine->RegisterSqlFunction<CatStacksFunction>(
+ CatStacksFunction::kFunctionName, -1, context->storage.get()));
+ RETURN_IF_ERROR(
+ engine->RegisterSqlFunction<StackFromStackProfileFrameFunction>(
+ StackFromStackProfileFrameFunction::kFunctionName, 1,
+ context->storage.get()));
+ return engine->RegisterSqlFunction<StackFromStackProfileCallsiteFunction>(
+ StackFromStackProfileCallsiteFunction::kFunctionName, -1,
context->storage.get());
}
diff --git a/src/trace_processor/prelude/functions/stack_functions.h b/src/trace_processor/prelude/functions/stack_functions.h
index 5acb046..7fd676c 100644
--- a/src/trace_processor/prelude/functions/stack_functions.h
+++ b/src/trace_processor/prelude/functions/stack_functions.h
@@ -26,6 +26,7 @@
namespace perfetto {
namespace trace_processor {
+class SqliteEngine;
class TraceProcessorContext;
// Registers the stack manipulation related functions:
@@ -49,7 +50,7 @@
// it generates a fake Frame
//
// See protos/perfetto/trace_processor/stack.proto
-base::Status RegisterStackFunctions(sqlite3* db,
+base::Status RegisterStackFunctions(SqliteEngine* engine,
TraceProcessorContext* context);
} // namespace trace_processor
diff --git a/src/trace_processor/prelude/functions/to_ftrace.h b/src/trace_processor/prelude/functions/to_ftrace.h
index c32ed79..9c55067 100644
--- a/src/trace_processor/prelude/functions/to_ftrace.h
+++ b/src/trace_processor/prelude/functions/to_ftrace.h
@@ -19,7 +19,7 @@
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/string_writer.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
diff --git a/src/trace_processor/prelude/functions/utils.h b/src/trace_processor/prelude/functions/utils.h
index 82b293f..21fed04 100644
--- a/src/trace_processor/prelude/functions/utils.h
+++ b/src/trace_processor/prelude/functions/utils.h
@@ -28,7 +28,7 @@
#include "src/trace_processor/prelude/functions/create_function_internal.h"
#include "src/trace_processor/util/status_macros.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
namespace perfetto {
namespace trace_processor {
diff --git a/src/trace_processor/prelude/functions/window_functions.h b/src/trace_processor/prelude/functions/window_functions.h
index 3a76fce..a3fbeaf 100644
--- a/src/trace_processor/prelude/functions/window_functions.h
+++ b/src/trace_processor/prelude/functions/window_functions.h
@@ -28,7 +28,7 @@
#include "src/trace_processor/prelude/functions/create_function_internal.h"
#include "src/trace_processor/util/status_macros.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
namespace perfetto {
namespace trace_processor {
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index da894d7..7045815 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -46,6 +46,7 @@
"../db",
"../importers/common",
"../importers/ftrace:ftrace_descriptors",
+ "../prelude/functions:interface",
"../prelude/table_functions:interface",
"../storage",
"../types",
diff --git a/src/trace_processor/sqlite/sqlite_engine.h b/src/trace_processor/sqlite/sqlite_engine.h
index 35865e3..5e09f05 100644
--- a/src/trace_processor/sqlite/sqlite_engine.h
+++ b/src/trace_processor/sqlite/sqlite_engine.h
@@ -24,10 +24,12 @@
#include "perfetto/base/status.h"
#include "src/trace_processor/db/table.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
#include "src/trace_processor/prelude/table_functions/table_function.h"
#include "src/trace_processor/sqlite/query_cache.h"
#include "src/trace_processor/sqlite/scoped_db.h"
#include "src/trace_processor/sqlite/sqlite_table.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
namespace perfetto {
namespace trace_processor {
@@ -48,7 +50,40 @@
// |name|.
void RegisterTable(const Table& table, const std::string& name);
- // Registers a trace processor C++ function with SQLite.
+ // Registers a trace processor C++ function to be runnable from SQL.
+ //
+ // The format of the function is given by the |SqlFunction|.
+ //
+ // |db|: sqlite3 database object
+ // |name|: name of the function in SQL
+ // |argc|: number of arguments for this function. This can be -1 if
+ // the number of arguments is variable.
+ // |ctx|: context object for the function (see SqlFunction::Run
+ // above);
+ // this object *must* outlive the function so should likely be
+ // either static or scoped to the lifetime of TraceProcessor.
+ // |determistic|: whether this function has deterministic output given the
+ // same set of arguments.
+ template <typename Function = SqlFunction>
+ base::Status RegisterSqlFunction(const char* name,
+ int argc,
+ typename Function::Context* ctx,
+ bool deterministic = true);
+
+ // Registers a trace processor C++ function to be runnable from SQL.
+ //
+ // This function is the same as the above except allows a unique_ptr to be
+ // passed for the context; this allows for SQLite to manage the lifetime of
+ // this pointer instead of the essentially static requirement of the context
+ // pointer above.
+ template <typename Function>
+ base::Status RegisterSqlFunction(
+ const char* name,
+ int argc,
+ std::unique_ptr<typename Function::Context> ctx,
+ bool deterministic = true);
+
+ // Registers a trace processor C++ table function with SQLite.
void RegisterTableFunction(std::unique_ptr<TableFunction> fn);
// Registers a SQLite virtual table module with the given name.
@@ -59,18 +94,7 @@
void RegisterVirtualTableModule(const std::string& module_name,
Context ctx,
SqliteTable::TableType table_type,
- bool updatable) {
- static_assert(std::is_base_of_v<SqliteTable, Vtab>,
- "Must subclass TypedSqliteTable");
-
- auto module_arg =
- Vtab::CreateModuleArg(this, std::move(ctx), table_type, updatable);
- sqlite3_module* module = &module_arg->module;
- int res = sqlite3_create_module_v2(
- db_.get(), module_name.c_str(), module, module_arg.release(),
- [](void* arg) { delete static_cast<typename Vtab::ModuleArg*>(arg); });
- PERFETTO_CHECK(res == SQLITE_OK);
- }
+ bool updatable);
// Declares a virtual table with SQLite.
//
@@ -104,4 +128,113 @@
} // namespace trace_processor
} // namespace perfetto
+// The rest of this file is just implementation details which we need
+// in the header file because it is templated code. We separate it out
+// like this to keep the API people actually care about easy to read.
+
+namespace perfetto {
+namespace trace_processor {
+namespace sqlite_internal {
+
+// RAII type to call Function::Cleanup when destroyed.
+template <typename Function>
+struct ScopedCleanup {
+ typename Function::Context* ctx;
+ ~ScopedCleanup() { Function::Cleanup(ctx); }
+};
+
+template <typename Function>
+void WrapSqlFunction(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ using Context = typename Function::Context;
+ Context* ud = static_cast<Context*>(sqlite3_user_data(ctx));
+
+ ScopedCleanup<Function> scoped_cleanup{ud};
+ SqlValue value{};
+ SqlFunction::Destructors destructors{};
+ base::Status status =
+ Function::Run(ud, static_cast<size_t>(argc), argv, value, destructors);
+ if (!status.ok()) {
+ sqlite3_result_error(ctx, status.c_message(), -1);
+ return;
+ }
+
+ if (Function::kVoidReturn) {
+ if (!value.is_null()) {
+ sqlite3_result_error(ctx, "void SQL function returned value", -1);
+ return;
+ }
+
+ // If the function doesn't want to return anything, set the "VOID"
+ // pointer type to a non-null value. Note that because of the weird
+ // way |sqlite3_value_pointer| works, we need to set some value even
+ // if we don't actually read it - just set it to a pointer to an empty
+ // string for this reason.
+ static char kVoidValue[] = "";
+ sqlite3_result_pointer(ctx, kVoidValue, "VOID", nullptr);
+ } else {
+ sqlite_utils::ReportSqlValue(ctx, value, destructors.string_destructor,
+ destructors.bytes_destructor);
+ }
+
+ status = Function::VerifyPostConditions(ud);
+ if (!status.ok()) {
+ sqlite3_result_error(ctx, status.c_message(), -1);
+ return;
+ }
+}
+
+} // namespace sqlite_internal
+
+template <typename Function>
+base::Status SqliteEngine::RegisterSqlFunction(const char* name,
+ int argc,
+ typename Function::Context* ctx,
+ 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,
+ sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr, nullptr);
+ if (ret != SQLITE_OK) {
+ return base::ErrStatus("Unable to register function with name %s", name);
+ }
+ return base::OkStatus();
+}
+
+template <typename Function>
+base::Status SqliteEngine::RegisterSqlFunction(
+ const char* name,
+ int argc,
+ std::unique_ptr<typename Function::Context> user_data,
+ bool deterministic) {
+ int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
+ int ret = sqlite3_create_function_v2(
+ db_.get(), name, static_cast<int>(argc), flags, user_data.release(),
+ sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr,
+ [](void* ptr) { delete static_cast<typename Function::Context*>(ptr); });
+ if (ret != SQLITE_OK) {
+ return base::ErrStatus("Unable to register function with name %s", name);
+ }
+ return base::OkStatus();
+}
+
+template <typename Vtab, typename Context>
+void SqliteEngine::RegisterVirtualTableModule(const std::string& module_name,
+ Context ctx,
+ SqliteTable::TableType table_type,
+ bool updatable) {
+ static_assert(std::is_base_of_v<SqliteTable, Vtab>,
+ "Must subclass TypedSqliteTable");
+
+ auto module_arg =
+ Vtab::CreateModuleArg(this, std::move(ctx), table_type, updatable);
+ sqlite3_module* module = &module_arg->module;
+ int res = sqlite3_create_module_v2(
+ db_.get(), module_name.c_str(), module, module_arg.release(),
+ [](void* arg) { delete static_cast<typename Vtab::ModuleArg*>(arg); });
+ PERFETTO_CHECK(res == SQLITE_OK);
+}
+
+} // namespace trace_processor
+} // namespace perfetto
+
#endif // SRC_TRACE_PROCESSOR_SQLITE_SQLITE_ENGINE_H_
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 41edebe..f82ca2e 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -53,7 +53,7 @@
#include "src/trace_processor/prelude/functions/import.h"
#include "src/trace_processor/prelude/functions/layout_functions.h"
#include "src/trace_processor/prelude/functions/pprof_functions.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
#include "src/trace_processor/prelude/functions/sqlite3_str_split.h"
#include "src/trace_processor/prelude/functions/stack_functions.h"
#include "src/trace_processor/prelude/functions/to_ftrace.h"
@@ -106,13 +106,13 @@
"* FROM sqlite_temp_master)";
template <typename SqlFunction, typename Ptr = typename SqlFunction::Context*>
-void RegisterFunction(sqlite3* db,
+void RegisterFunction(SqliteEngine* engine,
const char* name,
int argc,
Ptr context = nullptr,
bool deterministic = true) {
- auto status = RegisterSqlFunction<SqlFunction>(
- db, name, argc, std::move(context), deterministic);
+ auto status = engine->RegisterSqlFunction<SqlFunction>(
+ name, argc, std::move(context), deterministic);
if (!status.ok())
PERFETTO_ELOG("%s", status.c_message());
}
@@ -238,7 +238,7 @@
}
void SetupMetrics(TraceProcessor* tp,
- sqlite3* db,
+ SqliteEngine* engine,
std::vector<metrics::SqlMetricFile>* sql_metrics,
const std::vector<std::string>& extension_paths) {
const std::vector<std::string> sanitized_extension_paths =
@@ -268,10 +268,11 @@
}
}
- RegisterFunction<metrics::NullIfEmpty>(db, "NULL_IF_EMPTY", 1);
- RegisterFunction<metrics::UnwrapMetricProto>(db, "UNWRAP_METRIC_PROTO", 2);
+ RegisterFunction<metrics::NullIfEmpty>(engine, "NULL_IF_EMPTY", 1);
+ RegisterFunction<metrics::UnwrapMetricProto>(engine, "UNWRAP_METRIC_PROTO",
+ 2);
RegisterFunction<metrics::RunMetric>(
- db, "RUN_METRIC", -1,
+ engine, "RUN_METRIC", -1,
std::unique_ptr<metrics::RunMetric::Context>(
new metrics::RunMetric::Context{tp, sql_metrics}));
@@ -279,7 +280,7 @@
// functions are supported.
{
auto ret = sqlite3_create_function_v2(
- db, "RepeatedField", 1, SQLITE_UTF8, nullptr, nullptr,
+ engine->db(), "RepeatedField", 1, SQLITE_UTF8, nullptr, nullptr,
metrics::RepeatedFieldStep, metrics::RepeatedFieldFinal, nullptr);
if (ret)
PERFETTO_FATAL("Error initializing RepeatedField");
@@ -454,8 +455,8 @@
}
// Register SQL functions only used in local development instances.
-void RegisterDevFunctions(sqlite3* db) {
- RegisterFunction<WriteFile>(db, "WRITE_FILE", 2);
+void RegisterDevFunctions(SqliteEngine* engine) {
+ RegisterFunction<WriteFile>(engine, "WRITE_FILE", 2);
}
sql_modules::NameToModule GetStdlibModules() {
@@ -516,34 +517,34 @@
// New style function registration.
if (cfg.enable_dev_features) {
- RegisterDevFunctions(engine_.db());
+ RegisterDevFunctions(&engine_);
}
- RegisterFunction<Glob>(engine_.db(), "glob", 2);
- RegisterFunction<Hash>(engine_.db(), "HASH", -1);
- RegisterFunction<Base64Encode>(engine_.db(), "BASE64_ENCODE", 1);
- RegisterFunction<Demangle>(engine_.db(), "DEMANGLE", 1);
- RegisterFunction<SourceGeq>(engine_.db(), "SOURCE_GEQ", -1);
- RegisterFunction<ExportJson>(engine_.db(), "EXPORT_JSON", 1,
+ RegisterFunction<Glob>(&engine_, "glob", 2);
+ RegisterFunction<Hash>(&engine_, "HASH", -1);
+ RegisterFunction<Base64Encode>(&engine_, "BASE64_ENCODE", 1);
+ RegisterFunction<Demangle>(&engine_, "DEMANGLE", 1);
+ RegisterFunction<SourceGeq>(&engine_, "SOURCE_GEQ", -1);
+ RegisterFunction<ExportJson>(&engine_, "EXPORT_JSON", 1,
context_.storage.get(), false);
- RegisterFunction<ExtractArg>(engine_.db(), "EXTRACT_ARG", 2,
+ RegisterFunction<ExtractArg>(&engine_, "EXTRACT_ARG", 2,
context_.storage.get());
- RegisterFunction<AbsTimeStr>(engine_.db(), "ABS_TIME_STR", 1,
+ RegisterFunction<AbsTimeStr>(&engine_, "ABS_TIME_STR", 1,
context_.clock_converter.get());
- RegisterFunction<ToMonotonic>(engine_.db(), "TO_MONOTONIC", 1,
+ RegisterFunction<ToMonotonic>(&engine_, "TO_MONOTONIC", 1,
context_.clock_converter.get());
RegisterFunction<CreateFunction>(
- engine_.db(), "CREATE_FUNCTION", 3,
+ &engine_, "CREATE_FUNCTION", 3,
std::unique_ptr<CreateFunction::Context>(
- new CreateFunction::Context{engine_.db(), &create_function_state_}));
+ new CreateFunction::Context{&engine_, &create_function_state_}));
RegisterFunction<CreateViewFunction>(
- engine_.db(), "CREATE_VIEW_FUNCTION", 3,
+ &engine_, "CREATE_VIEW_FUNCTION", 3,
std::unique_ptr<CreateViewFunction::Context>(
new CreateViewFunction::Context{engine_.db()}));
- RegisterFunction<Import>(engine_.db(), "IMPORT", 1,
+ RegisterFunction<Import>(&engine_, "IMPORT", 1,
std::unique_ptr<Import::Context>(new Import::Context{
engine_.db(), this, &sql_modules_}));
RegisterFunction<ToFtrace>(
- engine_.db(), "TO_FTRACE", 1,
+ &engine_, "TO_FTRACE", 1,
std::unique_ptr<ToFtrace::Context>(new ToFtrace::Context{
context_.storage.get(), SystraceSerializer(&context_)}));
@@ -553,7 +554,7 @@
RegisterLastNonNullFunction(engine_.db());
RegisterValueAtMaxTsFunction(engine_.db());
{
- base::Status status = RegisterStackFunctions(engine_.db(), &context_);
+ base::Status status = RegisterStackFunctions(&engine_, &context_);
if (!status.ok())
PERFETTO_ELOG("%s", status.c_message());
}
@@ -594,8 +595,7 @@
PERFETTO_ELOG("%s", status.c_message());
}
- SetupMetrics(this, engine_.db(), &sql_metrics_,
- cfg.skip_builtin_metric_paths);
+ SetupMetrics(this, &engine_, &sql_metrics_, cfg.skip_builtin_metric_paths);
// Legacy tables.
engine_.RegisterVirtualTableModule<SqlStatsTable>(
@@ -964,7 +964,7 @@
auto fn_name = desc.full_name().substr(desc.package_name().size() + 1);
std::replace(fn_name.begin(), fn_name.end(), '.', '_');
RegisterFunction<metrics::BuildProto>(
- engine_.db(), fn_name.c_str(), -1,
+ &engine_, fn_name.c_str(), -1,
std::unique_ptr<metrics::BuildProto::Context>(
new metrics::BuildProto::Context{this, &pool_, i}));
}