/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "perfetto/base/logging.h" #include "perfetto/ext/base/string_utils.h" #include "perfetto/ext/base/version.h" #include "tools/trace_to_text/deobfuscate_profile.h" #include "tools/trace_to_text/symbolize_profile.h" #include "tools/trace_to_text/trace_to_hprof.h" #include "tools/trace_to_text/trace_to_json.h" #include "tools/trace_to_text/trace_to_profile.h" #include "tools/trace_to_text/trace_to_systrace.h" #include "tools/trace_to_text/trace_to_text.h" #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) #include #endif namespace perfetto { namespace trace_to_text { namespace { int Usage(const char* argv0) { fprintf(stderr, "Usage: %s MODE [OPTIONS] [input file] [output file]\n" "modes:\n" " systrace|json|ctrace|text|profile|hprof|symbolize|deobfuscate\n" "options:\n" " [--truncate start|end]\n" " [--full-sort]\n" "\"profile\" mode options:\n" " [--perf] generate a perf profile instead of a heap profile\n" " [--no-annotations] do not suffix frame names with derived " "annotations\n" " [--timestamps TIMESTAMP1,TIMESTAMP2,...] generate profiles " "only for these *specific* timestamps\n" " [--pid PID] generate profiles only for this process id\n", argv0); return 1; } uint64_t StringToUint64OrDie(const char* str) { char* end; uint64_t number = static_cast(strtoll(str, &end, 10)); if (*end != '\0') { PERFETTO_ELOG("Invalid %s. Expected decimal integer.", str); exit(1); } return number; } int Main(int argc, char** argv) { std::vector positional_args; Keep truncate_keep = Keep::kAll; uint64_t pid = 0; std::vector timestamps; bool full_sort = false; bool perf_profile = false; bool profile_no_annotations = false; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) { printf("%s\n", base::GetVersionString()); return 0; } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--truncate") == 0) { i++; if (i <= argc && strcmp(argv[i], "start") == 0) { truncate_keep = Keep::kStart; } else if (i <= argc && strcmp(argv[i], "end") == 0) { truncate_keep = Keep::kEnd; } else { PERFETTO_ELOG( "--truncate must specify whether to keep the end or the " "start of the trace."); return Usage(argv[0]); } } else if (i <= argc && strcmp(argv[i], "--pid") == 0) { i++; pid = StringToUint64OrDie(argv[i]); } else if (i <= argc && strcmp(argv[i], "--timestamps") == 0) { i++; std::vector ts_strings = base::SplitString(argv[i], ","); for (const std::string& ts : ts_strings) { timestamps.emplace_back(StringToUint64OrDie(ts.c_str())); } } else if (strcmp(argv[i], "--perf") == 0) { perf_profile = true; } else if (strcmp(argv[i], "--no-annotations") == 0) { profile_no_annotations = true; } else if (strcmp(argv[i], "--full-sort") == 0) { full_sort = true; } else { positional_args.push_back(argv[i]); } } if (positional_args.empty()) return Usage(argv[0]); std::istream* input_stream; std::ifstream file_istream; if (positional_args.size() > 1) { const char* file_path = positional_args[1]; file_istream.open(file_path, std::ios_base::in | std::ios_base::binary); if (!file_istream.is_open()) PERFETTO_FATAL("Could not open %s", file_path); input_stream = &file_istream; } else { #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) if (isatty(STDIN_FILENO)) { PERFETTO_ELOG("Reading from stdin but it's connected to a TTY"); PERFETTO_LOG("It is unlikely that you want to type in some binary."); PERFETTO_LOG("Either pass a file path to the cmdline or pipe stdin"); return Usage(argv[0]); } #endif input_stream = &std::cin; } std::ostream* output_stream; std::ofstream file_ostream; if (positional_args.size() > 2) { const char* file_path = positional_args[2]; file_ostream.open(file_path, std::ios_base::out | std::ios_base::trunc); if (!file_ostream.is_open()) PERFETTO_FATAL("Could not open %s", file_path); output_stream = &file_ostream; } else { output_stream = &std::cout; } std::string format(positional_args[0]); if ((format != "profile" && format != "hprof") && (pid != 0 || !timestamps.empty())) { PERFETTO_ELOG( "--pid and --timestamps are supported only for profile " "formats."); return 1; } if (perf_profile && format != "profile") { PERFETTO_ELOG("--perf requires profile format."); return 1; } if (format == "json") return TraceToJson(input_stream, output_stream, /*compress=*/false, truncate_keep, full_sort); if (format == "systrace") return TraceToSystrace(input_stream, output_stream, /*ctrace=*/false, truncate_keep, full_sort); if (format == "ctrace") return TraceToSystrace(input_stream, output_stream, /*ctrace=*/true, truncate_keep, full_sort); if (truncate_keep != Keep::kAll) { PERFETTO_ELOG( "--truncate is unsupported for text|profile|symbolize format."); return 1; } if (full_sort) { PERFETTO_ELOG( "--full-sort is unsupported for text|profile|symbolize format."); return 1; } if (format == "text") return TraceToText(input_stream, output_stream); if (format == "profile") { return perf_profile ? TraceToPerfProfile(input_stream, output_stream, pid, timestamps, !profile_no_annotations) : TraceToHeapProfile(input_stream, output_stream, pid, timestamps, !profile_no_annotations); } if (format == "hprof") return TraceToHprof(input_stream, output_stream, pid, timestamps); if (format == "symbolize") return SymbolizeProfile(input_stream, output_stream); if (format == "deobfuscate") return DeobfuscateProfile(input_stream, output_stream); return Usage(argv[0]); } } // namespace } // namespace trace_to_text } // namespace perfetto int main(int argc, char** argv) { return perfetto::trace_to_text::Main(argc, argv); }