• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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/sqlite/create_function_internal.h"
18 
19 #include "perfetto/base/status.h"
20 #include "perfetto/ext/base/string_view.h"
21 #include "src/trace_processor/sqlite/sqlite_utils.h"
22 #include "src/trace_processor/util/status_macros.h"
23 
24 namespace perfetto {
25 namespace trace_processor {
26 
IsValidName(base::StringView str)27 bool IsValidName(base::StringView str) {
28   auto pred = [](char c) { return !(isalnum(c) || c == '_'); };
29   return std::find_if(str.begin(), str.end(), pred) == str.end();
30 }
31 
ParseType(base::StringView str)32 base::Optional<SqlValue::Type> ParseType(base::StringView str) {
33   if (str == "INT" || str == "LONG" || str == "BOOL") {
34     return SqlValue::Type::kLong;
35   } else if (str == "DOUBLE" || str == "FLOAT") {
36     return SqlValue::Type::kDouble;
37   } else if (str == "STRING") {
38     return SqlValue::Type::kString;
39   } else if (str == "PROTO" || str == "BYTES") {
40     return SqlValue::Type::kBytes;
41   }
42   return base::nullopt;
43 }
44 
SqliteTypeToFriendlyString(SqlValue::Type type)45 const char* SqliteTypeToFriendlyString(SqlValue::Type type) {
46   switch (type) {
47     case SqlValue::Type::kNull:
48       return "NULL";
49     case SqlValue::Type::kLong:
50       return "INT/LONG/BOOL";
51     case SqlValue::Type::kDouble:
52       return "FLOAT/DOUBLE";
53     case SqlValue::Type::kString:
54       return "STRING";
55     case SqlValue::Type::kBytes:
56       return "BYTES/PROTO";
57   }
58   PERFETTO_FATAL("For GCC");
59 }
60 
TypeCheckSqliteValue(sqlite3_value * value,SqlValue::Type expected_type)61 base::Status TypeCheckSqliteValue(sqlite3_value* value,
62                                   SqlValue::Type expected_type) {
63   SqlValue::Type actual_type =
64       sqlite_utils::SqliteTypeToSqlValueType(sqlite3_value_type(value));
65   if (actual_type != SqlValue::Type::kNull && actual_type != expected_type) {
66     return base::ErrStatus(
67         "does not have expected type: expected %s, actual %s",
68         SqliteTypeToFriendlyString(expected_type),
69         SqliteTypeToFriendlyString(actual_type));
70   }
71   return base::OkStatus();
72 }
73 
ParseFunctionName(base::StringView raw,base::StringView & out)74 base::Status ParseFunctionName(base::StringView raw, base::StringView& out) {
75   size_t function_name_end = raw.find('(');
76   if (function_name_end == base::StringView::npos)
77     return base::ErrStatus("unable to find bracket starting argument list");
78 
79   base::StringView function_name = raw.substr(0, function_name_end);
80   if (!IsValidName(function_name)) {
81     return base::ErrStatus("function name %s is not alphanumeric",
82                            function_name.ToStdString().c_str());
83   }
84   out = function_name;
85   return base::OkStatus();
86 }
87 
ParseArgs(std::string args,std::vector<Prototype::Argument> & out)88 base::Status ParseArgs(std::string args,
89                        std::vector<Prototype::Argument>& out) {
90   for (const auto& arg : base::SplitString(args, ",")) {
91     const auto& arg_name_and_type = base::SplitString(arg, " ");
92     if (arg_name_and_type.size() != 2) {
93       return base::ErrStatus(
94           "argument %s in function prototype should be of the form `name type`",
95           arg.c_str());
96     }
97 
98     const auto& arg_name = arg_name_and_type[0];
99     const auto& arg_type_str = arg_name_and_type[1];
100     if (!IsValidName(base::StringView(arg_name)))
101       return base::ErrStatus("argument %s is not alphanumeric", arg.c_str());
102 
103     auto opt_arg_type = ParseType(base::StringView(arg_type_str));
104     if (!opt_arg_type)
105       return base::ErrStatus("unknown argument type in argument %s",
106                              arg.c_str());
107 
108     SqlValue::Type arg_type = *opt_arg_type;
109     PERFETTO_DCHECK(arg_type != SqlValue::Type::kNull);
110     out.push_back({arg_name, "$" + arg_name, arg_type});
111   }
112   return base::OkStatus();
113 }
114 
ParsePrototype(base::StringView raw,Prototype & out)115 base::Status ParsePrototype(base::StringView raw, Prototype& out) {
116   // Examples of function prototypes:
117   // ANDROID_SDK_LEVEL()
118   // STARTUP_SLICE(dur_ns INT)
119   // FIND_NEXT_SLICE_WITH_NAME(ts INT, name STRING)
120 
121   base::StringView function_name;
122   RETURN_IF_ERROR(ParseFunctionName(raw, function_name));
123 
124   size_t function_name_end = function_name.size();
125   size_t args_start = function_name_end + 1;
126   size_t args_end = raw.find(')', args_start);
127   if (args_end == base::StringView::npos)
128     return base::ErrStatus("unable to find bracket ending argument list");
129 
130   base::StringView args_str = raw.substr(args_start, args_end - args_start);
131   RETURN_IF_ERROR(ParseArgs(args_str.ToStdString(), out.arguments));
132 
133   out.function_name = function_name.ToStdString();
134   return base::OkStatus();
135 }
136 
SqliteRetToStatus(sqlite3 * db,const std::string & function_name,int ret)137 base::Status SqliteRetToStatus(sqlite3* db,
138                                const std::string& function_name,
139                                int ret) {
140   if (ret != SQLITE_ROW && ret != SQLITE_DONE) {
141     return base::ErrStatus("%s: SQLite error while executing function body: %s",
142                            function_name.c_str(), sqlite3_errmsg(db));
143   }
144   return base::OkStatus();
145 }
146 
MaybeBindArgument(sqlite3_stmt * stmt,const std::string & function_name,const Prototype::Argument & arg,sqlite3_value * value)147 base::Status MaybeBindArgument(sqlite3_stmt* stmt,
148                                const std::string& function_name,
149                                const Prototype::Argument& arg,
150                                sqlite3_value* value) {
151   int index = sqlite3_bind_parameter_index(stmt, arg.dollar_name.c_str());
152 
153   // If the argument is not in the query, this just means its an unused
154   // argument which we can just ignore.
155   if (index == 0)
156     return base::Status();
157 
158   int ret = sqlite3_bind_value(stmt, index, value);
159   if (ret != SQLITE_OK) {
160     return base::ErrStatus(
161         "%s: SQLite error while binding value to argument %s: %s",
162         function_name.c_str(), arg.name.c_str(),
163         sqlite3_errmsg(sqlite3_db_handle(stmt)));
164   }
165   return base::OkStatus();
166 }
167 
168 }  // namespace trace_processor
169 }  // namespace perfetto
170