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