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