• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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