• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 #include "src/trace_processor/prelude/functions/create_function.h"
18 
19 #include "perfetto/base/status.h"
20 #include "perfetto/trace_processor/basic_types.h"
21 #include "src/trace_processor/prelude/functions/create_function_internal.h"
22 #include "src/trace_processor/sqlite/scoped_db.h"
23 #include "src/trace_processor/sqlite/sqlite_engine.h"
24 #include "src/trace_processor/sqlite/sqlite_utils.h"
25 #include "src/trace_processor/tp_metatrace.h"
26 #include "src/trace_processor/util/status_macros.h"
27 
28 namespace perfetto {
29 namespace trace_processor {
30 
31 namespace {
32 
33 struct CreatedFunction : public SqlFunction {
34   struct Context {
35     SqliteEngine* engine;
36     Prototype prototype;
37     sql_argument::Type return_type;
38     std::string sql;
39     ScopedStmt stmt;
40   };
41 
42   static base::Status Run(Context* ctx,
43                           size_t argc,
44                           sqlite3_value** argv,
45                           SqlValue& out,
46                           Destructors&);
47   static base::Status VerifyPostConditions(Context*);
48   static void Cleanup(Context*);
49 };
50 
Run(CreatedFunction::Context * ctx,size_t argc,sqlite3_value ** argv,SqlValue & out,Destructors &)51 base::Status CreatedFunction::Run(CreatedFunction::Context* ctx,
52                                   size_t argc,
53                                   sqlite3_value** argv,
54                                   SqlValue& out,
55                                   Destructors&) {
56   if (argc != ctx->prototype.arguments.size()) {
57     return base::ErrStatus(
58         "%s: invalid number of args; expected %zu, received %zu",
59         ctx->prototype.function_name.c_str(), ctx->prototype.arguments.size(),
60         argc);
61   }
62 
63   // Type check all the arguments.
64   for (size_t i = 0; i < argc; ++i) {
65     sqlite3_value* arg = argv[i];
66     sql_argument::Type type = ctx->prototype.arguments[i].type();
67     base::Status status = sqlite_utils::TypeCheckSqliteValue(
68         arg, sql_argument::TypeToSqlValueType(type),
69         sql_argument::TypeToHumanFriendlyString(type));
70     if (!status.ok()) {
71       return base::ErrStatus("%s[arg=%s]: argument %zu %s",
72                              ctx->prototype.function_name.c_str(),
73                              sqlite3_value_text(arg), i, status.c_message());
74     }
75   }
76 
77   PERFETTO_TP_TRACE(
78       metatrace::Category::FUNCTION, "CREATE_FUNCTION",
79       [ctx, argv](metatrace::Record* r) {
80         r->AddArg("Function", ctx->prototype.function_name.c_str());
81         for (uint32_t i = 0; i < ctx->prototype.arguments.size(); ++i) {
82           std::string key = "Arg " + std::to_string(i);
83           const char* value =
84               reinterpret_cast<const char*>(sqlite3_value_text(argv[i]));
85           r->AddArg(base::StringView(key),
86                     value ? base::StringView(value) : base::StringView("NULL"));
87         }
88       });
89 
90   // Bind all the arguments to the appropriate places in the function.
91   for (size_t i = 0; i < argc; ++i) {
92     RETURN_IF_ERROR(MaybeBindArgument(ctx->stmt.get(),
93                                       ctx->prototype.function_name,
94                                       ctx->prototype.arguments[i], argv[i]));
95   }
96 
97   int ret = sqlite3_step(ctx->stmt.get());
98   RETURN_IF_ERROR(
99       SqliteRetToStatus(ctx->engine->db(), ctx->prototype.function_name, ret));
100   if (ret == SQLITE_DONE) {
101     // No return value means we just return don't set |out|.
102     return base::OkStatus();
103   }
104 
105   PERFETTO_DCHECK(ret == SQLITE_ROW);
106   size_t col_count = static_cast<size_t>(sqlite3_column_count(ctx->stmt.get()));
107   if (col_count != 1) {
108     return base::ErrStatus(
109         "%s: SQL definition should only return one column: returned %zu "
110         "columns",
111         ctx->prototype.function_name.c_str(), col_count);
112   }
113 
114   out = sqlite_utils::SqliteValueToSqlValue(
115       sqlite3_column_value(ctx->stmt.get(), 0));
116 
117   // If we return a bytes type but have a null pointer, SQLite will convert this
118   // to an SQL null. However, for proto build functions, we actively want to
119   // distinguish between nulls and 0 byte strings. Therefore, change the value
120   // to an empty string.
121   if (out.type == SqlValue::kBytes && out.bytes_value == nullptr) {
122     PERFETTO_DCHECK(out.bytes_count == 0);
123     out.bytes_value = "";
124   }
125   return base::OkStatus();
126 }
127 
VerifyPostConditions(Context * ctx)128 base::Status CreatedFunction::VerifyPostConditions(Context* ctx) {
129   int ret = sqlite3_step(ctx->stmt.get());
130   RETURN_IF_ERROR(
131       SqliteRetToStatus(ctx->engine->db(), ctx->prototype.function_name, ret));
132   if (ret == SQLITE_ROW) {
133     auto expanded_sql = sqlite_utils::ExpandedSqlForStmt(ctx->stmt.get());
134     return base::ErrStatus(
135         "%s: multiple values were returned when executing function body. "
136         "Executed SQL was %s",
137         ctx->prototype.function_name.c_str(), expanded_sql.get());
138   }
139   PERFETTO_DCHECK(ret == SQLITE_DONE);
140   return base::OkStatus();
141 }
142 
Cleanup(CreatedFunction::Context * ctx)143 void CreatedFunction::Cleanup(CreatedFunction::Context* ctx) {
144   sqlite3_reset(ctx->stmt.get());
145   sqlite3_clear_bindings(ctx->stmt.get());
146 }
147 
148 }  // namespace
149 
Run(SqliteEngine * engine,size_t argc,sqlite3_value ** argv,SqlValue &,Destructors &)150 base::Status CreateFunction::Run(SqliteEngine* engine,
151                                  size_t argc,
152                                  sqlite3_value** argv,
153                                  SqlValue&,
154                                  Destructors&) {
155   if (argc != 3) {
156     return base::ErrStatus(
157         "CREATE_FUNCTION: invalid number of args; expected %u, received %zu",
158         3u, argc);
159   }
160 
161   sqlite3_value* prototype_value = argv[0];
162   sqlite3_value* return_type_value = argv[1];
163   sqlite3_value* sql_defn_value = argv[2];
164 
165   // Type check all the arguments.
166   {
167     auto type_check = [prototype_value](sqlite3_value* value,
168                                         SqlValue::Type type, const char* desc) {
169       base::Status status = sqlite_utils::TypeCheckSqliteValue(value, type);
170       if (!status.ok()) {
171         return base::ErrStatus("CREATE_FUNCTION[prototype=%s]: %s %s",
172                                sqlite3_value_text(prototype_value), desc,
173                                status.c_message());
174       }
175       return base::OkStatus();
176     };
177 
178     RETURN_IF_ERROR(type_check(prototype_value, SqlValue::Type::kString,
179                                "function prototype (first argument)"));
180     RETURN_IF_ERROR(type_check(return_type_value, SqlValue::Type::kString,
181                                "return type (second argument)"));
182     RETURN_IF_ERROR(type_check(sql_defn_value, SqlValue::Type::kString,
183                                "SQL definition (third argument)"));
184   }
185 
186   // Extract the arguments from the value wrappers.
187   auto extract_string = [](sqlite3_value* value) -> base::StringView {
188     return reinterpret_cast<const char*>(sqlite3_value_text(value));
189   };
190   base::StringView prototype_str = extract_string(prototype_value);
191   base::StringView return_type_str = extract_string(return_type_value);
192   std::string sql_defn_str = extract_string(sql_defn_value).ToStdString();
193 
194   // Parse all the arguments into a more friendly form.
195   Prototype prototype;
196   base::Status status = ParsePrototype(prototype_str, prototype);
197   if (!status.ok()) {
198     return base::ErrStatus("CREATE_FUNCTION[prototype=%s]: %s",
199                            prototype_str.ToStdString().c_str(),
200                            status.c_message());
201   }
202 
203   // Parse the return type into a enum format.
204   auto opt_return_type = sql_argument::ParseType(return_type_str);
205   if (!opt_return_type) {
206     return base::ErrStatus(
207         "CREATE_FUNCTION[prototype=%s, return=%s]: unknown return type "
208         "specified",
209         prototype_str.ToStdString().c_str(),
210         return_type_str.ToStdString().c_str());
211   }
212 
213   int created_argc = static_cast<int>(prototype.arguments.size());
214   auto* fn_ctx =
215       engine->GetFunctionContext(prototype.function_name, created_argc);
216   if (fn_ctx) {
217     // If the function already exists, just verify that the prototype, return
218     // type and SQL matches exactly with what we already had registered. By
219     // doing this, we can avoid the problem plaguing C++ macros where macro
220     // ordering determines which one gets run.
221     auto* created_ctx = static_cast<CreatedFunction::Context*>(fn_ctx);
222     if (created_ctx->prototype != prototype) {
223       return base::ErrStatus(
224           "CREATE_FUNCTION[prototype=%s]: function prototype changed",
225           prototype_str.ToStdString().c_str());
226     }
227 
228     if (created_ctx->return_type != *opt_return_type) {
229       return base::ErrStatus(
230           "CREATE_FUNCTION[prototype=%s]: return type changed from %s to %s",
231           prototype_str.ToStdString().c_str(),
232           sql_argument::TypeToHumanFriendlyString(created_ctx->return_type),
233           return_type_str.ToStdString().c_str());
234     }
235 
236     if (created_ctx->sql != sql_defn_str) {
237       return base::ErrStatus(
238           "CREATE_FUNCTION[prototype=%s]: function SQL changed from %s to %s",
239           prototype_str.ToStdString().c_str(), created_ctx->sql.c_str(),
240           sql_defn_str.c_str());
241     }
242     return base::OkStatus();
243   }
244 
245   // Prepare the SQL definition as a statement using SQLite.
246   ScopedStmt stmt;
247   sqlite3_stmt* stmt_raw = nullptr;
248   int ret = sqlite3_prepare_v2(engine->db(), sql_defn_str.data(),
249                                static_cast<int>(sql_defn_str.size()), &stmt_raw,
250                                nullptr);
251   if (ret != SQLITE_OK) {
252     return base::ErrStatus(
253         "CREATE_FUNCTION[prototype=%s]: SQLite error when preparing "
254         "statement %s",
255         prototype_str.ToStdString().c_str(),
256         sqlite_utils::FormatErrorMessage(
257             stmt_raw, base::StringView(sql_defn_str), engine->db(), ret)
258             .c_message());
259   }
260   stmt.reset(stmt_raw);
261 
262   std::string function_name = prototype.function_name;
263   std::unique_ptr<CreatedFunction::Context> created_fn_ctx(
264       new CreatedFunction::Context{engine, std::move(prototype),
265                                    *opt_return_type, std::move(sql_defn_str),
266                                    std::move(stmt)});
267   RETURN_IF_ERROR(engine->RegisterSqlFunction<CreatedFunction>(
268       function_name.c_str(), created_argc, std::move(created_fn_ctx)));
269 
270   // CREATE_FUNCTION doesn't have a return value so just don't sent |out|.
271   return base::OkStatus();
272 }
273 
274 }  // namespace trace_processor
275 }  // namespace perfetto
276