blob: 2eefdc647e29d5b2529b19cb851a4a98c01c14f1 [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 Maganti6d4836e2019-05-16 16:43:27 +010019#include <getopt.h>
Lalit Maganti21160e82018-10-16 09:40:29 +010020#include <inttypes.h>
Lalit Maganti26f69bd2019-04-29 18:23:47 +010021#include <stdio.h>
Primiano Tucci7e330292018-08-24 19:10:52 +020022#include <sys/stat.h>
Primiano Tucci5968caf2018-08-06 10:31:46 +010023#include <unistd.h>
24
25#include <functional>
Lalit Maganti21160e82018-10-16 09:40:29 +010026#include <iostream>
Lalit Maganti7c959782019-04-02 16:54:12 +010027#include <vector>
Primiano Tucci5968caf2018-08-06 10:31:46 +010028
Primiano Tucci24647142018-08-13 21:18:24 +020029#include "perfetto/base/build_config.h"
Lalit Maganti260e04d2019-05-03 13:17:01 +010030#include "perfetto/base/file_utils.h"
Primiano Tucci5968caf2018-08-06 10:31:46 +010031#include "perfetto/base/logging.h"
Ioannis Ilkoseff38f52018-10-29 10:37:55 +000032#include "perfetto/base/scoped_file.h"
Lalit Maganti7c959782019-04-02 16:54:12 +010033#include "perfetto/base/string_splitter.h"
Primiano Tucci5968caf2018-08-06 10:31:46 +010034#include "perfetto/base/time.h"
Ioannis Ilkoseff38f52018-10-29 10:37:55 +000035#include "perfetto/trace_processor/trace_processor.h"
Primiano Tucci5968caf2018-08-06 10:31:46 +010036
Primiano Tucci24647142018-08-13 21:18:24 +020037#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
38 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
39 PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
40#define PERFETTO_HAS_SIGNAL_H() 1
41#else
42#define PERFETTO_HAS_SIGNAL_H() 0
43#endif
44
Hector Dearmane44ad452018-09-21 11:51:57 +010045#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
46#include <linenoise.h>
Hector Dearman36adc822018-10-02 11:35:08 +010047#include <pwd.h>
48#include <sys/types.h>
Primiano Tucci00da64a2019-02-22 14:51:10 +000049#include "perfetto_version.gen.h"
50#else
51#define PERFETTO_GET_GIT_REVISION() "unknown"
Hector Dearmane44ad452018-09-21 11:51:57 +010052#endif
53
Primiano Tucci24647142018-08-13 21:18:24 +020054#if PERFETTO_HAS_SIGNAL_H()
55#include <signal.h>
56#endif
57
Lalit Maganti1ebebf12018-10-15 17:24:04 +010058namespace perfetto {
59namespace trace_processor {
Primiano Tucci5968caf2018-08-06 10:31:46 +010060
61namespace {
Primiano Tucci24647142018-08-13 21:18:24 +020062TraceProcessor* g_tp;
63
Hector Dearmane44ad452018-09-21 11:51:57 +010064#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
65
Hector Dearman36adc822018-10-02 11:35:08 +010066bool EnsureDir(const std::string& path) {
67 return mkdir(path.c_str(), 0755) != -1 || errno == EEXIST;
68}
69
70bool EnsureFile(const std::string& path) {
Primiano Tuccif675dc22018-10-18 00:17:26 +020071 return base::OpenFile(path, O_RDONLY | O_CREAT, 0644).get() != -1;
Hector Dearman36adc822018-10-02 11:35:08 +010072}
73
74std::string GetConfigPath() {
75 const char* homedir = getenv("HOME");
76 if (homedir == nullptr)
77 homedir = getpwuid(getuid())->pw_dir;
78 if (homedir == nullptr)
79 return "";
80 return std::string(homedir) + "/.config";
81}
82
83std::string GetPerfettoPath() {
84 std::string config = GetConfigPath();
85 if (config == "")
86 return "";
87 return config + "/perfetto";
88}
89
90std::string GetHistoryPath() {
91 std::string perfetto = GetPerfettoPath();
92 if (perfetto == "")
93 return "";
94 return perfetto + "/.trace_processor_shell_history";
95}
96
Hector Dearmane44ad452018-09-21 11:51:57 +010097void SetupLineEditor() {
98 linenoiseSetMultiLine(true);
99 linenoiseHistorySetMaxLen(1000);
Hector Dearman36adc822018-10-02 11:35:08 +0100100
101 bool success = GetHistoryPath() != "";
102 success = success && EnsureDir(GetConfigPath());
103 success = success && EnsureDir(GetPerfettoPath());
104 success = success && EnsureFile(GetHistoryPath());
105 success = success && linenoiseHistoryLoad(GetHistoryPath().c_str()) != -1;
106 if (!success) {
107 PERFETTO_PLOG("Could not load history from %s", GetHistoryPath().c_str());
108 }
Primiano Tucci5968caf2018-08-06 10:31:46 +0100109}
110
Hector Dearman69356442018-09-21 14:10:57 +0100111void FreeLine(char* line) {
Hector Dearmane44ad452018-09-21 11:51:57 +0100112 linenoiseHistoryAdd(line);
Hector Dearman36adc822018-10-02 11:35:08 +0100113 linenoiseHistorySave(GetHistoryPath().c_str());
Hector Dearmane44ad452018-09-21 11:51:57 +0100114 linenoiseFree(line);
115}
116
117char* GetLine(const char* prompt) {
118 return linenoise(prompt);
119}
120
121#else
122
123void SetupLineEditor() {}
124
125void FreeLine(char* line) {
Greg Kaiserbc635a52018-09-24 06:12:09 -0700126 delete[] line;
Hector Dearmane44ad452018-09-21 11:51:57 +0100127}
128
129char* GetLine(const char* prompt) {
130 printf("\r%80s\r%s", "", prompt);
131 fflush(stdout);
132 char* line = new char[1024];
133 if (!fgets(line, 1024 - 1, stdin)) {
134 FreeLine(line);
135 return nullptr;
136 }
137 if (strlen(line) > 0)
138 line[strlen(line) - 1] = 0;
139 return line;
140}
141
142#endif
143
Lalit Maganti49ae9012019-04-03 16:10:39 +0100144bool PrintStats() {
145 auto it = g_tp->ExecuteQuery(
146 "SELECT name, idx, source, value from stats "
147 "where severity = 'error' and value > 0");
148
149 bool first = true;
150 for (uint32_t rows = 0; it.Next(); rows++) {
151 if (first) {
152 fprintf(stderr, "Error stats for this trace:\n");
153
154 for (uint32_t i = 0; i < it.ColumnCount(); i++)
155 fprintf(stderr, "%40s ", it.GetColumName(i).c_str());
156 fprintf(stderr, "\n");
157
158 for (uint32_t i = 0; i < it.ColumnCount(); i++)
159 fprintf(stderr, "%40s ", "----------------------------------------");
160 fprintf(stderr, "\n");
161
162 first = false;
163 }
164
165 for (uint32_t c = 0; c < it.ColumnCount(); c++) {
166 auto value = it.Get(c);
167 switch (value.type) {
168 case SqlValue::Type::kNull:
169 fprintf(stderr, "%-40.40s", "[NULL]");
170 break;
171 case SqlValue::Type::kDouble:
172 fprintf(stderr, "%40f", value.double_value);
173 break;
174 case SqlValue::Type::kLong:
175 fprintf(stderr, "%40" PRIi64, value.long_value);
176 break;
177 case SqlValue::Type::kString:
178 fprintf(stderr, "%-40.40s", value.string_value);
179 break;
Lalit Maganti62211072019-05-10 14:09:58 +0100180 case SqlValue::Type::kBytes:
181 printf("%-40.40s", "<raw bytes>");
182 break;
Lalit Maganti49ae9012019-04-03 16:10:39 +0100183 }
184 fprintf(stderr, " ");
185 }
186 fprintf(stderr, "\n");
187 }
188
Lalit Maganti1f067182019-05-09 14:50:05 +0100189 util::Status status = it.Status();
190 if (!status.ok()) {
191 PERFETTO_ELOG("Error while iterating stats %s", status.c_message());
Lalit Maganti49ae9012019-04-03 16:10:39 +0100192 return false;
193 }
194 return true;
195}
196
Sami Kyostila33668942018-11-13 16:33:32 +0000197int ExportTraceToDatabase(const std::string& output_name) {
198 PERFETTO_CHECK(output_name.find("'") == std::string::npos);
199 {
200 base::ScopedFile fd(base::OpenFile(output_name, O_CREAT | O_RDWR, 0600));
201 if (!fd) {
202 PERFETTO_PLOG("Failed to create file: %s", output_name.c_str());
203 return 1;
204 }
205 int res = ftruncate(fd.get(), 0);
206 PERFETTO_CHECK(res == 0);
207 }
208
Sami Kyostila33668942018-11-13 16:33:32 +0000209 std::string attach_sql =
210 "ATTACH DATABASE '" + output_name + "' AS perfetto_export";
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100211 auto attach_it = g_tp->ExecuteQuery(attach_sql);
Lalit Magantic72cea22019-04-08 12:29:15 +0100212 bool attach_has_more = attach_it.Next();
213 PERFETTO_DCHECK(!attach_has_more);
Lalit Maganti1f067182019-05-09 14:50:05 +0100214
215 util::Status status = attach_it.Status();
216 if (!status.ok()) {
217 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100218 return 1;
219 }
Sami Kyostila33668942018-11-13 16:33:32 +0000220
Lalit Magantic72cea22019-04-08 12:29:15 +0100221 auto tables_it = g_tp->ExecuteQuery(
222 "SELECT name FROM perfetto_tables UNION "
223 "SELECT name FROM sqlite_master WHERE type='table'");
224 for (uint32_t rows = 0; tables_it.Next(); rows++) {
225 std::string table_name = tables_it.Get(0).string_value;
226 PERFETTO_CHECK(table_name.find("'") == std::string::npos);
227 std::string export_sql = "CREATE TABLE perfetto_export." + table_name +
228 " AS SELECT * FROM " + table_name;
229
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100230 auto export_it = g_tp->ExecuteQuery(export_sql);
Lalit Magantic72cea22019-04-08 12:29:15 +0100231 bool export_has_more = export_it.Next();
232 PERFETTO_DCHECK(!export_has_more);
Lalit Maganti1f067182019-05-09 14:50:05 +0100233
234 status = export_it.Status();
235 if (!status.ok()) {
236 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100237 return 1;
238 }
239 }
Lalit Maganti1f067182019-05-09 14:50:05 +0100240 status = tables_it.Status();
241 if (!status.ok()) {
242 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100243 return 1;
244 }
245
246 auto detach_it = g_tp->ExecuteQuery("DETACH DATABASE perfetto_export");
247 bool detach_has_more = attach_it.Next();
248 PERFETTO_DCHECK(!detach_has_more);
Lalit Maganti1f067182019-05-09 14:50:05 +0100249 status = detach_it.Status();
250 if (!status.ok()) {
251 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100252 return 1;
253 }
Sami Kyostila33668942018-11-13 16:33:32 +0000254 return 0;
255}
256
Lalit Maganti7c959782019-04-02 16:54:12 +0100257int RunMetrics(const std::vector<std::string>& metric_names) {
Lalit Magantid9f86b62019-04-08 11:11:51 +0100258 std::vector<uint8_t> metric_result;
Lalit Magantid71a9452019-05-09 15:13:24 +0100259 util::Status status = g_tp->ComputeMetric(metric_names, &metric_result);
260 if (!status.ok()) {
261 PERFETTO_ELOG("Error when computing metrics: %s", status.c_message());
Lalit Magantid9f86b62019-04-08 11:11:51 +0100262 return 1;
Lalit Maganti7c959782019-04-02 16:54:12 +0100263 }
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100264 fwrite(metric_result.data(), sizeof(uint8_t), metric_result.size(), stdout);
Lalit Maganti7c959782019-04-02 16:54:12 +0100265 return 0;
266}
267
Lalit Magantic72cea22019-04-08 12:29:15 +0100268void PrintQueryResultInteractively(TraceProcessor::Iterator* it,
269 base::TimeNanos t_start) {
270 base::TimeNanos t_end = t_start;
271 for (uint32_t rows = 0; it->Next(); rows++) {
272 if (rows % 32 == 0) {
273 if (rows > 0) {
Primiano Tucci5968caf2018-08-06 10:31:46 +0100274 fprintf(stderr, "...\nType 'q' to stop, Enter for more records: ");
275 fflush(stderr);
276 char input[32];
277 if (!fgets(input, sizeof(input) - 1, stdin))
278 exit(0);
279 if (input[0] == 'q')
280 break;
Lalit Magantic72cea22019-04-08 12:29:15 +0100281 } else {
Lalit Magantiaac2f652019-04-30 12:16:21 +0100282 t_end = base::GetWallTimeNs();
Primiano Tucci5968caf2018-08-06 10:31:46 +0100283 }
Lalit Magantic72cea22019-04-08 12:29:15 +0100284 for (uint32_t i = 0; i < it->ColumnCount(); i++)
285 printf("%20s ", it->GetColumName(i).c_str());
Primiano Tucci5968caf2018-08-06 10:31:46 +0100286 printf("\n");
287
Lalit Magantic72cea22019-04-08 12:29:15 +0100288 for (uint32_t i = 0; i < it->ColumnCount(); i++)
Primiano Tucci5968caf2018-08-06 10:31:46 +0100289 printf("%20s ", "--------------------");
290 printf("\n");
291 }
292
Lalit Magantic72cea22019-04-08 12:29:15 +0100293 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
294 auto value = it->Get(c);
295 switch (value.type) {
296 case SqlValue::Type::kNull:
297 printf("%-20.20s", "[NULL]");
298 break;
299 case SqlValue::Type::kDouble:
300 printf("%20f", value.double_value);
301 break;
302 case SqlValue::Type::kLong:
303 printf("%20" PRIi64, value.long_value);
304 break;
305 case SqlValue::Type::kString:
306 printf("%-20.20s", value.string_value);
307 break;
Lalit Maganti62211072019-05-10 14:09:58 +0100308 case SqlValue::Type::kBytes:
309 printf("%-20.20s", "<raw bytes>");
310 break;
Lalit Maganti21160e82018-10-16 09:40:29 +0100311 }
312 printf(" ");
313 }
314 printf("\n");
315 }
Lalit Magantic72cea22019-04-08 12:29:15 +0100316
Lalit Maganti1f067182019-05-09 14:50:05 +0100317 util::Status status = it->Status();
318 if (!status.ok()) {
319 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100320 }
Lalit Maganti21160e82018-10-16 09:40:29 +0100321 printf("\nQuery executed in %.3f ms\n\n", (t_end - t_start).count() / 1E6);
322}
Sami Kyostila33668942018-11-13 16:33:32 +0000323
324void PrintShellUsage() {
325 PERFETTO_ELOG(
326 "Available commands:\n"
327 ".quit, .q Exit the shell.\n"
328 ".help This text.\n"
329 ".dump FILE Export the trace as a sqlite database.\n");
330}
331
Lalit Maganti21160e82018-10-16 09:40:29 +0100332int StartInteractiveShell() {
333 SetupLineEditor();
Primiano Tuccib75dcee2018-08-08 12:21:36 +0100334
Lalit Maganti21160e82018-10-16 09:40:29 +0100335 for (;;) {
336 char* line = GetLine("> ");
Sami Kyostila33668942018-11-13 16:33:32 +0000337 if (!line)
Lalit Maganti21160e82018-10-16 09:40:29 +0100338 break;
339 if (strcmp(line, "") == 0)
340 continue;
Sami Kyostila33668942018-11-13 16:33:32 +0000341 if (line[0] == '.') {
342 char command[32] = {};
343 char arg[1024] = {};
344 sscanf(line + 1, "%31s %1023s", command, arg);
345 if (strcmp(command, "quit") == 0 || strcmp(command, "q") == 0) {
346 break;
347 } else if (strcmp(command, "help") == 0) {
348 PrintShellUsage();
349 } else if (strcmp(command, "dump") == 0 && strlen(arg)) {
350 if (ExportTraceToDatabase(arg) != 0)
351 PERFETTO_ELOG("Database export failed");
352 } else {
353 PrintShellUsage();
354 }
355 continue;
356 }
Lalit Magantic72cea22019-04-08 12:29:15 +0100357
Lalit Maganti21160e82018-10-16 09:40:29 +0100358 base::TimeNanos t_start = base::GetWallTimeNs();
Lalit Magantic72cea22019-04-08 12:29:15 +0100359 auto it = g_tp->ExecuteQuery(line);
360 PrintQueryResultInteractively(&it, t_start);
Lalit Maganti21160e82018-10-16 09:40:29 +0100361
362 FreeLine(line);
363 }
364 return 0;
365}
366
Lalit Magantic72cea22019-04-08 12:29:15 +0100367void PrintQueryResultAsCsv(TraceProcessor::Iterator* it, FILE* output) {
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100368 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
369 if (c > 0)
370 fprintf(output, ",");
371 fprintf(output, "\"%s\"", it->GetColumName(c).c_str());
372 }
373 fprintf(output, "\n");
Lalit Maganti21160e82018-10-16 09:40:29 +0100374
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100375 for (uint32_t rows = 0; it->Next(); rows++) {
Lalit Magantic72cea22019-04-08 12:29:15 +0100376 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100377 if (c > 0)
378 fprintf(output, ",");
Lalit Magantib55c8842018-12-13 14:26:08 +0000379
Lalit Magantic72cea22019-04-08 12:29:15 +0100380 auto value = it->Get(c);
381 switch (value.type) {
382 case SqlValue::Type::kNull:
383 fprintf(output, "\"%s\"", "[NULL]");
384 break;
385 case SqlValue::Type::kDouble:
386 fprintf(output, "%f", value.double_value);
387 break;
388 case SqlValue::Type::kLong:
389 fprintf(output, "%" PRIi64, value.long_value);
390 break;
391 case SqlValue::Type::kString:
392 fprintf(output, "\"%s\"", value.string_value);
393 break;
Lalit Maganti62211072019-05-10 14:09:58 +0100394 case SqlValue::Type::kBytes:
395 fprintf(output, "\"%s\"", "<raw bytes>");
396 break;
Primiano Tucci5968caf2018-08-06 10:31:46 +0100397 }
398 }
Lalit Magantib55c8842018-12-13 14:26:08 +0000399 fprintf(output, "\n");
Primiano Tucci5968caf2018-08-06 10:31:46 +0100400 }
Lalit Maganti21160e82018-10-16 09:40:29 +0100401}
402
Florian Mayer3c742432019-05-21 11:35:47 +0100403bool IsBlankLine(char* buffer) {
404 size_t buf_size = strlen(buffer);
405 for (size_t i = 0; i < buf_size; ++i) {
406 if (buffer[i] != ' ' && buffer[i] != '\t' && buffer[i] != '\n')
407 return false;
408 }
409 return true;
410}
411
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000412bool LoadQueries(FILE* input, std::vector<std::string>* output) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100413 char buffer[4096];
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000414 while (!feof(input) && !ferror(input)) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100415 std::string sql_query;
416 while (fgets(buffer, sizeof(buffer), input)) {
Florian Mayer3c742432019-05-21 11:35:47 +0100417 if (IsBlankLine(buffer))
Lalit Maganti21160e82018-10-16 09:40:29 +0100418 break;
419 sql_query.append(buffer);
420 }
421 if (sql_query.back() == '\n')
422 sql_query.resize(sql_query.size() - 1);
Lalit Maganticbccb0a2018-12-04 20:14:42 +0000423
424 // If we have a new line at the end of the file or an extra new line
425 // somewhere in the file, we'll end up with an empty query which we should
426 // just ignore.
427 if (sql_query.empty())
428 continue;
429
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000430 output->push_back(sql_query);
431 }
432 if (ferror(input)) {
433 PERFETTO_ELOG("Error reading query file");
434 return false;
435 }
436 return true;
437}
438
439bool RunQueryAndPrintResult(const std::vector<std::string> queries,
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000440 FILE* output) {
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000441 bool is_first_query = true;
442 bool is_query_error = false;
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000443 bool has_output = false;
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000444 for (const auto& sql_query : queries) {
445 // Add an extra newline separator between query results.
446 if (!is_first_query)
447 fprintf(output, "\n");
448 is_first_query = false;
449
Lalit Maganti21160e82018-10-16 09:40:29 +0100450 PERFETTO_ILOG("Executing query: %s", sql_query.c_str());
451
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100452 auto it = g_tp->ExecuteQuery(sql_query);
Lalit Maganti1f067182019-05-09 14:50:05 +0100453 util::Status status = it.Status();
454 if (!status.ok()) {
455 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100456 is_query_error = true;
457 break;
458 }
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100459 if (it.ColumnCount() == 0) {
460 bool it_has_more = it.Next();
461 PERFETTO_DCHECK(!it_has_more);
462 continue;
463 }
464
465 if (has_output) {
Lalit Magantic72cea22019-04-08 12:29:15 +0100466 PERFETTO_ELOG(
467 "More than one query generated result rows. This is "
468 "unsupported.");
469 is_query_error = true;
470 break;
471 }
472 PrintQueryResultAsCsv(&it, output);
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100473 has_output = true;
Lalit Maganti21160e82018-10-16 09:40:29 +0100474 }
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000475 return !is_query_error;
Lalit Maganti21160e82018-10-16 09:40:29 +0100476}
477
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100478int MaybePrintPerfFile(const std::string& perf_file_path,
Lalit Maganti260e04d2019-05-03 13:17:01 +0100479 base::TimeNanos t_load,
480 base::TimeNanos t_run) {
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100481 if (perf_file_path.empty())
Lalit Maganti260e04d2019-05-03 13:17:01 +0100482 return 0;
483
484 char buf[128];
485 int count = snprintf(buf, sizeof(buf), "%" PRId64 ",%" PRId64,
486 static_cast<int64_t>(t_load.count()),
487 static_cast<int64_t>(t_run.count()));
488 if (count < 0) {
489 PERFETTO_ELOG("Failed to write perf data");
490 return 1;
491 }
492
493 auto fd(base::OpenFile(perf_file_path, O_WRONLY | O_CREAT | O_TRUNC, 066));
494 if (!fd) {
495 PERFETTO_ELOG("Failed to open perf file");
496 return 1;
497 }
498 base::WriteAll(fd.get(), buf, static_cast<size_t>(count));
499 return 0;
500}
501
Lalit Maganti21160e82018-10-16 09:40:29 +0100502void PrintUsage(char** argv) {
Sami Kyostila33668942018-11-13 16:33:32 +0000503 PERFETTO_ELOG(
504 "Interactive trace processor shell.\n"
505 "Usage: %s [OPTIONS] trace_file.pb\n\n"
506 "Options:\n"
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100507 " -h|--help Prints this usage.\n"
508 " -v|--version Prints the version of trace processor.\n"
509 " -d|--debug Enable virtual table debugging.\n"
510 " -p|--perf-file FILE Writes the time taken to ingest the trace and"
Lalit Maganti260e04d2019-05-03 13:17:01 +0100511 "execute the queries to the given file. Only valid with -q or "
512 "--run-metrics and the file will only be written if the execution is "
513 "successful\n"
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100514 " -q|--query-file FILE Read and execute an SQL query from a file.\n"
515 " -i|--interactive Starts interactive mode even after a query file "
516 "is specified with -q.\n"
517 " -e|--export FILE Export the trace into a SQLite database.\n"
518 " --run-metrics x,y,z Runs a comma separated list of metrics and "
Lalit Maganti7c959782019-04-02 16:54:12 +0100519 "prints the result as a TraceMetrics proto to stdout.\n",
Sami Kyostila33668942018-11-13 16:33:32 +0000520 argv[0]);
Primiano Tucci5968caf2018-08-06 10:31:46 +0100521}
522
Lalit Maganti1ebebf12018-10-15 17:24:04 +0100523int TraceProcessorMain(int argc, char** argv) {
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100524 enum LongOption {
525 OPT_RUN_METRICS = 1000,
526 };
527
528 static const struct option long_options[] = {
529 {"help", no_argument, nullptr, 'h'},
530 {"version", no_argument, nullptr, 'v'},
531 {"interactive", no_argument, nullptr, 'i'},
532 {"debug", no_argument, nullptr, 'd'},
533 {"perf-file", required_argument, nullptr, 'p'},
534 {"query-file", required_argument, nullptr, 'q'},
535 {"export", required_argument, nullptr, 'e'},
536 {"run-metrics", required_argument, nullptr, OPT_RUN_METRICS},
537 {nullptr, 0, nullptr, 0}};
538
539 std::string perf_file_path;
540 std::string query_file_path;
541 std::string sqlite_file_path;
542 std::string metric_names;
543 bool explicit_interactive = false;
544 int option_index = 0;
545 for (;;) {
546 int option =
547 getopt_long(argc, argv, "hvidp:q:e:", long_options, &option_index);
548
549 if (option == -1)
550 break; // EOF.
551
552 if (option == 'v') {
553 printf("%s\n", PERFETTO_GET_GIT_REVISION());
554 return 0;
555 }
556
557 if (option == 'i') {
558 explicit_interactive = true;
559 continue;
560 }
561
562 if (option == 'd') {
563 EnableSQLiteVtableDebugging();
564 continue;
565 }
566
567 if (option == 'p') {
568 perf_file_path = optarg;
569 continue;
570 }
571
572 if (option == 'q') {
573 query_file_path = optarg;
574 continue;
575 }
576
577 if (option == 'e') {
578 sqlite_file_path = optarg;
579 continue;
580 }
581
582 if (option == OPT_RUN_METRICS) {
583 metric_names = optarg;
584 continue;
585 }
586
587 PrintUsage(argv);
588 return option == 'h' ? 0 : 1;
589 }
590
591 // Ensure that we have the tracefile argument only at the end.
592 if (optind != argc - 1) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100593 PrintUsage(argv);
Primiano Tucci5968caf2018-08-06 10:31:46 +0100594 return 1;
595 }
596
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100597 const char* trace_file_path = argv[optind];
Lalit Maganti21160e82018-10-16 09:40:29 +0100598 if (trace_file_path == nullptr) {
599 PrintUsage(argv);
600 return 1;
601 }
602
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100603 bool launch_shell =
604 explicit_interactive || (metric_names.empty() && query_file_path.empty());
605
Lalit Maganti260e04d2019-05-03 13:17:01 +0100606 // Only allow non-interactive queries to emit perf data.
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100607 if (!perf_file_path.empty() && launch_shell) {
Lalit Maganti260e04d2019-05-03 13:17:01 +0100608 PrintUsage(argv);
609 return 1;
610 }
611
Primiano Tucci7e330292018-08-24 19:10:52 +0200612 // Load the trace file into the trace processor.
Ioannis Ilkoseff38f52018-10-29 10:37:55 +0000613 Config config;
Ioannis Ilkosf8371292018-11-05 16:47:35 +0000614 std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
Florian Mayerb03fd282018-10-03 16:05:16 +0100615 base::ScopedFile fd(base::OpenFile(trace_file_path, O_RDONLY));
Lalit Maganti22a1e0c2018-12-04 20:28:18 +0000616 if (!fd) {
617 PERFETTO_ELOG("Could not open trace file (path: %s)", trace_file_path);
618 return 1;
619 }
Primiano Tucci7e330292018-08-24 19:10:52 +0200620
621 // Load the trace in chunks using async IO. We create a simple pipeline where,
622 // at each iteration, we parse the current chunk and asynchronously start
623 // reading the next chunk.
624
625 // 1MB chunk size seems the best tradeoff on a MacBook Pro 2013 - i7 2.8 GHz.
626 constexpr size_t kChunkSize = 1024 * 1024;
627 struct aiocb cb {};
628 cb.aio_nbytes = kChunkSize;
629 cb.aio_fildes = *fd;
630
Florian Mayere37c6202018-10-18 12:07:59 +0100631 std::unique_ptr<uint8_t[]> aio_buf(new uint8_t[kChunkSize]);
Lalit Maganti9e6616e2019-04-03 13:19:44 +0100632#if defined(MEMORY_SANITIZER)
633 // Just initialize the memory to make the memory sanitizer happy as it
634 // cannot track aio calls below.
635 memset(aio_buf.get(), 0, kChunkSize);
636#endif
Florian Mayere37c6202018-10-18 12:07:59 +0100637 cb.aio_buf = aio_buf.get();
Primiano Tucci7e330292018-08-24 19:10:52 +0200638
639 PERFETTO_CHECK(aio_read(&cb) == 0);
640 struct aiocb* aio_list[1] = {&cb};
641
642 uint64_t file_size = 0;
Lalit Maganti260e04d2019-05-03 13:17:01 +0100643 auto t_load_start = base::GetWallTimeNs();
Primiano Tucci7e330292018-08-24 19:10:52 +0200644 for (int i = 0;; i++) {
645 if (i % 128 == 0)
646 fprintf(stderr, "\rLoading trace: %.2f MB\r", file_size / 1E6);
647
648 // Block waiting for the pending read to complete.
649 PERFETTO_CHECK(aio_suspend(aio_list, 1, nullptr) == 0);
650 auto rsize = aio_return(&cb);
651 if (rsize <= 0)
652 break;
653 file_size += static_cast<uint64_t>(rsize);
654
655 // Take ownership of the completed buffer and enqueue a new async read
656 // with a fresh buffer.
Florian Mayere37c6202018-10-18 12:07:59 +0100657 std::unique_ptr<uint8_t[]> buf(std::move(aio_buf));
658 aio_buf.reset(new uint8_t[kChunkSize]);
Lalit Maganti9e6616e2019-04-03 13:19:44 +0100659#if defined(MEMORY_SANITIZER)
660 // Just initialize the memory to make the memory sanitizer happy as it
661 // cannot track aio calls below.
662 memset(aio_buf.get(), 0, kChunkSize);
663#endif
Florian Mayere37c6202018-10-18 12:07:59 +0100664 cb.aio_buf = aio_buf.get();
Primiano Tucci7e330292018-08-24 19:10:52 +0200665 cb.aio_offset += rsize;
666 PERFETTO_CHECK(aio_read(&cb) == 0);
667
668 // Parse the completed buffer while the async read is in-flight.
Lalit Maganti1f067182019-05-09 14:50:05 +0100669 util::Status status = tp->Parse(std::move(buf), static_cast<size_t>(rsize));
670 if (PERFETTO_UNLIKELY(!status.ok())) {
671 PERFETTO_ELOG("Fatal error while parsing trace: %s", status.c_message());
Lalit Maganti4b2b2532019-05-09 11:03:23 +0100672 return 1;
Lalit Maganti1f067182019-05-09 14:50:05 +0100673 }
Primiano Tucci7e330292018-08-24 19:10:52 +0200674 }
Ioannis Ilkosf8371292018-11-05 16:47:35 +0000675 tp->NotifyEndOfFile();
Lalit Maganti260e04d2019-05-03 13:17:01 +0100676
677 auto t_load = base::GetWallTimeNs() - t_load_start;
678 double t_load_s = t_load.count() / 1E9;
Primiano Tucci7e330292018-08-24 19:10:52 +0200679 double size_mb = file_size / 1E6;
Lalit Maganti260e04d2019-05-03 13:17:01 +0100680 PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb,
681 size_mb / t_load_s);
Ioannis Ilkosf8371292018-11-05 16:47:35 +0000682 g_tp = tp.get();
Primiano Tucci5968caf2018-08-06 10:31:46 +0100683
Primiano Tucci24647142018-08-13 21:18:24 +0200684#if PERFETTO_HAS_SIGNAL_H()
Primiano Tucci7e330292018-08-24 19:10:52 +0200685 signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
Primiano Tucci24647142018-08-13 21:18:24 +0200686#endif
Primiano Tucci5968caf2018-08-06 10:31:46 +0100687
Lalit Maganti49ae9012019-04-03 16:10:39 +0100688 // Print out the stats to stderr for the trace.
689 if (!PrintStats()) {
690 return 1;
691 }
692
Lalit Maganti260e04d2019-05-03 13:17:01 +0100693 auto t_run_start = base::GetWallTimeNs();
694
Lalit Maganti7c959782019-04-02 16:54:12 +0100695 // First, see if we have some metrics to run. If we do, just run them and
696 // return.
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100697 if (!metric_names.empty()) {
Lalit Maganti7c959782019-04-02 16:54:12 +0100698 std::vector<std::string> metrics;
699 for (base::StringSplitter ss(metric_names, ','); ss.Next();) {
700 metrics.emplace_back(ss.cur_token());
701 }
Lalit Maganti260e04d2019-05-03 13:17:01 +0100702 int ret = RunMetrics(std::move(metrics));
703 if (!ret) {
704 auto t_query = base::GetWallTimeNs() - t_run_start;
705 ret = MaybePrintPerfFile(perf_file_path, t_load, t_query);
706 }
707 return ret;
Lalit Maganti7c959782019-04-02 16:54:12 +0100708 }
709
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000710 // If we were given a query file, load contents
711 std::vector<std::string> queries;
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100712 if (!query_file_path.empty()) {
713 base::ScopedFstream file(fopen(query_file_path.c_str(), "r"));
Lalit Maganti22a1e0c2018-12-04 20:28:18 +0000714 if (!file) {
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100715 PERFETTO_ELOG("Could not open query file (path: %s)",
716 query_file_path.c_str());
Lalit Maganti22a1e0c2018-12-04 20:28:18 +0000717 return 1;
718 }
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000719 if (!LoadQueries(file.get(), &queries)) {
720 return 1;
721 }
722 }
723
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000724 if (!RunQueryAndPrintResult(queries, stdout)) {
Lalit Maganti77f1dbe2019-02-27 12:58:01 +0000725 return 1;
Primiano Tucci7e330292018-08-24 19:10:52 +0200726 }
Hector Dearmane44ad452018-09-21 11:51:57 +0100727
Lalit Maganti6d4836e2019-05-16 16:43:27 +0100728 if (!sqlite_file_path.empty()) {
Sami Kyostila33668942018-11-13 16:33:32 +0000729 return ExportTraceToDatabase(sqlite_file_path);
730 }
731
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000732 if (!launch_shell) {
Lalit Maganti260e04d2019-05-03 13:17:01 +0100733 auto t_query = base::GetWallTimeNs() - t_run_start;
734 return MaybePrintPerfFile(perf_file_path, t_load, t_query);
Sami Kyostila33668942018-11-13 16:33:32 +0000735 }
736
Sami Kyostila33668942018-11-13 16:33:32 +0000737 return StartInteractiveShell();
Primiano Tucci5968caf2018-08-06 10:31:46 +0100738}
Lalit Maganti1ebebf12018-10-15 17:24:04 +0100739
740} // namespace
741
742} // namespace trace_processor
743} // namespace perfetto
744
745int main(int argc, char** argv) {
746 return perfetto::trace_processor::TraceProcessorMain(argc, argv);
747}