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