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