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