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