1 /*
2 * Copyright (C) 2018 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 <fcntl.h>
18 #include <sys/stat.h>
19 #include <algorithm>
20 #include <cctype>
21 #include <cerrno>
22 #include <chrono>
23 #include <cinttypes>
24 #include <cstdint>
25 #include <cstdio>
26 #include <cstdlib>
27 #include <cstring>
28 #include <memory>
29 #include <optional>
30 #include <string>
31 #include <string_view>
32 #include <unordered_set>
33 #include <utility>
34 #include <vector>
35
36 #include <google/protobuf/compiler/parser.h>
37 #include <google/protobuf/descriptor.pb.h>
38 #include <google/protobuf/dynamic_message.h>
39 #include <google/protobuf/io/tokenizer.h>
40 #include <google/protobuf/io/zero_copy_stream_impl.h>
41 #include <google/protobuf/text_format.h>
42
43 #include "perfetto/base/build_config.h"
44 #include "perfetto/base/logging.h"
45 #include "perfetto/base/status.h"
46 #include "perfetto/base/time.h"
47 #include "perfetto/ext/base/file_utils.h"
48 #include "perfetto/ext/base/getopt.h" // IWYU pragma: keep
49 #include "perfetto/ext/base/scoped_file.h"
50 #include "perfetto/ext/base/scoped_mmap.h"
51 #include "perfetto/ext/base/status_or.h"
52 #include "perfetto/ext/base/string_splitter.h"
53 #include "perfetto/ext/base/string_utils.h"
54 #include "perfetto/ext/base/version.h"
55 #include "perfetto/trace_processor/basic_types.h"
56 #include "perfetto/trace_processor/iterator.h"
57 #include "perfetto/trace_processor/metatrace_config.h"
58 #include "perfetto/trace_processor/read_trace.h"
59 #include "perfetto/trace_processor/trace_blob.h"
60 #include "perfetto/trace_processor/trace_blob_view.h"
61 #include "perfetto/trace_processor/trace_processor.h"
62 #include "src/profiling/deobfuscator.h"
63 #include "src/profiling/symbolizer/local_symbolizer.h"
64 #include "src/profiling/symbolizer/symbolize_database.h"
65 #include "src/profiling/symbolizer/symbolizer.h"
66 #include "src/trace_processor/metrics/all_chrome_metrics.descriptor.h"
67 #include "src/trace_processor/metrics/all_webview_metrics.descriptor.h"
68 #include "src/trace_processor/metrics/metrics.descriptor.h"
69 #include "src/trace_processor/read_trace_internal.h"
70 #include "src/trace_processor/rpc/stdiod.h"
71 #include "src/trace_processor/util/sql_modules.h"
72 #include "src/trace_processor/util/status_macros.h"
73
74 #include "protos/perfetto/trace_processor/trace_processor.pbzero.h"
75
76 #if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
77 #include "src/trace_processor/rpc/httpd.h"
78 #endif
79
80 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
81 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
82 PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
83 #define PERFETTO_HAS_SIGNAL_H() 1
84 #else
85 #define PERFETTO_HAS_SIGNAL_H() 0
86 #endif
87
88 #if PERFETTO_BUILDFLAG(PERFETTO_TP_LINENOISE)
89 #include <linenoise.h>
90 #include <pwd.h>
91 #include <sys/types.h>
92 #endif
93
94 #if PERFETTO_HAS_SIGNAL_H()
95 #include <signal.h>
96 #endif
97
98 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
99 #include <io.h>
100 #define ftruncate _chsize
101 #else
102 #include <dirent.h>
103 #endif
104
105 #if PERFETTO_BUILDFLAG(PERFETTO_TP_LINENOISE) && \
106 !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
107 #include <unistd.h> // For getuid() in GetConfigPath().
108 #endif
109
110 namespace perfetto::trace_processor {
111
112 namespace {
113 TraceProcessor* g_tp;
114
115 #if PERFETTO_BUILDFLAG(PERFETTO_TP_LINENOISE)
116
EnsureDir(const std::string & path)117 bool EnsureDir(const std::string& path) {
118 return base::Mkdir(path) || errno == EEXIST;
119 }
120
EnsureFile(const std::string & path)121 bool EnsureFile(const std::string& path) {
122 return base::OpenFile(path, O_RDONLY | O_CREAT, 0644).get() != -1;
123 }
124
GetConfigPath()125 std::string GetConfigPath() {
126 const char* homedir = getenv("HOME");
127 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
128 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
129 PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
130 if (homedir == nullptr)
131 homedir = getpwuid(getuid())->pw_dir;
132 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
133 if (homedir == nullptr)
134 homedir = getenv("USERPROFILE");
135 #endif
136 if (homedir == nullptr)
137 return "";
138 return std::string(homedir) + "/.config";
139 }
140
GetPerfettoPath()141 std::string GetPerfettoPath() {
142 std::string config = GetConfigPath();
143 if (config.empty())
144 return "";
145 return config + "/perfetto";
146 }
147
GetHistoryPath()148 std::string GetHistoryPath() {
149 std::string perfetto = GetPerfettoPath();
150 if (perfetto.empty())
151 return "";
152 return perfetto + "/.trace_processor_shell_history";
153 }
154
SetupLineEditor()155 void SetupLineEditor() {
156 linenoiseSetMultiLine(true);
157 linenoiseHistorySetMaxLen(1000);
158
159 bool success = !GetHistoryPath().empty();
160 success = success && EnsureDir(GetConfigPath());
161 success = success && EnsureDir(GetPerfettoPath());
162 success = success && EnsureFile(GetHistoryPath());
163 success = success && linenoiseHistoryLoad(GetHistoryPath().c_str()) != -1;
164 if (!success) {
165 PERFETTO_PLOG("Could not load history from %s", GetHistoryPath().c_str());
166 }
167 }
168
169 struct LineDeleter {
operator ()perfetto::trace_processor::__anon4e36744c0111::LineDeleter170 void operator()(char* p) const {
171 linenoiseHistoryAdd(p);
172 linenoiseHistorySave(GetHistoryPath().c_str());
173 linenoiseFree(p);
174 }
175 };
176
177 using ScopedLine = std::unique_ptr<char, LineDeleter>;
178
GetLine(const char * prompt)179 ScopedLine GetLine(const char* prompt) {
180 errno = 0;
181 auto line = ScopedLine(linenoise(prompt));
182 // linenoise returns a nullptr both for CTRL-C and CTRL-D, however in the
183 // former case it sets errno to EAGAIN.
184 // If the user press CTRL-C return "" instead of nullptr. We don't want the
185 // main loop to quit in that case as that is inconsistent with the behavior
186 // "CTRL-C interrupts the current query" and frustrating when hitting that
187 // a split second after the query is done.
188 if (!line && errno == EAGAIN)
189 return ScopedLine(strdup(""));
190 return line;
191 }
192
193 #else
194
SetupLineEditor()195 void SetupLineEditor() {}
196
197 using ScopedLine = std::unique_ptr<char>;
198
GetLine(const char * prompt)199 ScopedLine GetLine(const char* prompt) {
200 printf("\r%80s\r%s", "", prompt);
201 fflush(stdout);
202 ScopedLine line(new char[1024]);
203 if (!fgets(line.get(), 1024 - 1, stdin))
204 return nullptr;
205 if (strlen(line.get()) > 0)
206 line.get()[strlen(line.get()) - 1] = 0;
207 return line;
208 }
209
210 #endif // PERFETTO_TP_LINENOISE
211
PrintStats()212 base::Status PrintStats() {
213 auto it = g_tp->ExecuteQuery(
214 "SELECT name, idx, source, value from stats "
215 "where severity IN ('error', 'data_loss') and value > 0");
216
217 bool first = true;
218 while (it.Next()) {
219 if (first) {
220 fprintf(stderr, "Error stats for this trace:\n");
221
222 for (uint32_t i = 0; i < it.ColumnCount(); i++)
223 fprintf(stderr, "%40s ", it.GetColumnName(i).c_str());
224 fprintf(stderr, "\n");
225
226 for (uint32_t i = 0; i < it.ColumnCount(); i++)
227 fprintf(stderr, "%40s ", "----------------------------------------");
228 fprintf(stderr, "\n");
229
230 first = false;
231 }
232
233 for (uint32_t c = 0; c < it.ColumnCount(); c++) {
234 auto value = it.Get(c);
235 switch (value.type) {
236 case SqlValue::Type::kNull:
237 fprintf(stderr, "%-40.40s", "[NULL]");
238 break;
239 case SqlValue::Type::kDouble:
240 fprintf(stderr, "%40f", value.double_value);
241 break;
242 case SqlValue::Type::kLong:
243 fprintf(stderr, "%40" PRIi64, value.long_value);
244 break;
245 case SqlValue::Type::kString:
246 fprintf(stderr, "%-40.40s", value.string_value);
247 break;
248 case SqlValue::Type::kBytes:
249 printf("%-40.40s", "<raw bytes>");
250 break;
251 }
252 fprintf(stderr, " ");
253 }
254 fprintf(stderr, "\n");
255 }
256
257 base::Status status = it.Status();
258 if (!status.ok()) {
259 return base::ErrStatus("Error while iterating stats (%s)",
260 status.c_message());
261 }
262 return base::OkStatus();
263 }
264
ExportTraceToDatabase(const std::string & output_name)265 base::Status ExportTraceToDatabase(const std::string& output_name) {
266 PERFETTO_CHECK(output_name.find('\'') == std::string::npos);
267 {
268 base::ScopedFile fd(base::OpenFile(output_name, O_CREAT | O_RDWR, 0600));
269 if (!fd)
270 return base::ErrStatus("Failed to create file: %s", output_name.c_str());
271 int res = ftruncate(fd.get(), 0);
272 PERFETTO_CHECK(res == 0);
273 }
274
275 std::string attach_sql =
276 "ATTACH DATABASE '" + output_name + "' AS perfetto_export";
277 auto attach_it = g_tp->ExecuteQuery(attach_sql);
278 bool attach_has_more = attach_it.Next();
279 PERFETTO_DCHECK(!attach_has_more);
280
281 RETURN_IF_ERROR(attach_it.Status());
282
283 // Export real and virtual tables.
284 auto tables_it = g_tp->ExecuteQuery("SELECT name FROM perfetto_tables");
285 while (tables_it.Next()) {
286 std::string table_name = tables_it.Get(0).string_value;
287 PERFETTO_CHECK(!base::Contains(table_name, '\''));
288 std::string export_sql = "CREATE TABLE perfetto_export." + table_name +
289 " AS SELECT * FROM " + table_name;
290
291 auto export_it = g_tp->ExecuteQuery(export_sql);
292 bool export_has_more = export_it.Next();
293 PERFETTO_DCHECK(!export_has_more);
294 RETURN_IF_ERROR(export_it.Status());
295 }
296 RETURN_IF_ERROR(tables_it.Status());
297
298 // Export views.
299 auto views_it =
300 g_tp->ExecuteQuery("SELECT sql FROM sqlite_master WHERE type='view'");
301 while (views_it.Next()) {
302 std::string sql = views_it.Get(0).string_value;
303 // View statements are of the form "CREATE VIEW name AS stmt". We need to
304 // rewrite name to point to the exported db.
305 const std::string kPrefix = "CREATE VIEW ";
306 PERFETTO_CHECK(sql.find(kPrefix) == 0);
307 sql = sql.substr(0, kPrefix.size()) + "perfetto_export." +
308 sql.substr(kPrefix.size());
309
310 auto export_it = g_tp->ExecuteQuery(sql);
311 bool export_has_more = export_it.Next();
312 PERFETTO_DCHECK(!export_has_more);
313 RETURN_IF_ERROR(export_it.Status());
314 }
315 RETURN_IF_ERROR(views_it.Status());
316
317 auto detach_it = g_tp->ExecuteQuery("DETACH DATABASE perfetto_export");
318 bool detach_has_more = attach_it.Next();
319 PERFETTO_DCHECK(!detach_has_more);
320 return detach_it.Status();
321 }
322
323 class ErrorPrinter : public google::protobuf::io::ErrorCollector {
AddError(int line,int col,const std::string & msg)324 void AddError(int line, int col, const std::string& msg) override {
325 PERFETTO_ELOG("%d:%d: %s", line, col, msg.c_str());
326 }
327
AddWarning(int line,int col,const std::string & msg)328 void AddWarning(int line, int col, const std::string& msg) override {
329 PERFETTO_ILOG("%d:%d: %s", line, col, msg.c_str());
330 }
331 };
332
333 // This function returns an indentifier for a metric suitable for use
334 // as an SQL table name (i.e. containing no forward or backward slashes).
BaseName(std::string metric_path)335 std::string BaseName(std::string metric_path) {
336 std::replace(metric_path.begin(), metric_path.end(), '\\', '/');
337 auto slash_idx = metric_path.rfind('/');
338 return slash_idx == std::string::npos ? metric_path
339 : metric_path.substr(slash_idx + 1);
340 }
341
RegisterMetric(const std::string & register_metric)342 base::Status RegisterMetric(const std::string& register_metric) {
343 std::string sql;
344 base::ReadFile(register_metric, &sql);
345
346 std::string path = "shell/" + BaseName(register_metric);
347 return g_tp->RegisterMetric(path, sql);
348 }
349
ParseToFileDescriptorProto(const std::string & filename,google::protobuf::FileDescriptorProto * file_desc)350 base::Status ParseToFileDescriptorProto(
351 const std::string& filename,
352 google::protobuf::FileDescriptorProto* file_desc) {
353 base::ScopedFile file(base::OpenFile(filename, O_RDONLY));
354 if (file.get() == -1) {
355 return base::ErrStatus("Failed to open proto file %s", filename.c_str());
356 }
357
358 google::protobuf::io::FileInputStream stream(file.get());
359 ErrorPrinter printer;
360 google::protobuf::io::Tokenizer tokenizer(&stream, &printer);
361
362 google::protobuf::compiler::Parser parser;
363 parser.Parse(&tokenizer, file_desc);
364 return base::OkStatus();
365 }
366
ExtendMetricsProto(const std::string & extend_metrics_proto,google::protobuf::DescriptorPool * pool)367 base::Status ExtendMetricsProto(const std::string& extend_metrics_proto,
368 google::protobuf::DescriptorPool* pool) {
369 google::protobuf::FileDescriptorSet desc_set;
370 auto* file_desc = desc_set.add_file();
371 RETURN_IF_ERROR(ParseToFileDescriptorProto(extend_metrics_proto, file_desc));
372
373 file_desc->set_name(BaseName(extend_metrics_proto));
374 pool->BuildFile(*file_desc);
375
376 std::vector<uint8_t> metric_proto;
377 metric_proto.resize(desc_set.ByteSizeLong());
378 desc_set.SerializeToArray(metric_proto.data(),
379 static_cast<int>(metric_proto.size()));
380
381 return g_tp->ExtendMetricsProto(metric_proto.data(), metric_proto.size());
382 }
383
384 enum MetricV1OutputFormat {
385 kBinaryProto,
386 kTextProto,
387 kJson,
388 kNone,
389 };
390
391 struct MetricNameAndPath {
392 std::string name;
393 std::optional<std::string> no_ext_path;
394 };
395
RunMetrics(const std::vector<MetricNameAndPath> & metrics,MetricV1OutputFormat format)396 base::Status RunMetrics(const std::vector<MetricNameAndPath>& metrics,
397 MetricV1OutputFormat format) {
398 std::vector<std::string> metric_names(metrics.size());
399 for (size_t i = 0; i < metrics.size(); ++i) {
400 metric_names[i] = metrics[i].name;
401 }
402
403 switch (format) {
404 case MetricV1OutputFormat::kBinaryProto: {
405 std::vector<uint8_t> metric_result;
406 RETURN_IF_ERROR(g_tp->ComputeMetric(metric_names, &metric_result));
407 fwrite(metric_result.data(), sizeof(uint8_t), metric_result.size(),
408 stdout);
409 break;
410 }
411 case MetricV1OutputFormat::kJson: {
412 std::string out;
413 RETURN_IF_ERROR(g_tp->ComputeMetricText(
414 metric_names, TraceProcessor::MetricResultFormat::kJson, &out));
415 out += '\n';
416 fwrite(out.c_str(), sizeof(char), out.size(), stdout);
417 break;
418 }
419 case MetricV1OutputFormat::kTextProto: {
420 std::string out;
421 RETURN_IF_ERROR(g_tp->ComputeMetricText(
422 metric_names, TraceProcessor::MetricResultFormat::kProtoText, &out));
423 out += '\n';
424 fwrite(out.c_str(), sizeof(char), out.size(), stdout);
425 break;
426 }
427 case MetricV1OutputFormat::kNone:
428 break;
429 }
430
431 return base::OkStatus();
432 }
433
PrintQueryResultInteractively(Iterator * it,base::TimeNanos t_start,uint32_t column_width)434 void PrintQueryResultInteractively(Iterator* it,
435 base::TimeNanos t_start,
436 uint32_t column_width) {
437 base::TimeNanos t_end = base::GetWallTimeNs();
438 for (uint32_t rows = 0; it->Next(); rows++) {
439 if (rows % 32 == 0) {
440 if (rows == 0) {
441 t_end = base::GetWallTimeNs();
442 } else {
443 fprintf(stderr, "...\nType 'q' to stop, Enter for more records: ");
444 fflush(stderr);
445 char input[32];
446 if (!fgets(input, sizeof(input) - 1, stdin))
447 exit(0);
448 if (input[0] == 'q')
449 break;
450 }
451 for (uint32_t i = 0; i < it->ColumnCount(); i++)
452 printf("%-*.*s ", column_width, column_width,
453 it->GetColumnName(i).c_str());
454 printf("\n");
455
456 std::string divider(column_width, '-');
457 for (uint32_t i = 0; i < it->ColumnCount(); i++) {
458 printf("%-*s ", column_width, divider.c_str());
459 }
460 printf("\n");
461 }
462
463 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
464 auto value = it->Get(c);
465 switch (value.type) {
466 case SqlValue::Type::kNull:
467 printf("%-*s", column_width, "[NULL]");
468 break;
469 case SqlValue::Type::kDouble:
470 printf("%*f", column_width, value.double_value);
471 break;
472 case SqlValue::Type::kLong:
473 printf("%*" PRIi64, column_width, value.long_value);
474 break;
475 case SqlValue::Type::kString:
476 printf("%-*.*s", column_width, column_width, value.string_value);
477 break;
478 case SqlValue::Type::kBytes:
479 printf("%-*s", column_width, "<raw bytes>");
480 break;
481 }
482 printf(" ");
483 }
484 printf("\n");
485 }
486
487 base::Status status = it->Status();
488 if (!status.ok()) {
489 fprintf(stderr, "%s\n", status.c_message());
490 }
491 printf("\nQuery executed in %.3f ms\n\n",
492 static_cast<double>((t_end - t_start).count()) / 1E6);
493 }
494
495 struct QueryResult {
496 std::vector<std::string> column_names;
497 std::vector<std::vector<std::string>> rows;
498 };
499
ExtractQueryResult(Iterator * it,bool has_more)500 base::StatusOr<QueryResult> ExtractQueryResult(Iterator* it, bool has_more) {
501 QueryResult result;
502
503 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
504 fprintf(stderr, "column %u = %s\n", c, it->GetColumnName(c).c_str());
505 result.column_names.push_back(it->GetColumnName(c));
506 }
507
508 for (; has_more; has_more = it->Next()) {
509 std::vector<std::string> row;
510 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
511 SqlValue value = it->Get(c);
512 std::string str_value;
513 switch (value.type) {
514 case SqlValue::Type::kNull:
515 str_value = "\"[NULL]\"";
516 break;
517 case SqlValue::Type::kDouble:
518 str_value =
519 base::StackString<256>("%f", value.double_value).ToStdString();
520 break;
521 case SqlValue::Type::kLong:
522 str_value = base::StackString<256>("%" PRIi64, value.long_value)
523 .ToStdString();
524 break;
525 case SqlValue::Type::kString:
526 str_value = '"' + std::string(value.string_value) + '"';
527 break;
528 case SqlValue::Type::kBytes:
529 str_value = "\"<raw bytes>\"";
530 break;
531 }
532
533 row.push_back(std::move(str_value));
534 }
535 result.rows.push_back(std::move(row));
536 }
537 RETURN_IF_ERROR(it->Status());
538 return result;
539 }
540
PrintQueryResultAsCsv(const QueryResult & result,FILE * output)541 void PrintQueryResultAsCsv(const QueryResult& result, FILE* output) {
542 for (uint32_t c = 0; c < result.column_names.size(); c++) {
543 if (c > 0)
544 fprintf(output, ",");
545 fprintf(output, "\"%s\"", result.column_names[c].c_str());
546 }
547 fprintf(output, "\n");
548
549 for (const auto& row : result.rows) {
550 for (uint32_t c = 0; c < result.column_names.size(); c++) {
551 if (c > 0)
552 fprintf(output, ",");
553 fprintf(output, "%s", row[c].c_str());
554 }
555 fprintf(output, "\n");
556 }
557 }
558
RunQueriesWithoutOutput(const std::string & sql_query)559 base::Status RunQueriesWithoutOutput(const std::string& sql_query) {
560 auto it = g_tp->ExecuteQuery(sql_query);
561 if (it.StatementWithOutputCount() > 0)
562 return base::ErrStatus("Unexpected result from a query.");
563
564 RETURN_IF_ERROR(it.Status());
565 return it.Next() ? base::ErrStatus("Unexpected result from a query.")
566 : it.Status();
567 }
568
RunQueriesAndPrintResult(const std::string & sql_query,FILE * output)569 base::Status RunQueriesAndPrintResult(const std::string& sql_query,
570 FILE* output) {
571 PERFETTO_DLOG("Executing query: %s", sql_query.c_str());
572 auto query_start = std::chrono::steady_clock::now();
573
574 auto it = g_tp->ExecuteQuery(sql_query);
575 RETURN_IF_ERROR(it.Status());
576
577 bool has_more = it.Next();
578 RETURN_IF_ERROR(it.Status());
579
580 uint32_t prev_count = it.StatementCount() - 1;
581 uint32_t prev_with_output = has_more ? it.StatementWithOutputCount() - 1
582 : it.StatementWithOutputCount();
583 uint32_t prev_without_output_count = prev_count - prev_with_output;
584 if (prev_with_output > 0) {
585 return base::ErrStatus(
586 "Result rows were returned for multiples queries. Ensure that only the "
587 "final statement is a SELECT statment or use `suppress_query_output` "
588 "to prevent function invocations causing this "
589 "error (see "
590 "https://perfetto.dev/docs/contributing/"
591 "testing#trace-processor-diff-tests).");
592 }
593 for (uint32_t i = 0; i < prev_without_output_count; ++i) {
594 fprintf(output, "\n");
595 }
596 if (it.ColumnCount() == 0) {
597 PERFETTO_DCHECK(!has_more);
598 return base::OkStatus();
599 }
600
601 auto query_result = ExtractQueryResult(&it, has_more);
602 RETURN_IF_ERROR(query_result.status());
603
604 // We want to include the query iteration time (as it's a part of executing
605 // SQL and can be non-trivial), and we want to exclude the time spent printing
606 // the result (which can be significant for large results), so we materialise
607 // the results first, then take the measurement, then print them.
608 auto query_end = std::chrono::steady_clock::now();
609
610 PrintQueryResultAsCsv(query_result.value(), output);
611
612 auto dur = query_end - query_start;
613 PERFETTO_ILOG(
614 "Query execution time: %" PRIi64 " ms",
615 static_cast<int64_t>(
616 std::chrono::duration_cast<std::chrono::milliseconds>(dur).count()));
617 return base::OkStatus();
618 }
619
PrintPerfFile(const std::string & perf_file_path,base::TimeNanos t_load,base::TimeNanos t_run)620 base::Status PrintPerfFile(const std::string& perf_file_path,
621 base::TimeNanos t_load,
622 base::TimeNanos t_run) {
623 char buf[128];
624 size_t count = base::SprintfTrunc(buf, sizeof(buf), "%" PRId64 ",%" PRId64,
625 static_cast<int64_t>(t_load.count()),
626 static_cast<int64_t>(t_run.count()));
627 if (count == 0) {
628 return base::ErrStatus("Failed to write perf data");
629 }
630
631 auto fd(base::OpenFile(perf_file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666));
632 if (!fd) {
633 return base::ErrStatus("Failed to open perf file");
634 }
635 base::WriteAll(fd.get(), buf, count);
636 return base::OkStatus();
637 }
638
639 class MetricExtension {
640 public:
SetDiskPath(std::string path)641 void SetDiskPath(std::string path) {
642 AddTrailingSlashIfNeeded(path);
643 disk_path_ = std::move(path);
644 }
SetVirtualPath(std::string path)645 void SetVirtualPath(std::string path) {
646 AddTrailingSlashIfNeeded(path);
647 virtual_path_ = std::move(path);
648 }
649
650 // Disk location. Ends with a trailing slash.
disk_path() const651 const std::string& disk_path() const { return disk_path_; }
652 // Virtual location. Ends with a trailing slash.
virtual_path() const653 const std::string& virtual_path() const { return virtual_path_; }
654
655 private:
656 std::string disk_path_;
657 std::string virtual_path_;
658
AddTrailingSlashIfNeeded(std::string & path)659 static void AddTrailingSlashIfNeeded(std::string& path) {
660 if (path.length() > 0 && path[path.length() - 1] != '/') {
661 path.push_back('/');
662 }
663 }
664 };
665
ParseMetatraceCategories(std::string s)666 metatrace::MetatraceCategories ParseMetatraceCategories(std::string s) {
667 using Cat = metatrace::MetatraceCategories;
668 std::transform(s.begin(), s.end(), s.begin(),
669 [](unsigned char c) { return std::tolower(c); });
670 base::StringSplitter splitter(s, ',');
671
672 Cat result = Cat::NONE;
673 for (; splitter.Next();) {
674 std::string cur = splitter.cur_token();
675 if (cur == "all" || cur == "*") {
676 result = Cat::ALL;
677 } else if (cur == "query_toplevel") {
678 result = static_cast<Cat>(result | Cat::QUERY_TIMELINE);
679 } else if (cur == "query_detailed") {
680 result = static_cast<Cat>(result | Cat::QUERY_DETAILED);
681 } else if (cur == "function_call") {
682 result = static_cast<Cat>(result | Cat::FUNCTION_CALL);
683 } else if (cur == "db") {
684 result = static_cast<Cat>(result | Cat::DB);
685 } else if (cur == "api") {
686 result = static_cast<Cat>(result | Cat::API_TIMELINE);
687 } else {
688 PERFETTO_ELOG("Unknown metatrace category %s", cur.data());
689 exit(1);
690 }
691 }
692 return result;
693 }
694
695 struct CommandLineOptions {
696 std::string trace_file_path;
697
698 bool enable_httpd = false;
699 std::string port_number;
700 bool enable_stdiod = false;
701 bool launch_shell = false;
702
703 bool force_full_sort = false;
704 bool no_ftrace_raw = false;
705
706 std::string query_file_path;
707 std::string query_string;
708 std::string sql_module_path;
709 std::vector<std::string> override_sql_module_paths;
710
711 bool summary = false;
712 std::string summary_metrics_v2;
713 std::string summary_metadata_query;
714 std::vector<std::string> summary_specs;
715 std::string summary_output;
716
717 std::string metatrace_path;
718 size_t metatrace_buffer_capacity = 0;
719 metatrace::MetatraceCategories metatrace_categories =
720 static_cast<metatrace::MetatraceCategories>(
721 metatrace::MetatraceCategories::QUERY_TIMELINE |
722 metatrace::MetatraceCategories::API_TIMELINE);
723
724 bool dev = false;
725 std::vector<std::string> dev_flags;
726 bool extra_checks = false;
727 std::string export_file_path;
728 std::string perf_file_path;
729 bool wide = false;
730 bool analyze_trace_proto_content = false;
731 bool crop_track_events = false;
732 std::string register_files_dir;
733 std::string override_stdlib_path;
734
735 std::string pre_metrics_v1_path;
736 std::string metric_v1_names;
737 std::string metric_v1_output;
738 std::vector<std::string> raw_metric_v1_extensions;
739 };
740
PrintUsage(char ** argv)741 void PrintUsage(char** argv) {
742 PERFETTO_ELOG(R"(
743 Interactive trace processor shell.
744 Usage: %s [FLAGS] trace_file.pb
745
746 General purpose:
747 -h, --help Prints this guide.
748 -v, --version Prints the version of trace processor.
749
750 Behavioural:
751 -D, --httpd Enables the HTTP RPC server.
752 --http-port PORT Specify what port to run HTTP RPC server.
753 --stdiod Enables the stdio RPC server.
754 -i, --interactive Starts interactive mode even after
755 executing some other commands (-q, -Q,
756 --run-metrics, --summary).
757
758 Parsing:
759 --full-sort Forces the trace processor into performing
760 a full sort ignoring any windowing
761 logic.
762 --no-ftrace-raw Prevents ingestion of typed ftrace events
763 into the raw table. This significantly
764 reduces the memory usage of trace
765 processor when loading traces containing
766 ftrace events.
767
768 PerfettoSQL:
769 -q, --query-file FILE Read and execute an SQL query from a file.
770 If used with --run-metrics, the query is
771 executed after the selected metrics and
772 the metrics output is suppressed.
773 -Q, --query-string QUERY Execute the SQL query QUERY.
774 If used with --run-metrics, the query is
775 executed after the selected metrics and
776 the metrics output is suppressed.
777 --add-sql-module PACKAGE_PATH Files from the directory will be treated
778 as a new SQL package and can be used for
779 INCLUDE PERFETTO MODULE statements. The
780 name of the directory is the module name.
781 --override-sql-module PACKAGE_PATH Will override trace processor package with
782 passed contents. The outer directory will
783 specify the package name.
784
785 Trace summarization:
786 --summary Enables the trace summarization features of
787 trace processor. Required for any flags
788 starting with --summary-* to be meaningful.
789 --summary-format can be used to control the
790 output format.
791 --summary-metrics-v2 ID1,ID2,ID3 Specifies that the given v2 metrics (as
792 defined by a comma separated set of ids)
793 should be computed and returned as part of
794 the trace summary. The spec for every metric
795 must exist in one of the files passed to
796 --summary-spec.
797 --summary-metadata-query ID Specifies that the given query id should be
798 used to populate the `metadata` field of the
799 trace summary. The spec for the query must
800 exist in one of the files passed to
801 --summary-spec.
802 --summary-spec SUMMARY_PATH Parses the spec at the specified path and
803 makes it available to all summarization
804 operators (--summary-metrics-v2). Spec
805 files must be instances of the
806 perfetto.protos.TraceSummarySpec proto.
807 If the file extension is `.textproto` then
808 the spec file will be parsed as a
809 textproto. If the file extension is `.pb`
810 then it will be parsed as a binary
811 protobuf. Otherwise, heureustics will be
812 used to determine the format.
813 --summary-format [text,binary] Controls the serialization format of trace
814 summarization proto
815 (perfetto.protos.TraceSummary). If
816 `binary`, then the output is a binary
817 protobuf. If unspecified or `text` then
818 the output is a textproto.
819
820 Metatracing:
821 -m, --metatrace FILE Enables metatracing of trace processor
822 writing the resulting trace into FILE.
823 --metatrace-buffer-capacity N Sets metatrace event buffer to capture
824 last N events.
825 --metatrace-categories CATEGORIES A comma-separated list of metatrace
826 categories to enable.
827
828 Advanced:
829 --dev Enables features which are reserved for
830 local development use only and
831 *should not* be enabled on production
832 builds. The features behind this flag can
833 break at any time without any warning.
834 --dev-flag KEY=VALUE Set a development flag to the given value.
835 Does not have any affect unless --dev is
836 specified.
837 --extra-checks Enables additional checks which can catch
838 more SQL errors, but which incur
839 additional runtime overhead.
840 -e, --export FILE Export the contents of trace processor
841 into an SQLite database after running any
842 metrics or queries specified.
843 -p, --perf-file FILE Writes the time taken to ingest the trace
844 and execute the queries to the given file.
845 Only valid with -q or --run-metrics and
846 the file will only be written if the
847 execution is successful.
848 -W, --wide Prints interactive output with double
849 column width.
850 --analyze-trace-proto-content Enables trace proto content analysis in
851 trace processor.
852 --crop-track-events Ignores track event outside of the
853 range of interest in trace processor.
854 --register-files-dir PATH The contents of all files in this
855 directory and subdirectories will be made
856 available to the trace processor runtime.
857 Some importers can use this data to
858 augment trace data (e.g. decode ETM
859 instruction streams).
860 --override-stdlib=[path_to_stdlib] Will override trace_processor/stdlib with
861 passed contents. The outer directory will
862 be ignored. Only allowed when --dev is
863 specified.
864
865 Metrics (v1):
866
867 NOTE: the trace-based metrics system has been "soft" deprecated. Specifically,
868 all exisiting metrics will continue functioning but we will not be building
869 any new features nor developing any metrics there further. Please use the
870 metrics v2 system as part of trace summarization.
871
872 --run-metrics x,y,z Runs a comma separated list of metrics and
873 prints the result as a TraceMetrics proto
874 to stdout. The specified can either be
875 in-built metrics or SQL/proto files of
876 extension metrics.
877 --pre-metrics FILE Read and execute an SQL query from a file.
878 This query is executed before the selected
879 metrics and can't output any results.
880 --metrics-output=[binary|text|json] Allows the output of --run-metrics to be
881 specified in either proto binary, proto
882 text format or JSON format (default: proto
883 text).
884 --metric-extension DISK_PATH@VIRTUAL_PATH
885 Loads metric proto and sql files from
886 DISK_PATH/protos and DISK_PATH/sql
887 respectively, and mounts them onto
888 VIRTUAL_PATH.
889 )",
890 argv[0]);
891 }
892
ParseCommandLineOptions(int argc,char ** argv)893 CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
894 CommandLineOptions command_line_options;
895 enum LongOption {
896 OPT_HTTP_PORT = 1000,
897 OPT_STDIOD,
898
899 OPT_FORCE_FULL_SORT,
900 OPT_NO_FTRACE_RAW,
901
902 OPT_ADD_SQL_MODULE,
903 OPT_OVERRIDE_SQL_MODULE,
904
905 OPT_SUMMARY,
906 OPT_SUMMARY_METRICS_V2,
907 OPT_SUMMARY_METADATA_QUERY,
908 OPT_SUMMARY_SPEC,
909 OPT_SUMMARY_FORMAT,
910
911 OPT_METATRACE_BUFFER_CAPACITY,
912 OPT_METATRACE_CATEGORIES,
913
914 OPT_DEV,
915 OPT_DEV_FLAG,
916 OPT_EXTRA_CHECKS,
917 OPT_ANALYZE_TRACE_PROTO_CONTENT,
918 OPT_CROP_TRACK_EVENTS,
919 OPT_REGISTER_FILES_DIR,
920 OPT_OVERRIDE_STDLIB,
921
922 OPT_RUN_METRICS,
923 OPT_PRE_METRICS,
924 OPT_METRICS_OUTPUT,
925 OPT_METRIC_EXTENSION,
926 };
927
928 static const option long_options[] = {
929 {"help", no_argument, nullptr, 'h'},
930 {"version", no_argument, nullptr, 'v'},
931
932 {"httpd", no_argument, nullptr, 'D'},
933 {"http-port", required_argument, nullptr, OPT_HTTP_PORT},
934 {"stdiod", no_argument, nullptr, OPT_STDIOD},
935 {"interactive", no_argument, nullptr, 'i'},
936
937 {"full-sort", no_argument, nullptr, OPT_FORCE_FULL_SORT},
938 {"no-ftrace-raw", no_argument, nullptr, OPT_NO_FTRACE_RAW},
939
940 {"query-file", required_argument, nullptr, 'q'},
941 {"query-string", required_argument, nullptr, 'Q'},
942 {"add-sql-module", required_argument, nullptr, OPT_ADD_SQL_MODULE},
943 {"override-sql-module", required_argument, nullptr,
944 OPT_OVERRIDE_SQL_MODULE},
945
946 {"summary", no_argument, nullptr, OPT_SUMMARY},
947 {"summary-metrics-v2", required_argument, nullptr,
948 OPT_SUMMARY_METRICS_V2},
949 {"summary-metadata-query", required_argument, nullptr,
950 OPT_SUMMARY_METADATA_QUERY},
951 {"summary-spec", required_argument, nullptr, OPT_SUMMARY_SPEC},
952 {"summary-format", required_argument, nullptr, OPT_SUMMARY_FORMAT},
953
954 {"metatrace", required_argument, nullptr, 'm'},
955 {"metatrace-buffer-capacity", required_argument, nullptr,
956 OPT_METATRACE_BUFFER_CAPACITY},
957 {"metatrace-categories", required_argument, nullptr,
958 OPT_METATRACE_CATEGORIES},
959
960 {"dev", no_argument, nullptr, OPT_DEV},
961 {"dev-flag", required_argument, nullptr, OPT_DEV_FLAG},
962 {"extra-checks", no_argument, nullptr, OPT_EXTRA_CHECKS},
963 {"export", required_argument, nullptr, 'e'},
964 {"perf-file", required_argument, nullptr, 'p'},
965 {"wide", no_argument, nullptr, 'W'},
966 {"analyze-trace-proto-content", no_argument, nullptr,
967 OPT_ANALYZE_TRACE_PROTO_CONTENT},
968 {"crop-track-events", no_argument, nullptr, OPT_CROP_TRACK_EVENTS},
969 {"register-files-dir", required_argument, nullptr,
970 OPT_REGISTER_FILES_DIR},
971 {"override-stdlib", required_argument, nullptr, OPT_OVERRIDE_STDLIB},
972
973 {"run-metrics", required_argument, nullptr, OPT_RUN_METRICS},
974 {"pre-metrics", required_argument, nullptr, OPT_PRE_METRICS},
975 {"metrics-output", required_argument, nullptr, OPT_METRICS_OUTPUT},
976 {"metric-extension", required_argument, nullptr, OPT_METRIC_EXTENSION},
977
978 {nullptr, 0, nullptr, 0}};
979
980 bool explicit_interactive = false;
981 for (;;) {
982 int option =
983 getopt_long(argc, argv, "hvWiDdm:p:q:Q:e:", long_options, nullptr);
984
985 if (option == -1)
986 break; // EOF.
987
988 if (option == 'v') {
989 printf("%s\n", base::GetVersionString());
990 printf("Trace Processor RPC API version: %d\n",
991 protos::pbzero::TRACE_PROCESSOR_CURRENT_API_VERSION);
992 exit(0);
993 }
994
995 if (option == 'W') {
996 command_line_options.wide = true;
997 continue;
998 }
999
1000 if (option == 'p') {
1001 command_line_options.perf_file_path = optarg;
1002 continue;
1003 }
1004
1005 if (option == 'q') {
1006 command_line_options.query_file_path = optarg;
1007 continue;
1008 }
1009
1010 if (option == 'Q') {
1011 command_line_options.query_string = optarg;
1012 continue;
1013 }
1014
1015 if (option == 'D') {
1016 #if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
1017 command_line_options.enable_httpd = true;
1018 #else
1019 PERFETTO_FATAL("HTTP RPC module not supported in this build");
1020 #endif
1021 continue;
1022 }
1023
1024 if (option == OPT_HTTP_PORT) {
1025 command_line_options.port_number = optarg;
1026 continue;
1027 }
1028
1029 if (option == OPT_STDIOD) {
1030 command_line_options.enable_stdiod = true;
1031 continue;
1032 }
1033
1034 if (option == 'i') {
1035 explicit_interactive = true;
1036 continue;
1037 }
1038
1039 if (option == 'e') {
1040 command_line_options.export_file_path = optarg;
1041 continue;
1042 }
1043
1044 if (option == 'm') {
1045 command_line_options.metatrace_path = optarg;
1046 continue;
1047 }
1048
1049 if (option == OPT_METATRACE_BUFFER_CAPACITY) {
1050 command_line_options.metatrace_buffer_capacity =
1051 static_cast<size_t>(atoi(optarg));
1052 continue;
1053 }
1054
1055 if (option == OPT_METATRACE_CATEGORIES) {
1056 command_line_options.metatrace_categories =
1057 ParseMetatraceCategories(optarg);
1058 continue;
1059 }
1060
1061 if (option == OPT_FORCE_FULL_SORT) {
1062 command_line_options.force_full_sort = true;
1063 continue;
1064 }
1065
1066 if (option == OPT_NO_FTRACE_RAW) {
1067 command_line_options.no_ftrace_raw = true;
1068 continue;
1069 }
1070
1071 if (option == OPT_ANALYZE_TRACE_PROTO_CONTENT) {
1072 command_line_options.analyze_trace_proto_content = true;
1073 continue;
1074 }
1075
1076 if (option == OPT_CROP_TRACK_EVENTS) {
1077 command_line_options.crop_track_events = true;
1078 continue;
1079 }
1080
1081 if (option == OPT_DEV) {
1082 command_line_options.dev = true;
1083 continue;
1084 }
1085
1086 if (option == OPT_EXTRA_CHECKS) {
1087 command_line_options.extra_checks = true;
1088 continue;
1089 }
1090
1091 if (option == OPT_ADD_SQL_MODULE) {
1092 command_line_options.sql_module_path = optarg;
1093 continue;
1094 }
1095
1096 if (option == OPT_OVERRIDE_SQL_MODULE) {
1097 command_line_options.override_sql_module_paths.emplace_back(optarg);
1098 continue;
1099 }
1100
1101 if (option == OPT_OVERRIDE_STDLIB) {
1102 command_line_options.override_stdlib_path = optarg;
1103 continue;
1104 }
1105
1106 if (option == OPT_RUN_METRICS) {
1107 command_line_options.metric_v1_names = optarg;
1108 continue;
1109 }
1110
1111 if (option == OPT_PRE_METRICS) {
1112 command_line_options.pre_metrics_v1_path = optarg;
1113 continue;
1114 }
1115
1116 if (option == OPT_METRICS_OUTPUT) {
1117 command_line_options.metric_v1_output = optarg;
1118 continue;
1119 }
1120
1121 if (option == OPT_METRIC_EXTENSION) {
1122 command_line_options.raw_metric_v1_extensions.emplace_back(optarg);
1123 continue;
1124 }
1125
1126 if (option == OPT_DEV_FLAG) {
1127 command_line_options.dev_flags.emplace_back(optarg);
1128 continue;
1129 }
1130
1131 if (option == OPT_REGISTER_FILES_DIR) {
1132 command_line_options.register_files_dir = optarg;
1133 continue;
1134 }
1135
1136 if (option == OPT_SUMMARY) {
1137 command_line_options.summary = true;
1138 continue;
1139 }
1140
1141 if (option == OPT_SUMMARY_METRICS_V2) {
1142 command_line_options.summary_metrics_v2 = optarg;
1143 continue;
1144 }
1145
1146 if (option == OPT_SUMMARY_METADATA_QUERY) {
1147 command_line_options.summary_metadata_query = optarg;
1148 continue;
1149 }
1150
1151 if (option == OPT_SUMMARY_SPEC) {
1152 command_line_options.summary_specs.emplace_back(optarg);
1153 continue;
1154 }
1155
1156 if (option == OPT_SUMMARY_FORMAT) {
1157 command_line_options.summary_output = optarg;
1158 continue;
1159 }
1160
1161 PrintUsage(argv);
1162 exit(option == 'h' ? 0 : 1);
1163 }
1164
1165 command_line_options.launch_shell =
1166 explicit_interactive || (command_line_options.metric_v1_names.empty() &&
1167 command_line_options.query_file_path.empty() &&
1168 command_line_options.query_string.empty() &&
1169 command_line_options.export_file_path.empty() &&
1170 !command_line_options.summary);
1171
1172 // Only allow non-interactive queries to emit perf data.
1173 if (!command_line_options.perf_file_path.empty() &&
1174 command_line_options.launch_shell) {
1175 PrintUsage(argv);
1176 exit(1);
1177 }
1178
1179 if (command_line_options.summary &&
1180 !command_line_options.metric_v1_names.empty()) {
1181 PERFETTO_ELOG("Cannot specify both metrics v1 and trace summarization");
1182 exit(1);
1183 }
1184
1185 // The only case where we allow omitting the trace file path is when running
1186 // in --httpd or --stdiod mode. In all other cases, the last argument must be
1187 // the trace file.
1188 if (optind == argc - 1 && argv[optind]) {
1189 command_line_options.trace_file_path = argv[optind];
1190 } else if (!command_line_options.enable_httpd &&
1191 !command_line_options.enable_stdiod) {
1192 PrintUsage(argv);
1193 exit(1);
1194 }
1195
1196 return command_line_options;
1197 }
1198
ExtendPoolWithBinaryDescriptor(google::protobuf::DescriptorPool & pool,const void * data,int size,const std::vector<std::string> & skip_prefixes)1199 void ExtendPoolWithBinaryDescriptor(
1200 google::protobuf::DescriptorPool& pool,
1201 const void* data,
1202 int size,
1203 const std::vector<std::string>& skip_prefixes) {
1204 google::protobuf::FileDescriptorSet desc_set;
1205 PERFETTO_CHECK(desc_set.ParseFromArray(data, size));
1206 for (const auto& file_desc : desc_set.file()) {
1207 if (base::StartsWithAny(file_desc.name(), skip_prefixes))
1208 continue;
1209 pool.BuildFile(file_desc);
1210 }
1211 }
1212
LoadTrace(const std::string & trace_file_path,double * size_mb)1213 base::Status LoadTrace(const std::string& trace_file_path, double* size_mb) {
1214 base::Status read_status = ReadTraceUnfinalized(
1215 g_tp, trace_file_path.c_str(), [&size_mb](size_t parsed_size) {
1216 *size_mb = static_cast<double>(parsed_size) / 1E6;
1217 fprintf(stderr, "\rLoading trace: %.2f MB\r", *size_mb);
1218 });
1219 g_tp->Flush();
1220 if (!read_status.ok()) {
1221 return base::ErrStatus("Could not read trace file (path: %s): %s",
1222 trace_file_path.c_str(), read_status.c_message());
1223 }
1224
1225 std::unique_ptr<profiling::Symbolizer> symbolizer =
1226 profiling::LocalSymbolizerOrDie(profiling::GetPerfettoBinaryPath(),
1227 getenv("PERFETTO_SYMBOLIZER_MODE"));
1228
1229 if (symbolizer) {
1230 profiling::SymbolizeDatabase(
1231 g_tp, symbolizer.get(), [](const std::string& trace_proto) {
1232 std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]);
1233 memcpy(buf.get(), trace_proto.data(), trace_proto.size());
1234 auto status = g_tp->Parse(std::move(buf), trace_proto.size());
1235 if (!status.ok()) {
1236 PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s",
1237 status.message().c_str());
1238 return;
1239 }
1240 });
1241 g_tp->Flush();
1242 }
1243
1244 auto maybe_map = profiling::GetPerfettoProguardMapPath();
1245 if (!maybe_map.empty()) {
1246 profiling::ReadProguardMapsToDeobfuscationPackets(
1247 maybe_map, [](const std::string& trace_proto) {
1248 std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]);
1249 memcpy(buf.get(), trace_proto.data(), trace_proto.size());
1250 auto status = g_tp->Parse(std::move(buf), trace_proto.size());
1251 if (!status.ok()) {
1252 PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s",
1253 status.message().c_str());
1254 return;
1255 }
1256 });
1257 }
1258 return g_tp->NotifyEndOfFile();
1259 }
1260
RunQueries(const std::string & queries,bool expect_output)1261 base::Status RunQueries(const std::string& queries, bool expect_output) {
1262 if (expect_output) {
1263 return RunQueriesAndPrintResult(queries, stdout);
1264 }
1265 return RunQueriesWithoutOutput(queries);
1266 }
1267
RunQueriesFromFile(const std::string & query_file_path,bool expect_output)1268 base::Status RunQueriesFromFile(const std::string& query_file_path,
1269 bool expect_output) {
1270 std::string queries;
1271 if (!base::ReadFile(query_file_path, &queries)) {
1272 return base::ErrStatus("Unable to read file %s", query_file_path.c_str());
1273 }
1274 return RunQueries(queries, expect_output);
1275 }
1276
ParseSingleMetricExtensionPath(bool dev,const std::string & raw_extension,MetricExtension & parsed_extension)1277 base::Status ParseSingleMetricExtensionPath(bool dev,
1278 const std::string& raw_extension,
1279 MetricExtension& parsed_extension) {
1280 // We cannot easily use ':' as a path separator because windows paths can have
1281 // ':' in them (e.g. C:\foo\bar).
1282 std::vector<std::string> parts = base::SplitString(raw_extension, "@");
1283 if (parts.size() != 2 || parts[0].length() == 0 || parts[1].length() == 0) {
1284 return base::ErrStatus(
1285 "--metric-extension-dir must be of format disk_path@virtual_path");
1286 }
1287
1288 parsed_extension.SetDiskPath(std::move(parts[0]));
1289 parsed_extension.SetVirtualPath(std::move(parts[1]));
1290
1291 if (parsed_extension.virtual_path() == "/") {
1292 if (!dev) {
1293 return base::ErrStatus(
1294 "Local development features must be enabled (using the "
1295 "--dev flag) to override built-in metrics");
1296 }
1297 parsed_extension.SetVirtualPath("");
1298 }
1299
1300 if (parsed_extension.virtual_path() == "shell/") {
1301 return base::Status(
1302 "Cannot have 'shell/' as metric extension virtual path.");
1303 }
1304 return base::OkStatus();
1305 }
1306
CheckForDuplicateMetricExtension(const std::vector<MetricExtension> & metric_extensions)1307 base::Status CheckForDuplicateMetricExtension(
1308 const std::vector<MetricExtension>& metric_extensions) {
1309 std::unordered_set<std::string> disk_paths;
1310 std::unordered_set<std::string> virtual_paths;
1311 for (const auto& extension : metric_extensions) {
1312 auto ret = disk_paths.insert(extension.disk_path());
1313 if (!ret.second) {
1314 return base::ErrStatus(
1315 "Another metric extension is already using disk path %s",
1316 extension.disk_path().c_str());
1317 }
1318 ret = virtual_paths.insert(extension.virtual_path());
1319 if (!ret.second) {
1320 return base::ErrStatus(
1321 "Another metric extension is already using virtual path %s",
1322 extension.virtual_path().c_str());
1323 }
1324 }
1325 return base::OkStatus();
1326 }
1327
ParseMetricExtensionPaths(bool dev,const std::vector<std::string> & raw_metric_extensions,std::vector<MetricExtension> & metric_extensions)1328 base::Status ParseMetricExtensionPaths(
1329 bool dev,
1330 const std::vector<std::string>& raw_metric_extensions,
1331 std::vector<MetricExtension>& metric_extensions) {
1332 for (const auto& raw_extension : raw_metric_extensions) {
1333 metric_extensions.push_back({});
1334 RETURN_IF_ERROR(ParseSingleMetricExtensionPath(dev, raw_extension,
1335 metric_extensions.back()));
1336 }
1337 return CheckForDuplicateMetricExtension(metric_extensions);
1338 }
1339
IncludeSqlModule(std::string root,bool allow_override)1340 base::Status IncludeSqlModule(std::string root, bool allow_override) {
1341 // Remove trailing slash
1342 if (root.back() == '/')
1343 root = root.substr(0, root.length() - 1);
1344
1345 if (!base::FileExists(root))
1346 return base::ErrStatus("Directory %s does not exist.", root.c_str());
1347
1348 // Get module name
1349 size_t last_slash = root.rfind('/');
1350 if ((last_slash == std::string::npos) ||
1351 (root.find('.') != std::string::npos))
1352 return base::ErrStatus("Module path must point to the directory: %s",
1353 root.c_str());
1354
1355 std::string module_name = root.substr(last_slash + 1);
1356
1357 std::vector<std::string> paths;
1358 RETURN_IF_ERROR(base::ListFilesRecursive(root, paths));
1359 sql_modules::NameToPackage modules;
1360 for (const auto& path : paths) {
1361 if (base::GetFileExtension(path) != ".sql")
1362 continue;
1363
1364 std::string filename = root + "/" + path;
1365 std::string file_contents;
1366 if (!base::ReadFile(filename, &file_contents))
1367 return base::ErrStatus("Cannot read file %s", filename.c_str());
1368
1369 std::string import_key =
1370 module_name + "." + sql_modules::GetIncludeKey(path);
1371 modules.Insert(module_name, {})
1372 .first->push_back({import_key, file_contents});
1373 }
1374 for (auto module_it = modules.GetIterator(); module_it; ++module_it) {
1375 RETURN_IF_ERROR(
1376 g_tp->RegisterSqlPackage({/*name=*/module_it.key(),
1377 /*files=*/module_it.value(),
1378 /*allow_override=*/allow_override}));
1379 }
1380 return base::OkStatus();
1381 }
1382
LoadOverridenStdlib(std::string root)1383 base::Status LoadOverridenStdlib(std::string root) {
1384 // Remove trailing slash
1385 if (root.back() == '/') {
1386 root = root.substr(0, root.length() - 1);
1387 }
1388
1389 if (!base::FileExists(root)) {
1390 return base::ErrStatus("Directory '%s' does not exist.", root.c_str());
1391 }
1392
1393 std::vector<std::string> paths;
1394 RETURN_IF_ERROR(base::ListFilesRecursive(root, paths));
1395 sql_modules::NameToPackage packages;
1396 for (const auto& path : paths) {
1397 if (base::GetFileExtension(path) != ".sql") {
1398 continue;
1399 }
1400 std::string filename = root + "/" + path;
1401 std::string module_file;
1402 if (!base::ReadFile(filename, &module_file)) {
1403 return base::ErrStatus("Cannot read file '%s'", filename.c_str());
1404 }
1405 std::string module_name = sql_modules::GetIncludeKey(path);
1406 std::string package_name = sql_modules::GetPackageName(module_name);
1407 packages.Insert(package_name, {})
1408 .first->push_back({module_name, module_file});
1409 }
1410 for (auto package = packages.GetIterator(); package; ++package) {
1411 g_tp->RegisterSqlPackage({/*name=*/package.key(),
1412 /*files=*/package.value(),
1413 /*allow_override=*/true});
1414 }
1415
1416 return base::OkStatus();
1417 }
1418
LoadMetricExtensionProtos(const std::string & proto_root,const std::string & mount_path,google::protobuf::DescriptorPool & pool)1419 base::Status LoadMetricExtensionProtos(const std::string& proto_root,
1420 const std::string& mount_path,
1421 google::protobuf::DescriptorPool& pool) {
1422 if (!base::FileExists(proto_root)) {
1423 return base::ErrStatus(
1424 "Directory %s does not exist. Metric extension directory must contain "
1425 "a 'sql/' and 'protos/' subdirectory.",
1426 proto_root.c_str());
1427 }
1428 std::vector<std::string> proto_files;
1429 RETURN_IF_ERROR(base::ListFilesRecursive(proto_root, proto_files));
1430
1431 google::protobuf::FileDescriptorSet parsed_protos;
1432 for (const auto& file_path : proto_files) {
1433 if (base::GetFileExtension(file_path) != ".proto")
1434 continue;
1435 auto* file_desc = parsed_protos.add_file();
1436 ParseToFileDescriptorProto(proto_root + file_path, file_desc);
1437 file_desc->set_name(mount_path + file_path);
1438 }
1439
1440 std::vector<uint8_t> serialized_filedescset;
1441 serialized_filedescset.resize(parsed_protos.ByteSizeLong());
1442 parsed_protos.SerializeToArray(
1443 serialized_filedescset.data(),
1444 static_cast<int>(serialized_filedescset.size()));
1445
1446 // Extend the pool for any subsequent reflection-based operations
1447 // (e.g. output json)
1448 ExtendPoolWithBinaryDescriptor(
1449 pool, serialized_filedescset.data(),
1450 static_cast<int>(serialized_filedescset.size()), {});
1451 return g_tp->ExtendMetricsProto(serialized_filedescset.data(),
1452 serialized_filedescset.size());
1453 }
1454
LoadMetricExtensionSql(const std::string & sql_root,const std::string & mount_path)1455 base::Status LoadMetricExtensionSql(const std::string& sql_root,
1456 const std::string& mount_path) {
1457 if (!base::FileExists(sql_root)) {
1458 return base::ErrStatus(
1459 "Directory %s does not exist. Metric extension directory must contain "
1460 "a 'sql/' and 'protos/' subdirectory.",
1461 sql_root.c_str());
1462 }
1463
1464 std::vector<std::string> sql_files;
1465 RETURN_IF_ERROR(base::ListFilesRecursive(sql_root, sql_files));
1466 for (const auto& file_path : sql_files) {
1467 if (base::GetFileExtension(file_path) != ".sql")
1468 continue;
1469 std::string file_contents;
1470 if (!base::ReadFile(sql_root + file_path, &file_contents)) {
1471 return base::ErrStatus("Cannot read file %s", file_path.c_str());
1472 }
1473 RETURN_IF_ERROR(
1474 g_tp->RegisterMetric(mount_path + file_path, file_contents));
1475 }
1476 return base::OkStatus();
1477 }
1478
LoadMetricExtension(const MetricExtension & extension,google::protobuf::DescriptorPool & pool)1479 base::Status LoadMetricExtension(const MetricExtension& extension,
1480 google::protobuf::DescriptorPool& pool) {
1481 const std::string& disk_path = extension.disk_path();
1482 const std::string& virtual_path = extension.virtual_path();
1483
1484 if (!base::FileExists(disk_path)) {
1485 return base::ErrStatus("Metric extension directory %s does not exist",
1486 disk_path.c_str());
1487 }
1488
1489 // Note: Proto files must be loaded first, because we determine whether an SQL
1490 // file is a metric or not by checking if the name matches a field of the root
1491 // TraceMetrics proto.
1492 RETURN_IF_ERROR(LoadMetricExtensionProtos(
1493 disk_path + "protos/", kMetricProtoRoot + virtual_path, pool));
1494 RETURN_IF_ERROR(LoadMetricExtensionSql(disk_path + "sql/", virtual_path));
1495
1496 return base::OkStatus();
1497 }
1498
PopulateDescriptorPool(google::protobuf::DescriptorPool & pool,const std::vector<MetricExtension> & metric_extensions)1499 base::Status PopulateDescriptorPool(
1500 google::protobuf::DescriptorPool& pool,
1501 const std::vector<MetricExtension>& metric_extensions) {
1502 // TODO(b/182165266): There is code duplication here with trace_processor_impl
1503 // SetupMetrics. This will be removed when we switch the output formatter to
1504 // use internal DescriptorPool.
1505 std::vector<std::string> skip_prefixes;
1506 skip_prefixes.reserve(metric_extensions.size());
1507 for (const auto& ext : metric_extensions) {
1508 skip_prefixes.push_back(kMetricProtoRoot + ext.virtual_path());
1509 }
1510 ExtendPoolWithBinaryDescriptor(pool, kMetricsDescriptor.data(),
1511 kMetricsDescriptor.size(), skip_prefixes);
1512 ExtendPoolWithBinaryDescriptor(pool, kAllChromeMetricsDescriptor.data(),
1513 kAllChromeMetricsDescriptor.size(),
1514 skip_prefixes);
1515 ExtendPoolWithBinaryDescriptor(pool, kAllWebviewMetricsDescriptor.data(),
1516 kAllWebviewMetricsDescriptor.size(),
1517 skip_prefixes);
1518 return base::OkStatus();
1519 }
1520
LoadMetrics(const std::string & raw_metric_names,google::protobuf::DescriptorPool & pool,std::vector<MetricNameAndPath> & name_and_path)1521 base::Status LoadMetrics(const std::string& raw_metric_names,
1522 google::protobuf::DescriptorPool& pool,
1523 std::vector<MetricNameAndPath>& name_and_path) {
1524 std::vector<std::string> split;
1525 for (base::StringSplitter ss(raw_metric_names, ','); ss.Next();) {
1526 split.emplace_back(ss.cur_token());
1527 }
1528
1529 // For all metrics which are files, register them and extend the metrics
1530 // proto.
1531 for (const std::string& metric_or_path : split) {
1532 // If there is no extension, we assume it is a builtin metric.
1533 auto ext_idx = metric_or_path.rfind('.');
1534 if (ext_idx == std::string::npos) {
1535 name_and_path.emplace_back(
1536 MetricNameAndPath{metric_or_path, std::nullopt});
1537 continue;
1538 }
1539
1540 std::string no_ext_path = metric_or_path.substr(0, ext_idx);
1541
1542 // The proto must be extended before registering the metric.
1543 base::Status status = ExtendMetricsProto(no_ext_path + ".proto", &pool);
1544 if (!status.ok()) {
1545 return base::ErrStatus("Unable to extend metrics proto %s: %s",
1546 metric_or_path.c_str(), status.c_message());
1547 }
1548
1549 status = RegisterMetric(no_ext_path + ".sql");
1550 if (!status.ok()) {
1551 return base::ErrStatus("Unable to register metric %s: %s",
1552 metric_or_path.c_str(), status.c_message());
1553 }
1554 name_and_path.emplace_back(
1555 MetricNameAndPath{BaseName(no_ext_path), no_ext_path});
1556 }
1557 return base::OkStatus();
1558 }
1559
ParseMetricV1OutputFormat(const CommandLineOptions & options)1560 MetricV1OutputFormat ParseMetricV1OutputFormat(
1561 const CommandLineOptions& options) {
1562 if (!options.query_file_path.empty())
1563 return MetricV1OutputFormat::kNone;
1564 if (options.metric_v1_output == "binary")
1565 return MetricV1OutputFormat::kBinaryProto;
1566 if (options.metric_v1_output == "json")
1567 return MetricV1OutputFormat::kJson;
1568 return MetricV1OutputFormat::kTextProto;
1569 }
1570
LoadMetricsAndExtensionsSql(const std::vector<MetricNameAndPath> & metrics,const std::vector<MetricExtension> & extensions)1571 base::Status LoadMetricsAndExtensionsSql(
1572 const std::vector<MetricNameAndPath>& metrics,
1573 const std::vector<MetricExtension>& extensions) {
1574 for (const MetricExtension& extension : extensions) {
1575 const std::string& disk_path = extension.disk_path();
1576 const std::string& virtual_path = extension.virtual_path();
1577
1578 RETURN_IF_ERROR(LoadMetricExtensionSql(disk_path + "sql/", virtual_path));
1579 }
1580
1581 for (const MetricNameAndPath& metric : metrics) {
1582 // Ignore builtin metrics.
1583 if (!metric.no_ext_path.has_value())
1584 continue;
1585 RETURN_IF_ERROR(RegisterMetric(metric.no_ext_path.value() + ".sql"));
1586 }
1587 return base::OkStatus();
1588 }
1589
PrintShellUsage()1590 void PrintShellUsage() {
1591 PERFETTO_ELOG(R"(
1592 Available commands:
1593 .quit, .q Exit the shell.
1594 .help This text.
1595 .dump FILE Export the trace as a sqlite database.
1596 .read FILE Executes the queries in the FILE.
1597 .reset Destroys all tables/view created by the user.
1598 .load-metrics-sql Reloads SQL from extension and custom metric paths
1599 specified in command line args.
1600 .run-metrics Runs metrics specified in command line args
1601 and prints the result.
1602 .width WIDTH Changes the column width of interactive query
1603 output.
1604 )");
1605 }
1606
1607 struct InteractiveOptions {
1608 uint32_t column_width;
1609 MetricV1OutputFormat metric_v1_format;
1610 std::vector<MetricExtension> extensions;
1611 std::vector<MetricNameAndPath> metrics;
1612 const google::protobuf::DescriptorPool* pool;
1613 };
1614
StartInteractiveShell(const InteractiveOptions & options)1615 base::Status StartInteractiveShell(const InteractiveOptions& options) {
1616 SetupLineEditor();
1617
1618 uint32_t column_width = options.column_width;
1619 for (;;) {
1620 ScopedLine line = GetLine("> ");
1621 if (!line)
1622 break;
1623 if (strcmp(line.get(), "") == 0) {
1624 printf("If you want to quit either type .q or press CTRL-D (EOF)\n");
1625 continue;
1626 }
1627 if (line.get()[0] == '.') {
1628 char command[32] = {};
1629 char arg[1024] = {};
1630 sscanf(line.get() + 1, "%31s %1023s", command, arg);
1631 if (strcmp(command, "quit") == 0 || strcmp(command, "q") == 0) {
1632 break;
1633 }
1634 if (strcmp(command, "help") == 0) {
1635 PrintShellUsage();
1636 } else if (strcmp(command, "dump") == 0 && strlen(arg)) {
1637 if (!ExportTraceToDatabase(arg).ok())
1638 PERFETTO_ELOG("Database export failed");
1639 } else if (strcmp(command, "reset") == 0) {
1640 g_tp->RestoreInitialTables();
1641 } else if (strcmp(command, "read") == 0 && strlen(arg)) {
1642 base::Status status = RunQueriesFromFile(arg, true);
1643 if (!status.ok()) {
1644 PERFETTO_ELOG("%s", status.c_message());
1645 }
1646 } else if (strcmp(command, "width") == 0 && strlen(arg)) {
1647 std::optional<uint32_t> width = base::CStringToUInt32(arg);
1648 if (!width) {
1649 PERFETTO_ELOG("Invalid column width specified");
1650 continue;
1651 }
1652 column_width = *width;
1653 } else if (strcmp(command, "load-metrics-sql") == 0) {
1654 base::Status status =
1655 LoadMetricsAndExtensionsSql(options.metrics, options.extensions);
1656 if (!status.ok()) {
1657 PERFETTO_ELOG("%s", status.c_message());
1658 }
1659 } else if (strcmp(command, "run-metrics") == 0) {
1660 if (options.metrics.empty()) {
1661 PERFETTO_ELOG("No metrics specified on command line");
1662 continue;
1663 }
1664
1665 base::Status status =
1666 RunMetrics(options.metrics, options.metric_v1_format);
1667 if (!status.ok()) {
1668 fprintf(stderr, "%s\n", status.c_message());
1669 }
1670 } else {
1671 PrintShellUsage();
1672 }
1673 continue;
1674 }
1675
1676 base::TimeNanos t_start = base::GetWallTimeNs();
1677 auto it = g_tp->ExecuteQuery(line.get());
1678 PrintQueryResultInteractively(&it, t_start, column_width);
1679 }
1680 return base::OkStatus();
1681 }
1682
MaybeWriteMetatrace(const std::string & metatrace_path)1683 base::Status MaybeWriteMetatrace(const std::string& metatrace_path) {
1684 if (metatrace_path.empty()) {
1685 return base::OkStatus();
1686 }
1687 std::vector<uint8_t> serialized;
1688 RETURN_IF_ERROR(g_tp->DisableAndReadMetatrace(&serialized));
1689
1690 auto file = base::OpenFile(metatrace_path, O_CREAT | O_RDWR | O_TRUNC, 0600);
1691 if (!file)
1692 return base::ErrStatus("Unable to open metatrace file");
1693
1694 ssize_t res = base::WriteAll(*file, serialized.data(), serialized.size());
1695 if (res < 0)
1696 return base::ErrStatus("Error while writing metatrace file");
1697 return base::OkStatus();
1698 }
1699
MaybeUpdateSqlModules(const CommandLineOptions & options)1700 base::Status MaybeUpdateSqlModules(const CommandLineOptions& options) {
1701 if (!options.override_stdlib_path.empty()) {
1702 if (!options.dev)
1703 return base::ErrStatus("Overriding stdlib requires --dev flag");
1704
1705 auto status = LoadOverridenStdlib(options.override_stdlib_path);
1706 if (!status.ok())
1707 return base::ErrStatus("Couldn't override stdlib: %s",
1708 status.c_message());
1709 }
1710
1711 if (!options.override_sql_module_paths.empty()) {
1712 for (const auto& override_sql_module_path :
1713 options.override_sql_module_paths) {
1714 auto status = IncludeSqlModule(override_sql_module_path, true);
1715 if (!status.ok())
1716 return base::ErrStatus("Couldn't override stdlib module: %s",
1717 status.c_message());
1718 }
1719 }
1720
1721 if (!options.sql_module_path.empty()) {
1722 auto status = IncludeSqlModule(options.sql_module_path, false);
1723 if (!status.ok())
1724 return base::ErrStatus("Couldn't add SQL module: %s", status.c_message());
1725 }
1726 return base::OkStatus();
1727 }
1728
RegisterAllFilesInFolder(const std::string & path,TraceProcessor & tp)1729 base::Status RegisterAllFilesInFolder(const std::string& path,
1730 TraceProcessor& tp) {
1731 std::vector<std::string> files;
1732 RETURN_IF_ERROR(base::ListFilesRecursive(path, files));
1733 for (const std::string& file : files) {
1734 std::string file_full_path = path + "/" + file;
1735 base::ScopedMmap mmap = base::ReadMmapWholeFile(file_full_path.c_str());
1736 if (!mmap.IsValid()) {
1737 return base::ErrStatus("Failed to mmap file: %s", file_full_path.c_str());
1738 }
1739 RETURN_IF_ERROR(tp.RegisterFileContent(
1740 file_full_path, TraceBlobView(TraceBlob::FromMmap(std::move(mmap)))));
1741 }
1742 return base::OkStatus();
1743 }
1744
GuessSummarySpecFormat(const std::string & path,const std::string & content)1745 TraceSummarySpecBytes::Format GuessSummarySpecFormat(
1746 const std::string& path,
1747 const std::string& content) {
1748 if (base::EndsWith(path, ".pb")) {
1749 return TraceSummarySpecBytes::Format::kBinaryProto;
1750 }
1751 if (base::EndsWith(path, ".textproto")) {
1752 return TraceSummarySpecBytes::Format::kTextProto;
1753 }
1754 std::string_view content_str(content.c_str(),
1755 std::min<size_t>(content.size(), 128));
1756 auto fn = [](const char c) { return std::isspace(c) || std::isprint(c); };
1757 if (std::all_of(content_str.begin(), content_str.end(), fn)) {
1758 return TraceSummarySpecBytes::Format::kTextProto;
1759 }
1760 return TraceSummarySpecBytes::Format::kBinaryProto;
1761 }
1762
GetSummaryOutputFormat(const CommandLineOptions & options)1763 TraceSummaryOutputSpec::Format GetSummaryOutputFormat(
1764 const CommandLineOptions& options) {
1765 if (options.summary_output == "text" || options.summary_output == "") {
1766 return TraceSummaryOutputSpec::Format::kTextProto;
1767 }
1768 if (options.summary_output == "binary") {
1769 return TraceSummaryOutputSpec::Format::kBinaryProto;
1770 }
1771 PERFETTO_ELOG("Unknown summary output format %s",
1772 options.summary_output.c_str());
1773 exit(1);
1774 }
1775
TraceProcessorMain(int argc,char ** argv)1776 base::Status TraceProcessorMain(int argc, char** argv) {
1777 CommandLineOptions options = ParseCommandLineOptions(argc, argv);
1778
1779 Config config;
1780 config.sorting_mode = options.force_full_sort
1781 ? SortingMode::kForceFullSort
1782 : SortingMode::kDefaultHeuristics;
1783 config.ingest_ftrace_in_raw_table = !options.no_ftrace_raw;
1784 config.analyze_trace_proto_content = options.analyze_trace_proto_content;
1785 config.drop_track_event_data_before =
1786 options.crop_track_events
1787 ? DropTrackEventDataBefore::kTrackEventRangeOfInterest
1788 : DropTrackEventDataBefore::kNoDrop;
1789
1790 std::vector<MetricExtension> metric_extensions;
1791 RETURN_IF_ERROR(ParseMetricExtensionPaths(
1792 options.dev, options.raw_metric_v1_extensions, metric_extensions));
1793
1794 for (const auto& extension : metric_extensions) {
1795 config.skip_builtin_metric_paths.push_back(extension.virtual_path());
1796 }
1797
1798 if (options.dev) {
1799 config.enable_dev_features = true;
1800 for (const auto& flag_pair : options.dev_flags) {
1801 auto kv = base::SplitString(flag_pair, "=");
1802 if (kv.size() != 2) {
1803 PERFETTO_ELOG("Ignoring unknown dev flag format %s", flag_pair.c_str());
1804 continue;
1805 }
1806 config.dev_flags.emplace(kv[0], kv[1]);
1807 }
1808 }
1809
1810 if (options.extra_checks) {
1811 config.enable_extra_checks = true;
1812 }
1813
1814 std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
1815 g_tp = tp.get();
1816
1817 RETURN_IF_ERROR(MaybeUpdateSqlModules(options));
1818
1819 // Enable metatracing as soon as possible.
1820 if (!options.metatrace_path.empty()) {
1821 metatrace::MetatraceConfig metatrace_config;
1822 metatrace_config.override_buffer_size = options.metatrace_buffer_capacity;
1823 metatrace_config.categories = options.metatrace_categories;
1824 tp->EnableMetatrace(metatrace_config);
1825 }
1826
1827 if (!options.register_files_dir.empty()) {
1828 RETURN_IF_ERROR(RegisterAllFilesInFolder(options.register_files_dir, *tp));
1829 }
1830
1831 // Descriptor pool used for printing output as textproto. Building on top of
1832 // generated pool so default protos in google.protobuf.descriptor.proto are
1833 // available.
1834 // For some insane reason, the descriptor pool is not movable so we need to
1835 // create it here so we can create references and pass it everywhere.
1836 google::protobuf::DescriptorPool pool(
1837 google::protobuf::DescriptorPool::generated_pool());
1838 RETURN_IF_ERROR(PopulateDescriptorPool(pool, metric_extensions));
1839
1840 // We load all the metric extensions even when --run-metrics arg is not there,
1841 // because we want the metrics to be available in interactive mode or when
1842 // used in UI using httpd.
1843 // Metric extensions are also used to populate the descriptor pool.
1844 for (const auto& extension : metric_extensions) {
1845 RETURN_IF_ERROR(LoadMetricExtension(extension, pool));
1846 }
1847
1848 base::TimeNanos t_load{};
1849 if (!options.trace_file_path.empty()) {
1850 base::TimeNanos t_load_start = base::GetWallTimeNs();
1851 double size_mb = 0;
1852 RETURN_IF_ERROR(LoadTrace(options.trace_file_path, &size_mb));
1853 t_load = base::GetWallTimeNs() - t_load_start;
1854
1855 double t_load_s = static_cast<double>(t_load.count()) / 1E9;
1856 PERFETTO_ILOG("Trace loaded: %.2f MB in %.2fs (%.1f MB/s)", size_mb,
1857 t_load_s, size_mb / t_load_s);
1858
1859 RETURN_IF_ERROR(PrintStats());
1860 }
1861
1862 #if PERFETTO_HAS_SIGNAL_H()
1863 // Set up interrupt signal to allow the user to abort query.
1864 signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
1865 #endif
1866
1867 base::TimeNanos t_query_start = base::GetWallTimeNs();
1868 if (!options.pre_metrics_v1_path.empty()) {
1869 RETURN_IF_ERROR(RunQueriesFromFile(options.pre_metrics_v1_path, false));
1870 }
1871
1872 // Trace summarization
1873 if (options.summary) {
1874 PERFETTO_CHECK(options.metric_v1_names.empty());
1875
1876 std::vector<std::string> spec_content;
1877 spec_content.reserve(options.summary_specs.size());
1878 for (const auto& s : options.summary_specs) {
1879 spec_content.emplace_back();
1880 if (!base::ReadFile(s, &spec_content.back())) {
1881 return base::ErrStatus("Unable to read summary spec file %s",
1882 s.c_str());
1883 }
1884 }
1885
1886 std::vector<TraceSummarySpecBytes> specs;
1887 specs.reserve(options.summary_specs.size());
1888 for (uint32_t i = 0; i < options.summary_specs.size(); ++i) {
1889 specs.emplace_back(TraceSummarySpecBytes{
1890 reinterpret_cast<const uint8_t*>(spec_content[i].data()),
1891 spec_content[i].size(),
1892 GuessSummarySpecFormat(options.summary_specs[i], spec_content[i]),
1893 });
1894 }
1895
1896 TraceSummaryComputationSpec computation_config;
1897 computation_config.v2_metric_ids =
1898 base::SplitString(options.summary_metrics_v2, ",");
1899 computation_config.metadata_query_id =
1900 options.summary_metadata_query.empty()
1901 ? std::nullopt
1902 : std::make_optional(options.summary_metadata_query);
1903
1904 TraceSummaryOutputSpec output_spec;
1905 output_spec.format = GetSummaryOutputFormat(options);
1906
1907 std::vector<uint8_t> output;
1908 RETURN_IF_ERROR(
1909 g_tp->Summarize(computation_config, specs, &output, output_spec));
1910 if (options.query_file_path.empty()) {
1911 fwrite(output.data(), sizeof(char), output.size(), stdout);
1912 }
1913 }
1914
1915 // v1 metrics.
1916 std::vector<MetricNameAndPath> metrics;
1917 if (!options.metric_v1_names.empty()) {
1918 PERFETTO_CHECK(!options.summary);
1919 RETURN_IF_ERROR(LoadMetrics(options.metric_v1_names, pool, metrics));
1920 }
1921
1922 MetricV1OutputFormat metric_format = ParseMetricV1OutputFormat(options);
1923 if (!metrics.empty()) {
1924 RETURN_IF_ERROR(RunMetrics(metrics, metric_format));
1925 }
1926
1927 if (!options.query_file_path.empty()) {
1928 base::Status status = RunQueriesFromFile(options.query_file_path, true);
1929 if (!status.ok()) {
1930 // Write metatrace if needed before exiting.
1931 RETURN_IF_ERROR(MaybeWriteMetatrace(options.metatrace_path));
1932 return status;
1933 }
1934 }
1935
1936 if (!options.query_string.empty()) {
1937 base::Status status = RunQueries(options.query_string, true);
1938 if (!status.ok()) {
1939 // Write metatrace if needed before exiting.
1940 RETURN_IF_ERROR(MaybeWriteMetatrace(options.metatrace_path));
1941 return status;
1942 }
1943 }
1944
1945 base::TimeNanos t_query = base::GetWallTimeNs() - t_query_start;
1946
1947 if (!options.export_file_path.empty()) {
1948 RETURN_IF_ERROR(ExportTraceToDatabase(options.export_file_path));
1949 }
1950
1951 if (options.enable_httpd) {
1952 #if PERFETTO_HAS_SIGNAL_H()
1953 if (options.metatrace_path.empty()) {
1954 // Restore the default signal handler to allow the user to terminate
1955 // httpd server via Ctrl-C.
1956 signal(SIGINT, SIG_DFL);
1957 } else {
1958 // Write metatrace to file before exiting.
1959 static std::string* metatrace_path = &options.metatrace_path;
1960 signal(SIGINT, [](int) {
1961 MaybeWriteMetatrace(*metatrace_path);
1962 exit(1);
1963 });
1964 }
1965 #endif
1966
1967 #if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
1968 RunHttpRPCServer(std::move(tp), options.port_number);
1969 PERFETTO_FATAL("Should never return");
1970 #else
1971 PERFETTO_FATAL("HTTP not available");
1972 #endif
1973 }
1974
1975 if (options.enable_stdiod) {
1976 return RunStdioRpcServer(std::move(tp));
1977 }
1978
1979 if (options.launch_shell) {
1980 RETURN_IF_ERROR(StartInteractiveShell(
1981 InteractiveOptions{options.wide ? 40u : 20u, metric_format,
1982 metric_extensions, metrics, &pool}));
1983 } else if (!options.perf_file_path.empty()) {
1984 RETURN_IF_ERROR(PrintPerfFile(options.perf_file_path, t_load, t_query));
1985 }
1986
1987 RETURN_IF_ERROR(MaybeWriteMetatrace(options.metatrace_path));
1988
1989 return base::OkStatus();
1990 }
1991
1992 } // namespace
1993
1994 } // namespace perfetto::trace_processor
1995
main(int argc,char ** argv)1996 int main(int argc, char** argv) {
1997 auto status = perfetto::trace_processor::TraceProcessorMain(argc, argv);
1998 if (!status.ok()) {
1999 fprintf(stderr, "%s\n", status.c_message());
2000 return 1;
2001 }
2002 return 0;
2003 }
2004