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