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