blob: dc15c3d46af38e2a23eaf384d7a728a49c3fbc89 [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 <aio.h>
18#include <fcntl.h>
Lalit Maganti21160e82018-10-16 09:40:29 +010019#include <inttypes.h>
Primiano Tucci7e330292018-08-24 19:10:52 +020020#include <sys/stat.h>
Primiano Tucci5968caf2018-08-06 10:31:46 +010021#include <unistd.h>
22
23#include <functional>
Lalit Maganti21160e82018-10-16 09:40:29 +010024#include <iostream>
Primiano Tucci5968caf2018-08-06 10:31:46 +010025
Primiano Tucci24647142018-08-13 21:18:24 +020026#include "perfetto/base/build_config.h"
Primiano Tucci5968caf2018-08-06 10:31:46 +010027#include "perfetto/base/logging.h"
Ioannis Ilkoseff38f52018-10-29 10:37:55 +000028#include "perfetto/base/scoped_file.h"
Primiano Tucci5968caf2018-08-06 10:31:46 +010029#include "perfetto/base/time.h"
Ioannis Ilkoseff38f52018-10-29 10:37:55 +000030#include "perfetto/trace_processor/trace_processor.h"
Primiano Tucci5968caf2018-08-06 10:31:46 +010031
32#include "perfetto/trace_processor/raw_query.pb.h"
33
Primiano Tucci24647142018-08-13 21:18:24 +020034#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
35 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
36 PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
37#define PERFETTO_HAS_SIGNAL_H() 1
38#else
39#define PERFETTO_HAS_SIGNAL_H() 0
40#endif
41
Hector Dearmane44ad452018-09-21 11:51:57 +010042#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
43#include <linenoise.h>
Hector Dearman36adc822018-10-02 11:35:08 +010044#include <pwd.h>
45#include <sys/types.h>
Hector Dearmane44ad452018-09-21 11:51:57 +010046#endif
47
Primiano Tucci24647142018-08-13 21:18:24 +020048#if PERFETTO_HAS_SIGNAL_H()
49#include <signal.h>
50#endif
51
Lalit Maganti1ebebf12018-10-15 17:24:04 +010052namespace perfetto {
53namespace trace_processor {
Primiano Tucci5968caf2018-08-06 10:31:46 +010054
55namespace {
Primiano Tucci24647142018-08-13 21:18:24 +020056TraceProcessor* g_tp;
57
Hector Dearmane44ad452018-09-21 11:51:57 +010058#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
59
Hector Dearman36adc822018-10-02 11:35:08 +010060bool EnsureDir(const std::string& path) {
61 return mkdir(path.c_str(), 0755) != -1 || errno == EEXIST;
62}
63
64bool EnsureFile(const std::string& path) {
Primiano Tuccif675dc22018-10-18 00:17:26 +020065 return base::OpenFile(path, O_RDONLY | O_CREAT, 0644).get() != -1;
Hector Dearman36adc822018-10-02 11:35:08 +010066}
67
68std::string GetConfigPath() {
69 const char* homedir = getenv("HOME");
70 if (homedir == nullptr)
71 homedir = getpwuid(getuid())->pw_dir;
72 if (homedir == nullptr)
73 return "";
74 return std::string(homedir) + "/.config";
75}
76
77std::string GetPerfettoPath() {
78 std::string config = GetConfigPath();
79 if (config == "")
80 return "";
81 return config + "/perfetto";
82}
83
84std::string GetHistoryPath() {
85 std::string perfetto = GetPerfettoPath();
86 if (perfetto == "")
87 return "";
88 return perfetto + "/.trace_processor_shell_history";
89}
90
Hector Dearmane44ad452018-09-21 11:51:57 +010091void SetupLineEditor() {
92 linenoiseSetMultiLine(true);
93 linenoiseHistorySetMaxLen(1000);
Hector Dearman36adc822018-10-02 11:35:08 +010094
95 bool success = GetHistoryPath() != "";
96 success = success && EnsureDir(GetConfigPath());
97 success = success && EnsureDir(GetPerfettoPath());
98 success = success && EnsureFile(GetHistoryPath());
99 success = success && linenoiseHistoryLoad(GetHistoryPath().c_str()) != -1;
100 if (!success) {
101 PERFETTO_PLOG("Could not load history from %s", GetHistoryPath().c_str());
102 }
Primiano Tucci5968caf2018-08-06 10:31:46 +0100103}
104
Hector Dearman69356442018-09-21 14:10:57 +0100105void FreeLine(char* line) {
Hector Dearmane44ad452018-09-21 11:51:57 +0100106 linenoiseHistoryAdd(line);
Hector Dearman36adc822018-10-02 11:35:08 +0100107 linenoiseHistorySave(GetHistoryPath().c_str());
Hector Dearmane44ad452018-09-21 11:51:57 +0100108 linenoiseFree(line);
109}
110
111char* GetLine(const char* prompt) {
112 return linenoise(prompt);
113}
114
115#else
116
117void SetupLineEditor() {}
118
119void FreeLine(char* line) {
Greg Kaiserbc635a52018-09-24 06:12:09 -0700120 delete[] line;
Hector Dearmane44ad452018-09-21 11:51:57 +0100121}
122
123char* GetLine(const char* prompt) {
124 printf("\r%80s\r%s", "", prompt);
125 fflush(stdout);
126 char* line = new char[1024];
127 if (!fgets(line, 1024 - 1, stdin)) {
128 FreeLine(line);
129 return nullptr;
130 }
131 if (strlen(line) > 0)
132 line[strlen(line) - 1] = 0;
133 return line;
134}
135
136#endif
137
Sami Kyostila33668942018-11-13 16:33:32 +0000138int ExportTraceToDatabase(const std::string& output_name) {
139 PERFETTO_CHECK(output_name.find("'") == std::string::npos);
140 {
141 base::ScopedFile fd(base::OpenFile(output_name, O_CREAT | O_RDWR, 0600));
142 if (!fd) {
143 PERFETTO_PLOG("Failed to create file: %s", output_name.c_str());
144 return 1;
145 }
146 int res = ftruncate(fd.get(), 0);
147 PERFETTO_CHECK(res == 0);
148 }
149
150 // TODO(skyostil): Use synchronous queries.
151 std::string attach_sql =
152 "ATTACH DATABASE '" + output_name + "' AS perfetto_export";
153 protos::RawQueryArgs attach_query;
154 attach_query.set_sql_query(attach_sql);
155 g_tp->ExecuteQuery(
156 attach_query, [](const protos::RawQueryResult& attach_res) {
157 if (attach_res.has_error()) {
158 PERFETTO_ELOG("SQLite error: %s", attach_res.error().c_str());
159 return;
160 }
161 protos::RawQueryArgs list_query;
162 // Find all the virtual tables we have created internally as well as
163 // actual tables registered through SQL.
164 list_query.set_sql_query(
165 "SELECT name FROM perfetto_tables UNION "
166 "SELECT name FROM sqlite_master WHERE type='table'");
167 g_tp->ExecuteQuery(list_query, [](const protos::RawQueryResult& res) {
168 if (res.has_error()) {
169 PERFETTO_ELOG("SQLite error: %s", res.error().c_str());
170 return;
171 }
172 PERFETTO_CHECK(res.columns_size() == 1);
173 for (int r = 0; r < static_cast<int>(res.num_records()); r++) {
174 std::string table_name = res.columns(0).string_values(r);
175 PERFETTO_CHECK(table_name.find("'") == std::string::npos);
176 std::string export_sql = "CREATE TABLE perfetto_export." +
177 table_name + " AS SELECT * FROM " +
178 table_name;
179 protos::RawQueryArgs export_query;
180 export_query.set_sql_query(export_sql);
181 g_tp->ExecuteQuery(export_query,
182 [](const protos::RawQueryResult& export_res) {
183 if (export_res.has_error())
184 PERFETTO_ELOG("SQLite error: %s",
185 export_res.error().c_str());
186 });
187 }
188 });
189 });
190
191 protos::RawQueryArgs detach_query;
192 detach_query.set_sql_query("DETACH DATABASE perfetto_export");
193 g_tp->ExecuteQuery(detach_query, [](const protos::RawQueryResult& res) {
194 if (res.has_error())
195 PERFETTO_ELOG("SQLite error: %s", res.error().c_str());
196 });
197 return 0;
198}
199
Lalit Maganti21160e82018-10-16 09:40:29 +0100200void PrintQueryResultInteractively(base::TimeNanos t_start,
201 const protos::RawQueryResult& res) {
Primiano Tucci5968caf2018-08-06 10:31:46 +0100202 if (res.has_error()) {
203 PERFETTO_ELOG("SQLite error: %s", res.error().c_str());
204 return;
205 }
Primiano Tuccia5cc0932018-08-21 17:57:29 +0200206 PERFETTO_CHECK(res.columns_size() == res.column_descriptors_size());
Primiano Tucci5968caf2018-08-06 10:31:46 +0100207
208 base::TimeNanos t_end = base::GetWallTimeNs();
209
210 for (int r = 0; r < static_cast<int>(res.num_records()); r++) {
211 if (r % 32 == 0) {
212 if (r > 0) {
213 fprintf(stderr, "...\nType 'q' to stop, Enter for more records: ");
214 fflush(stderr);
215 char input[32];
216 if (!fgets(input, sizeof(input) - 1, stdin))
217 exit(0);
218 if (input[0] == 'q')
219 break;
220 }
221 for (const auto& col : res.column_descriptors())
222 printf("%20s ", col.name().c_str());
223 printf("\n");
224
225 for (int i = 0; i < res.columns_size(); i++)
226 printf("%20s ", "--------------------");
227 printf("\n");
228 }
229
Lalit Magantib55c8842018-12-13 14:26:08 +0000230 using ColumnDesc = protos::RawQueryResult::ColumnDesc;
Primiano Tucci5968caf2018-08-06 10:31:46 +0100231 for (int c = 0; c < res.columns_size(); c++) {
Lalit Magantib55c8842018-12-13 14:26:08 +0000232 if (res.columns(c).is_nulls(r)) {
233 printf("%-20.20s", "[NULL]");
234 } else {
235 switch (res.column_descriptors(c).type()) {
236 case ColumnDesc::STRING:
237 printf("%-20.20s", res.columns(c).string_values(r).c_str());
238 break;
239 case ColumnDesc::DOUBLE:
240 printf("%20f", res.columns(c).double_values(r));
241 break;
242 case ColumnDesc::LONG: {
243 auto value = res.columns(c).long_values(r);
244 printf("%20lld", value);
245 break;
246 }
247 case ColumnDesc::UNKNOWN:
248 PERFETTO_FATAL("Row should be null so handled above");
249 break;
Lalit Maganti21160e82018-10-16 09:40:29 +0100250 }
251 }
252 printf(" ");
253 }
254 printf("\n");
255 }
256 printf("\nQuery executed in %.3f ms\n\n", (t_end - t_start).count() / 1E6);
257}
Sami Kyostila33668942018-11-13 16:33:32 +0000258
259void PrintShellUsage() {
260 PERFETTO_ELOG(
261 "Available commands:\n"
262 ".quit, .q Exit the shell.\n"
263 ".help This text.\n"
264 ".dump FILE Export the trace as a sqlite database.\n");
265}
266
Lalit Maganti21160e82018-10-16 09:40:29 +0100267int StartInteractiveShell() {
268 SetupLineEditor();
Primiano Tuccib75dcee2018-08-08 12:21:36 +0100269
Lalit Maganti21160e82018-10-16 09:40:29 +0100270 for (;;) {
271 char* line = GetLine("> ");
Sami Kyostila33668942018-11-13 16:33:32 +0000272 if (!line)
Lalit Maganti21160e82018-10-16 09:40:29 +0100273 break;
274 if (strcmp(line, "") == 0)
275 continue;
Sami Kyostila33668942018-11-13 16:33:32 +0000276 if (line[0] == '.') {
277 char command[32] = {};
278 char arg[1024] = {};
279 sscanf(line + 1, "%31s %1023s", command, arg);
280 if (strcmp(command, "quit") == 0 || strcmp(command, "q") == 0) {
281 break;
282 } else if (strcmp(command, "help") == 0) {
283 PrintShellUsage();
284 } else if (strcmp(command, "dump") == 0 && strlen(arg)) {
285 if (ExportTraceToDatabase(arg) != 0)
286 PERFETTO_ELOG("Database export failed");
287 } else {
288 PrintShellUsage();
289 }
290 continue;
291 }
Lalit Maganti21160e82018-10-16 09:40:29 +0100292 protos::RawQueryArgs query;
293 query.set_sql_query(line);
294 base::TimeNanos t_start = base::GetWallTimeNs();
295 g_tp->ExecuteQuery(query, [t_start](const protos::RawQueryResult& res) {
296 PrintQueryResultInteractively(t_start, res);
297 });
298
299 FreeLine(line);
300 }
301 return 0;
302}
303
304void PrintQueryResultAsCsv(const protos::RawQueryResult& res, FILE* output) {
305 PERFETTO_CHECK(res.columns_size() == res.column_descriptors_size());
306
307 for (int r = 0; r < static_cast<int>(res.num_records()); r++) {
308 if (r == 0) {
309 for (int c = 0; c < res.column_descriptors_size(); c++) {
310 const auto& col = res.column_descriptors(c);
311 if (c > 0)
312 fprintf(output, ",");
313 fprintf(output, "\"%s\"", col.name().c_str());
314 }
315 fprintf(output, "\n");
316 }
317
318 for (int c = 0; c < res.columns_size(); c++) {
319 if (c > 0)
320 fprintf(output, ",");
Lalit Magantib55c8842018-12-13 14:26:08 +0000321
322 using ColumnDesc = protos::RawQueryResult::ColumnDesc;
323 if (res.columns(c).is_nulls(r)) {
324 fprintf(output, "\"%s\"", "[NULL]");
325 } else {
326 switch (res.column_descriptors(c).type()) {
327 case ColumnDesc::STRING:
328 fprintf(output, "\"%s\"", res.columns(c).string_values(r).c_str());
329 break;
330 case ColumnDesc::DOUBLE:
331 fprintf(output, "%f", res.columns(c).double_values(r));
332 break;
333 case ColumnDesc::LONG: {
334 auto value = res.columns(c).long_values(r);
335 fprintf(output, "%lld", value);
336 break;
337 }
338 case ColumnDesc::UNKNOWN:
339 PERFETTO_FATAL("Row should be null so handled above");
340 break;
Primiano Tuccib75dcee2018-08-08 12:21:36 +0100341 }
Primiano Tucci5968caf2018-08-06 10:31:46 +0100342 }
343 }
Lalit Magantib55c8842018-12-13 14:26:08 +0000344 fprintf(output, "\n");
Primiano Tucci5968caf2018-08-06 10:31:46 +0100345 }
Lalit Maganti21160e82018-10-16 09:40:29 +0100346}
347
348int RunQueryAndPrintResult(FILE* input, FILE* output) {
349 char buffer[4096];
350 bool is_first_query = true;
351 bool is_query_error = false;
352 bool has_output_printed = false;
353 while (!feof(input) && !ferror(input) && !is_query_error) {
354 // Add an extra newline separator between query results.
355 if (!is_first_query)
356 fprintf(output, "\n");
357 is_first_query = false;
358
359 std::string sql_query;
360 while (fgets(buffer, sizeof(buffer), input)) {
361 if (strncmp(buffer, "\n", sizeof(buffer)) == 0)
362 break;
363 sql_query.append(buffer);
364 }
365 if (sql_query.back() == '\n')
366 sql_query.resize(sql_query.size() - 1);
Lalit Maganticbccb0a2018-12-04 20:14:42 +0000367
368 // If we have a new line at the end of the file or an extra new line
369 // somewhere in the file, we'll end up with an empty query which we should
370 // just ignore.
371 if (sql_query.empty())
372 continue;
373
Lalit Maganti21160e82018-10-16 09:40:29 +0100374 PERFETTO_ILOG("Executing query: %s", sql_query.c_str());
375
376 protos::RawQueryArgs query;
377 query.set_sql_query(sql_query);
378 g_tp->ExecuteQuery(query, [output, &is_query_error, &has_output_printed](
379 const protos::RawQueryResult& res) {
380 if (res.has_error()) {
381 PERFETTO_ELOG("SQLite error: %s", res.error().c_str());
382 is_query_error = true;
383 return;
384 } else if (res.num_records() != 0) {
385 if (has_output_printed) {
386 PERFETTO_ELOG(
387 "More than one query generated result rows. This is "
388 "unsupported.");
389 is_query_error = true;
390 return;
391 }
392 has_output_printed = true;
393 }
394 PrintQueryResultAsCsv(res, output);
395 });
396 }
Lalit Maganti22a1e0c2018-12-04 20:28:18 +0000397 if (ferror(input)) {
398 PERFETTO_ELOG("Error reading query file");
399 }
400 return ferror(input) || is_query_error ? 1 : 0;
Lalit Maganti21160e82018-10-16 09:40:29 +0100401}
402
403void PrintUsage(char** argv) {
Sami Kyostila33668942018-11-13 16:33:32 +0000404 PERFETTO_ELOG(
405 "Interactive trace processor shell.\n"
406 "Usage: %s [OPTIONS] trace_file.pb\n\n"
407 "Options:\n"
408 " -d Enable virtual table debugging.\n"
409 " -q FILE Read and execute an SQL query from a file.\n"
410 " -e FILE Export the trace into a SQLite database.\n",
411 argv[0]);
Primiano Tucci5968caf2018-08-06 10:31:46 +0100412}
413
Lalit Maganti1ebebf12018-10-15 17:24:04 +0100414int TraceProcessorMain(int argc, char** argv) {
Primiano Tucci5968caf2018-08-06 10:31:46 +0100415 if (argc < 2) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100416 PrintUsage(argv);
Primiano Tucci5968caf2018-08-06 10:31:46 +0100417 return 1;
418 }
Primiano Tuccib2ea4d42018-08-21 15:05:13 +0200419 const char* trace_file_path = nullptr;
Lalit Maganti21160e82018-10-16 09:40:29 +0100420 const char* query_file_path = nullptr;
Sami Kyostila33668942018-11-13 16:33:32 +0000421 const char* sqlite_file_path = nullptr;
Primiano Tuccib2ea4d42018-08-21 15:05:13 +0200422 for (int i = 1; i < argc; i++) {
423 if (strcmp(argv[i], "-d") == 0) {
424 EnableSQLiteVtableDebugging();
425 continue;
426 }
Lalit Maganti21160e82018-10-16 09:40:29 +0100427 if (strcmp(argv[i], "-q") == 0) {
428 if (++i == argc) {
429 PrintUsage(argv);
430 return 1;
431 }
432 query_file_path = argv[i];
433 continue;
Sami Kyostila33668942018-11-13 16:33:32 +0000434 } else if (strcmp(argv[i], "-e") == 0) {
435 if (++i == argc) {
436 PrintUsage(argv);
437 return 1;
438 }
439 sqlite_file_path = argv[i];
440 continue;
441 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
442 PrintUsage(argv);
443 return 0;
444 } else if (argv[i][0] == '-') {
445 PERFETTO_ELOG("Unknown option: %s", argv[i]);
446 return 1;
Lalit Maganti21160e82018-10-16 09:40:29 +0100447 }
Primiano Tuccib2ea4d42018-08-21 15:05:13 +0200448 trace_file_path = argv[i];
449 }
Primiano Tucci5968caf2018-08-06 10:31:46 +0100450
Lalit Maganti21160e82018-10-16 09:40:29 +0100451 if (trace_file_path == nullptr) {
452 PrintUsage(argv);
453 return 1;
454 }
455
Primiano Tucci7e330292018-08-24 19:10:52 +0200456 // Load the trace file into the trace processor.
Ioannis Ilkoseff38f52018-10-29 10:37:55 +0000457 Config config;
Primiano Tucci81d0a392018-09-04 15:15:37 +0100458 config.optimization_mode = OptimizationMode::kMaxBandwidth;
Ioannis Ilkosf8371292018-11-05 16:47:35 +0000459 std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
Florian Mayerb03fd282018-10-03 16:05:16 +0100460 base::ScopedFile fd(base::OpenFile(trace_file_path, O_RDONLY));
Lalit Maganti22a1e0c2018-12-04 20:28:18 +0000461 if (!fd) {
462 PERFETTO_ELOG("Could not open trace file (path: %s)", trace_file_path);
463 return 1;
464 }
Primiano Tucci7e330292018-08-24 19:10:52 +0200465
466 // Load the trace in chunks using async IO. We create a simple pipeline where,
467 // at each iteration, we parse the current chunk and asynchronously start
468 // reading the next chunk.
469
470 // 1MB chunk size seems the best tradeoff on a MacBook Pro 2013 - i7 2.8 GHz.
471 constexpr size_t kChunkSize = 1024 * 1024;
472 struct aiocb cb {};
473 cb.aio_nbytes = kChunkSize;
474 cb.aio_fildes = *fd;
475
Florian Mayere37c6202018-10-18 12:07:59 +0100476 std::unique_ptr<uint8_t[]> aio_buf(new uint8_t[kChunkSize]);
477 cb.aio_buf = aio_buf.get();
Primiano Tucci7e330292018-08-24 19:10:52 +0200478
479 PERFETTO_CHECK(aio_read(&cb) == 0);
480 struct aiocb* aio_list[1] = {&cb};
481
482 uint64_t file_size = 0;
483 auto t_load_start = base::GetWallTimeMs();
484 for (int i = 0;; i++) {
485 if (i % 128 == 0)
486 fprintf(stderr, "\rLoading trace: %.2f MB\r", file_size / 1E6);
487
488 // Block waiting for the pending read to complete.
489 PERFETTO_CHECK(aio_suspend(aio_list, 1, nullptr) == 0);
490 auto rsize = aio_return(&cb);
491 if (rsize <= 0)
492 break;
493 file_size += static_cast<uint64_t>(rsize);
494
495 // Take ownership of the completed buffer and enqueue a new async read
496 // with a fresh buffer.
Florian Mayere37c6202018-10-18 12:07:59 +0100497 std::unique_ptr<uint8_t[]> buf(std::move(aio_buf));
498 aio_buf.reset(new uint8_t[kChunkSize]);
499 cb.aio_buf = aio_buf.get();
Primiano Tucci7e330292018-08-24 19:10:52 +0200500 cb.aio_offset += rsize;
501 PERFETTO_CHECK(aio_read(&cb) == 0);
502
503 // Parse the completed buffer while the async read is in-flight.
Ioannis Ilkosf8371292018-11-05 16:47:35 +0000504 tp->Parse(std::move(buf), static_cast<size_t>(rsize));
Primiano Tucci7e330292018-08-24 19:10:52 +0200505 }
Ioannis Ilkosf8371292018-11-05 16:47:35 +0000506 tp->NotifyEndOfFile();
Primiano Tucci7e330292018-08-24 19:10:52 +0200507 double t_load = (base::GetWallTimeMs() - t_load_start).count() / 1E3;
508 double size_mb = file_size / 1E6;
509 PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb, size_mb / t_load);
Ioannis Ilkosf8371292018-11-05 16:47:35 +0000510 g_tp = tp.get();
Primiano Tucci5968caf2018-08-06 10:31:46 +0100511
Primiano Tucci24647142018-08-13 21:18:24 +0200512#if PERFETTO_HAS_SIGNAL_H()
Primiano Tucci7e330292018-08-24 19:10:52 +0200513 signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
Primiano Tucci24647142018-08-13 21:18:24 +0200514#endif
Primiano Tucci5968caf2018-08-06 10:31:46 +0100515
Sami Kyostila33668942018-11-13 16:33:32 +0000516 int ret = 0;
517
518 // If we were given a query file, first load and execute it.
519 if (query_file_path) {
520 base::ScopedFstream file(fopen(query_file_path, "r"));
Lalit Maganti22a1e0c2018-12-04 20:28:18 +0000521 if (!file) {
522 PERFETTO_ELOG("Could not open query file (path: %s)", query_file_path);
523 return 1;
524 }
Sami Kyostila33668942018-11-13 16:33:32 +0000525 ret = RunQueryAndPrintResult(file.get(), stdout);
Primiano Tucci7e330292018-08-24 19:10:52 +0200526 }
Hector Dearmane44ad452018-09-21 11:51:57 +0100527
Sami Kyostila33668942018-11-13 16:33:32 +0000528 // After this we can dump the database and exit if needed.
529 if (ret == 0 && sqlite_file_path) {
530 return ExportTraceToDatabase(sqlite_file_path);
531 }
532
533 // If we ran an automated query, exit.
534 if (query_file_path) {
535 return ret;
536 }
537
538 // Otherwise start an interactive shell.
539 return StartInteractiveShell();
Primiano Tucci5968caf2018-08-06 10:31:46 +0100540}
Lalit Maganti1ebebf12018-10-15 17:24:04 +0100541
542} // namespace
543
544} // namespace trace_processor
545} // namespace perfetto
546
547int main(int argc, char** argv) {
548 return perfetto::trace_processor::TraceProcessorMain(argc, argv);
549}