blob: 594e13e9bae6e7ce86ab03fd971cbb0f811b8659 [file] [log] [blame]
Primiano Tucci5968caf2018-08-06 10:31:46 +01001/*
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
Primiano Tucci7e330292018-08-24 19:10:52 +020017#include <fcntl.h>
Lalit Maganti21160e82018-10-16 09:40:29 +010018#include <inttypes.h>
Lalit Maganti26f69bd2019-04-29 18:23:47 +010019#include <stdio.h>
Primiano Tucci7e330292018-08-24 19:10:52 +020020#include <sys/stat.h>
Primiano Tucci5968caf2018-08-06 10:31:46 +010021
22#include <functional>
Lalit Maganti21160e82018-10-16 09:40:29 +010023#include <iostream>
Lalit Maganti7c959782019-04-02 16:54:12 +010024#include <vector>
Primiano Tucci5968caf2018-08-06 10:31:46 +010025
Lalit Magantica4d5142019-05-28 13:25:47 +010026#include <google/protobuf/compiler/parser.h>
Lalit Maganti812ef672019-05-28 13:28:02 +010027#include <google/protobuf/dynamic_message.h>
Lalit Magantica4d5142019-05-28 13:25:47 +010028#include <google/protobuf/io/zero_copy_stream_impl.h>
Lalit Maganti812ef672019-05-28 13:28:02 +010029#include <google/protobuf/text_format.h>
Lalit Magantica4d5142019-05-28 13:25:47 +010030
Primiano Tucci24647142018-08-13 21:18:24 +020031#include "perfetto/base/build_config.h"
Primiano Tucci5968caf2018-08-06 10:31:46 +010032#include "perfetto/base/logging.h"
Eric Seckler83dcc8c2019-08-21 12:18:43 +010033#include "perfetto/base/time.h"
Primiano Tucci2c5488f2019-06-01 03:27:28 +010034#include "perfetto/ext/base/file_utils.h"
35#include "perfetto/ext/base/scoped_file.h"
36#include "perfetto/ext/base/string_splitter.h"
Eric Seckler8f70bbf2019-10-09 09:37:43 +010037#include "perfetto/trace_processor/read_trace.h"
Ioannis Ilkoseff38f52018-10-29 10:37:55 +000038#include "perfetto/trace_processor/trace_processor.h"
Deepanjan Royab8c05d2020-01-21 09:55:26 -050039#include "src/trace_processor/metrics/custom_options.descriptor.h"
Lalit Maganti812ef672019-05-28 13:28:02 +010040#include "src/trace_processor/metrics/metrics.descriptor.h"
Lalit Maganti25863f72019-08-28 22:14:25 +010041#include "src/trace_processor/proto_to_json.h"
Primiano Tucci5968caf2018-08-06 10:31:46 +010042
Primiano Tuccia36cccc2019-10-27 13:15:04 +010043#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
44#include "src/trace_processor/rpc/httpd.h"
45#endif
46
Florian Mayer6cc8b1d2019-12-18 16:37:32 +000047#include "src/profiling/symbolizer/symbolize_database.h"
48#include "src/profiling/symbolizer/symbolizer.h"
49
50#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
51#include "src/profiling/symbolizer/local_symbolizer.h"
52#endif
53
Primiano Tucci24647142018-08-13 21:18:24 +020054#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
55 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
56 PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
57#define PERFETTO_HAS_SIGNAL_H() 1
58#else
59#define PERFETTO_HAS_SIGNAL_H() 0
60#endif
61
Primiano Tucci02c11762019-08-30 00:57:59 +020062#if PERFETTO_BUILDFLAG(PERFETTO_TP_LINENOISE)
Hector Dearmane44ad452018-09-21 11:51:57 +010063#include <linenoise.h>
Hector Dearman36adc822018-10-02 11:35:08 +010064#include <pwd.h>
65#include <sys/types.h>
Primiano Tucci75ae50e2019-08-28 13:09:55 +020066#endif
67
68#if PERFETTO_BUILDFLAG(PERFETTO_VERSION_GEN)
Primiano Tucci00da64a2019-02-22 14:51:10 +000069#include "perfetto_version.gen.h"
70#else
71#define PERFETTO_GET_GIT_REVISION() "unknown"
Hector Dearmane44ad452018-09-21 11:51:57 +010072#endif
73
Primiano Tucci24647142018-08-13 21:18:24 +020074#if PERFETTO_HAS_SIGNAL_H()
75#include <signal.h>
76#endif
77
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +010078#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
79#define ftruncate _chsize
80#else
Lalit Magantif9d80302019-06-19 12:48:25 +010081#include <dirent.h>
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +010082#include <getopt.h>
83#endif
84
Lalit Maganti1ebebf12018-10-15 17:24:04 +010085namespace perfetto {
86namespace trace_processor {
Primiano Tucci5968caf2018-08-06 10:31:46 +010087
88namespace {
Primiano Tucci24647142018-08-13 21:18:24 +020089TraceProcessor* g_tp;
90
Primiano Tucci02c11762019-08-30 00:57:59 +020091#if PERFETTO_BUILDFLAG(PERFETTO_TP_LINENOISE)
Hector Dearmane44ad452018-09-21 11:51:57 +010092
Hector Dearman36adc822018-10-02 11:35:08 +010093bool EnsureDir(const std::string& path) {
94 return mkdir(path.c_str(), 0755) != -1 || errno == EEXIST;
95}
96
97bool EnsureFile(const std::string& path) {
Primiano Tuccif675dc22018-10-18 00:17:26 +020098 return base::OpenFile(path, O_RDONLY | O_CREAT, 0644).get() != -1;
Hector Dearman36adc822018-10-02 11:35:08 +010099}
100
101std::string GetConfigPath() {
102 const char* homedir = getenv("HOME");
103 if (homedir == nullptr)
104 homedir = getpwuid(getuid())->pw_dir;
105 if (homedir == nullptr)
106 return "";
107 return std::string(homedir) + "/.config";
108}
109
110std::string GetPerfettoPath() {
111 std::string config = GetConfigPath();
112 if (config == "")
113 return "";
114 return config + "/perfetto";
115}
116
117std::string GetHistoryPath() {
118 std::string perfetto = GetPerfettoPath();
119 if (perfetto == "")
120 return "";
121 return perfetto + "/.trace_processor_shell_history";
122}
123
Hector Dearmane44ad452018-09-21 11:51:57 +0100124void SetupLineEditor() {
125 linenoiseSetMultiLine(true);
126 linenoiseHistorySetMaxLen(1000);
Hector Dearman36adc822018-10-02 11:35:08 +0100127
128 bool success = GetHistoryPath() != "";
129 success = success && EnsureDir(GetConfigPath());
130 success = success && EnsureDir(GetPerfettoPath());
131 success = success && EnsureFile(GetHistoryPath());
132 success = success && linenoiseHistoryLoad(GetHistoryPath().c_str()) != -1;
133 if (!success) {
134 PERFETTO_PLOG("Could not load history from %s", GetHistoryPath().c_str());
135 }
Primiano Tucci5968caf2018-08-06 10:31:46 +0100136}
137
Florian Mayerb5fa0c92019-12-18 12:44:46 +0000138struct LineDeleter {
139 void operator()(char* p) const {
140 linenoiseHistoryAdd(p);
141 linenoiseHistorySave(GetHistoryPath().c_str());
142 linenoiseFree(p);
143 }
144};
Hector Dearmane44ad452018-09-21 11:51:57 +0100145
Florian Mayerb5fa0c92019-12-18 12:44:46 +0000146using ScopedLine = std::unique_ptr<char, LineDeleter>;
147
148ScopedLine GetLine(const char* prompt) {
149 return ScopedLine(linenoise(prompt));
Hector Dearmane44ad452018-09-21 11:51:57 +0100150}
151
152#else
153
154void SetupLineEditor() {}
155
Florian Mayerb5fa0c92019-12-18 12:44:46 +0000156using ScopedLine = std::unique_ptr<char>;
Hector Dearmane44ad452018-09-21 11:51:57 +0100157
Florian Mayerb5fa0c92019-12-18 12:44:46 +0000158ScopedLine GetLine(const char* prompt) {
Hector Dearmane44ad452018-09-21 11:51:57 +0100159 printf("\r%80s\r%s", "", prompt);
160 fflush(stdout);
Florian Mayerb5fa0c92019-12-18 12:44:46 +0000161 ScopedLine line(new char[1024]);
Lalit Maganti3be20912019-12-18 13:48:45 +0000162 if (!fgets(line.get(), 1024 - 1, stdin))
Hector Dearmane44ad452018-09-21 11:51:57 +0100163 return nullptr;
Lalit Maganti3be20912019-12-18 13:48:45 +0000164 if (strlen(line.get()) > 0)
165 line.get()[strlen(line.get()) - 1] = 0;
Hector Dearmane44ad452018-09-21 11:51:57 +0100166 return line;
167}
168
Primiano Tucci02c11762019-08-30 00:57:59 +0200169#endif // PERFETTO_TP_LINENOISE
Hector Dearmane44ad452018-09-21 11:51:57 +0100170
Lalit Maganti49ae9012019-04-03 16:10:39 +0100171bool PrintStats() {
172 auto it = g_tp->ExecuteQuery(
173 "SELECT name, idx, source, value from stats "
Ioannis Ilkos5e79b8a2019-05-23 18:09:54 +0100174 "where severity IN ('error', 'data_loss') and value > 0");
Lalit Maganti49ae9012019-04-03 16:10:39 +0100175
176 bool first = true;
177 for (uint32_t rows = 0; it.Next(); rows++) {
178 if (first) {
179 fprintf(stderr, "Error stats for this trace:\n");
180
181 for (uint32_t i = 0; i < it.ColumnCount(); i++)
Eric Secklera4f75b72019-12-09 12:08:06 +0000182 fprintf(stderr, "%40s ", it.GetColumnName(i).c_str());
Lalit Maganti49ae9012019-04-03 16:10:39 +0100183 fprintf(stderr, "\n");
184
185 for (uint32_t i = 0; i < it.ColumnCount(); i++)
186 fprintf(stderr, "%40s ", "----------------------------------------");
187 fprintf(stderr, "\n");
188
189 first = false;
190 }
191
192 for (uint32_t c = 0; c < it.ColumnCount(); c++) {
193 auto value = it.Get(c);
194 switch (value.type) {
195 case SqlValue::Type::kNull:
196 fprintf(stderr, "%-40.40s", "[NULL]");
197 break;
198 case SqlValue::Type::kDouble:
199 fprintf(stderr, "%40f", value.double_value);
200 break;
201 case SqlValue::Type::kLong:
202 fprintf(stderr, "%40" PRIi64, value.long_value);
203 break;
204 case SqlValue::Type::kString:
205 fprintf(stderr, "%-40.40s", value.string_value);
206 break;
Lalit Maganti62211072019-05-10 14:09:58 +0100207 case SqlValue::Type::kBytes:
208 printf("%-40.40s", "<raw bytes>");
209 break;
Lalit Maganti49ae9012019-04-03 16:10:39 +0100210 }
211 fprintf(stderr, " ");
212 }
213 fprintf(stderr, "\n");
214 }
215
Lalit Maganti1f067182019-05-09 14:50:05 +0100216 util::Status status = it.Status();
217 if (!status.ok()) {
218 PERFETTO_ELOG("Error while iterating stats %s", status.c_message());
Lalit Maganti49ae9012019-04-03 16:10:39 +0100219 return false;
220 }
221 return true;
222}
223
Sami Kyostila33668942018-11-13 16:33:32 +0000224int ExportTraceToDatabase(const std::string& output_name) {
225 PERFETTO_CHECK(output_name.find("'") == std::string::npos);
226 {
227 base::ScopedFile fd(base::OpenFile(output_name, O_CREAT | O_RDWR, 0600));
228 if (!fd) {
229 PERFETTO_PLOG("Failed to create file: %s", output_name.c_str());
230 return 1;
231 }
232 int res = ftruncate(fd.get(), 0);
233 PERFETTO_CHECK(res == 0);
234 }
235
Sami Kyostila33668942018-11-13 16:33:32 +0000236 std::string attach_sql =
237 "ATTACH DATABASE '" + output_name + "' AS perfetto_export";
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100238 auto attach_it = g_tp->ExecuteQuery(attach_sql);
Lalit Magantic72cea22019-04-08 12:29:15 +0100239 bool attach_has_more = attach_it.Next();
240 PERFETTO_DCHECK(!attach_has_more);
Lalit Maganti1f067182019-05-09 14:50:05 +0100241
242 util::Status status = attach_it.Status();
243 if (!status.ok()) {
244 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100245 return 1;
246 }
Sami Kyostila33668942018-11-13 16:33:32 +0000247
Lalit Magantic72cea22019-04-08 12:29:15 +0100248 auto tables_it = g_tp->ExecuteQuery(
249 "SELECT name FROM perfetto_tables UNION "
250 "SELECT name FROM sqlite_master WHERE type='table'");
251 for (uint32_t rows = 0; tables_it.Next(); rows++) {
252 std::string table_name = tables_it.Get(0).string_value;
253 PERFETTO_CHECK(table_name.find("'") == std::string::npos);
254 std::string export_sql = "CREATE TABLE perfetto_export." + table_name +
255 " AS SELECT * FROM " + table_name;
256
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100257 auto export_it = g_tp->ExecuteQuery(export_sql);
Lalit Magantic72cea22019-04-08 12:29:15 +0100258 bool export_has_more = export_it.Next();
259 PERFETTO_DCHECK(!export_has_more);
Lalit Maganti1f067182019-05-09 14:50:05 +0100260
261 status = export_it.Status();
262 if (!status.ok()) {
263 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100264 return 1;
265 }
266 }
Lalit Maganti1f067182019-05-09 14:50:05 +0100267 status = tables_it.Status();
268 if (!status.ok()) {
269 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100270 return 1;
271 }
272
273 auto detach_it = g_tp->ExecuteQuery("DETACH DATABASE perfetto_export");
274 bool detach_has_more = attach_it.Next();
275 PERFETTO_DCHECK(!detach_has_more);
Lalit Maganti1f067182019-05-09 14:50:05 +0100276 status = detach_it.Status();
277 if (!status.ok()) {
278 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100279 return 1;
280 }
Sami Kyostila33668942018-11-13 16:33:32 +0000281 return 0;
282}
283
Lalit Magantica4d5142019-05-28 13:25:47 +0100284class ErrorPrinter : public google::protobuf::io::ErrorCollector {
285 void AddError(int line, int col, const std::string& msg) override {
286 PERFETTO_ELOG("%d:%d: %s", line, col, msg.c_str());
287 }
288
289 void AddWarning(int line, int col, const std::string& msg) override {
290 PERFETTO_ILOG("%d:%d: %s", line, col, msg.c_str());
291 }
292};
293
Mikhail Khokhlov582f7ce2020-01-14 17:30:34 +0000294// This function returns an indentifier for a metric suitable for use
295// as an SQL table name (i.e. containing no forward or backward slashes).
296std::string BaseName(std::string metric_path) {
297 std::replace(metric_path.begin(), metric_path.end(), '\\', '/');
298 auto slash_idx = metric_path.rfind('/');
299 return slash_idx == std::string::npos ? metric_path
300 : metric_path.substr(slash_idx + 1);
301}
302
Lalit Magantica4d5142019-05-28 13:25:47 +0100303util::Status RegisterMetric(const std::string& register_metric) {
304 std::string sql;
305 base::ReadFile(register_metric, &sql);
306
Mikhail Khokhlov582f7ce2020-01-14 17:30:34 +0000307 std::string path = "shell/" + BaseName(register_metric);
Lalit Magantica4d5142019-05-28 13:25:47 +0100308
309 return g_tp->RegisterMetric(path, sql);
310}
311
Lalit Maganti812ef672019-05-28 13:28:02 +0100312util::Status ExtendMetricsProto(const std::string& extend_metrics_proto,
313 google::protobuf::DescriptorPool* pool) {
Lalit Magantica4d5142019-05-28 13:25:47 +0100314 google::protobuf::FileDescriptorSet desc_set;
315
316 base::ScopedFile file(base::OpenFile(extend_metrics_proto, O_RDONLY));
Lalit Magantif9d80302019-06-19 12:48:25 +0100317 if (file.get() == -1) {
318 return util::ErrStatus("Failed to open proto file %s",
319 extend_metrics_proto.c_str());
320 }
Lalit Magantica4d5142019-05-28 13:25:47 +0100321
322 google::protobuf::io::FileInputStream stream(file.get());
323 ErrorPrinter printer;
324 google::protobuf::io::Tokenizer tokenizer(&stream, &printer);
325
Deepanjan Royab8c05d2020-01-21 09:55:26 -0500326 auto* file_desc = desc_set.add_file();
Lalit Magantica4d5142019-05-28 13:25:47 +0100327 google::protobuf::compiler::Parser parser;
Deepanjan Royab8c05d2020-01-21 09:55:26 -0500328 parser.Parse(&tokenizer, file_desc);
Lalit Magantica4d5142019-05-28 13:25:47 +0100329
Deepanjan Royab8c05d2020-01-21 09:55:26 -0500330 file_desc->set_name(BaseName(extend_metrics_proto));
331 pool->BuildFile(*file_desc);
Lalit Maganti812ef672019-05-28 13:28:02 +0100332
Lalit Magantica4d5142019-05-28 13:25:47 +0100333 std::vector<uint8_t> metric_proto;
334 metric_proto.resize(static_cast<size_t>(desc_set.ByteSize()));
335 desc_set.SerializeToArray(metric_proto.data(),
336 static_cast<int>(metric_proto.size()));
337
338 return g_tp->ExtendMetricsProto(metric_proto.data(), metric_proto.size());
339}
340
Lalit Maganti25863f72019-08-28 22:14:25 +0100341enum OutputFormat {
342 kBinaryProto,
343 kTextProto,
344 kJson,
Rafal Slawikd28f0302019-10-22 17:00:04 +0100345 kNone,
Lalit Maganti25863f72019-08-28 22:14:25 +0100346};
347
Lalit Maganti812ef672019-05-28 13:28:02 +0100348int RunMetrics(const std::vector<std::string>& metric_names,
Lalit Maganti25863f72019-08-28 22:14:25 +0100349 OutputFormat format,
Lalit Maganti812ef672019-05-28 13:28:02 +0100350 const google::protobuf::DescriptorPool& pool) {
Lalit Magantid9f86b62019-04-08 11:11:51 +0100351 std::vector<uint8_t> metric_result;
Lalit Magantid71a9452019-05-09 15:13:24 +0100352 util::Status status = g_tp->ComputeMetric(metric_names, &metric_result);
353 if (!status.ok()) {
354 PERFETTO_ELOG("Error when computing metrics: %s", status.c_message());
Lalit Magantid9f86b62019-04-08 11:11:51 +0100355 return 1;
Lalit Maganti7c959782019-04-02 16:54:12 +0100356 }
Rafal Slawikd28f0302019-10-22 17:00:04 +0100357 if (format == OutputFormat::kNone) {
358 return 0;
359 }
Lalit Maganti25863f72019-08-28 22:14:25 +0100360 if (format == OutputFormat::kBinaryProto) {
Lalit Maganti812ef672019-05-28 13:28:02 +0100361 fwrite(metric_result.data(), sizeof(uint8_t), metric_result.size(), stdout);
Lalit Maganti25863f72019-08-28 22:14:25 +0100362 return 0;
363 }
364
365 google::protobuf::DynamicMessageFactory factory(&pool);
366 auto* descriptor = pool.FindMessageTypeByName("perfetto.protos.TraceMetrics");
367 std::unique_ptr<google::protobuf::Message> metrics(
368 factory.GetPrototype(descriptor)->New());
369 metrics->ParseFromArray(metric_result.data(),
370 static_cast<int>(metric_result.size()));
371
372 switch (format) {
373 case OutputFormat::kTextProto: {
374 std::string out;
375 google::protobuf::TextFormat::PrintToString(*metrics, &out);
376 fwrite(out.c_str(), sizeof(char), out.size(), stdout);
377 break;
378 }
379 case OutputFormat::kJson: {
Deepanjan Royab8c05d2020-01-21 09:55:26 -0500380 // We need to instantiate field options from dynamic message factory
381 // because otherwise it cannot parse our custom extensions.
382 const google::protobuf::Message* field_options_prototype =
383 factory.GetPrototype(
384 pool.FindMessageTypeByName("google.protobuf.FieldOptions"));
385 auto out = proto_to_json::MessageToJsonWithAnnotations(
386 *metrics, field_options_prototype, 0);
Lalit Maganti25863f72019-08-28 22:14:25 +0100387 fwrite(out.c_str(), sizeof(char), out.size(), stdout);
388 break;
389 }
390 case OutputFormat::kBinaryProto:
Rafal Slawikd28f0302019-10-22 17:00:04 +0100391 case OutputFormat::kNone:
Lalit Maganti25863f72019-08-28 22:14:25 +0100392 PERFETTO_FATAL("Unsupported output format.");
Lalit Maganti812ef672019-05-28 13:28:02 +0100393 }
Lalit Maganti7c959782019-04-02 16:54:12 +0100394 return 0;
395}
396
Lalit Magantic72cea22019-04-08 12:29:15 +0100397void PrintQueryResultInteractively(TraceProcessor::Iterator* it,
Lalit Magantia8dcfdd2019-07-08 18:14:27 +0100398 base::TimeNanos t_start,
399 uint32_t column_width) {
Lalit Magantic72cea22019-04-08 12:29:15 +0100400 base::TimeNanos t_end = t_start;
401 for (uint32_t rows = 0; it->Next(); rows++) {
402 if (rows % 32 == 0) {
403 if (rows > 0) {
Primiano Tucci5968caf2018-08-06 10:31:46 +0100404 fprintf(stderr, "...\nType 'q' to stop, Enter for more records: ");
405 fflush(stderr);
406 char input[32];
407 if (!fgets(input, sizeof(input) - 1, stdin))
408 exit(0);
409 if (input[0] == 'q')
410 break;
Lalit Magantic72cea22019-04-08 12:29:15 +0100411 } else {
Lalit Magantiaac2f652019-04-30 12:16:21 +0100412 t_end = base::GetWallTimeNs();
Primiano Tucci5968caf2018-08-06 10:31:46 +0100413 }
Lalit Magantic72cea22019-04-08 12:29:15 +0100414 for (uint32_t i = 0; i < it->ColumnCount(); i++)
Lalit Magantid74f36a2019-07-12 16:35:57 +0100415 printf("%-*.*s ", column_width, column_width,
Eric Secklera4f75b72019-12-09 12:08:06 +0000416 it->GetColumnName(i).c_str());
Primiano Tucci5968caf2018-08-06 10:31:46 +0100417 printf("\n");
418
Lalit Magantia8dcfdd2019-07-08 18:14:27 +0100419 std::string divider(column_width, '-');
420 for (uint32_t i = 0; i < it->ColumnCount(); i++) {
421 printf("%-*s ", column_width, divider.c_str());
422 }
Primiano Tucci5968caf2018-08-06 10:31:46 +0100423 printf("\n");
424 }
425
Lalit Magantic72cea22019-04-08 12:29:15 +0100426 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
427 auto value = it->Get(c);
428 switch (value.type) {
429 case SqlValue::Type::kNull:
Lalit Magantia8dcfdd2019-07-08 18:14:27 +0100430 printf("%-*s", column_width, "[NULL]");
Lalit Magantic72cea22019-04-08 12:29:15 +0100431 break;
432 case SqlValue::Type::kDouble:
Lalit Magantia8dcfdd2019-07-08 18:14:27 +0100433 printf("%*f", column_width, value.double_value);
Lalit Magantic72cea22019-04-08 12:29:15 +0100434 break;
435 case SqlValue::Type::kLong:
Lalit Magantia8dcfdd2019-07-08 18:14:27 +0100436 printf("%*" PRIi64, column_width, value.long_value);
Lalit Magantic72cea22019-04-08 12:29:15 +0100437 break;
438 case SqlValue::Type::kString:
Lalit Magantid74f36a2019-07-12 16:35:57 +0100439 printf("%-*.*s", column_width, column_width, value.string_value);
Lalit Magantic72cea22019-04-08 12:29:15 +0100440 break;
Lalit Maganti62211072019-05-10 14:09:58 +0100441 case SqlValue::Type::kBytes:
Lalit Magantia8dcfdd2019-07-08 18:14:27 +0100442 printf("%-*s", column_width, "<raw bytes>");
Lalit Maganti62211072019-05-10 14:09:58 +0100443 break;
Lalit Maganti21160e82018-10-16 09:40:29 +0100444 }
445 printf(" ");
446 }
447 printf("\n");
448 }
Lalit Magantic72cea22019-04-08 12:29:15 +0100449
Lalit Maganti1f067182019-05-09 14:50:05 +0100450 util::Status status = it->Status();
451 if (!status.ok()) {
452 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100453 }
Lalit Maganti21160e82018-10-16 09:40:29 +0100454 printf("\nQuery executed in %.3f ms\n\n", (t_end - t_start).count() / 1E6);
455}
Sami Kyostila33668942018-11-13 16:33:32 +0000456
457void PrintShellUsage() {
458 PERFETTO_ELOG(
459 "Available commands:\n"
460 ".quit, .q Exit the shell.\n"
461 ".help This text.\n"
Primiano Tucci11a79e72019-10-29 22:13:10 +0100462 ".dump FILE Export the trace as a sqlite database.\n"
463 ".reset Destroys all tables/view created by the user.\n");
Sami Kyostila33668942018-11-13 16:33:32 +0000464}
465
Lalit Magantia8dcfdd2019-07-08 18:14:27 +0100466int StartInteractiveShell(uint32_t column_width) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100467 SetupLineEditor();
Primiano Tuccib75dcee2018-08-08 12:21:36 +0100468
Lalit Maganti21160e82018-10-16 09:40:29 +0100469 for (;;) {
Florian Mayerb5fa0c92019-12-18 12:44:46 +0000470 ScopedLine line = GetLine("> ");
Sami Kyostila33668942018-11-13 16:33:32 +0000471 if (!line)
Lalit Maganti21160e82018-10-16 09:40:29 +0100472 break;
Florian Mayerb5fa0c92019-12-18 12:44:46 +0000473 if (strcmp(line.get(), "") == 0)
Lalit Maganti21160e82018-10-16 09:40:29 +0100474 continue;
Florian Mayerb5fa0c92019-12-18 12:44:46 +0000475 if (line.get()[0] == '.') {
Sami Kyostila33668942018-11-13 16:33:32 +0000476 char command[32] = {};
477 char arg[1024] = {};
Florian Mayerb5fa0c92019-12-18 12:44:46 +0000478 sscanf(line.get() + 1, "%31s %1023s", command, arg);
Sami Kyostila33668942018-11-13 16:33:32 +0000479 if (strcmp(command, "quit") == 0 || strcmp(command, "q") == 0) {
480 break;
481 } else if (strcmp(command, "help") == 0) {
482 PrintShellUsage();
483 } else if (strcmp(command, "dump") == 0 && strlen(arg)) {
484 if (ExportTraceToDatabase(arg) != 0)
485 PERFETTO_ELOG("Database export failed");
Primiano Tucci11a79e72019-10-29 22:13:10 +0100486 } else if (strcmp(command, "reset") == 0) {
487 g_tp->RestoreInitialTables();
Sami Kyostila33668942018-11-13 16:33:32 +0000488 } else {
489 PrintShellUsage();
490 }
491 continue;
492 }
Lalit Magantic72cea22019-04-08 12:29:15 +0100493
Lalit Maganti21160e82018-10-16 09:40:29 +0100494 base::TimeNanos t_start = base::GetWallTimeNs();
Florian Mayerb5fa0c92019-12-18 12:44:46 +0000495 auto it = g_tp->ExecuteQuery(line.get());
Lalit Magantia8dcfdd2019-07-08 18:14:27 +0100496 PrintQueryResultInteractively(&it, t_start, column_width);
Lalit Maganti21160e82018-10-16 09:40:29 +0100497 }
498 return 0;
499}
500
Mikhail Khokhlov7a3a8652019-07-08 13:56:39 +0100501util::Status PrintQueryResultAsCsv(TraceProcessor::Iterator* it, FILE* output) {
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100502 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
503 if (c > 0)
504 fprintf(output, ",");
Eric Secklera4f75b72019-12-09 12:08:06 +0000505 fprintf(output, "\"%s\"", it->GetColumnName(c).c_str());
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100506 }
507 fprintf(output, "\n");
Lalit Maganti21160e82018-10-16 09:40:29 +0100508
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100509 for (uint32_t rows = 0; it->Next(); rows++) {
Lalit Magantic72cea22019-04-08 12:29:15 +0100510 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100511 if (c > 0)
512 fprintf(output, ",");
Lalit Magantib55c8842018-12-13 14:26:08 +0000513
Lalit Magantic72cea22019-04-08 12:29:15 +0100514 auto value = it->Get(c);
515 switch (value.type) {
516 case SqlValue::Type::kNull:
517 fprintf(output, "\"%s\"", "[NULL]");
518 break;
519 case SqlValue::Type::kDouble:
520 fprintf(output, "%f", value.double_value);
521 break;
522 case SqlValue::Type::kLong:
523 fprintf(output, "%" PRIi64, value.long_value);
524 break;
525 case SqlValue::Type::kString:
526 fprintf(output, "\"%s\"", value.string_value);
527 break;
Lalit Maganti62211072019-05-10 14:09:58 +0100528 case SqlValue::Type::kBytes:
529 fprintf(output, "\"%s\"", "<raw bytes>");
530 break;
Primiano Tucci5968caf2018-08-06 10:31:46 +0100531 }
532 }
Lalit Magantib55c8842018-12-13 14:26:08 +0000533 fprintf(output, "\n");
Primiano Tucci5968caf2018-08-06 10:31:46 +0100534 }
Mikhail Khokhlov7a3a8652019-07-08 13:56:39 +0100535 return it->Status();
Lalit Maganti21160e82018-10-16 09:40:29 +0100536}
537
Florian Mayer3c742432019-05-21 11:35:47 +0100538bool IsBlankLine(char* buffer) {
539 size_t buf_size = strlen(buffer);
540 for (size_t i = 0; i < buf_size; ++i) {
Florian Mayer70995c22019-05-21 12:58:32 +0100541 // We can index into buffer[i+1], because strlen does not include the
542 // trailing \0, so even if \r is the last character, this is not out
543 // of bound.
544 if (buffer[i] == '\r') {
545 if (buffer[i + 1] != '\n')
546 return false;
547 } else if (buffer[i] != ' ' && buffer[i] != '\t' && buffer[i] != '\n') {
Florian Mayer3c742432019-05-21 11:35:47 +0100548 return false;
Florian Mayer70995c22019-05-21 12:58:32 +0100549 }
Florian Mayer3c742432019-05-21 11:35:47 +0100550 }
551 return true;
552}
553
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000554bool LoadQueries(FILE* input, std::vector<std::string>* output) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100555 char buffer[4096];
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000556 while (!feof(input) && !ferror(input)) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100557 std::string sql_query;
558 while (fgets(buffer, sizeof(buffer), input)) {
Florian Mayer3c742432019-05-21 11:35:47 +0100559 if (IsBlankLine(buffer))
Lalit Maganti21160e82018-10-16 09:40:29 +0100560 break;
561 sql_query.append(buffer);
562 }
563 if (sql_query.back() == '\n')
564 sql_query.resize(sql_query.size() - 1);
Lalit Maganticbccb0a2018-12-04 20:14:42 +0000565
566 // If we have a new line at the end of the file or an extra new line
567 // somewhere in the file, we'll end up with an empty query which we should
568 // just ignore.
569 if (sql_query.empty())
570 continue;
571
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000572 output->push_back(sql_query);
573 }
574 if (ferror(input)) {
575 PERFETTO_ELOG("Error reading query file");
576 return false;
577 }
578 return true;
579}
580
581bool RunQueryAndPrintResult(const std::vector<std::string> queries,
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000582 FILE* output) {
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000583 bool is_first_query = true;
584 bool is_query_error = false;
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000585 bool has_output = false;
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000586 for (const auto& sql_query : queries) {
587 // Add an extra newline separator between query results.
588 if (!is_first_query)
589 fprintf(output, "\n");
590 is_first_query = false;
591
Lalit Maganti21160e82018-10-16 09:40:29 +0100592 PERFETTO_ILOG("Executing query: %s", sql_query.c_str());
593
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100594 auto it = g_tp->ExecuteQuery(sql_query);
Lalit Maganti1f067182019-05-09 14:50:05 +0100595 util::Status status = it.Status();
596 if (!status.ok()) {
597 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100598 is_query_error = true;
599 break;
600 }
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100601 if (it.ColumnCount() == 0) {
602 bool it_has_more = it.Next();
603 PERFETTO_DCHECK(!it_has_more);
604 continue;
605 }
606
Hector Dearman2af21922019-09-03 13:48:38 +0100607 if (has_output) {
608 PERFETTO_ELOG(
609 "More than one query generated result rows. This is "
610 "unsupported.");
611 is_query_error = true;
612 break;
613 }
Mikhail Khokhlov7a3a8652019-07-08 13:56:39 +0100614 status = PrintQueryResultAsCsv(&it, output);
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100615 has_output = true;
Mikhail Khokhlov7a3a8652019-07-08 13:56:39 +0100616
617 if (!status.ok()) {
618 PERFETTO_ELOG("SQLite error: %s", status.c_message());
619 is_query_error = true;
620 }
Lalit Maganti21160e82018-10-16 09:40:29 +0100621 }
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000622 return !is_query_error;
Lalit Maganti21160e82018-10-16 09:40:29 +0100623}
624
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100625int MaybePrintPerfFile(const std::string& perf_file_path,
Lalit Maganti260e04d2019-05-03 13:17:01 +0100626 base::TimeNanos t_load,
627 base::TimeNanos t_run) {
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100628 if (perf_file_path.empty())
Lalit Maganti260e04d2019-05-03 13:17:01 +0100629 return 0;
630
631 char buf[128];
632 int count = snprintf(buf, sizeof(buf), "%" PRId64 ",%" PRId64,
633 static_cast<int64_t>(t_load.count()),
634 static_cast<int64_t>(t_run.count()));
635 if (count < 0) {
636 PERFETTO_ELOG("Failed to write perf data");
637 return 1;
638 }
639
Lalit Maganti04a450a2019-07-03 16:04:19 +0100640 auto fd(base::OpenFile(perf_file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666));
Lalit Maganti260e04d2019-05-03 13:17:01 +0100641 if (!fd) {
642 PERFETTO_ELOG("Failed to open perf file");
643 return 1;
644 }
645 base::WriteAll(fd.get(), buf, static_cast<size_t>(count));
646 return 0;
647}
648
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100649struct CommandLineOptions {
650 std::string perf_file_path;
651 std::string query_file_path;
652 std::string sqlite_file_path;
653 std::string metric_names;
654 std::string metric_output;
Lalit Magantif9d80302019-06-19 12:48:25 +0100655 std::string metric_extra;
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100656 std::string trace_file_path;
657 bool launch_shell = false;
Primiano Tuccia36cccc2019-10-27 13:15:04 +0100658 bool enable_httpd = false;
Lalit Magantia8dcfdd2019-07-08 18:14:27 +0100659 bool wide = false;
Lalit Maganti55d0bc72019-10-23 16:22:00 +0100660 bool force_full_sort = false;
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100661};
662
Lalit Magantiedace412019-06-18 13:28:28 +0100663#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
664void PrintUsage(char** argv) {
Mikhail Khokhlov582f7ce2020-01-14 17:30:34 +0000665 PERFETTO_ELOG(R"(
666Interactive trace processor shell.
667Usage: %s [OPTIONS] trace_file.pb
668
669Options:
670 -q, --query-file FILE Read and execute an SQL query from a file.
671 If used with --run-metrics, the query is
672 executed after the selected metrics and
673 the metrics output is suppressed.
674 --run-metrics x,y,z Runs a comma separated list of metrics and
675 prints the result as a TraceMetrics proto
676 to stdout. The specified can either be
677 in-built metrics or SQL/proto files of
678 extension metrics.
679 --metrics-output [binary|text|json] Allows the output of --run-metrics to be
680 specified in either proto binary, proto
681 text format or JSON format (default: proto
682 text).)",
683 argv[0]);
Lalit Magantiedace412019-06-18 13:28:28 +0100684}
685
686CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
687 CommandLineOptions command_line_options;
688
Mikhail Khokhlov582f7ce2020-01-14 17:30:34 +0000689 if (argc < 2 || argc % 2 == 1) {
Lalit Magantiedace412019-06-18 13:28:28 +0100690 PrintUsage(argv);
691 exit(1);
692 }
693
Mikhail Khokhlov582f7ce2020-01-14 17:30:34 +0000694 for (int i = 1; i < argc - 1; i += 2) {
695 if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--query-file") == 0) {
696 command_line_options.query_file_path = argv[i + 1];
697 } else if (strcmp(argv[i], "--run-metrics") == 0) {
698 command_line_options.metric_names = argv[i + 1];
699 } else if (strcmp(argv[i], "--metrics-output") == 0) {
700 command_line_options.metric_output = argv[i + 1];
701 } else {
702 PrintUsage(argv);
703 exit(1);
704 }
705 }
706 command_line_options.trace_file_path = argv[argc - 1];
707 command_line_options.launch_shell =
708 command_line_options.metric_names.empty() &&
709 command_line_options.query_file_path.empty();
Lalit Magantiedace412019-06-18 13:28:28 +0100710 return command_line_options;
711}
712
Lalit Magantif9d80302019-06-19 12:48:25 +0100713util::Status RegisterExtraMetrics(const std::string&, const std::string&) {
714 return util::ErrStatus("RegisterExtraMetrics not implemented on Windows");
715}
716
Lalit Magantiedace412019-06-18 13:28:28 +0100717#else // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
Lalit Magantif9d80302019-06-19 12:48:25 +0100718
Lalit Magantiedace412019-06-18 13:28:28 +0100719void PrintUsage(char** argv) {
720 PERFETTO_ELOG(R"(
721Interactive trace processor shell.
722Usage: %s [OPTIONS] trace_file.pb
723
724Options:
Lalit Maganti82adf6a2019-09-26 18:05:51 +0100725 -h, --help Prints this guide.
726 -v, --version Prints the version of trace processor.
727 -d, --debug Enable virtual table debugging.
728 -W, --wide Prints interactive output with double
729 column width.
730 -p, --perf-file FILE Writes the time taken to ingest the trace
731 and execute the queries to the given file.
732 Only valid with -q or --run-metrics and
733 the file will only be written if the
734 execution is successful.
735 -q, --query-file FILE Read and execute an SQL query from a file.
Rafal Slawikd28f0302019-10-22 17:00:04 +0100736 If used with --run-metrics, the query is
737 executed after the selected metrics and
738 the metrics output is suppressed.
Primiano Tuccia36cccc2019-10-27 13:15:04 +0100739 -D, --httpd Enables the HTTP RPC server.
Lalit Maganti82adf6a2019-09-26 18:05:51 +0100740 -i, --interactive Starts interactive mode even after a query
741 file is specified with -q or
742 --run-metrics.
743 -e, --export FILE Export the trace into a SQLite database.
744 --run-metrics x,y,z Runs a comma separated list of metrics and
745 prints the result as a TraceMetrics proto
746 to stdout. The specified can either be
747 in-built metrics or SQL/proto files of
748 extension metrics.
749 --metrics-output=[binary|text|json] Allows the output of --run-metrics to be
750 specified in either proto binary, proto
751 text format or JSON format (default: proto
752 text).
753 --extra-metrics PATH Registers all SQL files at the given path
754 to the trace processor and extends the
755 builtin metrics proto with
Lalit Maganti55d0bc72019-10-23 16:22:00 +0100756 $PATH/metrics-ext.proto.
757 --full-sort Forces the trace processor into performing
758 a full sort ignoring any windowing
759 logic.)",
Lalit Magantiedace412019-06-18 13:28:28 +0100760 argv[0]);
761}
Lalit Maganti260e04d2019-05-03 13:17:01 +0100762
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100763CommandLineOptions ParseCommandLineOptions(int argc, char** argv) {
764 CommandLineOptions command_line_options;
765 enum LongOption {
766 OPT_RUN_METRICS = 1000,
767 OPT_METRICS_OUTPUT,
Lalit Magantif9d80302019-06-19 12:48:25 +0100768 OPT_EXTRA_METRICS,
Lalit Maganti55d0bc72019-10-23 16:22:00 +0100769 OPT_FORCE_FULL_SORT,
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100770 };
771
772 static const struct option long_options[] = {
773 {"help", no_argument, nullptr, 'h'},
774 {"version", no_argument, nullptr, 'v'},
Lalit Magantia8dcfdd2019-07-08 18:14:27 +0100775 {"wide", no_argument, nullptr, 'W'},
Primiano Tuccia36cccc2019-10-27 13:15:04 +0100776 {"httpd", no_argument, nullptr, 'D'},
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100777 {"interactive", no_argument, nullptr, 'i'},
778 {"debug", no_argument, nullptr, 'd'},
779 {"perf-file", required_argument, nullptr, 'p'},
780 {"query-file", required_argument, nullptr, 'q'},
781 {"export", required_argument, nullptr, 'e'},
782 {"run-metrics", required_argument, nullptr, OPT_RUN_METRICS},
783 {"metrics-output", required_argument, nullptr, OPT_METRICS_OUTPUT},
Lalit Magantif9d80302019-06-19 12:48:25 +0100784 {"extra-metrics", required_argument, nullptr, OPT_EXTRA_METRICS},
Lalit Maganti55d0bc72019-10-23 16:22:00 +0100785 {"full-sort", no_argument, nullptr, OPT_FORCE_FULL_SORT},
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100786 {nullptr, 0, nullptr, 0}};
787
788 bool explicit_interactive = false;
789 int option_index = 0;
790 for (;;) {
791 int option =
Primiano Tuccia36cccc2019-10-27 13:15:04 +0100792 getopt_long(argc, argv, "hvWiDdp:q:e:", long_options, &option_index);
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100793
794 if (option == -1)
795 break; // EOF.
796
797 if (option == 'v') {
798 printf("%s\n", PERFETTO_GET_GIT_REVISION());
799 exit(0);
800 }
801
802 if (option == 'i') {
803 explicit_interactive = true;
804 continue;
805 }
806
Primiano Tuccia36cccc2019-10-27 13:15:04 +0100807 if (option == 'D') {
808#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
809 command_line_options.enable_httpd = true;
810#else
811 PERFETTO_FATAL("HTTP RPC module not supported in this build");
812#endif
813 continue;
814 }
815
Lalit Magantia8dcfdd2019-07-08 18:14:27 +0100816 if (option == 'W') {
817 command_line_options.wide = true;
818 continue;
819 }
820
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100821 if (option == 'd') {
822 EnableSQLiteVtableDebugging();
823 continue;
824 }
825
826 if (option == 'p') {
827 command_line_options.perf_file_path = optarg;
828 continue;
829 }
830
831 if (option == 'q') {
832 command_line_options.query_file_path = optarg;
833 continue;
834 }
835
836 if (option == 'e') {
837 command_line_options.sqlite_file_path = optarg;
838 continue;
839 }
840
841 if (option == OPT_RUN_METRICS) {
842 command_line_options.metric_names = optarg;
843 continue;
844 }
845
846 if (option == OPT_METRICS_OUTPUT) {
847 command_line_options.metric_output = optarg;
848 continue;
849 }
850
Lalit Magantif9d80302019-06-19 12:48:25 +0100851 if (option == OPT_EXTRA_METRICS) {
852 command_line_options.metric_extra = optarg;
853 continue;
854 }
855
Lalit Maganti55d0bc72019-10-23 16:22:00 +0100856 if (option == OPT_FORCE_FULL_SORT) {
857 command_line_options.force_full_sort = true;
858 continue;
859 }
860
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100861 PrintUsage(argv);
862 exit(option == 'h' ? 0 : 1);
863 }
864
865 command_line_options.launch_shell =
866 explicit_interactive || (command_line_options.metric_names.empty() &&
867 command_line_options.query_file_path.empty());
868
869 // Only allow non-interactive queries to emit perf data.
870 if (!command_line_options.perf_file_path.empty() &&
871 command_line_options.launch_shell) {
872 PrintUsage(argv);
873 exit(1);
874 }
875
Primiano Tucciee2ce1d2019-11-01 19:14:17 +0100876 // The only case where we allow omitting the trace file path is when running
877 // in --http mode. In all other cases, the last argument must be the trace
878 // file.
879 if (optind == argc - 1 && argv[optind]) {
880 command_line_options.trace_file_path = argv[optind];
881 } else if (!command_line_options.enable_httpd) {
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100882 PrintUsage(argv);
883 exit(1);
884 }
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100885 return command_line_options;
886}
Lalit Magantif9d80302019-06-19 12:48:25 +0100887
888util::Status RegisterExtraMetric(const std::string& parent_path,
889 const std::string& path) {
890 // Silently ignore any non-SQL files.
891 if (path.find(".sql") == std::string::npos)
892 return util::OkStatus();
893
894 std::string sql;
895 base::ReadFile(parent_path + "/" + path, &sql);
896 return g_tp->RegisterMetric(path, sql);
897}
898
899util::Status RegisterExtraMetrics(const std::string& path,
900 const std::string& group) {
901 std::string full_path = path + "/" + group;
902 DIR* dir = opendir(full_path.c_str());
903 if (dir == nullptr) {
904 return util::ErrStatus(
905 "Failed to open directory %s to register extra metrics",
906 full_path.c_str());
907 }
908
909 for (auto* dirent = readdir(dir); dirent != nullptr; dirent = readdir(dir)) {
910 util::Status status = util::OkStatus();
911 if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
912 continue;
913
914 if (dirent->d_type == DT_DIR) {
915 status = RegisterExtraMetrics(path, group + dirent->d_name + "/");
916 } else if (dirent->d_type == DT_REG) {
917 status = RegisterExtraMetric(path, group + dirent->d_name);
918 }
919 if (!status.ok())
920 return status;
921 }
922 return util::OkStatus();
923}
924
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100925#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
926
Deepanjan Royab8c05d2020-01-21 09:55:26 -0500927void ExtendPoolWithBinaryDescriptor(google::protobuf::DescriptorPool& pool,
928 const void* data,
929 int size) {
930 google::protobuf::FileDescriptorSet desc_set;
931 desc_set.ParseFromArray(data, size);
932 for (const auto& desc : desc_set.file()) {
933 pool.BuildFile(desc);
934 }
935}
936
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100937int TraceProcessorMain(int argc, char** argv) {
938 CommandLineOptions options = ParseCommandLineOptions(argc, argv);
939
940 // Load the trace file into the trace processor.
941 Config config;
Lalit Maganti55d0bc72019-10-23 16:22:00 +0100942 config.force_full_sort = options.force_full_sort;
943
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +0100944 std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
Ioannis Ilkosf8371292018-11-05 16:47:35 +0000945 g_tp = tp.get();
Primiano Tucci5968caf2018-08-06 10:31:46 +0100946
Primiano Tucciee2ce1d2019-11-01 19:14:17 +0100947 base::TimeNanos t_load{};
948 if (!options.trace_file_path.empty()) {
949 auto t_load_start = base::GetWallTimeNs();
950 double size_mb = 0;
951 util::Status read_status =
952 ReadTrace(tp.get(), options.trace_file_path.c_str(),
953 [&size_mb](size_t parsed_size) {
954 size_mb = parsed_size / 1E6;
955 fprintf(stderr, "\rLoading trace: %.2f MB\r", size_mb);
956 });
957 if (!read_status.ok()) {
958 PERFETTO_ELOG("Could not read trace file (path: %s): %s",
959 options.trace_file_path.c_str(), read_status.c_message());
960 return 1;
961 }
Florian Mayer6cc8b1d2019-12-18 16:37:32 +0000962
963 std::unique_ptr<profiling::Symbolizer> symbolizer;
964 auto binary_path = profiling::GetPerfettoBinaryPath();
965 if (!binary_path.empty()) {
966#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
967 symbolizer.reset(new profiling::LocalSymbolizer(std::move(binary_path)));
968#else
969 PERFETTO_FATAL("This build does not support local symbolization.");
970#endif
971 }
972 if (symbolizer) {
973 profiling::SymbolizeDatabase(
974 tp.get(), symbolizer.get(), [&tp](const std::string& trace_proto) {
975 std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]);
976 memcpy(buf.get(), trace_proto.data(), trace_proto.size());
977 auto status = tp->Parse(std::move(buf), trace_proto.size());
978 if (!status.ok()) {
979 PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s",
980 status.message().c_str());
981 return;
982 }
983 });
984 tp->NotifyEndOfFile();
985 }
986
Primiano Tucciee2ce1d2019-11-01 19:14:17 +0100987 t_load = base::GetWallTimeNs() - t_load_start;
988 double t_load_s = t_load.count() / 1E9;
989 PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb,
990 size_mb / t_load_s);
991 } // if (!trace_file_path.empty())
992
993#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
994 if (options.enable_httpd) {
995 RunHttpRPCServer(std::move(tp));
996 return 0;
997 }
998#endif
999
Primiano Tucci24647142018-08-13 21:18:24 +02001000#if PERFETTO_HAS_SIGNAL_H()
Primiano Tucci7e330292018-08-24 19:10:52 +02001001 signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
Primiano Tucci24647142018-08-13 21:18:24 +02001002#endif
Primiano Tucci5968caf2018-08-06 10:31:46 +01001003
Lalit Maganti49ae9012019-04-03 16:10:39 +01001004 // Print out the stats to stderr for the trace.
1005 if (!PrintStats()) {
1006 return 1;
1007 }
1008
Lalit Maganti260e04d2019-05-03 13:17:01 +01001009 auto t_run_start = base::GetWallTimeNs();
1010
Lalit Maganti812ef672019-05-28 13:28:02 +01001011 // Descriptor pool used for printing output as textproto.
Deepanjan Royab8c05d2020-01-21 09:55:26 -05001012 // Building on top of generated pool so default protos in
1013 // google.protobuf.descriptor.proto are available.
1014 google::protobuf::DescriptorPool pool(
1015 google::protobuf::DescriptorPool::generated_pool());
1016 ExtendPoolWithBinaryDescriptor(pool, kMetricsDescriptor.data(),
1017 kMetricsDescriptor.size());
1018 ExtendPoolWithBinaryDescriptor(pool, kCustomOptionsDescriptor.data(),
1019 kCustomOptionsDescriptor.size());
Lalit Maganti812ef672019-05-28 13:28:02 +01001020
Lalit Magantif9d80302019-06-19 12:48:25 +01001021 if (!options.metric_extra.empty()) {
1022 util::Status status = RegisterExtraMetrics(options.metric_extra, "");
1023 if (!status.ok()) {
1024 PERFETTO_ELOG("Failed to register extra metrics: %s", status.c_message());
1025 return 1;
1026 }
1027
1028 auto ext_proto = options.metric_extra + "/metrics-ext.proto";
1029 // Check if the file exists
1030 base::ScopedFile file(base::OpenFile(ext_proto, O_RDONLY));
1031 if (file.get() != -1) {
1032 status = ExtendMetricsProto(ext_proto, &pool);
1033 if (!status.ok()) {
1034 PERFETTO_ELOG("Failed to extend metrics proto: %s", status.c_message());
1035 return 1;
1036 }
1037 }
1038 }
1039
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +01001040 if (!options.metric_names.empty()) {
Lalit Maganti7c959782019-04-02 16:54:12 +01001041 std::vector<std::string> metrics;
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +01001042 for (base::StringSplitter ss(options.metric_names, ','); ss.Next();) {
Lalit Maganti7c959782019-04-02 16:54:12 +01001043 metrics.emplace_back(ss.cur_token());
1044 }
Lalit Maganti58e24922019-05-30 18:56:14 +01001045
1046 // For all metrics which are files, register them and extend the metrics
1047 // proto.
1048 for (size_t i = 0; i < metrics.size(); ++i) {
1049 const std::string& metric_or_path = metrics[i];
1050
1051 // If there is no extension, we assume it is a builtin metric.
1052 auto ext_idx = metric_or_path.rfind(".");
1053 if (ext_idx == std::string::npos)
1054 continue;
1055
1056 std::string no_ext_name = metric_or_path.substr(0, ext_idx);
1057 util::Status status = RegisterMetric(no_ext_name + ".sql");
1058 if (!status.ok()) {
1059 PERFETTO_ELOG("Unable to register metric %s: %s",
1060 metric_or_path.c_str(), status.c_message());
1061 return 1;
1062 }
1063
1064 status = ExtendMetricsProto(no_ext_name + ".proto", &pool);
1065 if (!status.ok()) {
1066 PERFETTO_ELOG("Unable to extend metrics proto %s: %s",
1067 metric_or_path.c_str(), status.c_message());
1068 return 1;
1069 }
1070
Mikhail Khokhlov582f7ce2020-01-14 17:30:34 +00001071 metrics[i] = BaseName(no_ext_name);
Lalit Maganti58e24922019-05-30 18:56:14 +01001072 }
1073
Lalit Maganti25863f72019-08-28 22:14:25 +01001074 OutputFormat format;
Rafal Slawikd28f0302019-10-22 17:00:04 +01001075 if (!options.query_file_path.empty()) {
1076 format = OutputFormat::kNone;
1077 } else if (options.metric_output == "binary") {
Lalit Maganti25863f72019-08-28 22:14:25 +01001078 format = OutputFormat::kBinaryProto;
1079 } else if (options.metric_output == "json") {
1080 format = OutputFormat::kJson;
1081 } else {
1082 format = OutputFormat::kTextProto;
1083 }
1084 int ret = RunMetrics(std::move(metrics), format, pool);
Lalit Maganti260e04d2019-05-03 13:17:01 +01001085 if (!ret) {
1086 auto t_query = base::GetWallTimeNs() - t_run_start;
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +01001087 ret = MaybePrintPerfFile(options.perf_file_path, t_load, t_query);
Lalit Maganti260e04d2019-05-03 13:17:01 +01001088 }
Lalit Maganti812ef672019-05-28 13:28:02 +01001089 if (ret)
1090 return ret;
Rafal Slawikd28f0302019-10-22 17:00:04 +01001091 }
Lalit Maganti7c959782019-04-02 16:54:12 +01001092
Rafal Slawikd28f0302019-10-22 17:00:04 +01001093 // If we were given a query file, load contents
1094 std::vector<std::string> queries;
1095 if (!options.query_file_path.empty()) {
1096 base::ScopedFstream file(fopen(options.query_file_path.c_str(), "r"));
1097 if (!file) {
1098 PERFETTO_ELOG("Could not open query file (path: %s)",
1099 options.query_file_path.c_str());
Lalit Maganti22a1e0c2018-12-04 20:28:18 +00001100 return 1;
1101 }
Rafal Slawikd28f0302019-10-22 17:00:04 +01001102 if (!LoadQueries(file.get(), &queries)) {
1103 return 1;
1104 }
1105 }
1106
1107 if (!RunQueryAndPrintResult(queries, stdout)) {
1108 return 1;
Primiano Tucci7e330292018-08-24 19:10:52 +02001109 }
Hector Dearmane44ad452018-09-21 11:51:57 +01001110
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +01001111 if (!options.sqlite_file_path.empty()) {
1112 return ExportTraceToDatabase(options.sqlite_file_path);
Sami Kyostila33668942018-11-13 16:33:32 +00001113 }
1114
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +01001115 if (!options.launch_shell) {
Lalit Maganti260e04d2019-05-03 13:17:01 +01001116 auto t_query = base::GetWallTimeNs() - t_run_start;
Mikhail Khokhlov8643d1c2019-06-04 12:02:47 +01001117 return MaybePrintPerfFile(options.perf_file_path, t_load, t_query);
Sami Kyostila33668942018-11-13 16:33:32 +00001118 }
1119
Lalit Magantia8dcfdd2019-07-08 18:14:27 +01001120 return StartInteractiveShell(options.wide ? 40 : 20);
Primiano Tucci5968caf2018-08-06 10:31:46 +01001121}
Lalit Maganti1ebebf12018-10-15 17:24:04 +01001122
1123} // namespace
1124
1125} // namespace trace_processor
1126} // namespace perfetto
1127
1128int main(int argc, char** argv) {
1129 return perfetto::trace_processor::TraceProcessorMain(argc, argv);
1130}