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
17 #include <stdio.h>
18
19 #include <fstream>
20 #include <iostream>
21 #include <limits>
22 #include <vector>
23
24 #include "perfetto/base/logging.h"
25 #include "perfetto/ext/base/string_utils.h"
26 #include "perfetto/ext/base/version.h"
27 #include "src/traceconv/deobfuscate_profile.h"
28 #include "src/traceconv/symbolize_profile.h"
29 #include "src/traceconv/trace_to_hprof.h"
30 #include "src/traceconv/trace_to_json.h"
31 #include "src/traceconv/trace_to_profile.h"
32 #include "src/traceconv/trace_to_systrace.h"
33 #include "src/traceconv/trace_to_text.h"
34
35 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
36 #include <fcntl.h>
37 #include <io.h>
38 #else
39 #include <unistd.h>
40 #endif
41
42 namespace perfetto {
43 namespace trace_to_text {
44 namespace {
45
Usage(const char * argv0)46 int Usage(const char* argv0) {
47 fprintf(stderr,
48 "Usage: %s MODE [OPTIONS] [input file] [output file]\n"
49 "modes:\n"
50 " systrace|json|ctrace|text|profile|hprof|symbolize|deobfuscate\n"
51 "options:\n"
52 " [--truncate start|end]\n"
53 " [--full-sort]\n"
54 "\"profile\" mode options:\n"
55 " [--perf] generate a perf profile instead of a heap profile\n"
56 " [--no-annotations] do not suffix frame names with derived "
57 "annotations\n"
58 " [--timestamps TIMESTAMP1,TIMESTAMP2,...] generate profiles "
59 "only for these *specific* timestamps\n"
60 " [--pid PID] generate profiles only for this process id\n",
61 argv0);
62 return 1;
63 }
64
StringToUint64OrDie(const char * str)65 uint64_t StringToUint64OrDie(const char* str) {
66 char* end;
67 uint64_t number = static_cast<uint64_t>(strtoll(str, &end, 10));
68 if (*end != '\0') {
69 PERFETTO_ELOG("Invalid %s. Expected decimal integer.", str);
70 exit(1);
71 }
72 return number;
73 }
74
Main(int argc,char ** argv)75 int Main(int argc, char** argv) {
76 std::vector<const char*> positional_args;
77 Keep truncate_keep = Keep::kAll;
78 uint64_t pid = 0;
79 std::vector<uint64_t> timestamps;
80 bool full_sort = false;
81 bool perf_profile = false;
82 bool profile_no_annotations = false;
83 for (int i = 1; i < argc; i++) {
84 if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
85 printf("%s\n", base::GetVersionString());
86 return 0;
87 } else if (strcmp(argv[i], "-t") == 0 ||
88 strcmp(argv[i], "--truncate") == 0) {
89 i++;
90 if (i <= argc && strcmp(argv[i], "start") == 0) {
91 truncate_keep = Keep::kStart;
92 } else if (i <= argc && strcmp(argv[i], "end") == 0) {
93 truncate_keep = Keep::kEnd;
94 } else {
95 PERFETTO_ELOG(
96 "--truncate must specify whether to keep the end or the "
97 "start of the trace.");
98 return Usage(argv[0]);
99 }
100 } else if (i <= argc && strcmp(argv[i], "--pid") == 0) {
101 i++;
102 pid = StringToUint64OrDie(argv[i]);
103 } else if (i <= argc && strcmp(argv[i], "--timestamps") == 0) {
104 i++;
105 std::vector<std::string> ts_strings = base::SplitString(argv[i], ",");
106 for (const std::string& ts : ts_strings) {
107 timestamps.emplace_back(StringToUint64OrDie(ts.c_str()));
108 }
109 } else if (strcmp(argv[i], "--perf") == 0) {
110 perf_profile = true;
111 } else if (strcmp(argv[i], "--no-annotations") == 0) {
112 profile_no_annotations = true;
113 } else if (strcmp(argv[i], "--full-sort") == 0) {
114 full_sort = true;
115 } else {
116 positional_args.push_back(argv[i]);
117 }
118 }
119
120 if (positional_args.empty())
121 return Usage(argv[0]);
122
123 std::istream* input_stream;
124 std::ifstream file_istream;
125 if (positional_args.size() > 1) {
126 const char* file_path = positional_args[1];
127 file_istream.open(file_path, std::ios_base::in | std::ios_base::binary);
128 if (!file_istream.is_open())
129 PERFETTO_FATAL("Could not open %s", file_path);
130 input_stream = &file_istream;
131 } else {
132 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
133 if (isatty(STDIN_FILENO)) {
134 PERFETTO_ELOG("Reading from stdin but it's connected to a TTY");
135 PERFETTO_LOG("It is unlikely that you want to type in some binary.");
136 PERFETTO_LOG("Either pass a file path to the cmdline or pipe stdin");
137 return Usage(argv[0]);
138 }
139 #endif
140 input_stream = &std::cin;
141 }
142
143 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
144 // We don't want the runtime to replace "\n" with "\r\n" on `std::cout`.
145 _setmode(_fileno(stdout), _O_BINARY);
146 #endif
147
148 std::ostream* output_stream;
149 std::ofstream file_ostream;
150 if (positional_args.size() > 2) {
151 const char* file_path = positional_args[2];
152 file_ostream.open(file_path, std::ios_base::out | std::ios_base::trunc |
153 std::ios_base::binary);
154 if (!file_ostream.is_open())
155 PERFETTO_FATAL("Could not open %s", file_path);
156 output_stream = &file_ostream;
157 } else {
158 output_stream = &std::cout;
159 }
160
161 std::string format(positional_args[0]);
162
163 if ((format != "profile" && format != "hprof") &&
164 (pid != 0 || !timestamps.empty())) {
165 PERFETTO_ELOG(
166 "--pid and --timestamps are supported only for profile "
167 "formats.");
168 return 1;
169 }
170 if (perf_profile && format != "profile") {
171 PERFETTO_ELOG("--perf requires profile format.");
172 return 1;
173 }
174
175 if (format == "json")
176 return TraceToJson(input_stream, output_stream, /*compress=*/false,
177 truncate_keep, full_sort);
178
179 if (format == "systrace")
180 return TraceToSystrace(input_stream, output_stream, /*ctrace=*/false,
181 truncate_keep, full_sort);
182
183 if (format == "ctrace")
184 return TraceToSystrace(input_stream, output_stream, /*ctrace=*/true,
185 truncate_keep, full_sort);
186
187 if (truncate_keep != Keep::kAll) {
188 PERFETTO_ELOG(
189 "--truncate is unsupported for text|profile|symbolize format.");
190 return 1;
191 }
192
193 if (full_sort) {
194 PERFETTO_ELOG(
195 "--full-sort is unsupported for text|profile|symbolize format.");
196 return 1;
197 }
198
199 if (format == "text") {
200 return TraceToText(input_stream, output_stream) ? 0 : 1;
201 }
202
203 if (format == "profile") {
204 return perf_profile
205 ? TraceToPerfProfile(input_stream, output_stream, pid,
206 timestamps, !profile_no_annotations)
207 : TraceToHeapProfile(input_stream, output_stream, pid,
208 timestamps, !profile_no_annotations);
209 }
210
211 if (format == "hprof")
212 return TraceToHprof(input_stream, output_stream, pid, timestamps);
213
214 if (format == "symbolize")
215 return SymbolizeProfile(input_stream, output_stream);
216
217 if (format == "deobfuscate")
218 return DeobfuscateProfile(input_stream, output_stream);
219 return Usage(argv[0]);
220 }
221
222 } // namespace
223 } // namespace trace_to_text
224 } // namespace perfetto
225
main(int argc,char ** argv)226 int main(int argc, char** argv) {
227 return perfetto::trace_to_text::Main(argc, argv);
228 }
229