aboutsummaryrefslogtreecommitdiff
path: root/src/trace_processor/trace_processor_shell.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/trace_processor/trace_processor_shell.cc')
-rw-r--r--src/trace_processor/trace_processor_shell.cc502
1 files changed, 232 insertions, 270 deletions
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 594e13e9b..b6fefe2b0 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
@@ -34,11 +33,13 @@
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
#include "perfetto/trace_processor/read_trace.h"
#include "perfetto/trace_processor/trace_processor.h"
#include "src/trace_processor/metrics/custom_options.descriptor.h"
#include "src/trace_processor/metrics/metrics.descriptor.h"
-#include "src/trace_processor/proto_to_json.h"
+#include "src/trace_processor/util/proto_to_json.h"
+#include "src/trace_processor/util/status_macros.h"
#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
#include "src/trace_processor/rpc/httpd.h"
@@ -168,7 +169,7 @@ ScopedLine GetLine(const char* prompt) {
#endif // PERFETTO_TP_LINENOISE
-bool PrintStats() {
+util::Status PrintStats() {
auto it = g_tp->ExecuteQuery(
"SELECT name, idx, source, value from stats "
"where severity IN ('error', 'data_loss') and value > 0");
@@ -215,20 +216,18 @@ bool PrintStats() {
util::Status status = it.Status();
if (!status.ok()) {
- PERFETTO_ELOG("Error while iterating stats %s", status.c_message());
- return false;
+ return util::ErrStatus("Error while iterating stats (%s)",
+ status.c_message());
}
- return true;
+ return util::OkStatus();
}
-int ExportTraceToDatabase(const std::string& output_name) {
+util::Status ExportTraceToDatabase(const std::string& output_name) {
PERFETTO_CHECK(output_name.find("'") == std::string::npos);
{
base::ScopedFile fd(base::OpenFile(output_name, O_CREAT | O_RDWR, 0600));
- if (!fd) {
- PERFETTO_PLOG("Failed to create file: %s", output_name.c_str());
- return 1;
- }
+ if (!fd)
+ return util::ErrStatus("Failed to create file: %s", output_name.c_str());
int res = ftruncate(fd.get(), 0);
PERFETTO_CHECK(res == 0);
}
@@ -240,11 +239,10 @@ int ExportTraceToDatabase(const std::string& output_name) {
PERFETTO_DCHECK(!attach_has_more);
util::Status status = attach_it.Status();
- if (!status.ok()) {
- PERFETTO_ELOG("SQLite error: %s", status.c_message());
- return 1;
- }
+ if (!status.ok())
+ return util::ErrStatus("SQLite error: %s", status.c_message());
+ // Export real and virtual tables.
auto tables_it = g_tp->ExecuteQuery(
"SELECT name FROM perfetto_tables UNION "
"SELECT name FROM sqlite_master WHERE type='table'");
@@ -259,26 +257,43 @@ int ExportTraceToDatabase(const std::string& output_name) {
PERFETTO_DCHECK(!export_has_more);
status = export_it.Status();
- if (!status.ok()) {
- PERFETTO_ELOG("SQLite error: %s", status.c_message());
- return 1;
- }
+ if (!status.ok())
+ return util::ErrStatus("SQLite error: %s", status.c_message());
}
status = tables_it.Status();
- if (!status.ok()) {
- PERFETTO_ELOG("SQLite error: %s", status.c_message());
- return 1;
+ if (!status.ok())
+ return util::ErrStatus("SQLite error: %s", status.c_message());
+
+ // Export views.
+ auto views_it =
+ g_tp->ExecuteQuery("SELECT sql FROM sqlite_master WHERE type='view'");
+ for (uint32_t rows = 0; views_it.Next(); rows++) {
+ std::string sql = views_it.Get(0).string_value;
+ // View statements are of the form "CREATE VIEW name AS stmt". We need to
+ // rewrite name to point to the exported db.
+ const std::string kPrefix = "CREATE VIEW ";
+ PERFETTO_CHECK(sql.find(kPrefix) == 0);
+ sql = sql.substr(0, kPrefix.size()) + "perfetto_export." +
+ sql.substr(kPrefix.size());
+
+ auto export_it = g_tp->ExecuteQuery(sql);
+ bool export_has_more = export_it.Next();
+ PERFETTO_DCHECK(!export_has_more);
+
+ status = export_it.Status();
+ if (!status.ok())
+ return util::ErrStatus("SQLite error: %s", status.c_message());
}
+ status = views_it.Status();
+ if (!status.ok())
+ return util::ErrStatus("SQLite error: %s", status.c_message());
auto detach_it = g_tp->ExecuteQuery("DETACH DATABASE perfetto_export");
bool detach_has_more = attach_it.Next();
PERFETTO_DCHECK(!detach_has_more);
status = detach_it.Status();
- if (!status.ok()) {
- PERFETTO_ELOG("SQLite error: %s", status.c_message());
- return 1;
- }
- return 0;
+ return status.ok() ? util::OkStatus()
+ : util::ErrStatus("SQLite error: %s", status.c_message());
}
class ErrorPrinter : public google::protobuf::io::ErrorCollector {
@@ -327,6 +342,23 @@ util::Status ExtendMetricsProto(const std::string& extend_metrics_proto,
google::protobuf::compiler::Parser parser;
parser.Parse(&tokenizer, file_desc);
+ // Go through all the imports (dependencies) and make the import
+ // paths relative to the Perfetto root. This allows trace processor embedders
+ // to have paths relative to their own root for imports when using metric
+ // proto extensions.
+ for (int i = 0; i < file_desc->dependency_size(); ++i) {
+ static constexpr char kPrefix[] = "protos/perfetto/metrics/";
+ auto* dep = file_desc->mutable_dependency(i);
+
+ // If the file being imported contains kPrefix, it is probably an import of
+ // a Perfetto metrics proto. Strip anything before kPrefix to ensure that
+ // we resolve the paths correctly.
+ size_t idx = dep->find(kPrefix);
+ if (idx != std::string::npos) {
+ *dep = dep->substr(idx);
+ }
+ }
+
file_desc->set_name(BaseName(extend_metrics_proto));
pool->BuildFile(*file_desc);
@@ -345,21 +377,21 @@ enum OutputFormat {
kNone,
};
-int RunMetrics(const std::vector<std::string>& metric_names,
- OutputFormat format,
- const google::protobuf::DescriptorPool& pool) {
+util::Status RunMetrics(const std::vector<std::string>& metric_names,
+ OutputFormat format,
+ const google::protobuf::DescriptorPool& pool) {
std::vector<uint8_t> metric_result;
util::Status status = g_tp->ComputeMetric(metric_names, &metric_result);
if (!status.ok()) {
- PERFETTO_ELOG("Error when computing metrics: %s", status.c_message());
- return 1;
+ return util::ErrStatus("Error when computing metrics: %s",
+ status.c_message());
}
if (format == OutputFormat::kNone) {
- return 0;
+ return util::OkStatus();
}
if (format == OutputFormat::kBinaryProto) {
fwrite(metric_result.data(), sizeof(uint8_t), metric_result.size(), stdout);
- return 0;
+ return util::OkStatus();
}
google::protobuf::DynamicMessageFactory factory(&pool);
@@ -391,7 +423,7 @@ int RunMetrics(const std::vector<std::string>& metric_names,
case OutputFormat::kNone:
PERFETTO_FATAL("Unsupported output format.");
}
- return 0;
+ return util::OkStatus();
}
void PrintQueryResultInteractively(TraceProcessor::Iterator* it,
@@ -463,7 +495,7 @@ void PrintShellUsage() {
".reset Destroys all tables/view created by the user.\n");
}
-int StartInteractiveShell(uint32_t column_width) {
+util::Status StartInteractiveShell(uint32_t column_width) {
SetupLineEditor();
for (;;) {
@@ -481,7 +513,7 @@ int StartInteractiveShell(uint32_t column_width) {
} else if (strcmp(command, "help") == 0) {
PrintShellUsage();
} else if (strcmp(command, "dump") == 0 && strlen(arg)) {
- if (ExportTraceToDatabase(arg) != 0)
+ if (!ExportTraceToDatabase(arg).ok())
PERFETTO_ELOG("Database export failed");
} else if (strcmp(command, "reset") == 0) {
g_tp->RestoreInitialTables();
@@ -495,7 +527,7 @@ int StartInteractiveShell(uint32_t column_width) {
auto it = g_tp->ExecuteQuery(line.get());
PrintQueryResultInteractively(&it, t_start, column_width);
}
- return 0;
+ return util::OkStatus();
}
util::Status PrintQueryResultAsCsv(TraceProcessor::Iterator* it, FILE* output) {
@@ -535,32 +567,37 @@ util::Status PrintQueryResultAsCsv(TraceProcessor::Iterator* it, FILE* output) {
return it->Status();
}
-bool IsBlankLine(char* buffer) {
- size_t buf_size = strlen(buffer);
- for (size_t i = 0; i < buf_size; ++i) {
- // We can index into buffer[i+1], because strlen does not include the
- // trailing \0, so even if \r is the last character, this is not out
- // of bound.
- if (buffer[i] == '\r') {
- if (buffer[i + 1] != '\n')
- return false;
- } else if (buffer[i] != ' ' && buffer[i] != '\t' && buffer[i] != '\n') {
- return false;
- }
- }
- return true;
+bool IsBlankLine(const std::string& buffer) {
+ return buffer == "\n" || buffer == "\r\n";
}
-bool LoadQueries(FILE* input, std::vector<std::string>* output) {
+bool IsCommentLine(const std::string& buffer) {
+ return base::StartsWith(buffer, "--");
+}
+
+bool HasEndOfQueryDelimiter(const std::string& buffer) {
+ return base::EndsWith(buffer, ";\n") || base::EndsWith(buffer, ";") ||
+ base::EndsWith(buffer, ";\r\n");
+}
+
+util::Status LoadQueries(FILE* input, std::vector<std::string>* output) {
char buffer[4096];
while (!feof(input) && !ferror(input)) {
std::string sql_query;
while (fgets(buffer, sizeof(buffer), input)) {
- if (IsBlankLine(buffer))
+ std::string line = buffer;
+ if (IsBlankLine(line))
+ break;
+
+ if (IsCommentLine(line))
+ continue;
+
+ sql_query.append(line);
+
+ if (HasEndOfQueryDelimiter(line))
break;
- sql_query.append(buffer);
}
- if (sql_query.back() == '\n')
+ if (!sql_query.empty() && sql_query.back() == '\n')
sql_query.resize(sql_query.size() - 1);
// If we have a new line at the end of the file or an extra new line
@@ -572,14 +609,13 @@ bool LoadQueries(FILE* input, std::vector<std::string>* output) {
output->push_back(sql_query);
}
if (ferror(input)) {
- PERFETTO_ELOG("Error reading query file");
- return false;
+ return util::ErrStatus("Error reading query file");
}
- return true;
+ return util::OkStatus();
}
-bool RunQueryAndPrintResult(const std::vector<std::string> queries,
- FILE* output) {
+util::Status RunQueryAndPrintResult(const std::vector<std::string>& queries,
+ FILE* output) {
bool is_first_query = true;
bool is_query_error = false;
bool has_output = false;
@@ -619,31 +655,28 @@ bool RunQueryAndPrintResult(const std::vector<std::string> queries,
is_query_error = true;
}
}
- return !is_query_error;
+ return is_query_error
+ ? util::ErrStatus("Encountered errors while running queries")
+ : util::OkStatus();
}
-int MaybePrintPerfFile(const std::string& perf_file_path,
- base::TimeNanos t_load,
- base::TimeNanos t_run) {
- if (perf_file_path.empty())
- return 0;
-
+util::Status PrintPerfFile(const std::string& perf_file_path,
+ base::TimeNanos t_load,
+ base::TimeNanos t_run) {
char buf[128];
int count = snprintf(buf, sizeof(buf), "%" PRId64 ",%" PRId64,
static_cast<int64_t>(t_load.count()),
static_cast<int64_t>(t_run.count()));
if (count < 0) {
- PERFETTO_ELOG("Failed to write perf data");
- return 1;
+ return util::ErrStatus("Failed to write perf data");
}
auto fd(base::OpenFile(perf_file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666));
if (!fd) {
- PERFETTO_ELOG("Failed to open perf file");
- return 1;
+ return util::ErrStatus("Failed to open perf file");
}
base::WriteAll(fd.get(), buf, static_cast<size_t>(count));
- return 0;
+ return util::OkStatus();
}
struct CommandLineOptions {
@@ -652,7 +685,6 @@ struct CommandLineOptions {
std::string sqlite_file_path;
std::string metric_names;
std::string metric_output;
- std::string metric_extra;
std::string trace_file_path;
bool launch_shell = false;
bool enable_httpd = false;
@@ -710,10 +742,6 @@ CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
return command_line_options;
}
-util::Status RegisterExtraMetrics(const std::string&, const std::string&) {
- return util::ErrStatus("RegisterExtraMetrics not implemented on Windows");
-}
-
#else // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
void PrintUsage(char** argv) {
@@ -740,7 +768,9 @@ Options:
-i, --interactive Starts interactive mode even after a query
file is specified with -q or
--run-metrics.
- -e, --export FILE Export the trace into a SQLite database.
+ -e, --export FILE Export the contents of trace processor
+ into an SQLite database after running any
+ metrics or queries specified.
--run-metrics x,y,z Runs a comma separated list of metrics and
prints the result as a TraceMetrics proto
to stdout. The specified can either be
@@ -750,10 +780,6 @@ Options:
specified in either proto binary, proto
text format or JSON format (default: proto
text).
- --extra-metrics PATH Registers all SQL files at the given path
- to the trace processor and extends the
- builtin metrics proto with
- $PATH/metrics-ext.proto.
--full-sort Forces the trace processor into performing
a full sort ignoring any windowing
logic.)",
@@ -765,7 +791,6 @@ CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
enum LongOption {
OPT_RUN_METRICS = 1000,
OPT_METRICS_OUTPUT,
- OPT_EXTRA_METRICS,
OPT_FORCE_FULL_SORT,
};
@@ -781,7 +806,6 @@ CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
{"export", required_argument, nullptr, 'e'},
{"run-metrics", required_argument, nullptr, OPT_RUN_METRICS},
{"metrics-output", required_argument, nullptr, OPT_METRICS_OUTPUT},
- {"extra-metrics", required_argument, nullptr, OPT_EXTRA_METRICS},
{"full-sort", no_argument, nullptr, OPT_FORCE_FULL_SORT},
{nullptr, 0, nullptr, 0}};
@@ -848,11 +872,6 @@ CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
continue;
}
- if (option == OPT_EXTRA_METRICS) {
- command_line_options.metric_extra = optarg;
- continue;
- }
-
if (option == OPT_FORCE_FULL_SORT) {
command_line_options.force_full_sort = true;
continue;
@@ -864,7 +883,8 @@ CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
command_line_options.launch_shell =
explicit_interactive || (command_line_options.metric_names.empty() &&
- command_line_options.query_file_path.empty());
+ command_line_options.query_file_path.empty() &&
+ command_line_options.sqlite_file_path.empty());
// Only allow non-interactive queries to emit perf data.
if (!command_line_options.perf_file_path.empty() &&
@@ -885,43 +905,6 @@ CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
return command_line_options;
}
-util::Status RegisterExtraMetric(const std::string& parent_path,
- const std::string& path) {
- // Silently ignore any non-SQL files.
- if (path.find(".sql") == std::string::npos)
- return util::OkStatus();
-
- std::string sql;
- base::ReadFile(parent_path + "/" + path, &sql);
- return g_tp->RegisterMetric(path, sql);
-}
-
-util::Status RegisterExtraMetrics(const std::string& path,
- const std::string& group) {
- std::string full_path = path + "/" + group;
- DIR* dir = opendir(full_path.c_str());
- if (dir == nullptr) {
- return util::ErrStatus(
- "Failed to open directory %s to register extra metrics",
- full_path.c_str());
- }
-
- for (auto* dirent = readdir(dir); dirent != nullptr; dirent = readdir(dir)) {
- util::Status status = util::OkStatus();
- if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
- continue;
-
- if (dirent->d_type == DT_DIR) {
- status = RegisterExtraMetrics(path, group + dirent->d_name + "/");
- } else if (dirent->d_type == DT_REG) {
- status = RegisterExtraMetric(path, group + dirent->d_name);
- }
- if (!status.ok())
- return status;
- }
- return util::OkStatus();
-}
-
#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
void ExtendPoolWithBinaryDescriptor(google::protobuf::DescriptorPool& pool,
@@ -934,80 +917,56 @@ void ExtendPoolWithBinaryDescriptor(google::protobuf::DescriptorPool& pool,
}
}
-int TraceProcessorMain(int argc, char** argv) {
- CommandLineOptions options = ParseCommandLineOptions(argc, argv);
-
- // Load the trace file into the trace processor.
- Config config;
- config.force_full_sort = options.force_full_sort;
-
- std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
- g_tp = tp.get();
-
- base::TimeNanos t_load{};
- if (!options.trace_file_path.empty()) {
- auto t_load_start = base::GetWallTimeNs();
- double size_mb = 0;
- util::Status read_status =
- ReadTrace(tp.get(), options.trace_file_path.c_str(),
- [&size_mb](size_t parsed_size) {
- size_mb = parsed_size / 1E6;
- fprintf(stderr, "\rLoading trace: %.2f MB\r", size_mb);
- });
- if (!read_status.ok()) {
- PERFETTO_ELOG("Could not read trace file (path: %s): %s",
- options.trace_file_path.c_str(), read_status.c_message());
- return 1;
- }
+util::Status LoadTrace(const std::string& trace_file_path, double* size_mb) {
+ util::Status read_status =
+ ReadTrace(g_tp, trace_file_path.c_str(), [&size_mb](size_t parsed_size) {
+ *size_mb = parsed_size / 1E6;
+ fprintf(stderr, "\rLoading trace: %.2f MB\r", *size_mb);
+ });
+ if (!read_status.ok()) {
+ return util::ErrStatus("Could not read trace file (path: %s): %s",
+ trace_file_path.c_str(), read_status.c_message());
+ }
- std::unique_ptr<profiling::Symbolizer> symbolizer;
- auto binary_path = profiling::GetPerfettoBinaryPath();
- if (!binary_path.empty()) {
+ std::unique_ptr<profiling::Symbolizer> symbolizer;
+ auto binary_path = profiling::GetPerfettoBinaryPath();
+ if (!binary_path.empty()) {
#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
symbolizer.reset(new profiling::LocalSymbolizer(std::move(binary_path)));
#else
PERFETTO_FATAL("This build does not support local symbolization.");
#endif
- }
- if (symbolizer) {
- profiling::SymbolizeDatabase(
- tp.get(), symbolizer.get(), [&tp](const std::string& trace_proto) {
- std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]);
- memcpy(buf.get(), trace_proto.data(), trace_proto.size());
- auto status = tp->Parse(std::move(buf), trace_proto.size());
- if (!status.ok()) {
- PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s",
- status.message().c_str());
- return;
- }
- });
- tp->NotifyEndOfFile();
- }
-
- t_load = base::GetWallTimeNs() - t_load_start;
- double t_load_s = t_load.count() / 1E9;
- PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb,
- size_mb / t_load_s);
- } // if (!trace_file_path.empty())
-
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
- if (options.enable_httpd) {
- RunHttpRPCServer(std::move(tp));
- return 0;
}
-#endif
-#if PERFETTO_HAS_SIGNAL_H()
- signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
-#endif
-
- // Print out the stats to stderr for the trace.
- if (!PrintStats()) {
- return 1;
+ if (symbolizer) {
+ profiling::SymbolizeDatabase(
+ g_tp, symbolizer.get(), [](const std::string& trace_proto) {
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]);
+ memcpy(buf.get(), trace_proto.data(), trace_proto.size());
+ auto status = g_tp->Parse(std::move(buf), trace_proto.size());
+ if (!status.ok()) {
+ PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s",
+ status.message().c_str());
+ return;
+ }
+ });
+ g_tp->NotifyEndOfFile();
}
+ return util::OkStatus();
+}
- auto t_run_start = base::GetWallTimeNs();
+util::Status RunQueries(const CommandLineOptions& options) {
+ std::vector<std::string> queries;
+ base::ScopedFstream file(fopen(options.query_file_path.c_str(), "r"));
+ if (!file) {
+ return util::ErrStatus("Could not open query file (path: %s)",
+ options.query_file_path.c_str());
+ }
+ RETURN_IF_ERROR(LoadQueries(file.get(), &queries));
+ return RunQueryAndPrintResult(queries, stdout);
+}
+util::Status RunMetrics(const CommandLineOptions& options) {
// Descriptor pool used for printing output as textproto.
// Building on top of generated pool so default protos in
// google.protobuf.descriptor.proto are available.
@@ -1018,106 +977,104 @@ int TraceProcessorMain(int argc, char** argv) {
ExtendPoolWithBinaryDescriptor(pool, kCustomOptionsDescriptor.data(),
kCustomOptionsDescriptor.size());
- if (!options.metric_extra.empty()) {
- util::Status status = RegisterExtraMetrics(options.metric_extra, "");
+ std::vector<std::string> metrics;
+ for (base::StringSplitter ss(options.metric_names, ','); ss.Next();) {
+ metrics.emplace_back(ss.cur_token());
+ }
+
+ // For all metrics which are files, register them and extend the metrics
+ // proto.
+ for (size_t i = 0; i < metrics.size(); ++i) {
+ const std::string& metric_or_path = metrics[i];
+
+ // If there is no extension, we assume it is a builtin metric.
+ auto ext_idx = metric_or_path.rfind(".");
+ if (ext_idx == std::string::npos)
+ continue;
+
+ std::string no_ext_name = metric_or_path.substr(0, ext_idx);
+ util::Status status = RegisterMetric(no_ext_name + ".sql");
if (!status.ok()) {
- PERFETTO_ELOG("Failed to register extra metrics: %s", status.c_message());
- return 1;
+ return util::ErrStatus("Unable to register metric %s: %s",
+ metric_or_path.c_str(), status.c_message());
}
- auto ext_proto = options.metric_extra + "/metrics-ext.proto";
- // Check if the file exists
- base::ScopedFile file(base::OpenFile(ext_proto, O_RDONLY));
- if (file.get() != -1) {
- status = ExtendMetricsProto(ext_proto, &pool);
- if (!status.ok()) {
- PERFETTO_ELOG("Failed to extend metrics proto: %s", status.c_message());
- return 1;
- }
+ status = ExtendMetricsProto(no_ext_name + ".proto", &pool);
+ if (!status.ok()) {
+ return util::ErrStatus("Unable to extend metrics proto %s: %s",
+ metric_or_path.c_str(), status.c_message());
}
+
+ metrics[i] = BaseName(no_ext_name);
}
- if (!options.metric_names.empty()) {
- std::vector<std::string> metrics;
- for (base::StringSplitter ss(options.metric_names, ','); ss.Next();) {
- metrics.emplace_back(ss.cur_token());
- }
+ OutputFormat format;
+ if (!options.query_file_path.empty()) {
+ format = OutputFormat::kNone;
+ } else if (options.metric_output == "binary") {
+ format = OutputFormat::kBinaryProto;
+ } else if (options.metric_output == "json") {
+ format = OutputFormat::kJson;
+ } else {
+ format = OutputFormat::kTextProto;
+ }
+ return RunMetrics(std::move(metrics), format, pool);
+}
- // For all metrics which are files, register them and extend the metrics
- // proto.
- for (size_t i = 0; i < metrics.size(); ++i) {
- const std::string& metric_or_path = metrics[i];
+util::Status TraceProcessorMain(int argc, char** argv) {
+ CommandLineOptions options = ParseCommandLineOptions(argc, argv);
- // If there is no extension, we assume it is a builtin metric.
- auto ext_idx = metric_or_path.rfind(".");
- if (ext_idx == std::string::npos)
- continue;
+ Config config;
+ config.force_full_sort = options.force_full_sort;
- std::string no_ext_name = metric_or_path.substr(0, ext_idx);
- util::Status status = RegisterMetric(no_ext_name + ".sql");
- if (!status.ok()) {
- PERFETTO_ELOG("Unable to register metric %s: %s",
- metric_or_path.c_str(), status.c_message());
- return 1;
- }
+ std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
+ g_tp = tp.get();
- status = ExtendMetricsProto(no_ext_name + ".proto", &pool);
- if (!status.ok()) {
- PERFETTO_ELOG("Unable to extend metrics proto %s: %s",
- metric_or_path.c_str(), status.c_message());
- return 1;
- }
+ base::TimeNanos t_load{};
+ if (!options.trace_file_path.empty()) {
+ base::TimeNanos t_load_start = base::GetWallTimeNs();
+ double size_mb = 0;
+ RETURN_IF_ERROR(LoadTrace(options.trace_file_path, &size_mb));
+ t_load = base::GetWallTimeNs() - t_load_start;
- metrics[i] = BaseName(no_ext_name);
- }
+ double t_load_s = t_load.count() / 1E9;
+ PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb,
+ size_mb / t_load_s);
- OutputFormat format;
- if (!options.query_file_path.empty()) {
- format = OutputFormat::kNone;
- } else if (options.metric_output == "binary") {
- format = OutputFormat::kBinaryProto;
- } else if (options.metric_output == "json") {
- format = OutputFormat::kJson;
- } else {
- format = OutputFormat::kTextProto;
- }
- int ret = RunMetrics(std::move(metrics), format, pool);
- if (!ret) {
- auto t_query = base::GetWallTimeNs() - t_run_start;
- ret = MaybePrintPerfFile(options.perf_file_path, t_load, t_query);
- }
- if (ret)
- return ret;
+ RETURN_IF_ERROR(PrintStats());
}
- // If we were given a query file, load contents
- std::vector<std::string> queries;
- if (!options.query_file_path.empty()) {
- base::ScopedFstream file(fopen(options.query_file_path.c_str(), "r"));
- if (!file) {
- PERFETTO_ELOG("Could not open query file (path: %s)",
- options.query_file_path.c_str());
- return 1;
- }
- if (!LoadQueries(file.get(), &queries)) {
- return 1;
- }
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
+ if (options.enable_httpd) {
+ RunHttpRPCServer(std::move(tp));
+ PERFETTO_FATAL("Should never return");
}
+#endif
- if (!RunQueryAndPrintResult(queries, stdout)) {
- return 1;
+#if PERFETTO_HAS_SIGNAL_H()
+ signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
+#endif
+
+ base::TimeNanos t_query_start = base::GetWallTimeNs();
+ if (!options.metric_names.empty()) {
+ RETURN_IF_ERROR(RunMetrics(options));
}
- if (!options.sqlite_file_path.empty()) {
- return ExportTraceToDatabase(options.sqlite_file_path);
+ if (!options.query_file_path.empty()) {
+ RETURN_IF_ERROR(RunQueries(options));
}
+ base::TimeNanos t_query = base::GetWallTimeNs() - t_query_start;
- if (!options.launch_shell) {
- auto t_query = base::GetWallTimeNs() - t_run_start;
- return MaybePrintPerfFile(options.perf_file_path, t_load, t_query);
+ if (!options.sqlite_file_path.empty()) {
+ RETURN_IF_ERROR(ExportTraceToDatabase(options.sqlite_file_path));
}
- return StartInteractiveShell(options.wide ? 40 : 20);
+ if (options.launch_shell) {
+ RETURN_IF_ERROR(StartInteractiveShell(options.wide ? 40 : 20));
+ } else if (!options.perf_file_path.empty()) {
+ RETURN_IF_ERROR(PrintPerfFile(options.perf_file_path, t_load, t_query));
+ }
+ return util::OkStatus();
}
} // namespace
@@ -1126,5 +1083,10 @@ int TraceProcessorMain(int argc, char** argv) {
} // namespace perfetto
int main(int argc, char** argv) {
- return perfetto::trace_processor::TraceProcessorMain(argc, argv);
+ auto status = perfetto::trace_processor::TraceProcessorMain(argc, argv);
+ if (!status.ok()) {
+ PERFETTO_ELOG("%s", status.c_message());
+ return 1;
+ }
+ return 0;
}