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