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