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