• 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/metrics/metrics.h"
18 
19 #include <regex>
20 #include <unordered_map>
21 #include <vector>
22 
23 #include "perfetto/ext/base/string_utils.h"
24 #include "perfetto/ext/base/utils.h"
25 #include "perfetto/protozero/scattered_heap_buffer.h"
26 #include "src/trace_processor/metrics/sql_metrics.h"
27 #include "src/trace_processor/tp_metatrace.h"
28 #include "src/trace_processor/util/status_macros.h"
29 
30 #include "protos/perfetto/common/descriptor.pbzero.h"
31 #include "protos/perfetto/trace_processor/metrics_impl.pbzero.h"
32 
33 namespace perfetto {
34 namespace trace_processor {
35 namespace metrics {
36 
37 namespace {
38 
39 // TODO(lalitm): delete this and use sqlite_utils when that is cleaned up of
40 // trace processor dependencies.
ExtractSqliteValue(sqlite3_value * value)41 const char* ExtractSqliteValue(sqlite3_value* value) {
42   auto type = sqlite3_value_type(value);
43   PERFETTO_DCHECK(type == SQLITE_TEXT);
44   return reinterpret_cast<const char*>(sqlite3_value_text(value));
45 }
46 
SqlValueFromSqliteValue(sqlite3_value * value)47 SqlValue SqlValueFromSqliteValue(sqlite3_value* value) {
48   SqlValue sql_value;
49   switch (sqlite3_value_type(value)) {
50     case SQLITE_INTEGER:
51       sql_value.type = SqlValue::Type::kLong;
52       sql_value.long_value = sqlite3_value_int64(value);
53       break;
54     case SQLITE_FLOAT:
55       sql_value.type = SqlValue::Type::kDouble;
56       sql_value.double_value = sqlite3_value_double(value);
57       break;
58     case SQLITE_TEXT:
59       sql_value.type = SqlValue::Type::kString;
60       sql_value.string_value =
61           reinterpret_cast<const char*>(sqlite3_value_text(value));
62       break;
63     case SQLITE_BLOB:
64       sql_value.type = SqlValue::Type::kBytes;
65       sql_value.bytes_value = sqlite3_value_blob(value);
66       sql_value.bytes_count = static_cast<size_t>(sqlite3_value_bytes(value));
67       break;
68   }
69   return sql_value;
70 }
71 
72 }  // namespace
73 
ProtoBuilder(const ProtoDescriptor * descriptor)74 ProtoBuilder::ProtoBuilder(const ProtoDescriptor* descriptor)
75     : descriptor_(descriptor) {}
76 
AppendSqlValue(const std::string & field_name,const SqlValue & value)77 util::Status ProtoBuilder::AppendSqlValue(const std::string& field_name,
78                                           const SqlValue& value) {
79   switch (value.type) {
80     case SqlValue::kLong:
81       return AppendLong(field_name, value.long_value);
82     case SqlValue::kDouble:
83       return AppendDouble(field_name, value.double_value);
84     case SqlValue::kString:
85       return AppendString(field_name, value.string_value);
86     case SqlValue::kBytes:
87       return AppendBytes(field_name,
88                          static_cast<const uint8_t*>(value.bytes_value),
89                          value.bytes_count);
90     case SqlValue::kNull:
91       // If the value is null, it's treated as the field being absent so we
92       // don't append anything.
93       return util::OkStatus();
94   }
95   PERFETTO_FATAL("For GCC");
96 }
97 
AppendLong(const std::string & field_name,int64_t value,bool is_inside_repeated)98 util::Status ProtoBuilder::AppendLong(const std::string& field_name,
99                                       int64_t value,
100                                       bool is_inside_repeated) {
101   auto field = descriptor_->FindFieldByName(field_name);
102   if (!field) {
103     return util::ErrStatus("Field with name %s not found in proto type %s",
104                            field_name.c_str(),
105                            descriptor_->full_name().c_str());
106   }
107 
108   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
109   if (field->is_repeated() && !is_inside_repeated) {
110     return util::ErrStatus(
111         "Unexpected long value for repeated field %s in proto type %s",
112         field_name.c_str(), descriptor_->full_name().c_str());
113   }
114 
115   switch (field->type()) {
116     case FieldDescriptorProto::TYPE_INT32:
117     case FieldDescriptorProto::TYPE_INT64:
118     case FieldDescriptorProto::TYPE_UINT32:
119     case FieldDescriptorProto::TYPE_BOOL:
120     case FieldDescriptorProto::TYPE_ENUM:
121       message_->AppendVarInt(field->number(), value);
122       break;
123     case FieldDescriptorProto::TYPE_SINT32:
124     case FieldDescriptorProto::TYPE_SINT64:
125       message_->AppendSignedVarInt(field->number(), value);
126       break;
127     case FieldDescriptorProto::TYPE_FIXED32:
128     case FieldDescriptorProto::TYPE_SFIXED32:
129     case FieldDescriptorProto::TYPE_FIXED64:
130     case FieldDescriptorProto::TYPE_SFIXED64:
131       message_->AppendFixed(field->number(), value);
132       break;
133     default: {
134       return util::ErrStatus(
135           "Tried to write value of type long into field %s (in proto type %s) "
136           "which has type %d",
137           field->name().c_str(), descriptor_->full_name().c_str(),
138           field->type());
139     }
140   }
141   return util::OkStatus();
142 }
143 
AppendDouble(const std::string & field_name,double value,bool is_inside_repeated)144 util::Status ProtoBuilder::AppendDouble(const std::string& field_name,
145                                         double value,
146                                         bool is_inside_repeated) {
147   auto field = descriptor_->FindFieldByName(field_name);
148   if (!field) {
149     return util::ErrStatus("Field with name %s not found in proto type %s",
150                            field_name.c_str(),
151                            descriptor_->full_name().c_str());
152   }
153 
154   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
155   if (field->is_repeated() && !is_inside_repeated) {
156     return util::ErrStatus(
157         "Unexpected double value for repeated field %s in proto type %s",
158         field_name.c_str(), descriptor_->full_name().c_str());
159   }
160 
161   switch (field->type()) {
162     case FieldDescriptorProto::TYPE_FLOAT:
163     case FieldDescriptorProto::TYPE_DOUBLE: {
164       if (field->type() == FieldDescriptorProto::TYPE_FLOAT) {
165         message_->AppendFixed(field->number(), static_cast<float>(value));
166       } else {
167         message_->AppendFixed(field->number(), value);
168       }
169       break;
170     }
171     default: {
172       return util::ErrStatus(
173           "Tried to write value of type double into field %s (in proto type "
174           "%s) which has type %d",
175           field->name().c_str(), descriptor_->full_name().c_str(),
176           field->type());
177     }
178   }
179   return util::OkStatus();
180 }
181 
AppendString(const std::string & field_name,base::StringView data,bool is_inside_repeated)182 util::Status ProtoBuilder::AppendString(const std::string& field_name,
183                                         base::StringView data,
184                                         bool is_inside_repeated) {
185   const FieldDescriptor* field = descriptor_->FindFieldByName(field_name);
186   if (!field) {
187     return util::ErrStatus("Field with name %s not found in proto type %s",
188                            field_name.c_str(),
189                            descriptor_->full_name().c_str());
190   }
191 
192   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
193   if (field->is_repeated() && !is_inside_repeated) {
194     return util::ErrStatus(
195         "Unexpected string value for repeated field %s in proto type %s",
196         field_name.c_str(), descriptor_->full_name().c_str());
197   }
198 
199   switch (field->type()) {
200     case FieldDescriptorProto::TYPE_STRING: {
201       message_->AppendBytes(field->number(), data.data(), data.size());
202       break;
203     }
204     default: {
205       return util::ErrStatus(
206           "Tried to write value of type string into field %s (in proto type "
207           "%s) which has type %d",
208           field->name().c_str(), descriptor_->full_name().c_str(),
209           field->type());
210     }
211   }
212   return util::OkStatus();
213 }
214 
AppendBytes(const std::string & field_name,const uint8_t * ptr,size_t size,bool is_inside_repeated)215 util::Status ProtoBuilder::AppendBytes(const std::string& field_name,
216                                        const uint8_t* ptr,
217                                        size_t size,
218                                        bool is_inside_repeated) {
219   const FieldDescriptor* field = descriptor_->FindFieldByName(field_name);
220   if (!field) {
221     return util::ErrStatus("Field with name %s not found in proto type %s",
222                            field_name.c_str(),
223                            descriptor_->full_name().c_str());
224   }
225 
226   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
227   if (field->is_repeated() && !is_inside_repeated)
228     return AppendRepeated(*field, ptr, size);
229 
230   if (field->type() == FieldDescriptorProto::TYPE_MESSAGE)
231     return AppendSingleMessage(*field, ptr, size);
232 
233   if (size == 0) {
234     return util::ErrStatus(
235         "Tried to write zero-sized value into field %s (in proto type "
236         "%s). Nulls are only supported for message protos; all other types "
237         "should ensure that nulls are not passed to proto builder functions by "
238         "using the SQLite IFNULL/COALESCE functions.",
239         field->name().c_str(), descriptor_->full_name().c_str());
240   }
241 
242   return util::ErrStatus(
243       "Tried to write value of type bytes into field %s (in proto type %s) "
244       "which has type %d",
245       field->name().c_str(), descriptor_->full_name().c_str(), field->type());
246 }
247 
AppendSingleMessage(const FieldDescriptor & field,const uint8_t * ptr,size_t size)248 util::Status ProtoBuilder::AppendSingleMessage(const FieldDescriptor& field,
249                                                const uint8_t* ptr,
250                                                size_t size) {
251   // If we have an zero sized bytes, we still want to propogate that the field
252   // message was set but empty.
253   if (size == 0) {
254     // ptr can be null and passing nullptr to AppendBytes feels dangerous so
255     // just pass an empty string (which will have a valid pointer always) and
256     // zero as the size.
257     message_->AppendBytes(field.number(), "", 0);
258     return util::OkStatus();
259   }
260 
261   protos::pbzero::ProtoBuilderResult::Decoder decoder(ptr, size);
262   if (decoder.is_repeated()) {
263     return util::ErrStatus("Cannot handle nested repeated messages in field %s",
264                            field.name().c_str());
265   }
266 
267   const auto& single_field = decoder.single();
268   protos::pbzero::SingleBuilderResult::Decoder single(single_field.data,
269                                                       single_field.size);
270 
271   if (single.type() != field.type()) {
272     return util::ErrStatus("Field %s has wrong type (expected %u, was %u)",
273                            field.name().c_str(), field.type(), single.type());
274   }
275 
276   auto actual_type_name = single.type_name().ToStdString();
277   if (actual_type_name != field.resolved_type_name()) {
278     return util::ErrStatus("Field %s has wrong type (expected %s, was %s)",
279                            field.name().c_str(), actual_type_name.c_str(),
280                            field.resolved_type_name().c_str());
281   }
282 
283   if (!single.has_protobuf()) {
284     return util::ErrStatus("Field %s has no proto bytes", field.name().c_str());
285   }
286 
287   // We disallow 0 size fields here as they should have been reported as null
288   // one layer down.
289   auto bytes = single.protobuf();
290   if (bytes.size == 0) {
291     return util::ErrStatus("Unexpected to see field %s with zero size",
292                            field.name().c_str());
293   }
294 
295   message_->AppendBytes(field.number(), bytes.data, bytes.size);
296   return util::OkStatus();
297 }
298 
AppendRepeated(const FieldDescriptor & field,const uint8_t * ptr,size_t size)299 util::Status ProtoBuilder::AppendRepeated(const FieldDescriptor& field,
300                                           const uint8_t* ptr,
301                                           size_t size) {
302   protos::pbzero::ProtoBuilderResult::Decoder decoder(ptr, size);
303   if (!decoder.is_repeated()) {
304     return util::ErrStatus(
305         "Unexpected message value for repeated field %s in proto type %s",
306         field.name().c_str(), descriptor_->full_name().c_str());
307   }
308 
309   const auto& rep = decoder.repeated();
310   protos::pbzero::RepeatedBuilderResult::Decoder repeated(rep.data, rep.size);
311 
312   for (auto it = repeated.value(); it; ++it) {
313     protos::pbzero::RepeatedBuilderResult::Value::Decoder value(*it);
314     util::Status status;
315     if (value.has_int_value()) {
316       status = AppendLong(field.name(), value.int_value(), true);
317     } else if (value.has_double_value()) {
318       status = AppendDouble(field.name(), value.double_value(), true);
319     } else if (value.has_string_value()) {
320       status = AppendString(field.name(),
321                             base::StringView(value.string_value()), true);
322     } else if (value.has_bytes_value()) {
323       const auto& bytes = value.bytes_value();
324       status = AppendBytes(field.name(), bytes.data, bytes.size, true);
325     } else {
326       status = util::ErrStatus("Unknown type in repeated field");
327     }
328 
329     if (!status.ok())
330       return status;
331   }
332   return util::OkStatus();
333 }
334 
SerializeToProtoBuilderResult()335 std::vector<uint8_t> ProtoBuilder::SerializeToProtoBuilderResult() {
336   std::vector<uint8_t> serialized = SerializeRaw();
337   if (serialized.empty())
338     return serialized;
339 
340   const auto& type_name = descriptor_->full_name();
341 
342   protozero::HeapBuffered<protos::pbzero::ProtoBuilderResult> result;
343   result->set_is_repeated(false);
344 
345   auto* single = result->set_single();
346   single->set_type(protos::pbzero::FieldDescriptorProto_Type_TYPE_MESSAGE);
347   single->set_type_name(type_name.c_str(), type_name.size());
348   single->set_protobuf(serialized.data(), serialized.size());
349   return result.SerializeAsArray();
350 }
351 
SerializeRaw()352 std::vector<uint8_t> ProtoBuilder::SerializeRaw() {
353   return message_.SerializeAsArray();
354 }
355 
RepeatedFieldBuilder()356 RepeatedFieldBuilder::RepeatedFieldBuilder() {
357   repeated_ = message_->set_repeated();
358 }
359 
AddSqlValue(SqlValue value)360 util::Status RepeatedFieldBuilder::AddSqlValue(SqlValue value) {
361   switch (value.type) {
362     case SqlValue::kLong:
363       AddLong(value.long_value);
364       break;
365     case SqlValue::kDouble:
366       AddDouble(value.double_value);
367       break;
368     case SqlValue::kString:
369       AddString(value.string_value);
370       break;
371     case SqlValue::kBytes:
372       AddBytes(static_cast<const uint8_t*>(value.bytes_value),
373                value.bytes_count);
374       break;
375     case SqlValue::kNull:
376       AddBytes(nullptr, 0);
377       break;
378   }
379   return util::OkStatus();
380 }
381 
AddLong(int64_t value)382 void RepeatedFieldBuilder::AddLong(int64_t value) {
383   has_data_ = true;
384   repeated_->add_value()->set_int_value(value);
385 }
386 
AddDouble(double value)387 void RepeatedFieldBuilder::AddDouble(double value) {
388   has_data_ = true;
389   repeated_->add_value()->set_double_value(value);
390 }
391 
AddString(base::StringView value)392 void RepeatedFieldBuilder::AddString(base::StringView value) {
393   has_data_ = true;
394   repeated_->add_value()->set_string_value(value.data(), value.size());
395 }
396 
AddBytes(const uint8_t * data,size_t size)397 void RepeatedFieldBuilder::AddBytes(const uint8_t* data, size_t size) {
398   has_data_ = true;
399   repeated_->add_value()->set_bytes_value(data, size);
400 }
401 
SerializeToProtoBuilderResult()402 std::vector<uint8_t> RepeatedFieldBuilder::SerializeToProtoBuilderResult() {
403   repeated_ = nullptr;
404   if (!has_data_)
405     return std::vector<uint8_t>();
406 
407   message_->set_is_repeated(true);
408   return message_.SerializeAsArray();
409 }
410 
TemplateReplace(const std::string & raw_text,const std::unordered_map<std::string,std::string> & substitutions,std::string * out)411 int TemplateReplace(
412     const std::string& raw_text,
413     const std::unordered_map<std::string, std::string>& substitutions,
414     std::string* out) {
415   std::regex re(R"(\{\{\s*(\w*)\s*\}\})", std::regex_constants::ECMAScript);
416 
417   auto it = std::sregex_iterator(raw_text.begin(), raw_text.end(), re);
418   auto regex_end = std::sregex_iterator();
419   auto start = raw_text.begin();
420   for (; it != regex_end; ++it) {
421     out->insert(out->end(), start, raw_text.begin() + it->position(0));
422 
423     auto value_it = substitutions.find(it->str(1));
424     if (value_it == substitutions.end())
425       return 1;
426 
427     const auto& value = value_it->second;
428     std::copy(value.begin(), value.end(), std::back_inserter(*out));
429     start = raw_text.begin() + it->position(0) + it->length(0);
430   }
431   out->insert(out->end(), start, raw_text.end());
432   return 0;
433 }
434 
NullIfEmpty(sqlite3_context * ctx,int argc,sqlite3_value ** argv)435 void NullIfEmpty(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
436   // SQLite should enforce this for us.
437   PERFETTO_CHECK(argc == 1);
438 
439   if (sqlite3_value_type(argv[0]) != SQLITE_BLOB) {
440     sqlite3_result_error(
441         ctx, "NULL_IF_EMPTY: should only be called with bytes argument", -1);
442     return;
443   }
444 
445   if (sqlite3_value_bytes(argv[0]) == 0) {
446     sqlite3_result_null(ctx);
447     return;
448   }
449 
450   sqlite3_result_value(ctx, argv[0]);
451 }
452 
RepeatedFieldStep(sqlite3_context * ctx,int argc,sqlite3_value ** argv)453 void RepeatedFieldStep(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
454   if (argc != 1) {
455     sqlite3_result_error(ctx, "RepeatedField: only expected one arg", -1);
456     return;
457   }
458 
459   // We use a double indirection here so we can use new and delete without
460   // needing to do dangerous dances with placement new and checking
461   // initalization.
462   auto** builder_ptr_ptr = static_cast<RepeatedFieldBuilder**>(
463       sqlite3_aggregate_context(ctx, sizeof(RepeatedFieldBuilder*)));
464 
465   // The memory returned from sqlite3_aggregate_context is zeroed on its first
466   // invocation so *builder_ptr_ptr will be nullptr on the first invocation of
467   // RepeatedFieldStep.
468   bool needs_init = *builder_ptr_ptr == nullptr;
469   if (needs_init) {
470     *builder_ptr_ptr = new RepeatedFieldBuilder();
471   }
472 
473   auto value = SqlValueFromSqliteValue(argv[0]);
474   RepeatedFieldBuilder* builder = *builder_ptr_ptr;
475   auto status = builder->AddSqlValue(value);
476   if (!status.ok()) {
477     sqlite3_result_error(ctx, status.c_message(), -1);
478   }
479 }
480 
RepeatedFieldFinal(sqlite3_context * ctx)481 void RepeatedFieldFinal(sqlite3_context* ctx) {
482   // Note: we choose the size intentionally to be zero because we don't want to
483   // allocate if the Step has never been called.
484   auto** builder_ptr_ptr =
485       static_cast<RepeatedFieldBuilder**>(sqlite3_aggregate_context(ctx, 0));
486 
487   // If Step has never been called, |builder_ptr_ptr| will be null.
488   if (builder_ptr_ptr == nullptr) {
489     sqlite3_result_null(ctx);
490     return;
491   }
492 
493   // Capture the context pointer so that it will be freed at the end of this
494   // function.
495   std::unique_ptr<RepeatedFieldBuilder> builder(*builder_ptr_ptr);
496   std::vector<uint8_t> raw = builder->SerializeToProtoBuilderResult();
497   if (raw.empty()) {
498     sqlite3_result_null(ctx);
499     return;
500   }
501 
502   std::unique_ptr<uint8_t[], base::FreeDeleter> data(
503       static_cast<uint8_t*>(malloc(raw.size())));
504   memcpy(data.get(), raw.data(), raw.size());
505   sqlite3_result_blob(ctx, data.release(), static_cast<int>(raw.size()), free);
506 }
507 
508 // SQLite function implementation used to build a proto directly in SQL. The
509 // proto to be built is given by the descriptor which is given as a context
510 // parameter to this function and chosen when this function is first registed
511 // with SQLite. The args of this function are key value pairs specifying the
512 // name of the field and its value. Nested messages are expected to be passed
513 // as byte blobs (as they were built recursively using this function).
514 // The return value is the built proto or an error about why the proto could
515 // not be built.
BuildProto(sqlite3_context * ctx,int argc,sqlite3_value ** argv)516 void BuildProto(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
517   const auto* fn_ctx =
518       static_cast<const BuildProtoContext*>(sqlite3_user_data(ctx));
519   if (argc % 2 != 0) {
520     util::Status error =
521         util::ErrStatus("Invalid number of args to %s BuildProto (got %d)",
522                         fn_ctx->desc->full_name().c_str(), argc);
523     sqlite3_result_error(ctx, error.c_message(), -1);
524     return;
525   }
526 
527   ProtoBuilder builder(fn_ctx->desc);
528   for (int i = 0; i < argc; i += 2) {
529     if (sqlite3_value_type(argv[i]) != SQLITE_TEXT) {
530       sqlite3_result_error(ctx, "BuildProto: Invalid args", -1);
531       return;
532     }
533 
534     auto* key = reinterpret_cast<const char*>(sqlite3_value_text(argv[i]));
535     auto value = SqlValueFromSqliteValue(argv[i + 1]);
536     auto status = builder.AppendSqlValue(key, value);
537     if (!status.ok()) {
538       sqlite3_result_error(ctx, status.c_message(), -1);
539       return;
540     }
541   }
542 
543   // Even if the message is empty, we don't return null here as we want the
544   // existence of the message to be respected.
545   std::vector<uint8_t> raw = builder.SerializeToProtoBuilderResult();
546   if (raw.empty()) {
547     // Passing nullptr to SQLite feels dangerous so just pass an empty string
548     // and zero as the size so we don't deref nullptr accidentially somewhere.
549     sqlite3_result_blob(ctx, "", 0, nullptr);
550     return;
551   }
552 
553   std::unique_ptr<uint8_t[], base::FreeDeleter> data(
554       static_cast<uint8_t*>(malloc(raw.size())));
555   memcpy(data.get(), raw.data(), raw.size());
556   sqlite3_result_blob(ctx, data.release(), static_cast<int>(raw.size()), free);
557 }
558 
RunMetric(sqlite3_context * ctx,int argc,sqlite3_value ** argv)559 void RunMetric(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
560   auto* fn_ctx = static_cast<RunMetricContext*>(sqlite3_user_data(ctx));
561   if (argc == 0 || sqlite3_value_type(argv[0]) != SQLITE_TEXT) {
562     sqlite3_result_error(ctx, "RUN_METRIC: Invalid arguments", -1);
563     return;
564   }
565 
566   const char* path = reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
567   auto metric_it = std::find_if(
568       fn_ctx->metrics->begin(), fn_ctx->metrics->end(),
569       [path](const SqlMetricFile& metric) { return metric.path == path; });
570   if (metric_it == fn_ctx->metrics->end()) {
571     sqlite3_result_error(ctx, "RUN_METRIC: Unknown filename provided", -1);
572     return;
573   }
574   const auto& sql = metric_it->sql;
575 
576   std::unordered_map<std::string, std::string> substitutions;
577   for (int i = 1; i < argc; i += 2) {
578     if (sqlite3_value_type(argv[i]) != SQLITE_TEXT) {
579       sqlite3_result_error(ctx, "RUN_METRIC: Invalid args", -1);
580       return;
581     }
582 
583     auto* key_str = ExtractSqliteValue(argv[i]);
584     auto* value_str = ExtractSqliteValue(argv[i + 1]);
585     substitutions[key_str] = value_str;
586   }
587 
588   for (const auto& query : base::SplitString(sql, ";\n")) {
589     std::string buffer;
590     int ret = TemplateReplace(query, substitutions, &buffer);
591     if (ret) {
592       char* error = sqlite3_mprintf(
593           "RUN_METRIC: Error when performing substitutions: %s", query.c_str());
594       sqlite3_result_error(ctx, error, -1);
595       return;
596     }
597 
598     PERFETTO_DLOG("RUN_METRIC: Executing query: %s", buffer.c_str());
599     auto it = fn_ctx->tp->ExecuteQuery(buffer);
600     it.Next();
601 
602     util::Status status = it.Status();
603     if (!status.ok()) {
604       char* error =
605           sqlite3_mprintf("RUN_METRIC: Error when running file %s: %s", path,
606                           status.c_message());
607       sqlite3_result_error(ctx, error, -1);
608       sqlite3_free(error);
609       return;
610     }
611   }
612   sqlite3_result_null(ctx);
613 }
614 
ComputeMetrics(TraceProcessor * tp,const std::vector<std::string> metrics_to_compute,const std::vector<SqlMetricFile> & sql_metrics,const ProtoDescriptor & root_descriptor,std::vector<uint8_t> * metrics_proto)615 util::Status ComputeMetrics(TraceProcessor* tp,
616                             const std::vector<std::string> metrics_to_compute,
617                             const std::vector<SqlMetricFile>& sql_metrics,
618                             const ProtoDescriptor& root_descriptor,
619                             std::vector<uint8_t>* metrics_proto) {
620   ProtoBuilder metric_builder(&root_descriptor);
621   for (const auto& name : metrics_to_compute) {
622     auto metric_it =
623         std::find_if(sql_metrics.begin(), sql_metrics.end(),
624                      [&name](const SqlMetricFile& metric) {
625                        return metric.proto_field_name.has_value() &&
626                               name == metric.proto_field_name.value();
627                      });
628     if (metric_it == sql_metrics.end())
629       return util::ErrStatus("Unknown metric %s", name.c_str());
630 
631     const auto& sql_metric = *metric_it;
632     auto queries = base::SplitString(sql_metric.sql, ";\n");
633     for (const auto& query : queries) {
634       PERFETTO_DLOG("Executing query: %s", query.c_str());
635       auto prep_it = tp->ExecuteQuery(query);
636       prep_it.Next();
637       RETURN_IF_ERROR(prep_it.Status());
638     }
639 
640     auto output_query =
641         "SELECT * FROM " + sql_metric.output_table_name.value() + ";";
642     PERFETTO_DLOG("Executing output query: %s", output_query.c_str());
643     PERFETTO_TP_TRACE("COMPUTE_METRIC_QUERY", [&](metatrace::Record* r) {
644       r->AddArg("SQL", output_query);
645     });
646 
647     auto it = tp->ExecuteQuery(output_query.c_str());
648     auto has_next = it.Next();
649     RETURN_IF_ERROR(it.Status());
650 
651     // Allow the query to return no rows. This has the same semantic as an
652     // empty proto being returned.
653     const auto& field_name = sql_metric.proto_field_name.value();
654     if (!has_next) {
655       metric_builder.AppendBytes(field_name, nullptr, 0);
656       continue;
657     }
658 
659     if (it.ColumnCount() != 1) {
660       return util::ErrStatus("Output table %s should have exactly one column",
661                              sql_metric.output_table_name.value().c_str());
662     }
663 
664     SqlValue col = it.Get(0);
665     if (col.type != SqlValue::kBytes) {
666       return util::ErrStatus("Output table %s column has invalid type",
667                              sql_metric.output_table_name.value().c_str());
668     }
669     RETURN_IF_ERROR(metric_builder.AppendSqlValue(field_name, col));
670 
671     has_next = it.Next();
672     if (has_next) {
673       return util::ErrStatus("Output table %s should have at most one row",
674                              sql_metric.output_table_name.value().c_str());
675     }
676 
677     RETURN_IF_ERROR(it.Status());
678   }
679   *metrics_proto = metric_builder.SerializeRaw();
680   return util::OkStatus();
681 }
682 
683 }  // namespace metrics
684 }  // namespace trace_processor
685 }  // namespace perfetto
686