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