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