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 <fcntl.h> |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 18 | #include <inttypes.h> |
Lalit Maganti | 26f69bd | 2019-04-29 18:23:47 +0100 | [diff] [blame] | 19 | #include <stdio.h> |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 20 | #include <sys/stat.h> |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 21 | |
| 22 | #include <functional> |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 23 | #include <iostream> |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 24 | #include <vector> |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 25 | |
Lalit Maganti | ca4d514 | 2019-05-28 13:25:47 +0100 | [diff] [blame] | 26 | #include <google/protobuf/compiler/parser.h> |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 27 | #include <google/protobuf/dynamic_message.h> |
Lalit Maganti | ca4d514 | 2019-05-28 13:25:47 +0100 | [diff] [blame] | 28 | #include <google/protobuf/io/zero_copy_stream_impl.h> |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 29 | #include <google/protobuf/text_format.h> |
Lalit Maganti | ca4d514 | 2019-05-28 13:25:47 +0100 | [diff] [blame] | 30 | |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 31 | #include "perfetto/base/build_config.h" |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 32 | #include "perfetto/base/logging.h" |
Eric Seckler | 83dcc8c | 2019-08-21 12:18:43 +0100 | [diff] [blame] | 33 | #include "perfetto/base/time.h" |
Primiano Tucci | 2c5488f | 2019-06-01 03:27:28 +0100 | [diff] [blame] | 34 | #include "perfetto/ext/base/file_utils.h" |
| 35 | #include "perfetto/ext/base/scoped_file.h" |
| 36 | #include "perfetto/ext/base/string_splitter.h" |
Eric Seckler | 8f70bbf | 2019-10-09 09:37:43 +0100 | [diff] [blame] | 37 | #include "perfetto/trace_processor/read_trace.h" |
Ioannis Ilkos | eff38f5 | 2018-10-29 10:37:55 +0000 | [diff] [blame] | 38 | #include "perfetto/trace_processor/trace_processor.h" |
Deepanjan Roy | ab8c05d | 2020-01-21 09:55:26 -0500 | [diff] [blame^] | 39 | #include "src/trace_processor/metrics/custom_options.descriptor.h" |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 40 | #include "src/trace_processor/metrics/metrics.descriptor.h" |
Lalit Maganti | 25863f7 | 2019-08-28 22:14:25 +0100 | [diff] [blame] | 41 | #include "src/trace_processor/proto_to_json.h" |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 42 | |
Primiano Tucci | a36cccc | 2019-10-27 13:15:04 +0100 | [diff] [blame] | 43 | #if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD) |
| 44 | #include "src/trace_processor/rpc/httpd.h" |
| 45 | #endif |
| 46 | |
Florian Mayer | 6cc8b1d | 2019-12-18 16:37:32 +0000 | [diff] [blame] | 47 | #include "src/profiling/symbolizer/symbolize_database.h" |
| 48 | #include "src/profiling/symbolizer/symbolizer.h" |
| 49 | |
| 50 | #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER) |
| 51 | #include "src/profiling/symbolizer/local_symbolizer.h" |
| 52 | #endif |
| 53 | |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 54 | #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ |
| 55 | PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ |
| 56 | PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX) |
| 57 | #define PERFETTO_HAS_SIGNAL_H() 1 |
| 58 | #else |
| 59 | #define PERFETTO_HAS_SIGNAL_H() 0 |
| 60 | #endif |
| 61 | |
Primiano Tucci | 02c1176 | 2019-08-30 00:57:59 +0200 | [diff] [blame] | 62 | #if PERFETTO_BUILDFLAG(PERFETTO_TP_LINENOISE) |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 63 | #include <linenoise.h> |
Hector Dearman | 36adc82 | 2018-10-02 11:35:08 +0100 | [diff] [blame] | 64 | #include <pwd.h> |
| 65 | #include <sys/types.h> |
Primiano Tucci | 75ae50e | 2019-08-28 13:09:55 +0200 | [diff] [blame] | 66 | #endif |
| 67 | |
| 68 | #if PERFETTO_BUILDFLAG(PERFETTO_VERSION_GEN) |
Primiano Tucci | 00da64a | 2019-02-22 14:51:10 +0000 | [diff] [blame] | 69 | #include "perfetto_version.gen.h" |
| 70 | #else |
| 71 | #define PERFETTO_GET_GIT_REVISION() "unknown" |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 72 | #endif |
| 73 | |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 74 | #if PERFETTO_HAS_SIGNAL_H() |
| 75 | #include <signal.h> |
| 76 | #endif |
| 77 | |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 78 | #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) |
| 79 | #define ftruncate _chsize |
| 80 | #else |
Lalit Maganti | f9d8030 | 2019-06-19 12:48:25 +0100 | [diff] [blame] | 81 | #include <dirent.h> |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 82 | #include <getopt.h> |
| 83 | #endif |
| 84 | |
Lalit Maganti | 1ebebf1 | 2018-10-15 17:24:04 +0100 | [diff] [blame] | 85 | namespace perfetto { |
| 86 | namespace trace_processor { |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 87 | |
| 88 | namespace { |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 89 | TraceProcessor* g_tp; |
| 90 | |
Primiano Tucci | 02c1176 | 2019-08-30 00:57:59 +0200 | [diff] [blame] | 91 | #if PERFETTO_BUILDFLAG(PERFETTO_TP_LINENOISE) |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 92 | |
Hector Dearman | 36adc82 | 2018-10-02 11:35:08 +0100 | [diff] [blame] | 93 | bool EnsureDir(const std::string& path) { |
| 94 | return mkdir(path.c_str(), 0755) != -1 || errno == EEXIST; |
| 95 | } |
| 96 | |
| 97 | bool EnsureFile(const std::string& path) { |
Primiano Tucci | f675dc2 | 2018-10-18 00:17:26 +0200 | [diff] [blame] | 98 | return base::OpenFile(path, O_RDONLY | O_CREAT, 0644).get() != -1; |
Hector Dearman | 36adc82 | 2018-10-02 11:35:08 +0100 | [diff] [blame] | 99 | } |
| 100 | |
| 101 | std::string GetConfigPath() { |
| 102 | const char* homedir = getenv("HOME"); |
| 103 | if (homedir == nullptr) |
| 104 | homedir = getpwuid(getuid())->pw_dir; |
| 105 | if (homedir == nullptr) |
| 106 | return ""; |
| 107 | return std::string(homedir) + "/.config"; |
| 108 | } |
| 109 | |
| 110 | std::string GetPerfettoPath() { |
| 111 | std::string config = GetConfigPath(); |
| 112 | if (config == "") |
| 113 | return ""; |
| 114 | return config + "/perfetto"; |
| 115 | } |
| 116 | |
| 117 | std::string GetHistoryPath() { |
| 118 | std::string perfetto = GetPerfettoPath(); |
| 119 | if (perfetto == "") |
| 120 | return ""; |
| 121 | return perfetto + "/.trace_processor_shell_history"; |
| 122 | } |
| 123 | |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 124 | void SetupLineEditor() { |
| 125 | linenoiseSetMultiLine(true); |
| 126 | linenoiseHistorySetMaxLen(1000); |
Hector Dearman | 36adc82 | 2018-10-02 11:35:08 +0100 | [diff] [blame] | 127 | |
| 128 | bool success = GetHistoryPath() != ""; |
| 129 | success = success && EnsureDir(GetConfigPath()); |
| 130 | success = success && EnsureDir(GetPerfettoPath()); |
| 131 | success = success && EnsureFile(GetHistoryPath()); |
| 132 | success = success && linenoiseHistoryLoad(GetHistoryPath().c_str()) != -1; |
| 133 | if (!success) { |
| 134 | PERFETTO_PLOG("Could not load history from %s", GetHistoryPath().c_str()); |
| 135 | } |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 136 | } |
| 137 | |
Florian Mayer | b5fa0c9 | 2019-12-18 12:44:46 +0000 | [diff] [blame] | 138 | struct LineDeleter { |
| 139 | void operator()(char* p) const { |
| 140 | linenoiseHistoryAdd(p); |
| 141 | linenoiseHistorySave(GetHistoryPath().c_str()); |
| 142 | linenoiseFree(p); |
| 143 | } |
| 144 | }; |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 145 | |
Florian Mayer | b5fa0c9 | 2019-12-18 12:44:46 +0000 | [diff] [blame] | 146 | using ScopedLine = std::unique_ptr<char, LineDeleter>; |
| 147 | |
| 148 | ScopedLine GetLine(const char* prompt) { |
| 149 | return ScopedLine(linenoise(prompt)); |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 150 | } |
| 151 | |
| 152 | #else |
| 153 | |
| 154 | void SetupLineEditor() {} |
| 155 | |
Florian Mayer | b5fa0c9 | 2019-12-18 12:44:46 +0000 | [diff] [blame] | 156 | using ScopedLine = std::unique_ptr<char>; |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 157 | |
Florian Mayer | b5fa0c9 | 2019-12-18 12:44:46 +0000 | [diff] [blame] | 158 | ScopedLine GetLine(const char* prompt) { |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 159 | printf("\r%80s\r%s", "", prompt); |
| 160 | fflush(stdout); |
Florian Mayer | b5fa0c9 | 2019-12-18 12:44:46 +0000 | [diff] [blame] | 161 | ScopedLine line(new char[1024]); |
Lalit Maganti | 3be2091 | 2019-12-18 13:48:45 +0000 | [diff] [blame] | 162 | if (!fgets(line.get(), 1024 - 1, stdin)) |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 163 | return nullptr; |
Lalit Maganti | 3be2091 | 2019-12-18 13:48:45 +0000 | [diff] [blame] | 164 | if (strlen(line.get()) > 0) |
| 165 | line.get()[strlen(line.get()) - 1] = 0; |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 166 | return line; |
| 167 | } |
| 168 | |
Primiano Tucci | 02c1176 | 2019-08-30 00:57:59 +0200 | [diff] [blame] | 169 | #endif // PERFETTO_TP_LINENOISE |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 170 | |
Lalit Maganti | 49ae901 | 2019-04-03 16:10:39 +0100 | [diff] [blame] | 171 | bool PrintStats() { |
| 172 | auto it = g_tp->ExecuteQuery( |
| 173 | "SELECT name, idx, source, value from stats " |
Ioannis Ilkos | 5e79b8a | 2019-05-23 18:09:54 +0100 | [diff] [blame] | 174 | "where severity IN ('error', 'data_loss') and value > 0"); |
Lalit Maganti | 49ae901 | 2019-04-03 16:10:39 +0100 | [diff] [blame] | 175 | |
| 176 | bool first = true; |
| 177 | for (uint32_t rows = 0; it.Next(); rows++) { |
| 178 | if (first) { |
| 179 | fprintf(stderr, "Error stats for this trace:\n"); |
| 180 | |
| 181 | for (uint32_t i = 0; i < it.ColumnCount(); i++) |
Eric Seckler | a4f75b7 | 2019-12-09 12:08:06 +0000 | [diff] [blame] | 182 | fprintf(stderr, "%40s ", it.GetColumnName(i).c_str()); |
Lalit Maganti | 49ae901 | 2019-04-03 16:10:39 +0100 | [diff] [blame] | 183 | fprintf(stderr, "\n"); |
| 184 | |
| 185 | for (uint32_t i = 0; i < it.ColumnCount(); i++) |
| 186 | fprintf(stderr, "%40s ", "----------------------------------------"); |
| 187 | fprintf(stderr, "\n"); |
| 188 | |
| 189 | first = false; |
| 190 | } |
| 191 | |
| 192 | for (uint32_t c = 0; c < it.ColumnCount(); c++) { |
| 193 | auto value = it.Get(c); |
| 194 | switch (value.type) { |
| 195 | case SqlValue::Type::kNull: |
| 196 | fprintf(stderr, "%-40.40s", "[NULL]"); |
| 197 | break; |
| 198 | case SqlValue::Type::kDouble: |
| 199 | fprintf(stderr, "%40f", value.double_value); |
| 200 | break; |
| 201 | case SqlValue::Type::kLong: |
| 202 | fprintf(stderr, "%40" PRIi64, value.long_value); |
| 203 | break; |
| 204 | case SqlValue::Type::kString: |
| 205 | fprintf(stderr, "%-40.40s", value.string_value); |
| 206 | break; |
Lalit Maganti | 6221107 | 2019-05-10 14:09:58 +0100 | [diff] [blame] | 207 | case SqlValue::Type::kBytes: |
| 208 | printf("%-40.40s", "<raw bytes>"); |
| 209 | break; |
Lalit Maganti | 49ae901 | 2019-04-03 16:10:39 +0100 | [diff] [blame] | 210 | } |
| 211 | fprintf(stderr, " "); |
| 212 | } |
| 213 | fprintf(stderr, "\n"); |
| 214 | } |
| 215 | |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 216 | util::Status status = it.Status(); |
| 217 | if (!status.ok()) { |
| 218 | PERFETTO_ELOG("Error while iterating stats %s", status.c_message()); |
Lalit Maganti | 49ae901 | 2019-04-03 16:10:39 +0100 | [diff] [blame] | 219 | return false; |
| 220 | } |
| 221 | return true; |
| 222 | } |
| 223 | |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 224 | int ExportTraceToDatabase(const std::string& output_name) { |
| 225 | PERFETTO_CHECK(output_name.find("'") == std::string::npos); |
| 226 | { |
| 227 | base::ScopedFile fd(base::OpenFile(output_name, O_CREAT | O_RDWR, 0600)); |
| 228 | if (!fd) { |
| 229 | PERFETTO_PLOG("Failed to create file: %s", output_name.c_str()); |
| 230 | return 1; |
| 231 | } |
| 232 | int res = ftruncate(fd.get(), 0); |
| 233 | PERFETTO_CHECK(res == 0); |
| 234 | } |
| 235 | |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 236 | std::string attach_sql = |
| 237 | "ATTACH DATABASE '" + output_name + "' AS perfetto_export"; |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 238 | auto attach_it = g_tp->ExecuteQuery(attach_sql); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 239 | bool attach_has_more = attach_it.Next(); |
| 240 | PERFETTO_DCHECK(!attach_has_more); |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 241 | |
| 242 | util::Status status = attach_it.Status(); |
| 243 | if (!status.ok()) { |
| 244 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 245 | return 1; |
| 246 | } |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 247 | |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 248 | auto tables_it = g_tp->ExecuteQuery( |
| 249 | "SELECT name FROM perfetto_tables UNION " |
| 250 | "SELECT name FROM sqlite_master WHERE type='table'"); |
| 251 | for (uint32_t rows = 0; tables_it.Next(); rows++) { |
| 252 | std::string table_name = tables_it.Get(0).string_value; |
| 253 | PERFETTO_CHECK(table_name.find("'") == std::string::npos); |
| 254 | std::string export_sql = "CREATE TABLE perfetto_export." + table_name + |
| 255 | " AS SELECT * FROM " + table_name; |
| 256 | |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 257 | auto export_it = g_tp->ExecuteQuery(export_sql); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 258 | bool export_has_more = export_it.Next(); |
| 259 | PERFETTO_DCHECK(!export_has_more); |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 260 | |
| 261 | status = export_it.Status(); |
| 262 | if (!status.ok()) { |
| 263 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 264 | return 1; |
| 265 | } |
| 266 | } |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 267 | status = tables_it.Status(); |
| 268 | if (!status.ok()) { |
| 269 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 270 | return 1; |
| 271 | } |
| 272 | |
| 273 | auto detach_it = g_tp->ExecuteQuery("DETACH DATABASE perfetto_export"); |
| 274 | bool detach_has_more = attach_it.Next(); |
| 275 | PERFETTO_DCHECK(!detach_has_more); |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 276 | status = detach_it.Status(); |
| 277 | if (!status.ok()) { |
| 278 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 279 | return 1; |
| 280 | } |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 281 | return 0; |
| 282 | } |
| 283 | |
Lalit Maganti | ca4d514 | 2019-05-28 13:25:47 +0100 | [diff] [blame] | 284 | class ErrorPrinter : public google::protobuf::io::ErrorCollector { |
| 285 | void AddError(int line, int col, const std::string& msg) override { |
| 286 | PERFETTO_ELOG("%d:%d: %s", line, col, msg.c_str()); |
| 287 | } |
| 288 | |
| 289 | void AddWarning(int line, int col, const std::string& msg) override { |
| 290 | PERFETTO_ILOG("%d:%d: %s", line, col, msg.c_str()); |
| 291 | } |
| 292 | }; |
| 293 | |
Mikhail Khokhlov | 582f7ce | 2020-01-14 17:30:34 +0000 | [diff] [blame] | 294 | // This function returns an indentifier for a metric suitable for use |
| 295 | // as an SQL table name (i.e. containing no forward or backward slashes). |
| 296 | std::string BaseName(std::string metric_path) { |
| 297 | std::replace(metric_path.begin(), metric_path.end(), '\\', '/'); |
| 298 | auto slash_idx = metric_path.rfind('/'); |
| 299 | return slash_idx == std::string::npos ? metric_path |
| 300 | : metric_path.substr(slash_idx + 1); |
| 301 | } |
| 302 | |
Lalit Maganti | ca4d514 | 2019-05-28 13:25:47 +0100 | [diff] [blame] | 303 | util::Status RegisterMetric(const std::string& register_metric) { |
| 304 | std::string sql; |
| 305 | base::ReadFile(register_metric, &sql); |
| 306 | |
Mikhail Khokhlov | 582f7ce | 2020-01-14 17:30:34 +0000 | [diff] [blame] | 307 | std::string path = "shell/" + BaseName(register_metric); |
Lalit Maganti | ca4d514 | 2019-05-28 13:25:47 +0100 | [diff] [blame] | 308 | |
| 309 | return g_tp->RegisterMetric(path, sql); |
| 310 | } |
| 311 | |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 312 | util::Status ExtendMetricsProto(const std::string& extend_metrics_proto, |
| 313 | google::protobuf::DescriptorPool* pool) { |
Lalit Maganti | ca4d514 | 2019-05-28 13:25:47 +0100 | [diff] [blame] | 314 | google::protobuf::FileDescriptorSet desc_set; |
| 315 | |
| 316 | base::ScopedFile file(base::OpenFile(extend_metrics_proto, O_RDONLY)); |
Lalit Maganti | f9d8030 | 2019-06-19 12:48:25 +0100 | [diff] [blame] | 317 | if (file.get() == -1) { |
| 318 | return util::ErrStatus("Failed to open proto file %s", |
| 319 | extend_metrics_proto.c_str()); |
| 320 | } |
Lalit Maganti | ca4d514 | 2019-05-28 13:25:47 +0100 | [diff] [blame] | 321 | |
| 322 | google::protobuf::io::FileInputStream stream(file.get()); |
| 323 | ErrorPrinter printer; |
| 324 | google::protobuf::io::Tokenizer tokenizer(&stream, &printer); |
| 325 | |
Deepanjan Roy | ab8c05d | 2020-01-21 09:55:26 -0500 | [diff] [blame^] | 326 | auto* file_desc = desc_set.add_file(); |
Lalit Maganti | ca4d514 | 2019-05-28 13:25:47 +0100 | [diff] [blame] | 327 | google::protobuf::compiler::Parser parser; |
Deepanjan Roy | ab8c05d | 2020-01-21 09:55:26 -0500 | [diff] [blame^] | 328 | parser.Parse(&tokenizer, file_desc); |
Lalit Maganti | ca4d514 | 2019-05-28 13:25:47 +0100 | [diff] [blame] | 329 | |
Deepanjan Roy | ab8c05d | 2020-01-21 09:55:26 -0500 | [diff] [blame^] | 330 | file_desc->set_name(BaseName(extend_metrics_proto)); |
| 331 | pool->BuildFile(*file_desc); |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 332 | |
Lalit Maganti | ca4d514 | 2019-05-28 13:25:47 +0100 | [diff] [blame] | 333 | std::vector<uint8_t> metric_proto; |
| 334 | metric_proto.resize(static_cast<size_t>(desc_set.ByteSize())); |
| 335 | desc_set.SerializeToArray(metric_proto.data(), |
| 336 | static_cast<int>(metric_proto.size())); |
| 337 | |
| 338 | return g_tp->ExtendMetricsProto(metric_proto.data(), metric_proto.size()); |
| 339 | } |
| 340 | |
Lalit Maganti | 25863f7 | 2019-08-28 22:14:25 +0100 | [diff] [blame] | 341 | enum OutputFormat { |
| 342 | kBinaryProto, |
| 343 | kTextProto, |
| 344 | kJson, |
Rafal Slawik | d28f030 | 2019-10-22 17:00:04 +0100 | [diff] [blame] | 345 | kNone, |
Lalit Maganti | 25863f7 | 2019-08-28 22:14:25 +0100 | [diff] [blame] | 346 | }; |
| 347 | |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 348 | int RunMetrics(const std::vector<std::string>& metric_names, |
Lalit Maganti | 25863f7 | 2019-08-28 22:14:25 +0100 | [diff] [blame] | 349 | OutputFormat format, |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 350 | const google::protobuf::DescriptorPool& pool) { |
Lalit Maganti | d9f86b6 | 2019-04-08 11:11:51 +0100 | [diff] [blame] | 351 | std::vector<uint8_t> metric_result; |
Lalit Maganti | d71a945 | 2019-05-09 15:13:24 +0100 | [diff] [blame] | 352 | util::Status status = g_tp->ComputeMetric(metric_names, &metric_result); |
| 353 | if (!status.ok()) { |
| 354 | PERFETTO_ELOG("Error when computing metrics: %s", status.c_message()); |
Lalit Maganti | d9f86b6 | 2019-04-08 11:11:51 +0100 | [diff] [blame] | 355 | return 1; |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 356 | } |
Rafal Slawik | d28f030 | 2019-10-22 17:00:04 +0100 | [diff] [blame] | 357 | if (format == OutputFormat::kNone) { |
| 358 | return 0; |
| 359 | } |
Lalit Maganti | 25863f7 | 2019-08-28 22:14:25 +0100 | [diff] [blame] | 360 | if (format == OutputFormat::kBinaryProto) { |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 361 | fwrite(metric_result.data(), sizeof(uint8_t), metric_result.size(), stdout); |
Lalit Maganti | 25863f7 | 2019-08-28 22:14:25 +0100 | [diff] [blame] | 362 | return 0; |
| 363 | } |
| 364 | |
| 365 | google::protobuf::DynamicMessageFactory factory(&pool); |
| 366 | auto* descriptor = pool.FindMessageTypeByName("perfetto.protos.TraceMetrics"); |
| 367 | std::unique_ptr<google::protobuf::Message> metrics( |
| 368 | factory.GetPrototype(descriptor)->New()); |
| 369 | metrics->ParseFromArray(metric_result.data(), |
| 370 | static_cast<int>(metric_result.size())); |
| 371 | |
| 372 | switch (format) { |
| 373 | case OutputFormat::kTextProto: { |
| 374 | std::string out; |
| 375 | google::protobuf::TextFormat::PrintToString(*metrics, &out); |
| 376 | fwrite(out.c_str(), sizeof(char), out.size(), stdout); |
| 377 | break; |
| 378 | } |
| 379 | case OutputFormat::kJson: { |
Deepanjan Roy | ab8c05d | 2020-01-21 09:55:26 -0500 | [diff] [blame^] | 380 | // We need to instantiate field options from dynamic message factory |
| 381 | // because otherwise it cannot parse our custom extensions. |
| 382 | const google::protobuf::Message* field_options_prototype = |
| 383 | factory.GetPrototype( |
| 384 | pool.FindMessageTypeByName("google.protobuf.FieldOptions")); |
| 385 | auto out = proto_to_json::MessageToJsonWithAnnotations( |
| 386 | *metrics, field_options_prototype, 0); |
Lalit Maganti | 25863f7 | 2019-08-28 22:14:25 +0100 | [diff] [blame] | 387 | fwrite(out.c_str(), sizeof(char), out.size(), stdout); |
| 388 | break; |
| 389 | } |
| 390 | case OutputFormat::kBinaryProto: |
Rafal Slawik | d28f030 | 2019-10-22 17:00:04 +0100 | [diff] [blame] | 391 | case OutputFormat::kNone: |
Lalit Maganti | 25863f7 | 2019-08-28 22:14:25 +0100 | [diff] [blame] | 392 | PERFETTO_FATAL("Unsupported output format."); |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 393 | } |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 394 | return 0; |
| 395 | } |
| 396 | |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 397 | void PrintQueryResultInteractively(TraceProcessor::Iterator* it, |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 398 | base::TimeNanos t_start, |
| 399 | uint32_t column_width) { |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 400 | base::TimeNanos t_end = t_start; |
| 401 | for (uint32_t rows = 0; it->Next(); rows++) { |
| 402 | if (rows % 32 == 0) { |
| 403 | if (rows > 0) { |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 404 | fprintf(stderr, "...\nType 'q' to stop, Enter for more records: "); |
| 405 | fflush(stderr); |
| 406 | char input[32]; |
| 407 | if (!fgets(input, sizeof(input) - 1, stdin)) |
| 408 | exit(0); |
| 409 | if (input[0] == 'q') |
| 410 | break; |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 411 | } else { |
Lalit Maganti | aac2f65 | 2019-04-30 12:16:21 +0100 | [diff] [blame] | 412 | t_end = base::GetWallTimeNs(); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 413 | } |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 414 | for (uint32_t i = 0; i < it->ColumnCount(); i++) |
Lalit Maganti | d74f36a | 2019-07-12 16:35:57 +0100 | [diff] [blame] | 415 | printf("%-*.*s ", column_width, column_width, |
Eric Seckler | a4f75b7 | 2019-12-09 12:08:06 +0000 | [diff] [blame] | 416 | it->GetColumnName(i).c_str()); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 417 | printf("\n"); |
| 418 | |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 419 | std::string divider(column_width, '-'); |
| 420 | for (uint32_t i = 0; i < it->ColumnCount(); i++) { |
| 421 | printf("%-*s ", column_width, divider.c_str()); |
| 422 | } |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 423 | printf("\n"); |
| 424 | } |
| 425 | |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 426 | for (uint32_t c = 0; c < it->ColumnCount(); c++) { |
| 427 | auto value = it->Get(c); |
| 428 | switch (value.type) { |
| 429 | case SqlValue::Type::kNull: |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 430 | printf("%-*s", column_width, "[NULL]"); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 431 | break; |
| 432 | case SqlValue::Type::kDouble: |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 433 | printf("%*f", column_width, value.double_value); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 434 | break; |
| 435 | case SqlValue::Type::kLong: |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 436 | printf("%*" PRIi64, column_width, value.long_value); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 437 | break; |
| 438 | case SqlValue::Type::kString: |
Lalit Maganti | d74f36a | 2019-07-12 16:35:57 +0100 | [diff] [blame] | 439 | printf("%-*.*s", column_width, column_width, value.string_value); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 440 | break; |
Lalit Maganti | 6221107 | 2019-05-10 14:09:58 +0100 | [diff] [blame] | 441 | case SqlValue::Type::kBytes: |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 442 | printf("%-*s", column_width, "<raw bytes>"); |
Lalit Maganti | 6221107 | 2019-05-10 14:09:58 +0100 | [diff] [blame] | 443 | break; |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 444 | } |
| 445 | printf(" "); |
| 446 | } |
| 447 | printf("\n"); |
| 448 | } |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 449 | |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 450 | util::Status status = it->Status(); |
| 451 | if (!status.ok()) { |
| 452 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 453 | } |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 454 | printf("\nQuery executed in %.3f ms\n\n", (t_end - t_start).count() / 1E6); |
| 455 | } |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 456 | |
| 457 | void PrintShellUsage() { |
| 458 | PERFETTO_ELOG( |
| 459 | "Available commands:\n" |
| 460 | ".quit, .q Exit the shell.\n" |
| 461 | ".help This text.\n" |
Primiano Tucci | 11a79e7 | 2019-10-29 22:13:10 +0100 | [diff] [blame] | 462 | ".dump FILE Export the trace as a sqlite database.\n" |
| 463 | ".reset Destroys all tables/view created by the user.\n"); |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 464 | } |
| 465 | |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 466 | int StartInteractiveShell(uint32_t column_width) { |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 467 | SetupLineEditor(); |
Primiano Tucci | b75dcee | 2018-08-08 12:21:36 +0100 | [diff] [blame] | 468 | |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 469 | for (;;) { |
Florian Mayer | b5fa0c9 | 2019-12-18 12:44:46 +0000 | [diff] [blame] | 470 | ScopedLine line = GetLine("> "); |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 471 | if (!line) |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 472 | break; |
Florian Mayer | b5fa0c9 | 2019-12-18 12:44:46 +0000 | [diff] [blame] | 473 | if (strcmp(line.get(), "") == 0) |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 474 | continue; |
Florian Mayer | b5fa0c9 | 2019-12-18 12:44:46 +0000 | [diff] [blame] | 475 | if (line.get()[0] == '.') { |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 476 | char command[32] = {}; |
| 477 | char arg[1024] = {}; |
Florian Mayer | b5fa0c9 | 2019-12-18 12:44:46 +0000 | [diff] [blame] | 478 | sscanf(line.get() + 1, "%31s %1023s", command, arg); |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 479 | if (strcmp(command, "quit") == 0 || strcmp(command, "q") == 0) { |
| 480 | break; |
| 481 | } else if (strcmp(command, "help") == 0) { |
| 482 | PrintShellUsage(); |
| 483 | } else if (strcmp(command, "dump") == 0 && strlen(arg)) { |
| 484 | if (ExportTraceToDatabase(arg) != 0) |
| 485 | PERFETTO_ELOG("Database export failed"); |
Primiano Tucci | 11a79e7 | 2019-10-29 22:13:10 +0100 | [diff] [blame] | 486 | } else if (strcmp(command, "reset") == 0) { |
| 487 | g_tp->RestoreInitialTables(); |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 488 | } else { |
| 489 | PrintShellUsage(); |
| 490 | } |
| 491 | continue; |
| 492 | } |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 493 | |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 494 | base::TimeNanos t_start = base::GetWallTimeNs(); |
Florian Mayer | b5fa0c9 | 2019-12-18 12:44:46 +0000 | [diff] [blame] | 495 | auto it = g_tp->ExecuteQuery(line.get()); |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 496 | PrintQueryResultInteractively(&it, t_start, column_width); |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 497 | } |
| 498 | return 0; |
| 499 | } |
| 500 | |
Mikhail Khokhlov | 7a3a865 | 2019-07-08 13:56:39 +0100 | [diff] [blame] | 501 | util::Status PrintQueryResultAsCsv(TraceProcessor::Iterator* it, FILE* output) { |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 502 | for (uint32_t c = 0; c < it->ColumnCount(); c++) { |
| 503 | if (c > 0) |
| 504 | fprintf(output, ","); |
Eric Seckler | a4f75b7 | 2019-12-09 12:08:06 +0000 | [diff] [blame] | 505 | fprintf(output, "\"%s\"", it->GetColumnName(c).c_str()); |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 506 | } |
| 507 | fprintf(output, "\n"); |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 508 | |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 509 | for (uint32_t rows = 0; it->Next(); rows++) { |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 510 | for (uint32_t c = 0; c < it->ColumnCount(); c++) { |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 511 | if (c > 0) |
| 512 | fprintf(output, ","); |
Lalit Maganti | b55c884 | 2018-12-13 14:26:08 +0000 | [diff] [blame] | 513 | |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 514 | auto value = it->Get(c); |
| 515 | switch (value.type) { |
| 516 | case SqlValue::Type::kNull: |
| 517 | fprintf(output, "\"%s\"", "[NULL]"); |
| 518 | break; |
| 519 | case SqlValue::Type::kDouble: |
| 520 | fprintf(output, "%f", value.double_value); |
| 521 | break; |
| 522 | case SqlValue::Type::kLong: |
| 523 | fprintf(output, "%" PRIi64, value.long_value); |
| 524 | break; |
| 525 | case SqlValue::Type::kString: |
| 526 | fprintf(output, "\"%s\"", value.string_value); |
| 527 | break; |
Lalit Maganti | 6221107 | 2019-05-10 14:09:58 +0100 | [diff] [blame] | 528 | case SqlValue::Type::kBytes: |
| 529 | fprintf(output, "\"%s\"", "<raw bytes>"); |
| 530 | break; |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 531 | } |
| 532 | } |
Lalit Maganti | b55c884 | 2018-12-13 14:26:08 +0000 | [diff] [blame] | 533 | fprintf(output, "\n"); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 534 | } |
Mikhail Khokhlov | 7a3a865 | 2019-07-08 13:56:39 +0100 | [diff] [blame] | 535 | return it->Status(); |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 536 | } |
| 537 | |
Florian Mayer | 3c74243 | 2019-05-21 11:35:47 +0100 | [diff] [blame] | 538 | bool IsBlankLine(char* buffer) { |
| 539 | size_t buf_size = strlen(buffer); |
| 540 | for (size_t i = 0; i < buf_size; ++i) { |
Florian Mayer | 70995c2 | 2019-05-21 12:58:32 +0100 | [diff] [blame] | 541 | // We can index into buffer[i+1], because strlen does not include the |
| 542 | // trailing \0, so even if \r is the last character, this is not out |
| 543 | // of bound. |
| 544 | if (buffer[i] == '\r') { |
| 545 | if (buffer[i + 1] != '\n') |
| 546 | return false; |
| 547 | } else if (buffer[i] != ' ' && buffer[i] != '\t' && buffer[i] != '\n') { |
Florian Mayer | 3c74243 | 2019-05-21 11:35:47 +0100 | [diff] [blame] | 548 | return false; |
Florian Mayer | 70995c2 | 2019-05-21 12:58:32 +0100 | [diff] [blame] | 549 | } |
Florian Mayer | 3c74243 | 2019-05-21 11:35:47 +0100 | [diff] [blame] | 550 | } |
| 551 | return true; |
| 552 | } |
| 553 | |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 554 | bool LoadQueries(FILE* input, std::vector<std::string>* output) { |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 555 | char buffer[4096]; |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 556 | while (!feof(input) && !ferror(input)) { |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 557 | std::string sql_query; |
| 558 | while (fgets(buffer, sizeof(buffer), input)) { |
Florian Mayer | 3c74243 | 2019-05-21 11:35:47 +0100 | [diff] [blame] | 559 | if (IsBlankLine(buffer)) |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 560 | break; |
| 561 | sql_query.append(buffer); |
| 562 | } |
| 563 | if (sql_query.back() == '\n') |
| 564 | sql_query.resize(sql_query.size() - 1); |
Lalit Maganti | cbccb0a | 2018-12-04 20:14:42 +0000 | [diff] [blame] | 565 | |
| 566 | // If we have a new line at the end of the file or an extra new line |
| 567 | // somewhere in the file, we'll end up with an empty query which we should |
| 568 | // just ignore. |
| 569 | if (sql_query.empty()) |
| 570 | continue; |
| 571 | |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 572 | output->push_back(sql_query); |
| 573 | } |
| 574 | if (ferror(input)) { |
| 575 | PERFETTO_ELOG("Error reading query file"); |
| 576 | return false; |
| 577 | } |
| 578 | return true; |
| 579 | } |
| 580 | |
| 581 | bool RunQueryAndPrintResult(const std::vector<std::string> queries, |
Ioannis Ilkos | 433c8e5 | 2019-02-11 12:52:35 +0000 | [diff] [blame] | 582 | FILE* output) { |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 583 | bool is_first_query = true; |
| 584 | bool is_query_error = false; |
Ioannis Ilkos | 433c8e5 | 2019-02-11 12:52:35 +0000 | [diff] [blame] | 585 | bool has_output = false; |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 586 | for (const auto& sql_query : queries) { |
| 587 | // Add an extra newline separator between query results. |
| 588 | if (!is_first_query) |
| 589 | fprintf(output, "\n"); |
| 590 | is_first_query = false; |
| 591 | |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 592 | PERFETTO_ILOG("Executing query: %s", sql_query.c_str()); |
| 593 | |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 594 | auto it = g_tp->ExecuteQuery(sql_query); |
Lalit Maganti | 1f06718 | 2019-05-09 14:50:05 +0100 | [diff] [blame] | 595 | util::Status status = it.Status(); |
| 596 | if (!status.ok()) { |
| 597 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
Lalit Maganti | c72cea2 | 2019-04-08 12:29:15 +0100 | [diff] [blame] | 598 | is_query_error = true; |
| 599 | break; |
| 600 | } |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 601 | if (it.ColumnCount() == 0) { |
| 602 | bool it_has_more = it.Next(); |
| 603 | PERFETTO_DCHECK(!it_has_more); |
| 604 | continue; |
| 605 | } |
| 606 | |
Hector Dearman | 2af2192 | 2019-09-03 13:48:38 +0100 | [diff] [blame] | 607 | if (has_output) { |
| 608 | PERFETTO_ELOG( |
| 609 | "More than one query generated result rows. This is " |
| 610 | "unsupported."); |
| 611 | is_query_error = true; |
| 612 | break; |
| 613 | } |
Mikhail Khokhlov | 7a3a865 | 2019-07-08 13:56:39 +0100 | [diff] [blame] | 614 | status = PrintQueryResultAsCsv(&it, output); |
Lalit Maganti | 73b1a0a | 2019-04-08 13:51:14 +0100 | [diff] [blame] | 615 | has_output = true; |
Mikhail Khokhlov | 7a3a865 | 2019-07-08 13:56:39 +0100 | [diff] [blame] | 616 | |
| 617 | if (!status.ok()) { |
| 618 | PERFETTO_ELOG("SQLite error: %s", status.c_message()); |
| 619 | is_query_error = true; |
| 620 | } |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 621 | } |
Ioannis Ilkos | 42dafcc | 2019-02-08 17:35:27 +0000 | [diff] [blame] | 622 | return !is_query_error; |
Lalit Maganti | 21160e8 | 2018-10-16 09:40:29 +0100 | [diff] [blame] | 623 | } |
| 624 | |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 625 | int MaybePrintPerfFile(const std::string& perf_file_path, |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 626 | base::TimeNanos t_load, |
| 627 | base::TimeNanos t_run) { |
Lalit Maganti | 6d4836e | 2019-05-16 16:43:27 +0100 | [diff] [blame] | 628 | if (perf_file_path.empty()) |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 629 | return 0; |
| 630 | |
| 631 | char buf[128]; |
| 632 | int count = snprintf(buf, sizeof(buf), "%" PRId64 ",%" PRId64, |
| 633 | static_cast<int64_t>(t_load.count()), |
| 634 | static_cast<int64_t>(t_run.count())); |
| 635 | if (count < 0) { |
| 636 | PERFETTO_ELOG("Failed to write perf data"); |
| 637 | return 1; |
| 638 | } |
| 639 | |
Lalit Maganti | 04a450a | 2019-07-03 16:04:19 +0100 | [diff] [blame] | 640 | auto fd(base::OpenFile(perf_file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666)); |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 641 | if (!fd) { |
| 642 | PERFETTO_ELOG("Failed to open perf file"); |
| 643 | return 1; |
| 644 | } |
| 645 | base::WriteAll(fd.get(), buf, static_cast<size_t>(count)); |
| 646 | return 0; |
| 647 | } |
| 648 | |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 649 | struct CommandLineOptions { |
| 650 | std::string perf_file_path; |
| 651 | std::string query_file_path; |
| 652 | std::string sqlite_file_path; |
| 653 | std::string metric_names; |
| 654 | std::string metric_output; |
Lalit Maganti | f9d8030 | 2019-06-19 12:48:25 +0100 | [diff] [blame] | 655 | std::string metric_extra; |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 656 | std::string trace_file_path; |
| 657 | bool launch_shell = false; |
Primiano Tucci | a36cccc | 2019-10-27 13:15:04 +0100 | [diff] [blame] | 658 | bool enable_httpd = false; |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 659 | bool wide = false; |
Lalit Maganti | 55d0bc7 | 2019-10-23 16:22:00 +0100 | [diff] [blame] | 660 | bool force_full_sort = false; |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 661 | }; |
| 662 | |
Lalit Maganti | edace41 | 2019-06-18 13:28:28 +0100 | [diff] [blame] | 663 | #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) |
| 664 | void PrintUsage(char** argv) { |
Mikhail Khokhlov | 582f7ce | 2020-01-14 17:30:34 +0000 | [diff] [blame] | 665 | PERFETTO_ELOG(R"( |
| 666 | Interactive trace processor shell. |
| 667 | Usage: %s [OPTIONS] trace_file.pb |
| 668 | |
| 669 | Options: |
| 670 | -q, --query-file FILE Read and execute an SQL query from a file. |
| 671 | If used with --run-metrics, the query is |
| 672 | executed after the selected metrics and |
| 673 | the metrics output is suppressed. |
| 674 | --run-metrics x,y,z Runs a comma separated list of metrics and |
| 675 | prints the result as a TraceMetrics proto |
| 676 | to stdout. The specified can either be |
| 677 | in-built metrics or SQL/proto files of |
| 678 | extension metrics. |
| 679 | --metrics-output [binary|text|json] Allows the output of --run-metrics to be |
| 680 | specified in either proto binary, proto |
| 681 | text format or JSON format (default: proto |
| 682 | text).)", |
| 683 | argv[0]); |
Lalit Maganti | edace41 | 2019-06-18 13:28:28 +0100 | [diff] [blame] | 684 | } |
| 685 | |
| 686 | CommandLineOptions ParseCommandLineOptions(int argc, char** argv) { |
| 687 | CommandLineOptions command_line_options; |
| 688 | |
Mikhail Khokhlov | 582f7ce | 2020-01-14 17:30:34 +0000 | [diff] [blame] | 689 | if (argc < 2 || argc % 2 == 1) { |
Lalit Maganti | edace41 | 2019-06-18 13:28:28 +0100 | [diff] [blame] | 690 | PrintUsage(argv); |
| 691 | exit(1); |
| 692 | } |
| 693 | |
Mikhail Khokhlov | 582f7ce | 2020-01-14 17:30:34 +0000 | [diff] [blame] | 694 | for (int i = 1; i < argc - 1; i += 2) { |
| 695 | if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--query-file") == 0) { |
| 696 | command_line_options.query_file_path = argv[i + 1]; |
| 697 | } else if (strcmp(argv[i], "--run-metrics") == 0) { |
| 698 | command_line_options.metric_names = argv[i + 1]; |
| 699 | } else if (strcmp(argv[i], "--metrics-output") == 0) { |
| 700 | command_line_options.metric_output = argv[i + 1]; |
| 701 | } else { |
| 702 | PrintUsage(argv); |
| 703 | exit(1); |
| 704 | } |
| 705 | } |
| 706 | command_line_options.trace_file_path = argv[argc - 1]; |
| 707 | command_line_options.launch_shell = |
| 708 | command_line_options.metric_names.empty() && |
| 709 | command_line_options.query_file_path.empty(); |
Lalit Maganti | edace41 | 2019-06-18 13:28:28 +0100 | [diff] [blame] | 710 | return command_line_options; |
| 711 | } |
| 712 | |
Lalit Maganti | f9d8030 | 2019-06-19 12:48:25 +0100 | [diff] [blame] | 713 | util::Status RegisterExtraMetrics(const std::string&, const std::string&) { |
| 714 | return util::ErrStatus("RegisterExtraMetrics not implemented on Windows"); |
| 715 | } |
| 716 | |
Lalit Maganti | edace41 | 2019-06-18 13:28:28 +0100 | [diff] [blame] | 717 | #else // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) |
Lalit Maganti | f9d8030 | 2019-06-19 12:48:25 +0100 | [diff] [blame] | 718 | |
Lalit Maganti | edace41 | 2019-06-18 13:28:28 +0100 | [diff] [blame] | 719 | void PrintUsage(char** argv) { |
| 720 | PERFETTO_ELOG(R"( |
| 721 | Interactive trace processor shell. |
| 722 | Usage: %s [OPTIONS] trace_file.pb |
| 723 | |
| 724 | Options: |
Lalit Maganti | 82adf6a | 2019-09-26 18:05:51 +0100 | [diff] [blame] | 725 | -h, --help Prints this guide. |
| 726 | -v, --version Prints the version of trace processor. |
| 727 | -d, --debug Enable virtual table debugging. |
| 728 | -W, --wide Prints interactive output with double |
| 729 | column width. |
| 730 | -p, --perf-file FILE Writes the time taken to ingest the trace |
| 731 | and execute the queries to the given file. |
| 732 | Only valid with -q or --run-metrics and |
| 733 | the file will only be written if the |
| 734 | execution is successful. |
| 735 | -q, --query-file FILE Read and execute an SQL query from a file. |
Rafal Slawik | d28f030 | 2019-10-22 17:00:04 +0100 | [diff] [blame] | 736 | If used with --run-metrics, the query is |
| 737 | executed after the selected metrics and |
| 738 | the metrics output is suppressed. |
Primiano Tucci | a36cccc | 2019-10-27 13:15:04 +0100 | [diff] [blame] | 739 | -D, --httpd Enables the HTTP RPC server. |
Lalit Maganti | 82adf6a | 2019-09-26 18:05:51 +0100 | [diff] [blame] | 740 | -i, --interactive Starts interactive mode even after a query |
| 741 | file is specified with -q or |
| 742 | --run-metrics. |
| 743 | -e, --export FILE Export the trace into a SQLite database. |
| 744 | --run-metrics x,y,z Runs a comma separated list of metrics and |
| 745 | prints the result as a TraceMetrics proto |
| 746 | to stdout. The specified can either be |
| 747 | in-built metrics or SQL/proto files of |
| 748 | extension metrics. |
| 749 | --metrics-output=[binary|text|json] Allows the output of --run-metrics to be |
| 750 | specified in either proto binary, proto |
| 751 | text format or JSON format (default: proto |
| 752 | text). |
| 753 | --extra-metrics PATH Registers all SQL files at the given path |
| 754 | to the trace processor and extends the |
| 755 | builtin metrics proto with |
Lalit Maganti | 55d0bc7 | 2019-10-23 16:22:00 +0100 | [diff] [blame] | 756 | $PATH/metrics-ext.proto. |
| 757 | --full-sort Forces the trace processor into performing |
| 758 | a full sort ignoring any windowing |
| 759 | logic.)", |
Lalit Maganti | edace41 | 2019-06-18 13:28:28 +0100 | [diff] [blame] | 760 | argv[0]); |
| 761 | } |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 762 | |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 763 | CommandLineOptions ParseCommandLineOptions(int argc, char** argv) { |
| 764 | CommandLineOptions command_line_options; |
| 765 | enum LongOption { |
| 766 | OPT_RUN_METRICS = 1000, |
| 767 | OPT_METRICS_OUTPUT, |
Lalit Maganti | f9d8030 | 2019-06-19 12:48:25 +0100 | [diff] [blame] | 768 | OPT_EXTRA_METRICS, |
Lalit Maganti | 55d0bc7 | 2019-10-23 16:22:00 +0100 | [diff] [blame] | 769 | OPT_FORCE_FULL_SORT, |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 770 | }; |
| 771 | |
| 772 | static const struct option long_options[] = { |
| 773 | {"help", no_argument, nullptr, 'h'}, |
| 774 | {"version", no_argument, nullptr, 'v'}, |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 775 | {"wide", no_argument, nullptr, 'W'}, |
Primiano Tucci | a36cccc | 2019-10-27 13:15:04 +0100 | [diff] [blame] | 776 | {"httpd", no_argument, nullptr, 'D'}, |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 777 | {"interactive", no_argument, nullptr, 'i'}, |
| 778 | {"debug", no_argument, nullptr, 'd'}, |
| 779 | {"perf-file", required_argument, nullptr, 'p'}, |
| 780 | {"query-file", required_argument, nullptr, 'q'}, |
| 781 | {"export", required_argument, nullptr, 'e'}, |
| 782 | {"run-metrics", required_argument, nullptr, OPT_RUN_METRICS}, |
| 783 | {"metrics-output", required_argument, nullptr, OPT_METRICS_OUTPUT}, |
Lalit Maganti | f9d8030 | 2019-06-19 12:48:25 +0100 | [diff] [blame] | 784 | {"extra-metrics", required_argument, nullptr, OPT_EXTRA_METRICS}, |
Lalit Maganti | 55d0bc7 | 2019-10-23 16:22:00 +0100 | [diff] [blame] | 785 | {"full-sort", no_argument, nullptr, OPT_FORCE_FULL_SORT}, |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 786 | {nullptr, 0, nullptr, 0}}; |
| 787 | |
| 788 | bool explicit_interactive = false; |
| 789 | int option_index = 0; |
| 790 | for (;;) { |
| 791 | int option = |
Primiano Tucci | a36cccc | 2019-10-27 13:15:04 +0100 | [diff] [blame] | 792 | getopt_long(argc, argv, "hvWiDdp:q:e:", long_options, &option_index); |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 793 | |
| 794 | if (option == -1) |
| 795 | break; // EOF. |
| 796 | |
| 797 | if (option == 'v') { |
| 798 | printf("%s\n", PERFETTO_GET_GIT_REVISION()); |
| 799 | exit(0); |
| 800 | } |
| 801 | |
| 802 | if (option == 'i') { |
| 803 | explicit_interactive = true; |
| 804 | continue; |
| 805 | } |
| 806 | |
Primiano Tucci | a36cccc | 2019-10-27 13:15:04 +0100 | [diff] [blame] | 807 | if (option == 'D') { |
| 808 | #if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD) |
| 809 | command_line_options.enable_httpd = true; |
| 810 | #else |
| 811 | PERFETTO_FATAL("HTTP RPC module not supported in this build"); |
| 812 | #endif |
| 813 | continue; |
| 814 | } |
| 815 | |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 816 | if (option == 'W') { |
| 817 | command_line_options.wide = true; |
| 818 | continue; |
| 819 | } |
| 820 | |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 821 | if (option == 'd') { |
| 822 | EnableSQLiteVtableDebugging(); |
| 823 | continue; |
| 824 | } |
| 825 | |
| 826 | if (option == 'p') { |
| 827 | command_line_options.perf_file_path = optarg; |
| 828 | continue; |
| 829 | } |
| 830 | |
| 831 | if (option == 'q') { |
| 832 | command_line_options.query_file_path = optarg; |
| 833 | continue; |
| 834 | } |
| 835 | |
| 836 | if (option == 'e') { |
| 837 | command_line_options.sqlite_file_path = optarg; |
| 838 | continue; |
| 839 | } |
| 840 | |
| 841 | if (option == OPT_RUN_METRICS) { |
| 842 | command_line_options.metric_names = optarg; |
| 843 | continue; |
| 844 | } |
| 845 | |
| 846 | if (option == OPT_METRICS_OUTPUT) { |
| 847 | command_line_options.metric_output = optarg; |
| 848 | continue; |
| 849 | } |
| 850 | |
Lalit Maganti | f9d8030 | 2019-06-19 12:48:25 +0100 | [diff] [blame] | 851 | if (option == OPT_EXTRA_METRICS) { |
| 852 | command_line_options.metric_extra = optarg; |
| 853 | continue; |
| 854 | } |
| 855 | |
Lalit Maganti | 55d0bc7 | 2019-10-23 16:22:00 +0100 | [diff] [blame] | 856 | if (option == OPT_FORCE_FULL_SORT) { |
| 857 | command_line_options.force_full_sort = true; |
| 858 | continue; |
| 859 | } |
| 860 | |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 861 | PrintUsage(argv); |
| 862 | exit(option == 'h' ? 0 : 1); |
| 863 | } |
| 864 | |
| 865 | command_line_options.launch_shell = |
| 866 | explicit_interactive || (command_line_options.metric_names.empty() && |
| 867 | command_line_options.query_file_path.empty()); |
| 868 | |
| 869 | // Only allow non-interactive queries to emit perf data. |
| 870 | if (!command_line_options.perf_file_path.empty() && |
| 871 | command_line_options.launch_shell) { |
| 872 | PrintUsage(argv); |
| 873 | exit(1); |
| 874 | } |
| 875 | |
Primiano Tucci | ee2ce1d | 2019-11-01 19:14:17 +0100 | [diff] [blame] | 876 | // The only case where we allow omitting the trace file path is when running |
| 877 | // in --http mode. In all other cases, the last argument must be the trace |
| 878 | // file. |
| 879 | if (optind == argc - 1 && argv[optind]) { |
| 880 | command_line_options.trace_file_path = argv[optind]; |
| 881 | } else if (!command_line_options.enable_httpd) { |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 882 | PrintUsage(argv); |
| 883 | exit(1); |
| 884 | } |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 885 | return command_line_options; |
| 886 | } |
Lalit Maganti | f9d8030 | 2019-06-19 12:48:25 +0100 | [diff] [blame] | 887 | |
| 888 | util::Status RegisterExtraMetric(const std::string& parent_path, |
| 889 | const std::string& path) { |
| 890 | // Silently ignore any non-SQL files. |
| 891 | if (path.find(".sql") == std::string::npos) |
| 892 | return util::OkStatus(); |
| 893 | |
| 894 | std::string sql; |
| 895 | base::ReadFile(parent_path + "/" + path, &sql); |
| 896 | return g_tp->RegisterMetric(path, sql); |
| 897 | } |
| 898 | |
| 899 | util::Status RegisterExtraMetrics(const std::string& path, |
| 900 | const std::string& group) { |
| 901 | std::string full_path = path + "/" + group; |
| 902 | DIR* dir = opendir(full_path.c_str()); |
| 903 | if (dir == nullptr) { |
| 904 | return util::ErrStatus( |
| 905 | "Failed to open directory %s to register extra metrics", |
| 906 | full_path.c_str()); |
| 907 | } |
| 908 | |
| 909 | for (auto* dirent = readdir(dir); dirent != nullptr; dirent = readdir(dir)) { |
| 910 | util::Status status = util::OkStatus(); |
| 911 | if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) |
| 912 | continue; |
| 913 | |
| 914 | if (dirent->d_type == DT_DIR) { |
| 915 | status = RegisterExtraMetrics(path, group + dirent->d_name + "/"); |
| 916 | } else if (dirent->d_type == DT_REG) { |
| 917 | status = RegisterExtraMetric(path, group + dirent->d_name); |
| 918 | } |
| 919 | if (!status.ok()) |
| 920 | return status; |
| 921 | } |
| 922 | return util::OkStatus(); |
| 923 | } |
| 924 | |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 925 | #endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) |
| 926 | |
Deepanjan Roy | ab8c05d | 2020-01-21 09:55:26 -0500 | [diff] [blame^] | 927 | void ExtendPoolWithBinaryDescriptor(google::protobuf::DescriptorPool& pool, |
| 928 | const void* data, |
| 929 | int size) { |
| 930 | google::protobuf::FileDescriptorSet desc_set; |
| 931 | desc_set.ParseFromArray(data, size); |
| 932 | for (const auto& desc : desc_set.file()) { |
| 933 | pool.BuildFile(desc); |
| 934 | } |
| 935 | } |
| 936 | |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 937 | int TraceProcessorMain(int argc, char** argv) { |
| 938 | CommandLineOptions options = ParseCommandLineOptions(argc, argv); |
| 939 | |
| 940 | // Load the trace file into the trace processor. |
| 941 | Config config; |
Lalit Maganti | 55d0bc7 | 2019-10-23 16:22:00 +0100 | [diff] [blame] | 942 | config.force_full_sort = options.force_full_sort; |
| 943 | |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 944 | std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config); |
Ioannis Ilkos | f837129 | 2018-11-05 16:47:35 +0000 | [diff] [blame] | 945 | g_tp = tp.get(); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 946 | |
Primiano Tucci | ee2ce1d | 2019-11-01 19:14:17 +0100 | [diff] [blame] | 947 | base::TimeNanos t_load{}; |
| 948 | if (!options.trace_file_path.empty()) { |
| 949 | auto t_load_start = base::GetWallTimeNs(); |
| 950 | double size_mb = 0; |
| 951 | util::Status read_status = |
| 952 | ReadTrace(tp.get(), options.trace_file_path.c_str(), |
| 953 | [&size_mb](size_t parsed_size) { |
| 954 | size_mb = parsed_size / 1E6; |
| 955 | fprintf(stderr, "\rLoading trace: %.2f MB\r", size_mb); |
| 956 | }); |
| 957 | if (!read_status.ok()) { |
| 958 | PERFETTO_ELOG("Could not read trace file (path: %s): %s", |
| 959 | options.trace_file_path.c_str(), read_status.c_message()); |
| 960 | return 1; |
| 961 | } |
Florian Mayer | 6cc8b1d | 2019-12-18 16:37:32 +0000 | [diff] [blame] | 962 | |
| 963 | std::unique_ptr<profiling::Symbolizer> symbolizer; |
| 964 | auto binary_path = profiling::GetPerfettoBinaryPath(); |
| 965 | if (!binary_path.empty()) { |
| 966 | #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER) |
| 967 | symbolizer.reset(new profiling::LocalSymbolizer(std::move(binary_path))); |
| 968 | #else |
| 969 | PERFETTO_FATAL("This build does not support local symbolization."); |
| 970 | #endif |
| 971 | } |
| 972 | if (symbolizer) { |
| 973 | profiling::SymbolizeDatabase( |
| 974 | tp.get(), symbolizer.get(), [&tp](const std::string& trace_proto) { |
| 975 | std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]); |
| 976 | memcpy(buf.get(), trace_proto.data(), trace_proto.size()); |
| 977 | auto status = tp->Parse(std::move(buf), trace_proto.size()); |
| 978 | if (!status.ok()) { |
| 979 | PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s", |
| 980 | status.message().c_str()); |
| 981 | return; |
| 982 | } |
| 983 | }); |
| 984 | tp->NotifyEndOfFile(); |
| 985 | } |
| 986 | |
Primiano Tucci | ee2ce1d | 2019-11-01 19:14:17 +0100 | [diff] [blame] | 987 | t_load = base::GetWallTimeNs() - t_load_start; |
| 988 | double t_load_s = t_load.count() / 1E9; |
| 989 | PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb, |
| 990 | size_mb / t_load_s); |
| 991 | } // if (!trace_file_path.empty()) |
| 992 | |
| 993 | #if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD) |
| 994 | if (options.enable_httpd) { |
| 995 | RunHttpRPCServer(std::move(tp)); |
| 996 | return 0; |
| 997 | } |
| 998 | #endif |
| 999 | |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 1000 | #if PERFETTO_HAS_SIGNAL_H() |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 1001 | signal(SIGINT, [](int) { g_tp->InterruptQuery(); }); |
Primiano Tucci | 2464714 | 2018-08-13 21:18:24 +0200 | [diff] [blame] | 1002 | #endif |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 1003 | |
Lalit Maganti | 49ae901 | 2019-04-03 16:10:39 +0100 | [diff] [blame] | 1004 | // Print out the stats to stderr for the trace. |
| 1005 | if (!PrintStats()) { |
| 1006 | return 1; |
| 1007 | } |
| 1008 | |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 1009 | auto t_run_start = base::GetWallTimeNs(); |
| 1010 | |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 1011 | // Descriptor pool used for printing output as textproto. |
Deepanjan Roy | ab8c05d | 2020-01-21 09:55:26 -0500 | [diff] [blame^] | 1012 | // Building on top of generated pool so default protos in |
| 1013 | // google.protobuf.descriptor.proto are available. |
| 1014 | google::protobuf::DescriptorPool pool( |
| 1015 | google::protobuf::DescriptorPool::generated_pool()); |
| 1016 | ExtendPoolWithBinaryDescriptor(pool, kMetricsDescriptor.data(), |
| 1017 | kMetricsDescriptor.size()); |
| 1018 | ExtendPoolWithBinaryDescriptor(pool, kCustomOptionsDescriptor.data(), |
| 1019 | kCustomOptionsDescriptor.size()); |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 1020 | |
Lalit Maganti | f9d8030 | 2019-06-19 12:48:25 +0100 | [diff] [blame] | 1021 | if (!options.metric_extra.empty()) { |
| 1022 | util::Status status = RegisterExtraMetrics(options.metric_extra, ""); |
| 1023 | if (!status.ok()) { |
| 1024 | PERFETTO_ELOG("Failed to register extra metrics: %s", status.c_message()); |
| 1025 | return 1; |
| 1026 | } |
| 1027 | |
| 1028 | auto ext_proto = options.metric_extra + "/metrics-ext.proto"; |
| 1029 | // Check if the file exists |
| 1030 | base::ScopedFile file(base::OpenFile(ext_proto, O_RDONLY)); |
| 1031 | if (file.get() != -1) { |
| 1032 | status = ExtendMetricsProto(ext_proto, &pool); |
| 1033 | if (!status.ok()) { |
| 1034 | PERFETTO_ELOG("Failed to extend metrics proto: %s", status.c_message()); |
| 1035 | return 1; |
| 1036 | } |
| 1037 | } |
| 1038 | } |
| 1039 | |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 1040 | if (!options.metric_names.empty()) { |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 1041 | std::vector<std::string> metrics; |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 1042 | for (base::StringSplitter ss(options.metric_names, ','); ss.Next();) { |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 1043 | metrics.emplace_back(ss.cur_token()); |
| 1044 | } |
Lalit Maganti | 58e2492 | 2019-05-30 18:56:14 +0100 | [diff] [blame] | 1045 | |
| 1046 | // For all metrics which are files, register them and extend the metrics |
| 1047 | // proto. |
| 1048 | for (size_t i = 0; i < metrics.size(); ++i) { |
| 1049 | const std::string& metric_or_path = metrics[i]; |
| 1050 | |
| 1051 | // If there is no extension, we assume it is a builtin metric. |
| 1052 | auto ext_idx = metric_or_path.rfind("."); |
| 1053 | if (ext_idx == std::string::npos) |
| 1054 | continue; |
| 1055 | |
| 1056 | std::string no_ext_name = metric_or_path.substr(0, ext_idx); |
| 1057 | util::Status status = RegisterMetric(no_ext_name + ".sql"); |
| 1058 | if (!status.ok()) { |
| 1059 | PERFETTO_ELOG("Unable to register metric %s: %s", |
| 1060 | metric_or_path.c_str(), status.c_message()); |
| 1061 | return 1; |
| 1062 | } |
| 1063 | |
| 1064 | status = ExtendMetricsProto(no_ext_name + ".proto", &pool); |
| 1065 | if (!status.ok()) { |
| 1066 | PERFETTO_ELOG("Unable to extend metrics proto %s: %s", |
| 1067 | metric_or_path.c_str(), status.c_message()); |
| 1068 | return 1; |
| 1069 | } |
| 1070 | |
Mikhail Khokhlov | 582f7ce | 2020-01-14 17:30:34 +0000 | [diff] [blame] | 1071 | metrics[i] = BaseName(no_ext_name); |
Lalit Maganti | 58e2492 | 2019-05-30 18:56:14 +0100 | [diff] [blame] | 1072 | } |
| 1073 | |
Lalit Maganti | 25863f7 | 2019-08-28 22:14:25 +0100 | [diff] [blame] | 1074 | OutputFormat format; |
Rafal Slawik | d28f030 | 2019-10-22 17:00:04 +0100 | [diff] [blame] | 1075 | if (!options.query_file_path.empty()) { |
| 1076 | format = OutputFormat::kNone; |
| 1077 | } else if (options.metric_output == "binary") { |
Lalit Maganti | 25863f7 | 2019-08-28 22:14:25 +0100 | [diff] [blame] | 1078 | format = OutputFormat::kBinaryProto; |
| 1079 | } else if (options.metric_output == "json") { |
| 1080 | format = OutputFormat::kJson; |
| 1081 | } else { |
| 1082 | format = OutputFormat::kTextProto; |
| 1083 | } |
| 1084 | int ret = RunMetrics(std::move(metrics), format, pool); |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 1085 | if (!ret) { |
| 1086 | auto t_query = base::GetWallTimeNs() - t_run_start; |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 1087 | ret = MaybePrintPerfFile(options.perf_file_path, t_load, t_query); |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 1088 | } |
Lalit Maganti | 812ef67 | 2019-05-28 13:28:02 +0100 | [diff] [blame] | 1089 | if (ret) |
| 1090 | return ret; |
Rafal Slawik | d28f030 | 2019-10-22 17:00:04 +0100 | [diff] [blame] | 1091 | } |
Lalit Maganti | 7c95978 | 2019-04-02 16:54:12 +0100 | [diff] [blame] | 1092 | |
Rafal Slawik | d28f030 | 2019-10-22 17:00:04 +0100 | [diff] [blame] | 1093 | // If we were given a query file, load contents |
| 1094 | std::vector<std::string> queries; |
| 1095 | if (!options.query_file_path.empty()) { |
| 1096 | base::ScopedFstream file(fopen(options.query_file_path.c_str(), "r")); |
| 1097 | if (!file) { |
| 1098 | PERFETTO_ELOG("Could not open query file (path: %s)", |
| 1099 | options.query_file_path.c_str()); |
Lalit Maganti | 22a1e0c | 2018-12-04 20:28:18 +0000 | [diff] [blame] | 1100 | return 1; |
| 1101 | } |
Rafal Slawik | d28f030 | 2019-10-22 17:00:04 +0100 | [diff] [blame] | 1102 | if (!LoadQueries(file.get(), &queries)) { |
| 1103 | return 1; |
| 1104 | } |
| 1105 | } |
| 1106 | |
| 1107 | if (!RunQueryAndPrintResult(queries, stdout)) { |
| 1108 | return 1; |
Primiano Tucci | 7e33029 | 2018-08-24 19:10:52 +0200 | [diff] [blame] | 1109 | } |
Hector Dearman | e44ad45 | 2018-09-21 11:51:57 +0100 | [diff] [blame] | 1110 | |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 1111 | if (!options.sqlite_file_path.empty()) { |
| 1112 | return ExportTraceToDatabase(options.sqlite_file_path); |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 1113 | } |
| 1114 | |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 1115 | if (!options.launch_shell) { |
Lalit Maganti | 260e04d | 2019-05-03 13:17:01 +0100 | [diff] [blame] | 1116 | auto t_query = base::GetWallTimeNs() - t_run_start; |
Mikhail Khokhlov | 8643d1c | 2019-06-04 12:02:47 +0100 | [diff] [blame] | 1117 | return MaybePrintPerfFile(options.perf_file_path, t_load, t_query); |
Sami Kyostila | 3366894 | 2018-11-13 16:33:32 +0000 | [diff] [blame] | 1118 | } |
| 1119 | |
Lalit Maganti | a8dcfdd | 2019-07-08 18:14:27 +0100 | [diff] [blame] | 1120 | return StartInteractiveShell(options.wide ? 40 : 20); |
Primiano Tucci | 5968caf | 2018-08-06 10:31:46 +0100 | [diff] [blame] | 1121 | } |
Lalit Maganti | 1ebebf1 | 2018-10-15 17:24:04 +0100 | [diff] [blame] | 1122 | |
| 1123 | } // namespace |
| 1124 | |
| 1125 | } // namespace trace_processor |
| 1126 | } // namespace perfetto |
| 1127 | |
| 1128 | int main(int argc, char** argv) { |
| 1129 | return perfetto::trace_processor::TraceProcessorMain(argc, argv); |
| 1130 | } |