blob: 501ad6d5a1ff107714f38616f690e967f065d2ba [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>
Lalit Maganti26f69bd2019-04-29 18:23:47 +010020#include <stdio.h>
Primiano Tucci7e330292018-08-24 19:10:52 +020021#include <sys/stat.h>
Primiano Tucci5968caf2018-08-06 10:31:46 +010022#include <unistd.h>
23
24#include <functional>
Lalit Maganti21160e82018-10-16 09:40:29 +010025#include <iostream>
Lalit Maganti7c959782019-04-02 16:54:12 +010026#include <vector>
Primiano Tucci5968caf2018-08-06 10:31:46 +010027
Primiano Tucci24647142018-08-13 21:18:24 +020028#include "perfetto/base/build_config.h"
Lalit Maganti260e04d2019-05-03 13:17:01 +010029#include "perfetto/base/file_utils.h"
Primiano Tucci5968caf2018-08-06 10:31:46 +010030#include "perfetto/base/logging.h"
Ioannis Ilkoseff38f52018-10-29 10:37:55 +000031#include "perfetto/base/scoped_file.h"
Lalit Maganti7c959782019-04-02 16:54:12 +010032#include "perfetto/base/string_splitter.h"
Primiano Tucci5968caf2018-08-06 10:31:46 +010033#include "perfetto/base/time.h"
Ioannis Ilkoseff38f52018-10-29 10:37:55 +000034#include "perfetto/trace_processor/trace_processor.h"
Primiano Tucci5968caf2018-08-06 10:31:46 +010035
Primiano Tucci24647142018-08-13 21:18:24 +020036#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
37 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
38 PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
39#define PERFETTO_HAS_SIGNAL_H() 1
40#else
41#define PERFETTO_HAS_SIGNAL_H() 0
42#endif
43
Hector Dearmane44ad452018-09-21 11:51:57 +010044#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
45#include <linenoise.h>
Hector Dearman36adc822018-10-02 11:35:08 +010046#include <pwd.h>
47#include <sys/types.h>
Primiano Tucci00da64a2019-02-22 14:51:10 +000048#include "perfetto_version.gen.h"
49#else
50#define PERFETTO_GET_GIT_REVISION() "unknown"
Hector Dearmane44ad452018-09-21 11:51:57 +010051#endif
52
Primiano Tucci24647142018-08-13 21:18:24 +020053#if PERFETTO_HAS_SIGNAL_H()
54#include <signal.h>
55#endif
56
Lalit Maganti1ebebf12018-10-15 17:24:04 +010057namespace perfetto {
58namespace trace_processor {
Primiano Tucci5968caf2018-08-06 10:31:46 +010059
60namespace {
Primiano Tucci24647142018-08-13 21:18:24 +020061TraceProcessor* g_tp;
62
Hector Dearmane44ad452018-09-21 11:51:57 +010063#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
64
Hector Dearman36adc822018-10-02 11:35:08 +010065bool EnsureDir(const std::string& path) {
66 return mkdir(path.c_str(), 0755) != -1 || errno == EEXIST;
67}
68
69bool EnsureFile(const std::string& path) {
Primiano Tuccif675dc22018-10-18 00:17:26 +020070 return base::OpenFile(path, O_RDONLY | O_CREAT, 0644).get() != -1;
Hector Dearman36adc822018-10-02 11:35:08 +010071}
72
73std::string GetConfigPath() {
74 const char* homedir = getenv("HOME");
75 if (homedir == nullptr)
76 homedir = getpwuid(getuid())->pw_dir;
77 if (homedir == nullptr)
78 return "";
79 return std::string(homedir) + "/.config";
80}
81
82std::string GetPerfettoPath() {
83 std::string config = GetConfigPath();
84 if (config == "")
85 return "";
86 return config + "/perfetto";
87}
88
89std::string GetHistoryPath() {
90 std::string perfetto = GetPerfettoPath();
91 if (perfetto == "")
92 return "";
93 return perfetto + "/.trace_processor_shell_history";
94}
95
Hector Dearmane44ad452018-09-21 11:51:57 +010096void SetupLineEditor() {
97 linenoiseSetMultiLine(true);
98 linenoiseHistorySetMaxLen(1000);
Hector Dearman36adc822018-10-02 11:35:08 +010099
100 bool success = GetHistoryPath() != "";
101 success = success && EnsureDir(GetConfigPath());
102 success = success && EnsureDir(GetPerfettoPath());
103 success = success && EnsureFile(GetHistoryPath());
104 success = success && linenoiseHistoryLoad(GetHistoryPath().c_str()) != -1;
105 if (!success) {
106 PERFETTO_PLOG("Could not load history from %s", GetHistoryPath().c_str());
107 }
Primiano Tucci5968caf2018-08-06 10:31:46 +0100108}
109
Hector Dearman69356442018-09-21 14:10:57 +0100110void FreeLine(char* line) {
Hector Dearmane44ad452018-09-21 11:51:57 +0100111 linenoiseHistoryAdd(line);
Hector Dearman36adc822018-10-02 11:35:08 +0100112 linenoiseHistorySave(GetHistoryPath().c_str());
Hector Dearmane44ad452018-09-21 11:51:57 +0100113 linenoiseFree(line);
114}
115
116char* GetLine(const char* prompt) {
117 return linenoise(prompt);
118}
119
120#else
121
122void SetupLineEditor() {}
123
124void FreeLine(char* line) {
Greg Kaiserbc635a52018-09-24 06:12:09 -0700125 delete[] line;
Hector Dearmane44ad452018-09-21 11:51:57 +0100126}
127
128char* GetLine(const char* prompt) {
129 printf("\r%80s\r%s", "", prompt);
130 fflush(stdout);
131 char* line = new char[1024];
132 if (!fgets(line, 1024 - 1, stdin)) {
133 FreeLine(line);
134 return nullptr;
135 }
136 if (strlen(line) > 0)
137 line[strlen(line) - 1] = 0;
138 return line;
139}
140
141#endif
142
Lalit Maganti49ae9012019-04-03 16:10:39 +0100143bool PrintStats() {
144 auto it = g_tp->ExecuteQuery(
145 "SELECT name, idx, source, value from stats "
146 "where severity = 'error' and value > 0");
147
148 bool first = true;
149 for (uint32_t rows = 0; it.Next(); rows++) {
150 if (first) {
151 fprintf(stderr, "Error stats for this trace:\n");
152
153 for (uint32_t i = 0; i < it.ColumnCount(); i++)
154 fprintf(stderr, "%40s ", it.GetColumName(i).c_str());
155 fprintf(stderr, "\n");
156
157 for (uint32_t i = 0; i < it.ColumnCount(); i++)
158 fprintf(stderr, "%40s ", "----------------------------------------");
159 fprintf(stderr, "\n");
160
161 first = false;
162 }
163
164 for (uint32_t c = 0; c < it.ColumnCount(); c++) {
165 auto value = it.Get(c);
166 switch (value.type) {
167 case SqlValue::Type::kNull:
168 fprintf(stderr, "%-40.40s", "[NULL]");
169 break;
170 case SqlValue::Type::kDouble:
171 fprintf(stderr, "%40f", value.double_value);
172 break;
173 case SqlValue::Type::kLong:
174 fprintf(stderr, "%40" PRIi64, value.long_value);
175 break;
176 case SqlValue::Type::kString:
177 fprintf(stderr, "%-40.40s", value.string_value);
178 break;
Lalit Maganti62211072019-05-10 14:09:58 +0100179 case SqlValue::Type::kBytes:
180 printf("%-40.40s", "<raw bytes>");
181 break;
Lalit Maganti49ae9012019-04-03 16:10:39 +0100182 }
183 fprintf(stderr, " ");
184 }
185 fprintf(stderr, "\n");
186 }
187
Lalit Maganti1f067182019-05-09 14:50:05 +0100188 util::Status status = it.Status();
189 if (!status.ok()) {
190 PERFETTO_ELOG("Error while iterating stats %s", status.c_message());
Lalit Maganti49ae9012019-04-03 16:10:39 +0100191 return false;
192 }
193 return true;
194}
195
Sami Kyostila33668942018-11-13 16:33:32 +0000196int ExportTraceToDatabase(const std::string& output_name) {
197 PERFETTO_CHECK(output_name.find("'") == std::string::npos);
198 {
199 base::ScopedFile fd(base::OpenFile(output_name, O_CREAT | O_RDWR, 0600));
200 if (!fd) {
201 PERFETTO_PLOG("Failed to create file: %s", output_name.c_str());
202 return 1;
203 }
204 int res = ftruncate(fd.get(), 0);
205 PERFETTO_CHECK(res == 0);
206 }
207
Sami Kyostila33668942018-11-13 16:33:32 +0000208 std::string attach_sql =
209 "ATTACH DATABASE '" + output_name + "' AS perfetto_export";
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100210 auto attach_it = g_tp->ExecuteQuery(attach_sql);
Lalit Magantic72cea22019-04-08 12:29:15 +0100211 bool attach_has_more = attach_it.Next();
212 PERFETTO_DCHECK(!attach_has_more);
Lalit Maganti1f067182019-05-09 14:50:05 +0100213
214 util::Status status = attach_it.Status();
215 if (!status.ok()) {
216 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100217 return 1;
218 }
Sami Kyostila33668942018-11-13 16:33:32 +0000219
Lalit Magantic72cea22019-04-08 12:29:15 +0100220 auto tables_it = g_tp->ExecuteQuery(
221 "SELECT name FROM perfetto_tables UNION "
222 "SELECT name FROM sqlite_master WHERE type='table'");
223 for (uint32_t rows = 0; tables_it.Next(); rows++) {
224 std::string table_name = tables_it.Get(0).string_value;
225 PERFETTO_CHECK(table_name.find("'") == std::string::npos);
226 std::string export_sql = "CREATE TABLE perfetto_export." + table_name +
227 " AS SELECT * FROM " + table_name;
228
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100229 auto export_it = g_tp->ExecuteQuery(export_sql);
Lalit Magantic72cea22019-04-08 12:29:15 +0100230 bool export_has_more = export_it.Next();
231 PERFETTO_DCHECK(!export_has_more);
Lalit Maganti1f067182019-05-09 14:50:05 +0100232
233 status = export_it.Status();
234 if (!status.ok()) {
235 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100236 return 1;
237 }
238 }
Lalit Maganti1f067182019-05-09 14:50:05 +0100239 status = tables_it.Status();
240 if (!status.ok()) {
241 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100242 return 1;
243 }
244
245 auto detach_it = g_tp->ExecuteQuery("DETACH DATABASE perfetto_export");
246 bool detach_has_more = attach_it.Next();
247 PERFETTO_DCHECK(!detach_has_more);
Lalit Maganti1f067182019-05-09 14:50:05 +0100248 status = detach_it.Status();
249 if (!status.ok()) {
250 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100251 return 1;
252 }
Sami Kyostila33668942018-11-13 16:33:32 +0000253 return 0;
254}
255
Lalit Maganti7c959782019-04-02 16:54:12 +0100256int RunMetrics(const std::vector<std::string>& metric_names) {
Lalit Magantid9f86b62019-04-08 11:11:51 +0100257 std::vector<uint8_t> metric_result;
Lalit Magantid71a9452019-05-09 15:13:24 +0100258 util::Status status = g_tp->ComputeMetric(metric_names, &metric_result);
259 if (!status.ok()) {
260 PERFETTO_ELOG("Error when computing metrics: %s", status.c_message());
Lalit Magantid9f86b62019-04-08 11:11:51 +0100261 return 1;
Lalit Maganti7c959782019-04-02 16:54:12 +0100262 }
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100263 fwrite(metric_result.data(), sizeof(uint8_t), metric_result.size(), stdout);
Lalit Maganti7c959782019-04-02 16:54:12 +0100264 return 0;
265}
266
Lalit Magantic72cea22019-04-08 12:29:15 +0100267void PrintQueryResultInteractively(TraceProcessor::Iterator* it,
268 base::TimeNanos t_start) {
269 base::TimeNanos t_end = t_start;
270 for (uint32_t rows = 0; it->Next(); rows++) {
271 if (rows % 32 == 0) {
272 if (rows > 0) {
Primiano Tucci5968caf2018-08-06 10:31:46 +0100273 fprintf(stderr, "...\nType 'q' to stop, Enter for more records: ");
274 fflush(stderr);
275 char input[32];
276 if (!fgets(input, sizeof(input) - 1, stdin))
277 exit(0);
278 if (input[0] == 'q')
279 break;
Lalit Magantic72cea22019-04-08 12:29:15 +0100280 } else {
Lalit Magantiaac2f652019-04-30 12:16:21 +0100281 t_end = base::GetWallTimeNs();
Primiano Tucci5968caf2018-08-06 10:31:46 +0100282 }
Lalit Magantic72cea22019-04-08 12:29:15 +0100283 for (uint32_t i = 0; i < it->ColumnCount(); i++)
284 printf("%20s ", it->GetColumName(i).c_str());
Primiano Tucci5968caf2018-08-06 10:31:46 +0100285 printf("\n");
286
Lalit Magantic72cea22019-04-08 12:29:15 +0100287 for (uint32_t i = 0; i < it->ColumnCount(); i++)
Primiano Tucci5968caf2018-08-06 10:31:46 +0100288 printf("%20s ", "--------------------");
289 printf("\n");
290 }
291
Lalit Magantic72cea22019-04-08 12:29:15 +0100292 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
293 auto value = it->Get(c);
294 switch (value.type) {
295 case SqlValue::Type::kNull:
296 printf("%-20.20s", "[NULL]");
297 break;
298 case SqlValue::Type::kDouble:
299 printf("%20f", value.double_value);
300 break;
301 case SqlValue::Type::kLong:
302 printf("%20" PRIi64, value.long_value);
303 break;
304 case SqlValue::Type::kString:
305 printf("%-20.20s", value.string_value);
306 break;
Lalit Maganti62211072019-05-10 14:09:58 +0100307 case SqlValue::Type::kBytes:
308 printf("%-20.20s", "<raw bytes>");
309 break;
Lalit Maganti21160e82018-10-16 09:40:29 +0100310 }
311 printf(" ");
312 }
313 printf("\n");
314 }
Lalit Magantic72cea22019-04-08 12:29:15 +0100315
Lalit Maganti1f067182019-05-09 14:50:05 +0100316 util::Status status = it->Status();
317 if (!status.ok()) {
318 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100319 }
Lalit Maganti21160e82018-10-16 09:40:29 +0100320 printf("\nQuery executed in %.3f ms\n\n", (t_end - t_start).count() / 1E6);
321}
Sami Kyostila33668942018-11-13 16:33:32 +0000322
323void PrintShellUsage() {
324 PERFETTO_ELOG(
325 "Available commands:\n"
326 ".quit, .q Exit the shell.\n"
327 ".help This text.\n"
328 ".dump FILE Export the trace as a sqlite database.\n");
329}
330
Lalit Maganti21160e82018-10-16 09:40:29 +0100331int StartInteractiveShell() {
332 SetupLineEditor();
Primiano Tuccib75dcee2018-08-08 12:21:36 +0100333
Lalit Maganti21160e82018-10-16 09:40:29 +0100334 for (;;) {
335 char* line = GetLine("> ");
Sami Kyostila33668942018-11-13 16:33:32 +0000336 if (!line)
Lalit Maganti21160e82018-10-16 09:40:29 +0100337 break;
338 if (strcmp(line, "") == 0)
339 continue;
Sami Kyostila33668942018-11-13 16:33:32 +0000340 if (line[0] == '.') {
341 char command[32] = {};
342 char arg[1024] = {};
343 sscanf(line + 1, "%31s %1023s", command, arg);
344 if (strcmp(command, "quit") == 0 || strcmp(command, "q") == 0) {
345 break;
346 } else if (strcmp(command, "help") == 0) {
347 PrintShellUsage();
348 } else if (strcmp(command, "dump") == 0 && strlen(arg)) {
349 if (ExportTraceToDatabase(arg) != 0)
350 PERFETTO_ELOG("Database export failed");
351 } else {
352 PrintShellUsage();
353 }
354 continue;
355 }
Lalit Magantic72cea22019-04-08 12:29:15 +0100356
Lalit Maganti21160e82018-10-16 09:40:29 +0100357 base::TimeNanos t_start = base::GetWallTimeNs();
Lalit Magantic72cea22019-04-08 12:29:15 +0100358 auto it = g_tp->ExecuteQuery(line);
359 PrintQueryResultInteractively(&it, t_start);
Lalit Maganti21160e82018-10-16 09:40:29 +0100360
361 FreeLine(line);
362 }
363 return 0;
364}
365
Lalit Magantic72cea22019-04-08 12:29:15 +0100366void PrintQueryResultAsCsv(TraceProcessor::Iterator* it, FILE* output) {
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100367 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
368 if (c > 0)
369 fprintf(output, ",");
370 fprintf(output, "\"%s\"", it->GetColumName(c).c_str());
371 }
372 fprintf(output, "\n");
Lalit Maganti21160e82018-10-16 09:40:29 +0100373
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100374 for (uint32_t rows = 0; it->Next(); rows++) {
Lalit Magantic72cea22019-04-08 12:29:15 +0100375 for (uint32_t c = 0; c < it->ColumnCount(); c++) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100376 if (c > 0)
377 fprintf(output, ",");
Lalit Magantib55c8842018-12-13 14:26:08 +0000378
Lalit Magantic72cea22019-04-08 12:29:15 +0100379 auto value = it->Get(c);
380 switch (value.type) {
381 case SqlValue::Type::kNull:
382 fprintf(output, "\"%s\"", "[NULL]");
383 break;
384 case SqlValue::Type::kDouble:
385 fprintf(output, "%f", value.double_value);
386 break;
387 case SqlValue::Type::kLong:
388 fprintf(output, "%" PRIi64, value.long_value);
389 break;
390 case SqlValue::Type::kString:
391 fprintf(output, "\"%s\"", value.string_value);
392 break;
Lalit Maganti62211072019-05-10 14:09:58 +0100393 case SqlValue::Type::kBytes:
394 fprintf(output, "\"%s\"", "<raw bytes>");
395 break;
Primiano Tucci5968caf2018-08-06 10:31:46 +0100396 }
397 }
Lalit Magantib55c8842018-12-13 14:26:08 +0000398 fprintf(output, "\n");
Primiano Tucci5968caf2018-08-06 10:31:46 +0100399 }
Lalit Maganti21160e82018-10-16 09:40:29 +0100400}
401
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000402bool LoadQueries(FILE* input, std::vector<std::string>* output) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100403 char buffer[4096];
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000404 while (!feof(input) && !ferror(input)) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100405 std::string sql_query;
406 while (fgets(buffer, sizeof(buffer), input)) {
407 if (strncmp(buffer, "\n", sizeof(buffer)) == 0)
408 break;
409 sql_query.append(buffer);
410 }
411 if (sql_query.back() == '\n')
412 sql_query.resize(sql_query.size() - 1);
Lalit Maganticbccb0a2018-12-04 20:14:42 +0000413
414 // If we have a new line at the end of the file or an extra new line
415 // somewhere in the file, we'll end up with an empty query which we should
416 // just ignore.
417 if (sql_query.empty())
418 continue;
419
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000420 output->push_back(sql_query);
421 }
422 if (ferror(input)) {
423 PERFETTO_ELOG("Error reading query file");
424 return false;
425 }
426 return true;
427}
428
429bool RunQueryAndPrintResult(const std::vector<std::string> queries,
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000430 FILE* output) {
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000431 bool is_first_query = true;
432 bool is_query_error = false;
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000433 bool has_output = false;
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000434 for (const auto& sql_query : queries) {
435 // Add an extra newline separator between query results.
436 if (!is_first_query)
437 fprintf(output, "\n");
438 is_first_query = false;
439
Lalit Maganti21160e82018-10-16 09:40:29 +0100440 PERFETTO_ILOG("Executing query: %s", sql_query.c_str());
441
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100442 auto it = g_tp->ExecuteQuery(sql_query);
Lalit Maganti1f067182019-05-09 14:50:05 +0100443 util::Status status = it.Status();
444 if (!status.ok()) {
445 PERFETTO_ELOG("SQLite error: %s", status.c_message());
Lalit Magantic72cea22019-04-08 12:29:15 +0100446 is_query_error = true;
447 break;
448 }
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100449 if (it.ColumnCount() == 0) {
450 bool it_has_more = it.Next();
451 PERFETTO_DCHECK(!it_has_more);
452 continue;
453 }
454
455 if (has_output) {
Lalit Magantic72cea22019-04-08 12:29:15 +0100456 PERFETTO_ELOG(
457 "More than one query generated result rows. This is "
458 "unsupported.");
459 is_query_error = true;
460 break;
461 }
462 PrintQueryResultAsCsv(&it, output);
Lalit Maganti73b1a0a2019-04-08 13:51:14 +0100463 has_output = true;
Lalit Maganti21160e82018-10-16 09:40:29 +0100464 }
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000465 return !is_query_error;
Lalit Maganti21160e82018-10-16 09:40:29 +0100466}
467
Lalit Maganti260e04d2019-05-03 13:17:01 +0100468int MaybePrintPerfFile(const char* perf_file_path,
469 base::TimeNanos t_load,
470 base::TimeNanos t_run) {
471 if (!perf_file_path)
472 return 0;
473
474 char buf[128];
475 int count = snprintf(buf, sizeof(buf), "%" PRId64 ",%" PRId64,
476 static_cast<int64_t>(t_load.count()),
477 static_cast<int64_t>(t_run.count()));
478 if (count < 0) {
479 PERFETTO_ELOG("Failed to write perf data");
480 return 1;
481 }
482
483 auto fd(base::OpenFile(perf_file_path, O_WRONLY | O_CREAT | O_TRUNC, 066));
484 if (!fd) {
485 PERFETTO_ELOG("Failed to open perf file");
486 return 1;
487 }
488 base::WriteAll(fd.get(), buf, static_cast<size_t>(count));
489 return 0;
490}
491
Lalit Maganti21160e82018-10-16 09:40:29 +0100492void PrintUsage(char** argv) {
Sami Kyostila33668942018-11-13 16:33:32 +0000493 PERFETTO_ELOG(
494 "Interactive trace processor shell.\n"
495 "Usage: %s [OPTIONS] trace_file.pb\n\n"
496 "Options:\n"
Lalit Maganti7c959782019-04-02 16:54:12 +0100497 " -d Enable virtual table debugging.\n"
Lalit Maganti260e04d2019-05-03 13:17:01 +0100498 " -p FILE Writes the time taken to ingest the trace and"
499 "execute the queries to the given file. Only valid with -q or "
500 "--run-metrics and the file will only be written if the execution is "
501 "successful\n"
Lalit Maganti7c959782019-04-02 16:54:12 +0100502 " -s FILE Read and execute contents of file before "
503 "launching an interactive shell.\n"
504 " -q FILE Read and execute an SQL query from a file.\n"
505 " -e FILE Export the trace into a SQLite database.\n"
506 " --run-metrics x,y,z Runs a comma separated list of metrics and "
507 "prints the result as a TraceMetrics proto to stdout.\n",
Sami Kyostila33668942018-11-13 16:33:32 +0000508 argv[0]);
Primiano Tucci5968caf2018-08-06 10:31:46 +0100509}
510
Lalit Maganti1ebebf12018-10-15 17:24:04 +0100511int TraceProcessorMain(int argc, char** argv) {
Primiano Tucci5968caf2018-08-06 10:31:46 +0100512 if (argc < 2) {
Lalit Maganti21160e82018-10-16 09:40:29 +0100513 PrintUsage(argv);
Primiano Tucci5968caf2018-08-06 10:31:46 +0100514 return 1;
515 }
Lalit Maganti260e04d2019-05-03 13:17:01 +0100516 const char* perf_file_path = nullptr;
Primiano Tuccib2ea4d42018-08-21 15:05:13 +0200517 const char* trace_file_path = nullptr;
Lalit Maganti21160e82018-10-16 09:40:29 +0100518 const char* query_file_path = nullptr;
Sami Kyostila33668942018-11-13 16:33:32 +0000519 const char* sqlite_file_path = nullptr;
Lalit Maganti7c959782019-04-02 16:54:12 +0100520 const char* metric_names = nullptr;
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000521 bool launch_shell = true;
Primiano Tuccib2ea4d42018-08-21 15:05:13 +0200522 for (int i = 1; i < argc; i++) {
Primiano Tucci00da64a2019-02-22 14:51:10 +0000523 if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
524 printf("%s\n", PERFETTO_GET_GIT_REVISION());
525 exit(0);
526 }
Primiano Tuccib2ea4d42018-08-21 15:05:13 +0200527 if (strcmp(argv[i], "-d") == 0) {
528 EnableSQLiteVtableDebugging();
529 continue;
Lalit Maganti260e04d2019-05-03 13:17:01 +0100530 } else if (strcmp(argv[i], "-p") == 0) {
531 if (++i == argc) {
532 PrintUsage(argv);
533 return 1;
534 }
535 perf_file_path = argv[i];
536 continue;
537 } else if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "-s") == 0) {
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000538 launch_shell = strcmp(argv[i], "-s") == 0;
Lalit Maganti21160e82018-10-16 09:40:29 +0100539 if (++i == argc) {
540 PrintUsage(argv);
541 return 1;
542 }
543 query_file_path = argv[i];
544 continue;
Sami Kyostila33668942018-11-13 16:33:32 +0000545 } else if (strcmp(argv[i], "-e") == 0) {
546 if (++i == argc) {
547 PrintUsage(argv);
548 return 1;
549 }
550 sqlite_file_path = argv[i];
551 continue;
Lalit Maganti7c959782019-04-02 16:54:12 +0100552 } else if (strcmp(argv[i], "--run-metrics") == 0) {
553 if (++i == argc) {
554 PrintUsage(argv);
555 return 1;
556 }
Lalit Maganti260e04d2019-05-03 13:17:01 +0100557 launch_shell = false;
Lalit Maganti7c959782019-04-02 16:54:12 +0100558 metric_names = argv[i];
559 continue;
Sami Kyostila33668942018-11-13 16:33:32 +0000560 } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
561 PrintUsage(argv);
562 return 0;
563 } else if (argv[i][0] == '-') {
564 PERFETTO_ELOG("Unknown option: %s", argv[i]);
565 return 1;
Lalit Maganti21160e82018-10-16 09:40:29 +0100566 }
Primiano Tuccib2ea4d42018-08-21 15:05:13 +0200567 trace_file_path = argv[i];
568 }
Primiano Tucci5968caf2018-08-06 10:31:46 +0100569
Lalit Maganti21160e82018-10-16 09:40:29 +0100570 if (trace_file_path == nullptr) {
571 PrintUsage(argv);
572 return 1;
573 }
574
Lalit Maganti260e04d2019-05-03 13:17:01 +0100575 // Only allow non-interactive queries to emit perf data.
576 if (perf_file_path && launch_shell) {
577 PrintUsage(argv);
578 return 1;
579 }
580
Primiano Tucci7e330292018-08-24 19:10:52 +0200581 // Load the trace file into the trace processor.
Ioannis Ilkoseff38f52018-10-29 10:37:55 +0000582 Config config;
Ioannis Ilkosf8371292018-11-05 16:47:35 +0000583 std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
Florian Mayerb03fd282018-10-03 16:05:16 +0100584 base::ScopedFile fd(base::OpenFile(trace_file_path, O_RDONLY));
Lalit Maganti22a1e0c2018-12-04 20:28:18 +0000585 if (!fd) {
586 PERFETTO_ELOG("Could not open trace file (path: %s)", trace_file_path);
587 return 1;
588 }
Primiano Tucci7e330292018-08-24 19:10:52 +0200589
590 // Load the trace in chunks using async IO. We create a simple pipeline where,
591 // at each iteration, we parse the current chunk and asynchronously start
592 // reading the next chunk.
593
594 // 1MB chunk size seems the best tradeoff on a MacBook Pro 2013 - i7 2.8 GHz.
595 constexpr size_t kChunkSize = 1024 * 1024;
596 struct aiocb cb {};
597 cb.aio_nbytes = kChunkSize;
598 cb.aio_fildes = *fd;
599
Florian Mayere37c6202018-10-18 12:07:59 +0100600 std::unique_ptr<uint8_t[]> aio_buf(new uint8_t[kChunkSize]);
Lalit Maganti9e6616e2019-04-03 13:19:44 +0100601#if defined(MEMORY_SANITIZER)
602 // Just initialize the memory to make the memory sanitizer happy as it
603 // cannot track aio calls below.
604 memset(aio_buf.get(), 0, kChunkSize);
605#endif
Florian Mayere37c6202018-10-18 12:07:59 +0100606 cb.aio_buf = aio_buf.get();
Primiano Tucci7e330292018-08-24 19:10:52 +0200607
608 PERFETTO_CHECK(aio_read(&cb) == 0);
609 struct aiocb* aio_list[1] = {&cb};
610
611 uint64_t file_size = 0;
Lalit Maganti260e04d2019-05-03 13:17:01 +0100612 auto t_load_start = base::GetWallTimeNs();
Primiano Tucci7e330292018-08-24 19:10:52 +0200613 for (int i = 0;; i++) {
614 if (i % 128 == 0)
615 fprintf(stderr, "\rLoading trace: %.2f MB\r", file_size / 1E6);
616
617 // Block waiting for the pending read to complete.
618 PERFETTO_CHECK(aio_suspend(aio_list, 1, nullptr) == 0);
619 auto rsize = aio_return(&cb);
620 if (rsize <= 0)
621 break;
622 file_size += static_cast<uint64_t>(rsize);
623
624 // Take ownership of the completed buffer and enqueue a new async read
625 // with a fresh buffer.
Florian Mayere37c6202018-10-18 12:07:59 +0100626 std::unique_ptr<uint8_t[]> buf(std::move(aio_buf));
627 aio_buf.reset(new uint8_t[kChunkSize]);
Lalit Maganti9e6616e2019-04-03 13:19:44 +0100628#if defined(MEMORY_SANITIZER)
629 // Just initialize the memory to make the memory sanitizer happy as it
630 // cannot track aio calls below.
631 memset(aio_buf.get(), 0, kChunkSize);
632#endif
Florian Mayere37c6202018-10-18 12:07:59 +0100633 cb.aio_buf = aio_buf.get();
Primiano Tucci7e330292018-08-24 19:10:52 +0200634 cb.aio_offset += rsize;
635 PERFETTO_CHECK(aio_read(&cb) == 0);
636
637 // Parse the completed buffer while the async read is in-flight.
Lalit Maganti1f067182019-05-09 14:50:05 +0100638 util::Status status = tp->Parse(std::move(buf), static_cast<size_t>(rsize));
639 if (PERFETTO_UNLIKELY(!status.ok())) {
640 PERFETTO_ELOG("Fatal error while parsing trace: %s", status.c_message());
Lalit Maganti4b2b2532019-05-09 11:03:23 +0100641 return 1;
Lalit Maganti1f067182019-05-09 14:50:05 +0100642 }
Primiano Tucci7e330292018-08-24 19:10:52 +0200643 }
Ioannis Ilkosf8371292018-11-05 16:47:35 +0000644 tp->NotifyEndOfFile();
Lalit Maganti260e04d2019-05-03 13:17:01 +0100645
646 auto t_load = base::GetWallTimeNs() - t_load_start;
647 double t_load_s = t_load.count() / 1E9;
Primiano Tucci7e330292018-08-24 19:10:52 +0200648 double size_mb = file_size / 1E6;
Lalit Maganti260e04d2019-05-03 13:17:01 +0100649 PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb,
650 size_mb / t_load_s);
Ioannis Ilkosf8371292018-11-05 16:47:35 +0000651 g_tp = tp.get();
Primiano Tucci5968caf2018-08-06 10:31:46 +0100652
Primiano Tucci24647142018-08-13 21:18:24 +0200653#if PERFETTO_HAS_SIGNAL_H()
Primiano Tucci7e330292018-08-24 19:10:52 +0200654 signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
Primiano Tucci24647142018-08-13 21:18:24 +0200655#endif
Primiano Tucci5968caf2018-08-06 10:31:46 +0100656
Lalit Maganti49ae9012019-04-03 16:10:39 +0100657 // Print out the stats to stderr for the trace.
658 if (!PrintStats()) {
659 return 1;
660 }
661
Lalit Maganti260e04d2019-05-03 13:17:01 +0100662 auto t_run_start = base::GetWallTimeNs();
663
Lalit Maganti7c959782019-04-02 16:54:12 +0100664 // First, see if we have some metrics to run. If we do, just run them and
665 // return.
666 if (metric_names) {
667 std::vector<std::string> metrics;
668 for (base::StringSplitter ss(metric_names, ','); ss.Next();) {
669 metrics.emplace_back(ss.cur_token());
670 }
Lalit Maganti260e04d2019-05-03 13:17:01 +0100671 int ret = RunMetrics(std::move(metrics));
672 if (!ret) {
673 auto t_query = base::GetWallTimeNs() - t_run_start;
674 ret = MaybePrintPerfFile(perf_file_path, t_load, t_query);
675 }
676 return ret;
Lalit Maganti7c959782019-04-02 16:54:12 +0100677 }
678
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000679 // If we were given a query file, load contents
680 std::vector<std::string> queries;
Sami Kyostila33668942018-11-13 16:33:32 +0000681 if (query_file_path) {
682 base::ScopedFstream file(fopen(query_file_path, "r"));
Lalit Maganti22a1e0c2018-12-04 20:28:18 +0000683 if (!file) {
684 PERFETTO_ELOG("Could not open query file (path: %s)", query_file_path);
685 return 1;
686 }
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000687 if (!LoadQueries(file.get(), &queries)) {
688 return 1;
689 }
690 }
691
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000692 if (!RunQueryAndPrintResult(queries, stdout)) {
Lalit Maganti77f1dbe2019-02-27 12:58:01 +0000693 return 1;
Primiano Tucci7e330292018-08-24 19:10:52 +0200694 }
Hector Dearmane44ad452018-09-21 11:51:57 +0100695
Ioannis Ilkos42dafcc2019-02-08 17:35:27 +0000696 if (sqlite_file_path) {
Sami Kyostila33668942018-11-13 16:33:32 +0000697 return ExportTraceToDatabase(sqlite_file_path);
698 }
699
Ioannis Ilkos433c8e52019-02-11 12:52:35 +0000700 if (!launch_shell) {
Lalit Maganti260e04d2019-05-03 13:17:01 +0100701 auto t_query = base::GetWallTimeNs() - t_run_start;
702 return MaybePrintPerfFile(perf_file_path, t_load, t_query);
Sami Kyostila33668942018-11-13 16:33:32 +0000703 }
704
Sami Kyostila33668942018-11-13 16:33:32 +0000705 return StartInteractiveShell();
Primiano Tucci5968caf2018-08-06 10:31:46 +0100706}
Lalit Maganti1ebebf12018-10-15 17:24:04 +0100707
708} // namespace
709
710} // namespace trace_processor
711} // namespace perfetto
712
713int main(int argc, char** argv) {
714 return perfetto::trace_processor::TraceProcessorMain(argc, argv);
715}