Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 1 | /* |
| 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 Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 17 | #include <aio.h> |
| 18 | #include <fcntl.h> |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 19 | #include <getopt.h> |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 20 | #include <inttypes.h> |
Lalit Maganti | 26f69bd | 2019-04-29 18:23:47 +0100 | [diff] [blame] | 21 | #include <stdio.h> |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 22 | #include <sys/stat.h> |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 23 | #include <unistd.h> |
| 24 | |
| 25 | #include <functional> |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 26 | #include <iostream> |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 27 | #include <vector> |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 28 | |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 29 | #include "perfetto/base/build_config.h" |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 30 | #include "perfetto/base/file_utils.h" |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 31 | #include "perfetto/base/logging.h" |
Ioannis Ilkos | eff38f5 | 2018-10-29 10:37:55 +0000 | [diff] [blame] | 32 | #include "perfetto/base/scoped_file.h" |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 33 | #include "perfetto/base/string_splitter.h" |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 34 | #include "perfetto/base/time.h" |
Ioannis Ilkos | eff38f5 | 2018-10-29 10:37:55 +0000 | [diff] [blame] | 35 | #include "perfetto/trace_processor/trace_processor.h" |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 36 | |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 37 | #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 Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 45 | #if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD) |
| 46 | #include <linenoise.h> |
Hector Dearman | 36adc82 | 2018-10-02 11:35:08 +0100 | [diff] [blame] | 47 | #include <pwd.h> |
| 48 | #include <sys/types.h> |
Primiano Tucci | 00da64a | 2019-02-22 14:51:10 +0000 | [diff] [blame] | 49 | #include "perfetto_version.gen.h" |
| 50 | #else |
| 51 | #define PERFETTO_GET_GIT_REVISION() "unknown" |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 52 | #endif |
| 53 | |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 54 | #if PERFETTO_HAS_SIGNAL_H() |
| 55 | #include <signal.h> |
| 56 | #endif |
| 57 | |
Lalit Maganti | 1ebebf1 | 2018-10-15 17:24:04 +0100 | [diff] [blame] | 58 | namespace perfetto { |
| 59 | namespace trace_processor { |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 60 | |
| 61 | namespace { |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 62 | TraceProcessor* g_tp; |
| 63 | |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 64 | #if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD) |
| 65 | |
Hector Dearman | 36adc82 | 2018-10-02 11:35:08 +0100 | [diff] [blame] | 66 | bool EnsureDir(const std::string& path) { |
| 67 | return mkdir(path.c_str(), 0755) != -1 || errno == EEXIST; |
| 68 | } |
| 69 | |
| 70 | bool EnsureFile(const std::string& path) { |
Primiano Tucci | f675dc2 | 2018-10-18 00:17:26 +0200 | [diff] [blame] | 71 | return base::OpenFile(path, O_RDONLY | O_CREAT, 0644).get() != -1; |
Hector Dearman | 36adc82 | 2018-10-02 11:35:08 +0100 | [diff] [blame] | 72 | } |
| 73 | |
| 74 | std::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 | |
| 83 | std::string GetPerfettoPath() { |
| 84 | std::string config = GetConfigPath(); |
| 85 | if (config == "") |
| 86 | return ""; |
| 87 | return config + "/perfetto"; |
| 88 | } |
| 89 | |
| 90 | std::string GetHistoryPath() { |
| 91 | std::string perfetto = GetPerfettoPath(); |
| 92 | if (perfetto == "") |
| 93 | return ""; |
| 94 | return perfetto + "/.trace_processor_shell_history"; |
| 95 | } |
| 96 | |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 97 | void SetupLineEditor() { |
| 98 | linenoiseSetMultiLine(true); |
| 99 | linenoiseHistorySetMaxLen(1000); |
Hector Dearman | 36adc82 | 2018-10-02 11:35:08 +0100 | [diff] [blame] | 100 | |
| 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 Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 109 | } |
| 110 | |
Hector Dearman | 6935644 | 2018-09-21 14:10:57 +0100 | [diff] [blame] | 111 | void FreeLine(char* line) { |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 112 | linenoiseHistoryAdd(line); |
Hector Dearman | 36adc82 | 2018-10-02 11:35:08 +0100 | [diff] [blame] | 113 | linenoiseHistorySave(GetHistoryPath().c_str()); |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 114 | linenoiseFree(line); |
| 115 | } |
| 116 | |
| 117 | char* GetLine(const char* prompt) { |
| 118 | return linenoise(prompt); |
| 119 | } |
| 120 | |
| 121 | #else |
| 122 | |
| 123 | void SetupLineEditor() {} |
| 124 | |
| 125 | void FreeLine(char* line) { |
Greg Kaiser | bc635a5 | 2018-09-24 06:12:09 -0700 | [diff] [blame] | 126 | delete[] line; |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 127 | } |
| 128 | |
| 129 | char* 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 Maganti | 49ae901 | 2019-04-03 16:10:39 +0100 | [diff] [blame] | 144 | bool 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 Maganti | 6221107 | 2019-05-10 14:09:58 +0100 | [diff] [blame] | 180 | case SqlValue::Type::kBytes: |
| 181 | printf("%-40.40s", "<raw bytes>"); |
| 182 | break; |
Lalit Maganti | 49ae901 | 2019-04-03 16:10:39 +0100 | [diff] [blame] | 183 | } |
| 184 | fprintf(stderr, " "); |
| 185 | } |
| 186 | fprintf(stderr, "\n"); |
| 187 | } |
| 188 | |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 189 | util::Status status = it.Status(); |
| 190 | if (!status.ok()) { |
| 191 | PERFETTO_ELOG("Error while iterating stats %s", status.c_message()); |
Lalit Maganti | 49ae901 | 2019-04-03 16:10:39 +0100 | [diff] [blame] | 192 | return false; |
| 193 | } |
| 194 | return true; |
| 195 | } |
| 196 | |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 197 | int 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 Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 209 | std::string attach_sql = |
| 210 | "ATTACH DATABASE '" + output_name + "' AS perfetto_export"; |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 211 | auto attach_it = g_tp->ExecuteQuery(attach_sql); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 212 | bool attach_has_more = attach_it.Next(); |
| 213 | PERFETTO_DCHECK(!attach_has_more); |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 214 | |
| 215 | util::Status status = attach_it.Status(); |
| 216 | if (!status.ok()) { |
| 217 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 218 | return 1; |
| 219 | } |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 220 | |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 221 | 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 Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 230 | auto export_it = g_tp->ExecuteQuery(export_sql); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 231 | bool export_has_more = export_it.Next(); |
| 232 | PERFETTO_DCHECK(!export_has_more); |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 233 | |
| 234 | status = export_it.Status(); |
| 235 | if (!status.ok()) { |
| 236 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 237 | return 1; |
| 238 | } |
| 239 | } |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 240 | status = tables_it.Status(); |
| 241 | if (!status.ok()) { |
| 242 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 243 | 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 Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 249 | status = detach_it.Status(); |
| 250 | if (!status.ok()) { |
| 251 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 252 | return 1; |
| 253 | } |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 254 | return 0; |
| 255 | } |
| 256 | |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 257 | int RunMetrics(const std::vector<std::string>& metric_names) { |
Lalit Maganti | d9f86b6 | 2019-04-08 11:11:51 +0100 | [diff] [blame] | 258 | std::vector<uint8_t> metric_result; |
Lalit Maganti | d71a945 | 2019-05-09 15:13:24 +0100 | [diff] [blame] | 259 | 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 Maganti | d9f86b6 | 2019-04-08 11:11:51 +0100 | [diff] [blame] | 262 | return 1; |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 263 | } |
Lalit Maganti | 26f69bd | 2019-04-29 18:23:47 +0100 | [diff] [blame] | 264 | fwrite(metric_result.data(), sizeof(uint8_t), metric_result.size(), stdout); |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 265 | return 0; |
| 266 | } |
| 267 | |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 268 | void 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 Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 274 | 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 Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 281 | } else { |
Lalit Maganti | aac2f65 | 2019-04-30 12:16:21 +0100 | [diff] [blame] | 282 | t_end = base::GetWallTimeNs(); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 283 | } |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 284 | for (uint32_t i = 0; i < it->ColumnCount(); i++) |
| 285 | printf("%20s ", it->GetColumName(i).c_str()); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 286 | printf("\n"); |
| 287 | |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 288 | for (uint32_t i = 0; i < it->ColumnCount(); i++) |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 289 | printf("%20s ", "--------------------"); |
| 290 | printf("\n"); |
| 291 | } |
| 292 | |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 293 | 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 Maganti | 6221107 | 2019-05-10 14:09:58 +0100 | [diff] [blame] | 308 | case SqlValue::Type::kBytes: |
| 309 | printf("%-20.20s", "<raw bytes>"); |
| 310 | break; |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 311 | } |
| 312 | printf(" "); |
| 313 | } |
| 314 | printf("\n"); |
| 315 | } |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 316 | |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 317 | util::Status status = it->Status(); |
| 318 | if (!status.ok()) { |
| 319 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 320 | } |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 321 | printf("\nQuery executed in %.3f ms\n\n", (t_end - t_start).count() / 1E6); |
| 322 | } |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 323 | |
| 324 | void 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 Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 332 | int StartInteractiveShell() { |
| 333 | SetupLineEditor(); |
Primiano Tucci | b75dcee | 2018-08-08 12:21:36 +0100 | [diff] [blame] | 334 | |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 335 | for (;;) { |
| 336 | char* line = GetLine("> "); |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 337 | if (!line) |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 338 | break; |
| 339 | if (strcmp(line, "") == 0) |
| 340 | continue; |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 341 | 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 Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 357 | |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 358 | base::TimeNanos t_start = base::GetWallTimeNs(); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 359 | auto it = g_tp->ExecuteQuery(line); |
| 360 | PrintQueryResultInteractively(&it, t_start); |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 361 | |
| 362 | FreeLine(line); |
| 363 | } |
| 364 | return 0; |
| 365 | } |
| 366 | |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 367 | void PrintQueryResultAsCsv(TraceProcessor::Iterator* it, FILE* output) { |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 368 | 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 Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 374 | |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 375 | for (uint32_t rows = 0; it->Next(); rows++) { |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 376 | for (uint32_t c = 0; c < it->ColumnCount(); c++) { |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 377 | if (c > 0) |
| 378 | fprintf(output, ","); |
Lalit Maganti | b55c884 | 2018-12-13 14:26:08 +0000 | [diff] [blame] | 379 | |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 380 | 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 Maganti | 6221107 | 2019-05-10 14:09:58 +0100 | [diff] [blame] | 394 | case SqlValue::Type::kBytes: |
| 395 | fprintf(output, "\"%s\"", "<raw bytes>"); |
| 396 | break; |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 397 | } |
| 398 | } |
Lalit Maganti | b55c884 | 2018-12-13 14:26:08 +0000 | [diff] [blame] | 399 | fprintf(output, "\n"); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 400 | } |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 401 | } |
| 402 | |
Florian Mayer | 3c74243 | 2019-05-21 11:35:47 +0100 | [diff] [blame^] | 403 | bool 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 Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 412 | bool LoadQueries(FILE* input, std::vector<std::string>* output) { |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 413 | char buffer[4096]; |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 414 | while (!feof(input) && !ferror(input)) { |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 415 | std::string sql_query; |
| 416 | while (fgets(buffer, sizeof(buffer), input)) { |
Florian Mayer | 3c74243 | 2019-05-21 11:35:47 +0100 | [diff] [blame^] | 417 | if (IsBlankLine(buffer)) |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 418 | break; |
| 419 | sql_query.append(buffer); |
| 420 | } |
| 421 | if (sql_query.back() == '\n') |
| 422 | sql_query.resize(sql_query.size() - 1); |
Lalit Maganti | cbccb0a | 2018-12-04 20:14:42 +0000 | [diff] [blame] | 423 | |
| 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 Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 430 | 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 | |
| 439 | bool RunQueryAndPrintResult(const std::vector<std::string> queries, |
Ioannis Ilkos | 433c8e5 | 2019-02-11 12:52:35 +0000 | [diff] [blame] | 440 | FILE* output) { |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 441 | bool is_first_query = true; |
| 442 | bool is_query_error = false; |
Ioannis Ilkos | 433c8e5 | 2019-02-11 12:52:35 +0000 | [diff] [blame] | 443 | bool has_output = false; |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 444 | 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 Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 450 | PERFETTO_ILOG("Executing query: %s", sql_query.c_str()); |
| 451 | |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 452 | auto it = g_tp->ExecuteQuery(sql_query); |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 453 | util::Status status = it.Status(); |
| 454 | if (!status.ok()) { |
| 455 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 456 | is_query_error = true; |
| 457 | break; |
| 458 | } |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 459 | 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 Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 466 | 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 Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 473 | has_output = true; |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 474 | } |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 475 | return !is_query_error; |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 476 | } |
| 477 | |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 478 | int MaybePrintPerfFile(const std::string& perf_file_path, |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 479 | base::TimeNanos t_load, |
| 480 | base::TimeNanos t_run) { |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 481 | if (perf_file_path.empty()) |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 482 | 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 Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 502 | void PrintUsage(char** argv) { |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 503 | PERFETTO_ELOG( |
| 504 | "Interactive trace processor shell.\n" |
| 505 | "Usage: %s [OPTIONS] trace_file.pb\n\n" |
| 506 | "Options:\n" |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 507 | " -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 Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 511 | "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 Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 514 | " -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 Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 519 | "prints the result as a TraceMetrics proto to stdout.\n", |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 520 | argv[0]); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 521 | } |
| 522 | |
Lalit Maganti | 1ebebf1 | 2018-10-15 17:24:04 +0100 | [diff] [blame] | 523 | int TraceProcessorMain(int argc, char** argv) { |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 524 | 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 Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 593 | PrintUsage(argv); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 594 | return 1; |
| 595 | } |
| 596 | |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 597 | const char* trace_file_path = argv[optind]; |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 598 | if (trace_file_path == nullptr) { |
| 599 | PrintUsage(argv); |
| 600 | return 1; |
| 601 | } |
| 602 | |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 603 | bool launch_shell = |
| 604 | explicit_interactive || (metric_names.empty() && query_file_path.empty()); |
| 605 | |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 606 | // Only allow non-interactive queries to emit perf data. |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 607 | if (!perf_file_path.empty() && launch_shell) { |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 608 | PrintUsage(argv); |
| 609 | return 1; |
| 610 | } |
| 611 | |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 612 | // Load the trace file into the trace processor. |
Ioannis Ilkos | eff38f5 | 2018-10-29 10:37:55 +0000 | [diff] [blame] | 613 | Config config; |
Ioannis Ilkos | f837129 | 2018-11-05 16:47:35 +0000 | [diff] [blame] | 614 | std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config); |
Florian Mayer | b03fd28 | 2018-10-03 16:05:16 +0100 | [diff] [blame] | 615 | base::ScopedFile fd(base::OpenFile(trace_file_path, O_RDONLY)); |
Lalit Maganti | 22a1e0c | 2018-12-04 20:28:18 +0000 | [diff] [blame] | 616 | if (!fd) { |
| 617 | PERFETTO_ELOG("Could not open trace file (path: %s)", trace_file_path); |
| 618 | return 1; |
| 619 | } |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 620 | |
| 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 Mayer | e37c620 | 2018-10-18 12:07:59 +0100 | [diff] [blame] | 631 | std::unique_ptr<uint8_t[]> aio_buf(new uint8_t[kChunkSize]); |
Lalit Maganti | 9e6616e | 2019-04-03 13:19:44 +0100 | [diff] [blame] | 632 | #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 Mayer | e37c620 | 2018-10-18 12:07:59 +0100 | [diff] [blame] | 637 | cb.aio_buf = aio_buf.get(); |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 638 | |
| 639 | PERFETTO_CHECK(aio_read(&cb) == 0); |
| 640 | struct aiocb* aio_list[1] = {&cb}; |
| 641 | |
| 642 | uint64_t file_size = 0; |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 643 | auto t_load_start = base::GetWallTimeNs(); |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 644 | 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 Mayer | e37c620 | 2018-10-18 12:07:59 +0100 | [diff] [blame] | 657 | std::unique_ptr<uint8_t[]> buf(std::move(aio_buf)); |
| 658 | aio_buf.reset(new uint8_t[kChunkSize]); |
Lalit Maganti | 9e6616e | 2019-04-03 13:19:44 +0100 | [diff] [blame] | 659 | #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 Mayer | e37c620 | 2018-10-18 12:07:59 +0100 | [diff] [blame] | 664 | cb.aio_buf = aio_buf.get(); |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 665 | 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 Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 669 | 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 Maganti | 4b2b253 | 2019-05-09 11:03:23 +0100 | [diff] [blame] | 672 | return 1; |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 673 | } |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 674 | } |
Ioannis Ilkos | f837129 | 2018-11-05 16:47:35 +0000 | [diff] [blame] | 675 | tp->NotifyEndOfFile(); |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 676 | |
| 677 | auto t_load = base::GetWallTimeNs() - t_load_start; |
| 678 | double t_load_s = t_load.count() / 1E9; |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 679 | double size_mb = file_size / 1E6; |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 680 | PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb, |
| 681 | size_mb / t_load_s); |
Ioannis Ilkos | f837129 | 2018-11-05 16:47:35 +0000 | [diff] [blame] | 682 | g_tp = tp.get(); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 683 | |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 684 | #if PERFETTO_HAS_SIGNAL_H() |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 685 | signal(SIGINT, [](int) { g_tp->InterruptQuery(); }); |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 686 | #endif |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 687 | |
Lalit Maganti | 49ae901 | 2019-04-03 16:10:39 +0100 | [diff] [blame] | 688 | // Print out the stats to stderr for the trace. |
| 689 | if (!PrintStats()) { |
| 690 | return 1; |
| 691 | } |
| 692 | |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 693 | auto t_run_start = base::GetWallTimeNs(); |
| 694 | |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 695 | // First, see if we have some metrics to run. If we do, just run them and |
| 696 | // return. |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 697 | if (!metric_names.empty()) { |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 698 | std::vector<std::string> metrics; |
| 699 | for (base::StringSplitter ss(metric_names, ','); ss.Next();) { |
| 700 | metrics.emplace_back(ss.cur_token()); |
| 701 | } |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 702 | 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 Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 708 | } |
| 709 | |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 710 | // If we were given a query file, load contents |
| 711 | std::vector<std::string> queries; |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 712 | if (!query_file_path.empty()) { |
| 713 | base::ScopedFstream file(fopen(query_file_path.c_str(), "r")); |
Lalit Maganti | 22a1e0c | 2018-12-04 20:28:18 +0000 | [diff] [blame] | 714 | if (!file) { |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 715 | PERFETTO_ELOG("Could not open query file (path: %s)", |
| 716 | query_file_path.c_str()); |
Lalit Maganti | 22a1e0c | 2018-12-04 20:28:18 +0000 | [diff] [blame] | 717 | return 1; |
| 718 | } |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 719 | if (!LoadQueries(file.get(), &queries)) { |
| 720 | return 1; |
| 721 | } |
| 722 | } |
| 723 | |
Ioannis Ilkos | 433c8e5 | 2019-02-11 12:52:35 +0000 | [diff] [blame] | 724 | if (!RunQueryAndPrintResult(queries, stdout)) { |
Lalit Maganti | 77f1dbe | 2019-02-27 12:58:01 +0000 | [diff] [blame] | 725 | return 1; |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 726 | } |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 727 | |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 728 | if (!sqlite_file_path.empty()) { |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 729 | return ExportTraceToDatabase(sqlite_file_path); |
| 730 | } |
| 731 | |
Ioannis Ilkos | 433c8e5 | 2019-02-11 12:52:35 +0000 | [diff] [blame] | 732 | if (!launch_shell) { |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 733 | auto t_query = base::GetWallTimeNs() - t_run_start; |
| 734 | return MaybePrintPerfFile(perf_file_path, t_load, t_query); |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 735 | } |
| 736 | |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 737 | return StartInteractiveShell(); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 738 | } |
Lalit Maganti | 1ebebf1 | 2018-10-15 17:24:04 +0100 | [diff] [blame] | 739 | |
| 740 | } // namespace |
| 741 | |
| 742 | } // namespace trace_processor |
| 743 | } // namespace perfetto |
| 744 | |
| 745 | int main(int argc, char** argv) { |
| 746 | return perfetto::trace_processor::TraceProcessorMain(argc, argv); |
| 747 | } |