| /* | |
| * 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 "perfetto/ext/trace_processor/trace_processor_shell.h" | |
| #include <cctype> | |
| #include <cerrno> | |
| #include <cstddef> | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <functional> | |
| #include <memory> | |
| #include <string> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <vector> | |
| #include <google/protobuf/compiler/parser.h> | |
| #include <google/protobuf/descriptor.pb.h> | |
| #include <google/protobuf/dynamic_message.h> | |
| #include <google/protobuf/io/tokenizer.h> | |
| #include <google/protobuf/io/zero_copy_stream_impl.h> | |
| #include <google/protobuf/text_format.h> | |
| #include "perfetto/base/build_config.h" | |
| #include "perfetto/base/compiler.h" | |
| #include "perfetto/base/logging.h" | |
| #include "perfetto/base/status.h" | |
| #include "perfetto/ext/base/file_utils.h" | |
| #include "perfetto/ext/base/getopt.h" // IWYU pragma: keep | |
| #include "perfetto/ext/base/progress_reporter.h" | |
| #include "perfetto/ext/base/status_macros.h" | |
| #include "perfetto/ext/base/string_utils.h" | |
| #include "perfetto/ext/base/version.h" | |
| #include "perfetto/trace_processor/basic_types.h" | |
| #include "perfetto/trace_processor/metatrace_config.h" | |
| #include "perfetto/trace_processor/read_trace.h" | |
| #include "perfetto/trace_processor/trace_processor.h" | |
| #include "src/trace_processor/local_file_system.h" | |
| #include "src/trace_processor/read_trace_internal.h" | |
| #include "src/trace_processor/rpc/rpc.h" | |
| #include "src/trace_processor/rpc/stdiod.h" | |
| #include "src/trace_processor/shell/bundle_subcommand.h" | |
| #include "src/trace_processor/shell/common_flags.h" | |
| #include "src/trace_processor/shell/convert_subcommand.h" | |
| #include "src/trace_processor/shell/export_subcommand.h" | |
| #include "src/trace_processor/shell/interactive_subcommand.h" | |
| #include "src/trace_processor/shell/metatrace.h" | |
| #include "src/trace_processor/shell/metrics_subcommand.h" | |
| #include "src/trace_processor/shell/query_subcommand.h" | |
| #include "src/trace_processor/shell/server_subcommand.h" | |
| #include "src/trace_processor/shell/subcommand.h" | |
| #include "src/trace_processor/shell/summarize_subcommand.h" | |
| #include "src/trace_processor/shell/traceconv_compat.h" | |
| #include "src/trace_processor/shell/util_subcommand.h" | |
| #include "src/trace_processor/util/symbolizer/symbolize_database.h" | |
| #include "protos/perfetto/trace_processor/trace_processor.pbzero.h" | |
| #if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD) | |
| #include "src/trace_processor/rpc/httpd.h" | |
| #endif | |
| #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ | |
| PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ | |
| PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \ | |
| PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) | |
| #define PERFETTO_HAS_SIGNAL_H() 1 | |
| #else | |
| #define PERFETTO_HAS_SIGNAL_H() 0 | |
| #endif | |
| #if PERFETTO_HAS_SIGNAL_H() | |
| #include <signal.h> | |
| #endif | |
| #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) | |
| #include <io.h> | |
| #define ftruncate _chsize | |
| #else | |
| #include <dirent.h> | |
| #include <unistd.h> | |
| #endif | |
| namespace perfetto::trace_processor { | |
| namespace { | |
| struct CommandLineOptions { | |
| std::string trace_file_path; | |
| bool enable_httpd = false; | |
| std::string port_number; | |
| std::string listen_ip; | |
| std::vector<std::string> additional_cors_origins; | |
| bool enable_stdiod = false; | |
| bool launch_shell = false; | |
| bool force_full_sort = false; | |
| bool no_ftrace_raw = false; | |
| std::string query_file_path; | |
| std::string query_string; | |
| std::vector<std::string> sql_package_paths; | |
| std::vector<std::string> override_sql_package_paths; | |
| bool summary = false; | |
| std::string summary_metrics_v2; | |
| std::string summary_metadata_query; | |
| std::vector<std::string> summary_specs; | |
| std::string summary_output; | |
| std::string metatrace_path; | |
| size_t metatrace_buffer_capacity = 0; | |
| metatrace::MetatraceCategories metatrace_categories = | |
| static_cast<metatrace::MetatraceCategories>( | |
| metatrace::MetatraceCategories::QUERY_TIMELINE | | |
| metatrace::MetatraceCategories::API_TIMELINE); | |
| bool dev = false; | |
| std::vector<std::string> dev_flags; | |
| bool extra_checks = false; | |
| std::string export_file_path; | |
| std::string perf_file_path; | |
| bool wide = false; | |
| bool analyze_trace_proto_content = false; | |
| bool crop_track_events = false; | |
| bool allow_sql_file_access = false; | |
| std::string register_files_dir; | |
| std::string override_stdlib_path; | |
| std::string pre_metrics_v1_path; | |
| std::string metric_v1_names; | |
| std::string metric_v1_output; | |
| std::vector<std::string> raw_metric_v1_extensions; | |
| }; | |
| void PrintSubcommandHelp(const char* argv0) { | |
| printf(R"(Perfetto Trace Processor. | |
| Usage: %s [command] [flags] [trace_file] | |
| If no command is given, opens an interactive SQL shell on the trace file. | |
| trace_file can be a local path, an http(s):// URL, or a Perfetto UI share link | |
| (https://ui.perfetto.dev/#!/?s=<hash>). | |
| Commands: | |
| query Load a trace and run a SQL query. | |
| interactive Interactive SQL shell (default if no command is given). | |
| convert Convert trace format. | |
| summarize Compute a trace summary from specs and/or built-in metrics. | |
| bundle Bundle a trace with symbols and deobfuscation data. | |
| util Low-level trace utilities (symbolize, deobfuscate). | |
| server Start an RPC server (http or stdio). | |
| export Export a trace to a database file. | |
| metrics Run v1 metrics (deprecated; use 'summarize --metrics-v2'). | |
| Common flags (apply to all commands): | |
| -h, --help Show help (per-command if after a command). | |
| -v, --version Print version. | |
| --no-progress Disable live progress. | |
| --quiet Suppress progress and routine status output. | |
| --full-sort Force full sort ignoring windowing. | |
| --no-ftrace-raw Prevent ingestion of typed ftrace into raw table. | |
| --add-sql-package PATH Register SQL files from a directory as a package. | |
| -m, --metatrace FILE Enable metatracing, write to FILE. | |
| Run '%s help <command>' for per-command flags and details. | |
| Scripting or AI agent? Run '%s help agent' for a short guide: keeping a trace | |
| loaded across many queries, discovering tables and the SQL standard library. | |
| Examples: | |
| tp trace.pb Interactive shell. | |
| tp query trace.pb "SELECT ts, dur FROM slice" Run a query. | |
| tp query -f queries.sql trace.pb Run queries from file. | |
| tp server http Start HTTP server. | |
| tp summarize --metrics-v2 all trace.pb Summarize a trace. | |
| tp convert json trace.pb out.json Convert to JSON. | |
| tp query "https://.../trace.pftrace" "SELECT ..." Query a trace from a URL. | |
| tp "https://ui.perfetto.dev/#!/?s=<hash>" Open a UI share link. | |
| Classic interface: | |
| The previous flat-flag interface (-q, --httpd, --summary, -e, etc.) is | |
| fully supported and will remain so. Existing scripts will continue to work. | |
| Run '%s --help-classic' to see the classic flag reference. | |
| )", | |
| argv0, argv0, argv0, argv0); | |
| } | |
| // Printed by `trace_processor help agent`: a compact, self-contained guide | |
| // to driving trace_processor from scripts and coding agents, kept in the | |
| // binary so it is available wherever trace_processor is, with no separate | |
| // install. Agents rarely open --help on their own; the parse-time tip in | |
| // the query subcommand is what points them here. The guided workflows | |
| // live in the Perfetto agent skill (see the link at the end). | |
| void PrintAgentGuide(const char* argv0) { | |
| // Meant to be copied into scripts: show the bare program name rather than | |
| // whatever absolute path this binary was invoked as. | |
| const char* slash = strrchr(argv0, '/'); | |
| const char* name = slash ? slash + 1 : argv0; | |
| printf(R"(trace_processor for scripts and AI agents | |
| ========================================== | |
| 1. Load the trace once, then run many queries against it. | |
| Parsing is the slow part (seconds to minutes); a warm session pays it once. | |
| %s server unix --name SESSION --daemonize TRACE_FILE | |
| %s query --remote SESSION "SELECT ts, dur, name FROM slice LIMIT 5" | |
| %s query --remote SESSION -f queries.sql # long SQL from a file | |
| %s server kill SESSION # when completely done | |
| Tables and modules created in one call stay available for the next. | |
| Several ';'-separated statements per call are fine; each result set is | |
| printed in turn. One-shot form (re-parses the trace every time, only for a | |
| single quick question): %s query TRACE_FILE "SELECT ..." | |
| 2. Reading output. Run by a coding agent (e.g. CLAUDECODE set, stdout not a | |
| terminal), `query` is in agent mode: | |
| - Results are compact markdown tables. | |
| - Results over 1000 rows or 10KB show their first and last 20 rows and a | |
| footer like `(first 20 and last 20 of 5000 rows. ...)`. Aggregate or | |
| LIMIT rather than raising the caps (--max-rows/--max-bytes, 0 = no | |
| limit). | |
| - Cells are cut at 500 characters (--max-cell-width). | |
| - Errors are one JSON line on stderr with the error, the failing | |
| statement, its location (query:LINE:COL) and often a hint. | |
| --agent / --no-agent force it on or off; --format csv gives plain CSV. | |
| 3. Discover instead of guessing. | |
| SELECT * FROM slice LIMIT 0; -- exact columns of any | |
| -- table, view or query | |
| SELECT name FROM perfetto_tables; -- every table/view | |
| SELECT qualified_name, object_type, short_description | |
| FROM __intrinsic_stdlib_objects | |
| WHERE exposed = 1 AND regexp('startup|launch', summary, 'i') | |
| LIMIT 20; -- search the stdlib | |
| SELECT summary FROM __intrinsic_stdlib_objects | |
| WHERE qualified_name = 'android.startup.startups.android_startups'; | |
| -- docs for one object | |
| INCLUDE PERFETTO MODULE android.startup.startups; -- then use it | |
| The standard library (https://perfetto.dev/docs/analysis/stdlib-docs) has | |
| ready-made tables for most common questions; prefer it over hand-written | |
| joins on raw tables. | |
| 4. Core tables: slice (anything with a duration), thread, process, | |
| thread_state (Running/Runnable/Sleeping), sched (on-CPU time), counter, | |
| track, args. Frequently useful modules: | |
| slices.with_context thread_slice / process_slice / thread_or_process_slice | |
| sched.with_context sched_with_thread_process | |
| android.startup.startups android_startups | |
| android.anrs android_anrs | |
| android.frames.timeline android_frames | |
| android.memory.heap_graph.dominator_tree Java heap retained sizes | |
| linux.cpu.frequency cpu frequency residency | |
| stacks.cpu_profiling CPU sampling call stacks | |
| 5. PerfettoSQL rules of thumb. | |
| - Join on utid/upid (unique per trace), never tid/pid (recycled by the OS); | |
| report thread/process names to the user, not ids. | |
| - dur = -1 means the slice was still open at trace end; dur = 0 is an | |
| instant event. Handle both when summing durations. | |
| - Use GLOB or regexp(pattern, str, 'i') for matching, not LIKE. | |
| - Aggregate (COUNT/GROUP BY/LIMIT) rather than dumping raw rows. | |
| 6. More: https://perfetto.dev/docs/analysis/trace-processor (reference), | |
| https://perfetto.dev/docs/getting-started/using-ai (agent skill with | |
| guided Android memory and GPU workflows, plus an installable bundle). | |
| )", | |
| name, name, name, name, name); | |
| } | |
| void PrintClassicUsage(char** argv) { | |
| PERFETTO_ELOG(R"( | |
| Interactive trace processor shell. | |
| Usage: %s [FLAGS] trace_file.pb | |
| General purpose: | |
| -h, --help Prints this guide. | |
| -v, --version Prints the version of trace processor. | |
| Behavioural: | |
| -D, --httpd Enables the HTTP RPC server. | |
| --http-port PORT Specify what port to run HTTP RPC server. | |
| --http-ip-address ip Specify what ip address to run HTTP RPC server. | |
| --http-additional-cors-origins origin1,origin2,... | |
| Specify a comma-separated list of | |
| additional CORS allowed origins for the | |
| HTTP RPC server. These are in addition to | |
| the default origins: [https://ui.perfetto.dev, | |
| http://localhost:10000, http://127.0.0.1:10000] | |
| --stdiod Enables the stdio RPC server. | |
| -i, --interactive Starts interactive mode even after | |
| executing some other commands (-q, -Q, | |
| --run-metrics, --summary). | |
| Parsing: | |
| --full-sort Forces the trace processor into performing | |
| a full sort ignoring any windowing | |
| logic. | |
| --no-ftrace-raw Prevents ingestion of typed ftrace events | |
| into the raw table. This significantly | |
| reduces the memory usage of trace | |
| processor when loading traces containing | |
| ftrace events. | |
| PerfettoSQL: | |
| -q, --query-file FILE Read and execute an SQL query from a file. | |
| If used with --run-metrics, the query is | |
| executed after the selected metrics and | |
| the metrics output is suppressed. | |
| -Q, --query-string QUERY Execute the SQL query QUERY. | |
| If used with --run-metrics, the query is | |
| executed after the selected metrics and | |
| the metrics output is suppressed. | |
| --add-sql-package PATH[@PACKAGE] Registers SQL files from a directory as | |
| a package for use with INCLUDE PERFETTO | |
| MODULE statements. | |
| By default, the directory name becomes the | |
| root package name. Use @PACKAGE to | |
| override. | |
| Given a directory structure: | |
| mydir/ | |
| utils.sql | |
| helpers/common.sql | |
| --add-sql-package ./mydir | |
| Registers modules as: | |
| mydir.utils | |
| mydir.helpers.common | |
| Usage: INCLUDE PERFETTO MODULE mydir.utils; | |
| --add-sql-package ./mydir@foo | |
| Registers modules as: | |
| foo.utils | |
| foo.helpers.common | |
| Usage: INCLUDE PERFETTO MODULE foo.utils; | |
| --add-sql-package ./mydir@foo.bar.baz | |
| Registers modules as: | |
| foo.bar.baz.utils | |
| foo.bar.baz.helpers.common | |
| Usage: INCLUDE PERFETTO MODULE foo.bar.*; | |
| Trace summarization: | |
| --summary Enables the trace summarization features of | |
| trace processor. Required for any flags | |
| starting with --summary-* to be meaningful. | |
| --summary-format can be used to control the | |
| output format. | |
| --summary-metrics-v2 ID1,ID2,ID3 Specifies that the given v2 metrics (as | |
| defined by a comma separated set of ids) | |
| should be computed and returned as part of | |
| the trace summary. The spec for every metric | |
| must exist in one of the files passed to | |
| --summary-spec. Specify `all` to execute all | |
| available v2 metrics. | |
| --summary-metadata-query ID Specifies that the given query id should be | |
| used to populate the `metadata` field of the | |
| trace summary. The spec for the query must | |
| exist in one of the files passed to | |
| --summary-spec. | |
| --summary-spec SUMMARY_PATH Parses the spec at the specified path and | |
| makes it available to all summarization | |
| operators (--summary-metrics-v2). Spec | |
| files must be instances of the | |
| perfetto.protos.TraceSummarySpec proto. | |
| If the file extension is `.textproto` then | |
| the spec file will be parsed as a | |
| textproto. If the file extension is `.pb` | |
| then it will be parsed as a binary | |
| protobuf. Otherwise, heureustics will be | |
| used to determine the format. | |
| --summary-format [text,binary] Controls the serialization format of trace | |
| summarization proto | |
| (perfetto.protos.TraceSummary). If | |
| `binary`, then the output is a binary | |
| protobuf. If unspecified or `text` then | |
| the output is a textproto. | |
| Metatracing: | |
| -m, --metatrace FILE Enables metatracing of trace processor | |
| writing the resulting trace into FILE. | |
| --metatrace-buffer-capacity N Sets metatrace event buffer to capture | |
| last N events. | |
| --metatrace-categories CATEGORIES A comma-separated list of metatrace | |
| categories to enable. | |
| Advanced: | |
| --allow-sql-file-access Allows SQL functions to access files | |
| visible to the shell process. Do not | |
| enable this for untrusted SQL. Disabled | |
| by default. | |
| --dev Enables features which are reserved for | |
| local development use only and *should | |
| not* be enabled on production builds. The | |
| features behind this flag can break at | |
| any time without any warning. | |
| --dev-flag KEY=VALUE Set a development flag to the given value. | |
| Does not have any affect unless --dev is | |
| specified. | |
| --extra-checks Enables additional checks which can catch | |
| more SQL errors, but which incur | |
| additional runtime overhead. | |
| -e, --export FILE Export the contents of trace processor | |
| into an SQLite database after running any | |
| metrics or queries specified. | |
| -p, --perf-file FILE Writes the time taken to ingest the trace | |
| and execute the queries to the given file. | |
| Only valid with -q or --run-metrics and | |
| the file will only be written if the | |
| execution is successful. | |
| --analyze-trace-proto-content Enables trace proto content analysis in | |
| trace processor. | |
| --crop-track-events Ignores track event outside of the | |
| range of interest in trace processor. | |
| --register-files-dir PATH The contents of all files in this | |
| directory and subdirectories will be made | |
| available to the trace processor runtime. | |
| Some importers can use this data to | |
| augment trace data (e.g. decode ETM | |
| instruction streams). | |
| --override-stdlib=[path_to_stdlib] Will override trace_processor/stdlib with | |
| passed contents. The outer directory will | |
| be ignored. Only allowed when --dev is | |
| specified. | |
| --override-sql-package PATH[@PKG] Same as --add-sql-package but allows | |
| overriding existing user-registered | |
| packages with the same name. This bypasses | |
| checks trace processor makes around | |
| packages already existing and clashing | |
| with stdlib package names so should be | |
| used with caution. | |
| Structured queries: | |
| --structured-query-spec SPEC_PATH Parses the spec at the specified path and | |
| makes queries available for execution. | |
| Spec files must be instances of the | |
| perfetto.protos.TraceSummarySpec proto. | |
| If the file extension is `.textproto` then | |
| the spec file will be parsed as a | |
| textproto. If the file extension is `.pb` | |
| then it will be parsed as a binary | |
| protobuf. Otherwise, heuristics will be | |
| used to determine the format. | |
| --structured-query-id ID Specifies that the structured query with | |
| the given ID should be executed. The spec | |
| for the query must exist in one of the | |
| files passed to --structured-query-spec. | |
| Metrics (v1): | |
| NOTE: the trace-based metrics system has been "soft" deprecated. Specifically, | |
| all existing metrics will continue functioning but we will not be building | |
| any new features nor developing any metrics there further. Please use the | |
| metrics v2 system as part of trace summarization. | |
| --run-metrics x,y,z Runs a comma separated list of metrics and | |
| prints the result as a TraceMetrics proto | |
| to stdout. The specified can either be | |
| in-built metrics or SQL/proto files of | |
| extension metrics. | |
| --pre-metrics FILE Read and execute an SQL query from a file. | |
| This query is executed before the selected | |
| metrics and can't output any results. | |
| --metrics-output=[binary|text|json] Allows the output of --run-metrics to be | |
| specified in either proto binary, proto | |
| text format or JSON format (default: proto | |
| text). | |
| --metric-extension DISK_PATH@VIRTUAL_PATH | |
| Loads metric proto and sql files from | |
| DISK_PATH/protos and DISK_PATH/sql | |
| respectively, and mounts them onto | |
| VIRTUAL_PATH. | |
| )", | |
| argv[0]); | |
| } | |
| enum LongOption { | |
| OPT_HTTP_PORT = 1000, | |
| OPT_HTTP_IP, | |
| OPT_HTTP_ADDITIONAL_CORS_ORIGINS, | |
| OPT_STDIOD, | |
| OPT_FORCE_FULL_SORT, | |
| OPT_NO_FTRACE_RAW, | |
| OPT_ADD_SQL_PACKAGE, | |
| OPT_OVERRIDE_SQL_PACKAGE, | |
| OPT_STRUCTURED_QUERY_SPEC, | |
| OPT_STRUCTURED_QUERY_ID, | |
| OPT_SUMMARY, | |
| OPT_SUMMARY_METRICS_V2, | |
| OPT_SUMMARY_METADATA_QUERY, | |
| OPT_SUMMARY_SPEC, | |
| OPT_SUMMARY_FORMAT, | |
| OPT_METATRACE_BUFFER_CAPACITY, | |
| OPT_METATRACE_CATEGORIES, | |
| OPT_DEV, | |
| OPT_DEV_FLAG, | |
| OPT_EXTRA_CHECKS, | |
| OPT_ANALYZE_TRACE_PROTO_CONTENT, | |
| OPT_CROP_TRACK_EVENTS, | |
| OPT_ALLOW_SQL_FILE_ACCESS, | |
| OPT_REGISTER_FILES_DIR, | |
| OPT_OVERRIDE_STDLIB, | |
| OPT_RUN_METRICS, | |
| OPT_PRE_METRICS, | |
| OPT_METRICS_OUTPUT, | |
| OPT_METRIC_EXTENSION, | |
| OPT_HELP_CLASSIC, | |
| }; | |
| constexpr char kShortOptions[] = "hvWiDdm:p:q:Q:e:"; | |
| const option kLongOptions[] = { | |
| {"help", no_argument, nullptr, 'h'}, | |
| {"help-classic", no_argument, nullptr, OPT_HELP_CLASSIC}, | |
| {"version", no_argument, nullptr, 'v'}, | |
| {"httpd", no_argument, nullptr, 'D'}, | |
| {"http-port", required_argument, nullptr, OPT_HTTP_PORT}, | |
| {"http-ip-address", required_argument, nullptr, OPT_HTTP_IP}, | |
| {"http-additional-cors-origins", required_argument, nullptr, | |
| OPT_HTTP_ADDITIONAL_CORS_ORIGINS}, | |
| {"stdiod", no_argument, nullptr, OPT_STDIOD}, | |
| {"interactive", no_argument, nullptr, 'i'}, | |
| {"full-sort", no_argument, nullptr, OPT_FORCE_FULL_SORT}, | |
| {"no-ftrace-raw", no_argument, nullptr, OPT_NO_FTRACE_RAW}, | |
| {"query-file", required_argument, nullptr, 'q'}, | |
| {"query-string", required_argument, nullptr, 'Q'}, | |
| {"add-sql-package", required_argument, nullptr, OPT_ADD_SQL_PACKAGE}, | |
| {"override-sql-package", required_argument, nullptr, | |
| OPT_OVERRIDE_SQL_PACKAGE}, | |
| {"summary", no_argument, nullptr, OPT_SUMMARY}, | |
| {"summary-metrics-v2", required_argument, nullptr, OPT_SUMMARY_METRICS_V2}, | |
| {"summary-metadata-query", required_argument, nullptr, | |
| OPT_SUMMARY_METADATA_QUERY}, | |
| {"summary-spec", required_argument, nullptr, OPT_SUMMARY_SPEC}, | |
| {"summary-format", required_argument, nullptr, OPT_SUMMARY_FORMAT}, | |
| {"metatrace", required_argument, nullptr, 'm'}, | |
| {"metatrace-buffer-capacity", required_argument, nullptr, | |
| OPT_METATRACE_BUFFER_CAPACITY}, | |
| {"metatrace-categories", required_argument, nullptr, | |
| OPT_METATRACE_CATEGORIES}, | |
| {"dev", no_argument, nullptr, OPT_DEV}, | |
| {"dev-flag", required_argument, nullptr, OPT_DEV_FLAG}, | |
| {"extra-checks", no_argument, nullptr, OPT_EXTRA_CHECKS}, | |
| {"export", required_argument, nullptr, 'e'}, | |
| {"perf-file", required_argument, nullptr, 'p'}, | |
| {"wide", no_argument, nullptr, 'W'}, | |
| {"analyze-trace-proto-content", no_argument, nullptr, | |
| OPT_ANALYZE_TRACE_PROTO_CONTENT}, | |
| {"crop-track-events", no_argument, nullptr, OPT_CROP_TRACK_EVENTS}, | |
| {"allow-sql-file-access", no_argument, nullptr, OPT_ALLOW_SQL_FILE_ACCESS}, | |
| {"register-files-dir", required_argument, nullptr, OPT_REGISTER_FILES_DIR}, | |
| {"override-stdlib", required_argument, nullptr, OPT_OVERRIDE_STDLIB}, | |
| {"run-metrics", required_argument, nullptr, OPT_RUN_METRICS}, | |
| {"pre-metrics", required_argument, nullptr, OPT_PRE_METRICS}, | |
| {"metrics-output", required_argument, nullptr, OPT_METRICS_OUTPUT}, | |
| {"metric-extension", required_argument, nullptr, OPT_METRIC_EXTENSION}, | |
| {nullptr, 0, nullptr, 0}}; | |
| CommandLineOptions ParseCommandLineOptions(int argc, char** argv) { | |
| CommandLineOptions command_line_options; | |
| bool explicit_interactive = false; | |
| for (;;) { | |
| int option = getopt_long(argc, argv, kShortOptions, kLongOptions, nullptr); | |
| if (option == -1) | |
| break; // EOF. | |
| if (option == 'v') { | |
| printf("%s\n", base::GetVersionString()); | |
| printf("Trace Processor RPC API version: %d\n", | |
| protos::pbzero::TRACE_PROCESSOR_CURRENT_API_VERSION); | |
| exit(0); | |
| } | |
| if (option == 'W') { | |
| command_line_options.wide = true; | |
| continue; | |
| } | |
| if (option == 'p') { | |
| command_line_options.perf_file_path = optarg; | |
| continue; | |
| } | |
| if (option == 'q') { | |
| command_line_options.query_file_path = optarg; | |
| continue; | |
| } | |
| if (option == 'Q') { | |
| command_line_options.query_string = optarg; | |
| continue; | |
| } | |
| if (option == 'D') { | |
| #if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD) | |
| command_line_options.enable_httpd = true; | |
| #else | |
| PERFETTO_FATAL("HTTP RPC module not supported in this build"); | |
| #endif | |
| continue; | |
| } | |
| if (option == OPT_HTTP_PORT) { | |
| command_line_options.port_number = optarg; | |
| continue; | |
| } | |
| if (option == OPT_HTTP_IP) { | |
| command_line_options.listen_ip = optarg; | |
| continue; | |
| } | |
| if (option == OPT_HTTP_ADDITIONAL_CORS_ORIGINS) { | |
| command_line_options.additional_cors_origins = | |
| base::SplitString(optarg, ","); | |
| continue; | |
| } | |
| if (option == OPT_STDIOD) { | |
| command_line_options.enable_stdiod = true; | |
| continue; | |
| } | |
| if (option == 'i') { | |
| explicit_interactive = true; | |
| continue; | |
| } | |
| if (option == 'e') { | |
| command_line_options.export_file_path = optarg; | |
| continue; | |
| } | |
| if (option == 'm') { | |
| command_line_options.metatrace_path = optarg; | |
| continue; | |
| } | |
| if (option == OPT_METATRACE_BUFFER_CAPACITY) { | |
| command_line_options.metatrace_buffer_capacity = | |
| static_cast<size_t>(atoi(optarg)); | |
| continue; | |
| } | |
| if (option == OPT_METATRACE_CATEGORIES) { | |
| command_line_options.metatrace_categories = | |
| ParseMetatraceCategories(optarg); | |
| continue; | |
| } | |
| if (option == OPT_FORCE_FULL_SORT) { | |
| command_line_options.force_full_sort = true; | |
| continue; | |
| } | |
| if (option == OPT_NO_FTRACE_RAW) { | |
| command_line_options.no_ftrace_raw = true; | |
| continue; | |
| } | |
| if (option == OPT_ANALYZE_TRACE_PROTO_CONTENT) { | |
| command_line_options.analyze_trace_proto_content = true; | |
| continue; | |
| } | |
| if (option == OPT_CROP_TRACK_EVENTS) { | |
| command_line_options.crop_track_events = true; | |
| continue; | |
| } | |
| if (option == OPT_ALLOW_SQL_FILE_ACCESS) { | |
| command_line_options.allow_sql_file_access = true; | |
| continue; | |
| } | |
| if (option == OPT_DEV) { | |
| command_line_options.dev = true; | |
| continue; | |
| } | |
| if (option == OPT_EXTRA_CHECKS) { | |
| command_line_options.extra_checks = true; | |
| continue; | |
| } | |
| if (option == OPT_ADD_SQL_PACKAGE) { | |
| command_line_options.sql_package_paths.emplace_back(optarg); | |
| continue; | |
| } | |
| if (option == OPT_OVERRIDE_SQL_PACKAGE) { | |
| command_line_options.override_sql_package_paths.emplace_back(optarg); | |
| continue; | |
| } | |
| if (option == OPT_OVERRIDE_STDLIB) { | |
| command_line_options.override_stdlib_path = optarg; | |
| continue; | |
| } | |
| if (option == OPT_RUN_METRICS) { | |
| command_line_options.metric_v1_names = optarg; | |
| continue; | |
| } | |
| if (option == OPT_PRE_METRICS) { | |
| command_line_options.pre_metrics_v1_path = optarg; | |
| continue; | |
| } | |
| if (option == OPT_METRICS_OUTPUT) { | |
| command_line_options.metric_v1_output = optarg; | |
| continue; | |
| } | |
| if (option == OPT_METRIC_EXTENSION) { | |
| command_line_options.raw_metric_v1_extensions.emplace_back(optarg); | |
| continue; | |
| } | |
| if (option == OPT_DEV_FLAG) { | |
| command_line_options.dev_flags.emplace_back(optarg); | |
| continue; | |
| } | |
| if (option == OPT_REGISTER_FILES_DIR) { | |
| command_line_options.register_files_dir = optarg; | |
| continue; | |
| } | |
| if (option == OPT_SUMMARY) { | |
| command_line_options.summary = true; | |
| continue; | |
| } | |
| if (option == OPT_SUMMARY_METRICS_V2) { | |
| command_line_options.summary_metrics_v2 = optarg; | |
| continue; | |
| } | |
| if (option == OPT_SUMMARY_METADATA_QUERY) { | |
| command_line_options.summary_metadata_query = optarg; | |
| continue; | |
| } | |
| if (option == OPT_SUMMARY_SPEC) { | |
| command_line_options.summary_specs.emplace_back(optarg); | |
| continue; | |
| } | |
| if (option == OPT_SUMMARY_FORMAT) { | |
| command_line_options.summary_output = optarg; | |
| continue; | |
| } | |
| if (option == OPT_HELP_CLASSIC) { | |
| PrintClassicUsage(argv); | |
| exit(0); | |
| } | |
| if (option == 'h') { | |
| PrintSubcommandHelp(argv[0]); | |
| exit(0); | |
| } | |
| PrintClassicUsage(argv); | |
| exit(1); | |
| } | |
| command_line_options.launch_shell = | |
| explicit_interactive || (command_line_options.metric_v1_names.empty() && | |
| command_line_options.query_file_path.empty() && | |
| command_line_options.query_string.empty() && | |
| command_line_options.export_file_path.empty() && | |
| !command_line_options.summary); | |
| // Only allow non-interactive queries to emit perf data. | |
| if (!command_line_options.perf_file_path.empty() && | |
| command_line_options.launch_shell) { | |
| PrintClassicUsage(argv); | |
| exit(1); | |
| } | |
| if (command_line_options.summary && | |
| !command_line_options.metric_v1_names.empty()) { | |
| PERFETTO_ELOG("Cannot specify both metrics v1 and trace summarization"); | |
| exit(1); | |
| } | |
| // The only case where we allow omitting the trace file path is when running | |
| // in --httpd or --stdiod mode. In all other cases, the last argument must be | |
| // the trace file. | |
| if (optind == argc - 1 && argv[optind]) { | |
| command_line_options.trace_file_path = argv[optind]; | |
| } else if (!command_line_options.enable_httpd && | |
| !command_line_options.enable_stdiod) { | |
| PrintClassicUsage(argv); | |
| exit(1); | |
| } | |
| return command_line_options; | |
| } | |
| class DefaultPlatformInterface : public TraceProcessorShell::PlatformInterface { | |
| public: | |
| ~DefaultPlatformInterface() override; | |
| Config DefaultConfig() const override { return {}; } | |
| io::FileSystem* GetFileSystem() override { | |
| if (!file_system_) { | |
| file_system_ = io::CreateLocalFileSystem(); | |
| } | |
| return file_system_; | |
| } | |
| base::Status OnTraceProcessorCreated(TraceProcessor*) override { | |
| return base::OkStatus(); | |
| } | |
| base::Status LoadTrace( | |
| TraceProcessor* trace_processor, | |
| const std::string& path, | |
| std::function<void(size_t)> progress_callback) override { | |
| // The shell opts into loading traces directly from http(s) URLs and from | |
| // Perfetto UI share links; the user opts in by passing such a path. Bytes | |
| // downloaded this way are cached on local disk so re-running on the same | |
| // URL doesn't re-download. | |
| ReadTraceArgs args; | |
| args.allow_http = true; | |
| args.allow_perfetto_ui_links = true; | |
| args.cache_downloads = true; | |
| return ReadTraceUnfinalized(trace_processor, path.c_str(), | |
| progress_callback, args); | |
| } | |
| private: | |
| io::FileSystem* file_system_ = nullptr; | |
| }; | |
| DefaultPlatformInterface::~DefaultPlatformInterface() = default; | |
| } // namespace | |
| TraceProcessorShell::TraceProcessorShell( | |
| std::unique_ptr<PlatformInterface> platform_interface) | |
| : platform_interface_(std::move(platform_interface)) {} | |
| std::unique_ptr<TraceProcessorShell> TraceProcessorShell::Create( | |
| std::unique_ptr<PlatformInterface> platform_interface) { | |
| return std::unique_ptr<TraceProcessorShell>( | |
| new TraceProcessorShell(std::move(platform_interface))); | |
| } | |
| std::unique_ptr<TraceProcessorShell> | |
| TraceProcessorShell::CreateWithDefaultPlatform() { | |
| return std::unique_ptr<TraceProcessorShell>( | |
| new TraceProcessorShell(std::make_unique<DefaultPlatformInterface>())); | |
| } | |
| base::Status TraceProcessorShell::Run(int argc, char** argv) { | |
| // traceconv compatibility shim: when invoked under the legacy "traceconv" | |
| // name, the first positional is a traceconv MODE. Map it onto the new | |
| // subcommands (e.g. `traceconv json a b` -> `convert json a b`, | |
| // `traceconv symbolize ...` -> `util symbolize ...`, `traceconv bundle ...` | |
| // stays `bundle ...`). The rewritten argv is owned by these vectors for the | |
| // rest of Run and flows through the normal dispatch below. | |
| std::vector<std::string> traceconv_storage; | |
| std::vector<char*> traceconv_ptrs; | |
| if (shell::InvokedAsTraceconv(argv[0])) { | |
| traceconv_storage = shell::RewriteTraceconvArgs(argc, argv); | |
| if (!traceconv_storage.empty()) { | |
| for (auto& s : traceconv_storage) | |
| traceconv_ptrs.push_back(s.data()); | |
| traceconv_ptrs.push_back(nullptr); | |
| argc = static_cast<int>(traceconv_storage.size()); | |
| argv = traceconv_ptrs.data(); | |
| } | |
| } | |
| // Subcommand dispatch: if a positional argument matches a known subcommand | |
| // name, route to it. Otherwise fall through to classic path. | |
| { | |
| shell::QuerySubcommand query_subcommand; | |
| shell::InteractiveSubcommand interactive_subcommand; | |
| shell::ServerSubcommand server_subcommand; | |
| shell::SummarizeSubcommand summarize_subcommand; | |
| shell::ExportSubcommand export_subcommand; | |
| shell::MetricsSubcommand metrics_subcommand; | |
| shell::ConvertSubcommand convert_subcommand; | |
| shell::BundleSubcommand bundle_subcommand; | |
| shell::UtilSubcommand util_subcommand; | |
| std::vector<shell::Subcommand*> subcommands = { | |
| &query_subcommand, &interactive_subcommand, &convert_subcommand, | |
| &summarize_subcommand, &bundle_subcommand, &util_subcommand, | |
| &server_subcommand, &export_subcommand, &metrics_subcommand, | |
| }; | |
| // Handle "help" pseudo-subcommand: `tp help <command>` or bare `tp help`. | |
| for (int i = 1; i < argc; ++i) { | |
| if (argv[i][0] == '-') | |
| continue; | |
| if (strcmp(argv[i], "help") == 0) { | |
| if (i + 1 < argc) { | |
| if (strcmp(argv[i + 1], "agent") == 0) { | |
| PrintAgentGuide(argv[0]); | |
| return base::OkStatus(); | |
| } | |
| // `help <command>` -- find the named subcommand and print its usage. | |
| for (auto* sc : subcommands) { | |
| if (strcmp(sc->name(), argv[i + 1]) == 0) { | |
| printf("%s", shell::FormatSubcommandUsage(argv[0], sc).c_str()); | |
| return base::OkStatus(); | |
| } | |
| } | |
| return base::ErrStatus("Unknown command '%s'.", argv[i + 1]); | |
| } | |
| // Bare `help` -- same as --help. | |
| PrintSubcommandHelp(argv[0]); | |
| return base::OkStatus(); | |
| } | |
| break; // First non-flag, non-help positional -> stop. | |
| } | |
| // Build the set of flags that consume an argument, derived from the | |
| // classic kLongOptions array, kShortOptions, and subcommand FlagSpecs. | |
| std::unordered_set<std::string> flags_with_arg; | |
| for (const auto* o = kLongOptions; o->name; ++o) { | |
| if (o->has_arg == required_argument) | |
| flags_with_arg.insert("--" + std::string(o->name)); | |
| } | |
| for (const char* p = kShortOptions; *p; ++p) { | |
| if (*(p + 1) == ':') { | |
| flags_with_arg.insert(std::string("-") + *p); | |
| ++p; // skip ':' | |
| } | |
| } | |
| for (auto* sc : subcommands) { | |
| for (const auto& f : sc->GetFlags()) { | |
| if (f.has_arg) { | |
| flags_with_arg.insert("--" + std::string(f.long_name)); | |
| if (f.short_name) | |
| flags_with_arg.insert(std::string(1, '-') + f.short_name); | |
| } | |
| } | |
| } | |
| auto result = | |
| shell::FindSubcommandInArgs(argc, argv, subcommands, flags_with_arg); | |
| if (result.subcommand) { | |
| // If the matched word is also a file on disk, it's ambiguous -- the user | |
| // might have meant it as a trace file. Emit a hint. | |
| if (base::FileExists(argv[result.argv_index])) { | |
| PERFETTO_ELOG( | |
| "Note: '%s' matches both a subcommand and a file on disk. " | |
| "Interpreting as subcommand. To use it as a trace file, " | |
| "use './%s'.", | |
| argv[result.argv_index], argv[result.argv_index]); | |
| } | |
| // Remove the subcommand name from argv. | |
| for (int i = result.argv_index; i < argc - 1; ++i) | |
| argv[i] = argv[i + 1]; | |
| argc--; | |
| shell::GlobalOptions global; | |
| shell::SubcommandContext ctx; | |
| ctx.platform = platform_interface_.get(); | |
| ctx.global = &global; | |
| auto usage = shell::FormatSubcommandUsage(argv[0], result.subcommand); | |
| { | |
| auto parse_status = | |
| shell::ParseFlags(result.subcommand, &ctx, argc, argv); | |
| if (!parse_status.ok()) { | |
| bool already_printed = | |
| parse_status.GetPayload("perfetto.dev/has_printed_error") | |
| .has_value(); | |
| if (already_printed) { | |
| return base::ErrStatus("\n%s", usage.c_str()); | |
| } | |
| return base::ErrStatus("%s\n\n%s", parse_status.c_message(), | |
| usage.c_str()); | |
| } | |
| } | |
| base::ProgressReporter::GetInstance().set_enabled(!global.no_progress && | |
| !global.quiet); | |
| if (global.help) { | |
| printf("%s", usage.c_str()); | |
| return base::OkStatus(); | |
| } | |
| if (global.version) { | |
| printf("%s\n", base::GetVersionString()); | |
| printf("Trace Processor RPC API version: %d\n", | |
| protos::pbzero::TRACE_PROCESSOR_CURRENT_API_VERSION); | |
| return base::OkStatus(); | |
| } | |
| RETURN_IF_ERROR(profiling::ResolveDebuginfodOptions( | |
| global.debuginfod_options, &global.debuginfod)); | |
| // Parse metric extensions and populate their descriptor pool. The | |
| // pool is always created (built-in metrics need it for output | |
| // formatting); extensions just add to it. | |
| RETURN_IF_ERROR(ParseMetricExtensionPaths(global.dev, | |
| global.raw_metric_v1_extensions, | |
| global.metric_extensions)); | |
| global.metric_descriptor_pool = | |
| std::make_unique<google::protobuf::DescriptorPool>( | |
| google::protobuf::DescriptorPool::generated_pool()); | |
| RETURN_IF_ERROR(PopulateDescriptorPool(*global.metric_descriptor_pool, | |
| global.metric_extensions)); | |
| return result.subcommand->Run(ctx); | |
| } | |
| } | |
| // No arguments at all: show the subcommand-based help. | |
| if (argc == 1) { | |
| PrintSubcommandHelp(argv[0]); | |
| return base::OkStatus(); | |
| } | |
| // Classic flag path: translate classic flags into a subcommand invocation | |
| // and re-dispatch through the subcommand machinery above. | |
| CommandLineOptions options = ParseCommandLineOptions(argc, argv); | |
| // Build a synthetic argv for the target subcommand. | |
| std::vector<std::string> args; | |
| args.emplace_back(argv[0]); | |
| // Forward global flags. | |
| auto add_global_flags = [&]() { | |
| if (options.force_full_sort) | |
| args.emplace_back("--full-sort"); | |
| if (options.no_ftrace_raw) | |
| args.emplace_back("--no-ftrace-raw"); | |
| if (options.analyze_trace_proto_content) | |
| args.emplace_back("--analyze-trace-proto-content"); | |
| if (options.crop_track_events) | |
| args.emplace_back("--crop-track-events"); | |
| if (options.allow_sql_file_access) | |
| args.emplace_back("--allow-sql-file-access"); | |
| if (options.dev) | |
| args.emplace_back("--dev"); | |
| for (const auto& f : options.dev_flags) { | |
| args.emplace_back("--dev-flag"); | |
| args.emplace_back(f); | |
| } | |
| if (options.extra_checks) | |
| args.emplace_back("--extra-checks"); | |
| for (const auto& p : options.sql_package_paths) { | |
| args.emplace_back("--add-sql-package"); | |
| args.emplace_back(p); | |
| } | |
| for (const auto& p : options.override_sql_package_paths) { | |
| args.emplace_back("--override-sql-package"); | |
| args.emplace_back(p); | |
| } | |
| if (!options.override_stdlib_path.empty()) { | |
| args.emplace_back("--override-stdlib"); | |
| args.emplace_back(options.override_stdlib_path); | |
| } | |
| if (!options.register_files_dir.empty()) { | |
| args.emplace_back("--register-files-dir"); | |
| args.emplace_back(options.register_files_dir); | |
| } | |
| for (const auto& e : options.raw_metric_v1_extensions) { | |
| args.emplace_back("--metric-extension"); | |
| args.emplace_back(e); | |
| } | |
| if (!options.metatrace_path.empty()) { | |
| args.emplace_back("--metatrace"); | |
| args.emplace_back(options.metatrace_path); | |
| } | |
| if (options.metatrace_buffer_capacity > 0) { | |
| args.emplace_back("--metatrace-buffer-capacity"); | |
| args.emplace_back(std::to_string(options.metatrace_buffer_capacity)); | |
| } | |
| }; | |
| // Determine which subcommand to dispatch to. | |
| // | |
| // For subcommands that themselves consume a positional mode argument | |
| // (e.g. "server http", "server stdio", "export sqlite"), emit all flags | |
| // *before* the mode positional. Our Windows getopt_compat implementation | |
| // does not GNU-style permute argv, so any flag appearing after a positional | |
| // would be left as a positional, which would then be misinterpreted as the | |
| // trace file by the subcommand. | |
| if (options.enable_httpd) { | |
| args.emplace_back("server"); | |
| if (!options.port_number.empty()) { | |
| args.emplace_back("--port"); | |
| args.emplace_back(options.port_number); | |
| } | |
| if (!options.listen_ip.empty()) { | |
| args.emplace_back("--ip-address"); | |
| args.emplace_back(options.listen_ip); | |
| } | |
| for (const auto& o : options.additional_cors_origins) { | |
| args.emplace_back("--additional-cors-origins"); | |
| args.emplace_back(o); | |
| } | |
| add_global_flags(); | |
| args.emplace_back("http"); | |
| if (!options.trace_file_path.empty()) | |
| args.emplace_back(options.trace_file_path); | |
| } else if (options.enable_stdiod) { | |
| args.emplace_back("server"); | |
| add_global_flags(); | |
| args.emplace_back("stdio"); | |
| if (!options.trace_file_path.empty()) | |
| args.emplace_back(options.trace_file_path); | |
| } else if (options.summary) { | |
| args.emplace_back("summarize"); | |
| if (!options.summary_metrics_v2.empty()) { | |
| args.emplace_back("--metrics-v2"); | |
| args.emplace_back(options.summary_metrics_v2); | |
| } | |
| if (!options.summary_metadata_query.empty()) { | |
| args.emplace_back("--metadata-query"); | |
| args.emplace_back(options.summary_metadata_query); | |
| } | |
| if (!options.summary_output.empty()) { | |
| args.emplace_back("--format"); | |
| args.emplace_back(options.summary_output); | |
| } | |
| if (!options.query_file_path.empty()) { | |
| args.emplace_back("--post-query"); | |
| args.emplace_back(options.query_file_path); | |
| } | |
| if (!options.perf_file_path.empty()) { | |
| args.emplace_back("--perf-file"); | |
| args.emplace_back(options.perf_file_path); | |
| } | |
| if (options.launch_shell) | |
| args.emplace_back("-i"); | |
| add_global_flags(); | |
| if (!options.trace_file_path.empty()) | |
| args.emplace_back(options.trace_file_path); | |
| for (const auto& s : options.summary_specs) | |
| args.emplace_back(s); | |
| } else if (!options.metric_v1_names.empty()) { | |
| args.emplace_back("metrics"); | |
| args.emplace_back("--run"); | |
| args.emplace_back(options.metric_v1_names); | |
| if (!options.pre_metrics_v1_path.empty()) { | |
| args.emplace_back("--pre"); | |
| args.emplace_back(options.pre_metrics_v1_path); | |
| } | |
| if (!options.metric_v1_output.empty()) { | |
| args.emplace_back("--output"); | |
| args.emplace_back(options.metric_v1_output); | |
| } | |
| if (!options.query_file_path.empty()) { | |
| args.emplace_back("--post-query"); | |
| args.emplace_back(options.query_file_path); | |
| } | |
| if (!options.perf_file_path.empty()) { | |
| args.emplace_back("--perf-file"); | |
| args.emplace_back(options.perf_file_path); | |
| } | |
| if (options.launch_shell) | |
| args.emplace_back("-i"); | |
| add_global_flags(); | |
| if (!options.trace_file_path.empty()) | |
| args.emplace_back(options.trace_file_path); | |
| } else if (!options.export_file_path.empty()) { | |
| if (!options.query_file_path.empty()) { | |
| return base::ErrStatus( | |
| "Cannot combine -e (export) with -q (query). " | |
| "Use the 'export' subcommand directly."); | |
| } | |
| args.emplace_back("export"); | |
| args.emplace_back("-o"); | |
| args.emplace_back(options.export_file_path); | |
| add_global_flags(); | |
| args.emplace_back("sqlite"); | |
| if (!options.trace_file_path.empty()) | |
| args.emplace_back(options.trace_file_path); | |
| } else if (!options.query_file_path.empty() || | |
| !options.query_string.empty()) { | |
| args.emplace_back("query"); | |
| if (!options.query_file_path.empty()) { | |
| args.emplace_back("-f"); | |
| args.emplace_back(options.query_file_path); | |
| } | |
| if (options.launch_shell) | |
| args.emplace_back("-i"); | |
| if (options.wide) | |
| args.emplace_back("-W"); | |
| // Agent mode is a feature of the query subcommand: the classic interface | |
| // keeps printing exactly what it always has. | |
| args.emplace_back("--no-agent"); | |
| if (!options.perf_file_path.empty()) { | |
| args.emplace_back("--perf-file"); | |
| args.emplace_back(options.perf_file_path); | |
| } | |
| add_global_flags(); | |
| if (!options.trace_file_path.empty()) | |
| args.emplace_back(options.trace_file_path); | |
| if (!options.query_string.empty()) | |
| args.emplace_back(options.query_string); | |
| } else { | |
| // Default: interactive shell. | |
| args.emplace_back("interactive"); | |
| if (options.wide) | |
| args.emplace_back("-W"); | |
| add_global_flags(); | |
| if (!options.trace_file_path.empty()) | |
| args.emplace_back(options.trace_file_path); | |
| } | |
| // Convert to argc/argv and re-enter Run() which will match the subcommand. | |
| std::vector<char*> new_argv; | |
| new_argv.reserve(args.size()); | |
| for (auto& a : args) | |
| new_argv.emplace_back(a.data()); | |
| return Run(static_cast<int>(new_argv.size()), new_argv.data()); | |
| } | |
| int PERFETTO_EXPORT_ENTRYPOINT TraceProcessorShellMain(int argc, char** argv) { | |
| auto shell = TraceProcessorShell::CreateWithDefaultPlatform(); | |
| auto status = shell->Run(argc, argv); | |
| if (!status.ok()) { | |
| // Some errors are printed where they happen (e.g. as JSON for agents). | |
| if (!status.GetPayload("perfetto.dev/has_printed_error")) { | |
| fprintf(stderr, "%s\n", status.c_message()); | |
| } | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| } // namespace perfetto::trace_processor |