xref: /aosp_15_r20/system/extras/torq/torq.py (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker#
2*288bf522SAndroid Build Coastguard Worker# Copyright (C) 2024 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker#
4*288bf522SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker#
8*288bf522SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker#
10*288bf522SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker# limitations under the License.
15*288bf522SAndroid Build Coastguard Worker#
16*288bf522SAndroid Build Coastguard Worker
17*288bf522SAndroid Build Coastguard Workerimport argparse
18*288bf522SAndroid Build Coastguard Workerimport os
19*288bf522SAndroid Build Coastguard Workerfrom command import ProfilerCommand, ConfigCommand, OpenCommand
20*288bf522SAndroid Build Coastguard Workerfrom device import AdbDevice
21*288bf522SAndroid Build Coastguard Workerfrom validation_error import ValidationError
22*288bf522SAndroid Build Coastguard Workerfrom config_builder import PREDEFINED_PERFETTO_CONFIGS
23*288bf522SAndroid Build Coastguard Workerfrom utils import path_exists
24*288bf522SAndroid Build Coastguard Workerfrom validate_simpleperf import verify_simpleperf_args
25*288bf522SAndroid Build Coastguard Worker
26*288bf522SAndroid Build Coastguard WorkerDEFAULT_DUR_MS = 10000
27*288bf522SAndroid Build Coastguard WorkerMIN_DURATION_MS = 3000
28*288bf522SAndroid Build Coastguard WorkerDEFAULT_OUT_DIR = "."
29*288bf522SAndroid Build Coastguard Worker
30*288bf522SAndroid Build Coastguard Worker
31*288bf522SAndroid Build Coastguard Workerdef create_parser():
32*288bf522SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(prog='torq command',
33*288bf522SAndroid Build Coastguard Worker                                   description=('Torq CLI tool for performance'
34*288bf522SAndroid Build Coastguard Worker                                                ' tests.'))
35*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-e', '--event',
36*288bf522SAndroid Build Coastguard Worker                      choices=['boot', 'user-switch', 'app-startup', 'custom'],
37*288bf522SAndroid Build Coastguard Worker                      default='custom', help='The event to trace/profile.')
38*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-p', '--profiler', choices=['perfetto', 'simpleperf'],
39*288bf522SAndroid Build Coastguard Worker                      default='perfetto', help='The performance data source.')
40*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-o', '--out-dir', default=DEFAULT_OUT_DIR,
41*288bf522SAndroid Build Coastguard Worker                      help='The path to the output directory.')
42*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-d', '--dur-ms', type=int, default=DEFAULT_DUR_MS,
43*288bf522SAndroid Build Coastguard Worker                      help=('The duration (ms) of the event. Determines when'
44*288bf522SAndroid Build Coastguard Worker                            ' to stop collecting performance data.'))
45*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-a', '--app',
46*288bf522SAndroid Build Coastguard Worker                      help='The package name of the app we want to start.')
47*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-r', '--runs', type=int, default=1,
48*288bf522SAndroid Build Coastguard Worker                      help=('The number of times to run the event and'
49*288bf522SAndroid Build Coastguard Worker                            ' capture the perf data.'))
50*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-s', '--simpleperf-event', action='append',
51*288bf522SAndroid Build Coastguard Worker                      help=('Simpleperf supported events to be collected.'
52*288bf522SAndroid Build Coastguard Worker                            ' e.g. cpu-cycles, instructions'))
53*288bf522SAndroid Build Coastguard Worker  parser.add_argument('--perfetto-config', default='default',
54*288bf522SAndroid Build Coastguard Worker                      help=('Predefined perfetto configs can be used:'
55*288bf522SAndroid Build Coastguard Worker                            ' %s. A filepath with a custom config could'
56*288bf522SAndroid Build Coastguard Worker                            ' also be provided.'
57*288bf522SAndroid Build Coastguard Worker                            % (", ".join(PREDEFINED_PERFETTO_CONFIGS.keys()))))
58*288bf522SAndroid Build Coastguard Worker  parser.add_argument('--between-dur-ms', type=int, default=DEFAULT_DUR_MS,
59*288bf522SAndroid Build Coastguard Worker                      help='Time (ms) to wait before executing the next event.')
60*288bf522SAndroid Build Coastguard Worker  parser.add_argument('--ui', action=argparse.BooleanOptionalAction,
61*288bf522SAndroid Build Coastguard Worker                      help=('Specifies opening of UI visualization tool'
62*288bf522SAndroid Build Coastguard Worker                            ' after profiling is complete.'))
63*288bf522SAndroid Build Coastguard Worker  parser.add_argument('--excluded-ftrace-events', action='append',
64*288bf522SAndroid Build Coastguard Worker                      help=('Excludes specified ftrace event from the perfetto'
65*288bf522SAndroid Build Coastguard Worker                            ' config events.'))
66*288bf522SAndroid Build Coastguard Worker  parser.add_argument('--included-ftrace-events', action='append',
67*288bf522SAndroid Build Coastguard Worker                      help=('Includes specified ftrace event in the perfetto'
68*288bf522SAndroid Build Coastguard Worker                            ' config events.'))
69*288bf522SAndroid Build Coastguard Worker  parser.add_argument('--from-user', type=int,
70*288bf522SAndroid Build Coastguard Worker                      help='The user id from which to start the user switch')
71*288bf522SAndroid Build Coastguard Worker  parser.add_argument('--to-user', type=int,
72*288bf522SAndroid Build Coastguard Worker                      help='The user id of user that system is switching to.')
73*288bf522SAndroid Build Coastguard Worker  parser.add_argument('--serial',
74*288bf522SAndroid Build Coastguard Worker                      help=(('Specifies serial of the device that will be'
75*288bf522SAndroid Build Coastguard Worker                             ' used.')))
76*288bf522SAndroid Build Coastguard Worker  parser.add_argument('--symbols',
77*288bf522SAndroid Build Coastguard Worker                      help='Specifies path to symbols library.')
78*288bf522SAndroid Build Coastguard Worker  subparsers = parser.add_subparsers(dest='subcommands', help='Subcommands')
79*288bf522SAndroid Build Coastguard Worker  config_parser = subparsers.add_parser('config',
80*288bf522SAndroid Build Coastguard Worker                                        help=('The config subcommand used'
81*288bf522SAndroid Build Coastguard Worker                                              ' to list and show the'
82*288bf522SAndroid Build Coastguard Worker                                              ' predefined perfetto configs.'))
83*288bf522SAndroid Build Coastguard Worker  config_subparsers = config_parser.add_subparsers(dest='config_subcommand',
84*288bf522SAndroid Build Coastguard Worker                                                   help=('torq config'
85*288bf522SAndroid Build Coastguard Worker                                                         ' subcommands'))
86*288bf522SAndroid Build Coastguard Worker  config_subparsers.add_parser('list',
87*288bf522SAndroid Build Coastguard Worker                               help=('Command to list the predefined'
88*288bf522SAndroid Build Coastguard Worker                                     ' perfetto configs'))
89*288bf522SAndroid Build Coastguard Worker  config_show_parser = config_subparsers.add_parser('show',
90*288bf522SAndroid Build Coastguard Worker                                                    help=('Command to print'
91*288bf522SAndroid Build Coastguard Worker                                                          ' the '
92*288bf522SAndroid Build Coastguard Worker                                                          ' perfetto config'
93*288bf522SAndroid Build Coastguard Worker                                                          ' in the terminal.'))
94*288bf522SAndroid Build Coastguard Worker  config_show_parser.add_argument('config_name',
95*288bf522SAndroid Build Coastguard Worker                                  choices=['lightweight', 'default', 'memory'],
96*288bf522SAndroid Build Coastguard Worker                                  help=('Name of the predefined perfetto'
97*288bf522SAndroid Build Coastguard Worker                                        ' config to print.'))
98*288bf522SAndroid Build Coastguard Worker  config_pull_parser = config_subparsers.add_parser('pull',
99*288bf522SAndroid Build Coastguard Worker                                                    help=('Command to copy'
100*288bf522SAndroid Build Coastguard Worker                                                          ' a predefined config'
101*288bf522SAndroid Build Coastguard Worker                                                          ' to the specified'
102*288bf522SAndroid Build Coastguard Worker                                                          ' file path.'))
103*288bf522SAndroid Build Coastguard Worker  config_pull_parser.add_argument('config_name',
104*288bf522SAndroid Build Coastguard Worker                                  choices=['lightweight', 'default', 'memory'],
105*288bf522SAndroid Build Coastguard Worker                                  help='Name of the predefined config to copy')
106*288bf522SAndroid Build Coastguard Worker  config_pull_parser.add_argument('file_path', nargs='?',
107*288bf522SAndroid Build Coastguard Worker                                  help=('File path to copy the predefined'
108*288bf522SAndroid Build Coastguard Worker                                        ' config to'))
109*288bf522SAndroid Build Coastguard Worker  open_parser = subparsers.add_parser('open',
110*288bf522SAndroid Build Coastguard Worker                                      help=('The open subcommand is used '
111*288bf522SAndroid Build Coastguard Worker                                            'to open trace files in the '
112*288bf522SAndroid Build Coastguard Worker                                            'perfetto ui.'))
113*288bf522SAndroid Build Coastguard Worker  open_parser.add_argument('file_path', help='Path to trace file.')
114*288bf522SAndroid Build Coastguard Worker  return parser
115*288bf522SAndroid Build Coastguard Worker
116*288bf522SAndroid Build Coastguard Worker
117*288bf522SAndroid Build Coastguard Workerdef user_changed_default_arguments(args):
118*288bf522SAndroid Build Coastguard Worker  return any([args.event != "custom",
119*288bf522SAndroid Build Coastguard Worker              args.profiler != "perfetto",
120*288bf522SAndroid Build Coastguard Worker              args.out_dir != DEFAULT_OUT_DIR,
121*288bf522SAndroid Build Coastguard Worker              args.dur_ms != DEFAULT_DUR_MS,
122*288bf522SAndroid Build Coastguard Worker              args.app is not None,
123*288bf522SAndroid Build Coastguard Worker              args.runs != 1,
124*288bf522SAndroid Build Coastguard Worker              args.simpleperf_event is not None,
125*288bf522SAndroid Build Coastguard Worker              args.perfetto_config != "default",
126*288bf522SAndroid Build Coastguard Worker              args.between_dur_ms != DEFAULT_DUR_MS,
127*288bf522SAndroid Build Coastguard Worker              args.ui is not None,
128*288bf522SAndroid Build Coastguard Worker              args.excluded_ftrace_events is not None,
129*288bf522SAndroid Build Coastguard Worker              args.included_ftrace_events is not None,
130*288bf522SAndroid Build Coastguard Worker              args.from_user is not None,
131*288bf522SAndroid Build Coastguard Worker              args.to_user is not None,
132*288bf522SAndroid Build Coastguard Worker              args.serial is not None])
133*288bf522SAndroid Build Coastguard Worker
134*288bf522SAndroid Build Coastguard Worker
135*288bf522SAndroid Build Coastguard Workerdef verify_args(args):
136*288bf522SAndroid Build Coastguard Worker  if (args.subcommands is not None and
137*288bf522SAndroid Build Coastguard Worker      user_changed_default_arguments(args)):
138*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
139*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because profiler command is followed by a config"
140*288bf522SAndroid Build Coastguard Worker         " command."),
141*288bf522SAndroid Build Coastguard Worker        "Remove the 'config' subcommand to profile the device instead.")
142*288bf522SAndroid Build Coastguard Worker
143*288bf522SAndroid Build Coastguard Worker  if args.out_dir != DEFAULT_OUT_DIR and not os.path.isdir(args.out_dir):
144*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
145*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because --out-dir is not a valid directory"
146*288bf522SAndroid Build Coastguard Worker         " path: %s." % args.out_dir), None)
147*288bf522SAndroid Build Coastguard Worker
148*288bf522SAndroid Build Coastguard Worker  if args.dur_ms < MIN_DURATION_MS:
149*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
150*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because --dur-ms cannot be set to a value smaller"
151*288bf522SAndroid Build Coastguard Worker         " than %d." % MIN_DURATION_MS),
152*288bf522SAndroid Build Coastguard Worker        ("Set --dur-ms %d to capture a trace for %d seconds."
153*288bf522SAndroid Build Coastguard Worker         % (MIN_DURATION_MS, (MIN_DURATION_MS / 1000))))
154*288bf522SAndroid Build Coastguard Worker
155*288bf522SAndroid Build Coastguard Worker  if args.from_user is not None and args.event != "user-switch":
156*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
157*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because --from-user is passed, but --event is not"
158*288bf522SAndroid Build Coastguard Worker         " set to user-switch."),
159*288bf522SAndroid Build Coastguard Worker        ("Set --event user-switch --from-user %s to perform a user-switch from"
160*288bf522SAndroid Build Coastguard Worker         " user %s." % (args.from_user, args.from_user)))
161*288bf522SAndroid Build Coastguard Worker
162*288bf522SAndroid Build Coastguard Worker  if args.to_user is not None and args.event != "user-switch":
163*288bf522SAndroid Build Coastguard Worker    return None, ValidationError((
164*288bf522SAndroid Build Coastguard Worker        "Command is invalid because --to-user is passed, but --event is not set"
165*288bf522SAndroid Build Coastguard Worker        " to user-switch."),
166*288bf522SAndroid Build Coastguard Worker        ("Set --event user-switch --to-user %s to perform a user-switch to user"
167*288bf522SAndroid Build Coastguard Worker         " %s." % (args.to_user, args.to_user)))
168*288bf522SAndroid Build Coastguard Worker
169*288bf522SAndroid Build Coastguard Worker  if args.event == "user-switch" and args.to_user is None:
170*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
171*288bf522SAndroid Build Coastguard Worker        "Command is invalid because --to-user is not passed.",
172*288bf522SAndroid Build Coastguard Worker        ("Set --event %s --to-user <user-id> to perform a %s."
173*288bf522SAndroid Build Coastguard Worker         % (args.event, args.event)))
174*288bf522SAndroid Build Coastguard Worker
175*288bf522SAndroid Build Coastguard Worker  # TODO(b/374313202): Support for simpleperf boot event will
176*288bf522SAndroid Build Coastguard Worker  #                    be added in the future
177*288bf522SAndroid Build Coastguard Worker  if args.event == "boot" and args.profiler == "simpleperf":
178*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
179*288bf522SAndroid Build Coastguard Worker        "Boot event is not yet implemented for simpleperf.",
180*288bf522SAndroid Build Coastguard Worker        "Please try another event.")
181*288bf522SAndroid Build Coastguard Worker
182*288bf522SAndroid Build Coastguard Worker  if args.app is not None and args.event != "app-startup":
183*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
184*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because --app is passed and --event is not set"
185*288bf522SAndroid Build Coastguard Worker         " to app-startup."),
186*288bf522SAndroid Build Coastguard Worker        ("To profile an app startup run:"
187*288bf522SAndroid Build Coastguard Worker         " torq --event app-startup --app <package-name>"))
188*288bf522SAndroid Build Coastguard Worker
189*288bf522SAndroid Build Coastguard Worker  if args.event == "app-startup" and args.app is None:
190*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
191*288bf522SAndroid Build Coastguard Worker        "Command is invalid because --app is not passed.",
192*288bf522SAndroid Build Coastguard Worker        ("Set --event %s --app <package> to perform an %s."
193*288bf522SAndroid Build Coastguard Worker         % (args.event, args.event)))
194*288bf522SAndroid Build Coastguard Worker
195*288bf522SAndroid Build Coastguard Worker  if args.runs < 1:
196*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
197*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because --runs cannot be set to a value smaller"
198*288bf522SAndroid Build Coastguard Worker         " than 1."), None)
199*288bf522SAndroid Build Coastguard Worker
200*288bf522SAndroid Build Coastguard Worker  if args.runs > 1 and args.ui:
201*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(("Command is invalid because --ui cannot be"
202*288bf522SAndroid Build Coastguard Worker                                  " passed if --runs is set to a value greater"
203*288bf522SAndroid Build Coastguard Worker                                  " than 1."),
204*288bf522SAndroid Build Coastguard Worker                                 ("Set torq -r %d --no-ui to perform %d runs."
205*288bf522SAndroid Build Coastguard Worker                                  % (args.runs, args.runs)))
206*288bf522SAndroid Build Coastguard Worker
207*288bf522SAndroid Build Coastguard Worker  if args.simpleperf_event is not None and args.profiler != "simpleperf":
208*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
209*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because --simpleperf-event cannot be passed"
210*288bf522SAndroid Build Coastguard Worker         " if --profiler is not set to simpleperf."),
211*288bf522SAndroid Build Coastguard Worker        ("To capture the simpleperf event run:"
212*288bf522SAndroid Build Coastguard Worker         " torq --profiler simpleperf --simpleperf-event %s"
213*288bf522SAndroid Build Coastguard Worker         % " --simpleperf-event ".join(args.simpleperf_event)))
214*288bf522SAndroid Build Coastguard Worker
215*288bf522SAndroid Build Coastguard Worker  if (args.simpleperf_event is not None and
216*288bf522SAndroid Build Coastguard Worker      len(args.simpleperf_event) != len(set(args.simpleperf_event))):
217*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
218*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because redundant calls to --simpleperf-event"
219*288bf522SAndroid Build Coastguard Worker         " cannot be made."),
220*288bf522SAndroid Build Coastguard Worker        ("Only set --simpleperf-event cpu-cycles once if you want"
221*288bf522SAndroid Build Coastguard Worker         " to collect cpu-cycles."))
222*288bf522SAndroid Build Coastguard Worker
223*288bf522SAndroid Build Coastguard Worker  if args.perfetto_config != "default":
224*288bf522SAndroid Build Coastguard Worker    if args.profiler != "perfetto":
225*288bf522SAndroid Build Coastguard Worker      return None, ValidationError(
226*288bf522SAndroid Build Coastguard Worker          ("Command is invalid because --perfetto-config cannot be passed"
227*288bf522SAndroid Build Coastguard Worker           " if --profiler is not set to perfetto."),
228*288bf522SAndroid Build Coastguard Worker          ("Set --profiler perfetto to choose a perfetto-config"
229*288bf522SAndroid Build Coastguard Worker           " to use."))
230*288bf522SAndroid Build Coastguard Worker
231*288bf522SAndroid Build Coastguard Worker  if (args.perfetto_config not in PREDEFINED_PERFETTO_CONFIGS and
232*288bf522SAndroid Build Coastguard Worker      not os.path.isfile(args.perfetto_config)):
233*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
234*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because --perfetto-config is not a valid"
235*288bf522SAndroid Build Coastguard Worker         " file path: %s" % args.perfetto_config),
236*288bf522SAndroid Build Coastguard Worker        ("Predefined perfetto configs can be used:\n"
237*288bf522SAndroid Build Coastguard Worker         "\t torq --perfetto-config %s\n"
238*288bf522SAndroid Build Coastguard Worker         "\t A filepath with a config can also be used:\n"
239*288bf522SAndroid Build Coastguard Worker         "\t torq --perfetto-config <config-filepath>"
240*288bf522SAndroid Build Coastguard Worker         % ("\n\t torq --perfetto-config"
241*288bf522SAndroid Build Coastguard Worker            " ".join(PREDEFINED_PERFETTO_CONFIGS.keys()))))
242*288bf522SAndroid Build Coastguard Worker
243*288bf522SAndroid Build Coastguard Worker  if args.between_dur_ms < MIN_DURATION_MS:
244*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
245*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because --between-dur-ms cannot be set to a"
246*288bf522SAndroid Build Coastguard Worker         " smaller value than %d." % MIN_DURATION_MS),
247*288bf522SAndroid Build Coastguard Worker        ("Set --between-dur-ms %d to wait %d seconds between"
248*288bf522SAndroid Build Coastguard Worker         " each run." % (MIN_DURATION_MS, (MIN_DURATION_MS / 1000))))
249*288bf522SAndroid Build Coastguard Worker
250*288bf522SAndroid Build Coastguard Worker  if args.between_dur_ms != DEFAULT_DUR_MS and args.runs == 1:
251*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
252*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because --between-dur-ms cannot be passed"
253*288bf522SAndroid Build Coastguard Worker         " if --runs is not a value greater than 1."),
254*288bf522SAndroid Build Coastguard Worker        "Set --runs 2 to run 2 tests.")
255*288bf522SAndroid Build Coastguard Worker
256*288bf522SAndroid Build Coastguard Worker  if args.excluded_ftrace_events is not None and args.profiler != "perfetto":
257*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
258*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because --excluded-ftrace-events cannot be passed"
259*288bf522SAndroid Build Coastguard Worker         " if --profiler is not set to perfetto."),
260*288bf522SAndroid Build Coastguard Worker        ("Set --profiler perfetto to exclude an ftrace event"
261*288bf522SAndroid Build Coastguard Worker         " from perfetto config."))
262*288bf522SAndroid Build Coastguard Worker
263*288bf522SAndroid Build Coastguard Worker  if (args.excluded_ftrace_events is not None and
264*288bf522SAndroid Build Coastguard Worker      len(args.excluded_ftrace_events) != len(set(
265*288bf522SAndroid Build Coastguard Worker          args.excluded_ftrace_events))):
266*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
267*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because duplicate ftrace events cannot be"
268*288bf522SAndroid Build Coastguard Worker         " included in --excluded-ftrace-events."),
269*288bf522SAndroid Build Coastguard Worker        ("--excluded-ftrace-events should only include one instance of an"
270*288bf522SAndroid Build Coastguard Worker         " ftrace event."))
271*288bf522SAndroid Build Coastguard Worker
272*288bf522SAndroid Build Coastguard Worker  if args.included_ftrace_events is not None and args.profiler != "perfetto":
273*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
274*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because --included-ftrace-events cannot be passed"
275*288bf522SAndroid Build Coastguard Worker         " if --profiler is not set to perfetto."),
276*288bf522SAndroid Build Coastguard Worker        ("Set --profiler perfetto to include an ftrace event"
277*288bf522SAndroid Build Coastguard Worker         " in perfetto config."))
278*288bf522SAndroid Build Coastguard Worker
279*288bf522SAndroid Build Coastguard Worker  if (args.included_ftrace_events is not None and
280*288bf522SAndroid Build Coastguard Worker      len(args.included_ftrace_events) != len(set(
281*288bf522SAndroid Build Coastguard Worker          args.included_ftrace_events))):
282*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
283*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because duplicate ftrace events cannot be"
284*288bf522SAndroid Build Coastguard Worker         " included in --included-ftrace-events."),
285*288bf522SAndroid Build Coastguard Worker        ("--included-ftrace-events should only include one instance of an"
286*288bf522SAndroid Build Coastguard Worker         " ftrace event."))
287*288bf522SAndroid Build Coastguard Worker
288*288bf522SAndroid Build Coastguard Worker  if (args.included_ftrace_events is not None and
289*288bf522SAndroid Build Coastguard Worker      args.excluded_ftrace_events is not None):
290*288bf522SAndroid Build Coastguard Worker    ftrace_event_intersection = sorted((set(args.excluded_ftrace_events) &
291*288bf522SAndroid Build Coastguard Worker                                        set(args.included_ftrace_events)))
292*288bf522SAndroid Build Coastguard Worker    if len(ftrace_event_intersection):
293*288bf522SAndroid Build Coastguard Worker      return None, ValidationError(
294*288bf522SAndroid Build Coastguard Worker          ("Command is invalid because ftrace event(s): %s cannot be both"
295*288bf522SAndroid Build Coastguard Worker           " included and excluded." % ", ".join(ftrace_event_intersection)),
296*288bf522SAndroid Build Coastguard Worker          ("\n\t ".join("Only set --excluded-ftrace-events %s if you want to"
297*288bf522SAndroid Build Coastguard Worker                        " exclude %s from the config or"
298*288bf522SAndroid Build Coastguard Worker                        " --included-ftrace-events %s if you want to include %s"
299*288bf522SAndroid Build Coastguard Worker                        " in the config."
300*288bf522SAndroid Build Coastguard Worker                        % (event, event, event, event)
301*288bf522SAndroid Build Coastguard Worker                        for event in ftrace_event_intersection)))
302*288bf522SAndroid Build Coastguard Worker
303*288bf522SAndroid Build Coastguard Worker  if args.subcommands == "config" and args.config_subcommand is None:
304*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
305*288bf522SAndroid Build Coastguard Worker        ("Command is invalid because torq config cannot be called"
306*288bf522SAndroid Build Coastguard Worker         " without a subcommand."),
307*288bf522SAndroid Build Coastguard Worker        ("Use one of the following subcommands:\n"
308*288bf522SAndroid Build Coastguard Worker         "\t torq config list\n"
309*288bf522SAndroid Build Coastguard Worker         "\t torq config show\n"
310*288bf522SAndroid Build Coastguard Worker         "\t torq config pull\n"))
311*288bf522SAndroid Build Coastguard Worker
312*288bf522SAndroid Build Coastguard Worker  if args.profiler == "simpleperf" and args.simpleperf_event is None:
313*288bf522SAndroid Build Coastguard Worker    args.simpleperf_event = ['cpu-cycles']
314*288bf522SAndroid Build Coastguard Worker
315*288bf522SAndroid Build Coastguard Worker  if args.ui is None:
316*288bf522SAndroid Build Coastguard Worker    args.ui = args.runs == 1
317*288bf522SAndroid Build Coastguard Worker
318*288bf522SAndroid Build Coastguard Worker  if args.subcommands == "config" and args.config_subcommand == "pull":
319*288bf522SAndroid Build Coastguard Worker    if args.file_path is None:
320*288bf522SAndroid Build Coastguard Worker      args.file_path = "./" + args.config_name + ".pbtxt"
321*288bf522SAndroid Build Coastguard Worker    elif not os.path.isfile(args.file_path):
322*288bf522SAndroid Build Coastguard Worker      return None, ValidationError(
323*288bf522SAndroid Build Coastguard Worker          ("Command is invalid because %s is not a valid filepath."
324*288bf522SAndroid Build Coastguard Worker           % args.file_path),
325*288bf522SAndroid Build Coastguard Worker          ("A default filepath can be used if you do not specify a file-path:\n"
326*288bf522SAndroid Build Coastguard Worker           "\t torq pull default to copy to ./default.pbtxt\n"
327*288bf522SAndroid Build Coastguard Worker           "\t torq pull lightweight to copy to ./lightweight.pbtxt\n"
328*288bf522SAndroid Build Coastguard Worker           "\t torq pull memory to copy to ./memory.pbtxt"))
329*288bf522SAndroid Build Coastguard Worker
330*288bf522SAndroid Build Coastguard Worker  if args.subcommands == "open" and not path_exists(args.file_path):
331*288bf522SAndroid Build Coastguard Worker    return None, ValidationError(
332*288bf522SAndroid Build Coastguard Worker        "Command is invalid because %s is an invalid file path."
333*288bf522SAndroid Build Coastguard Worker        % args.file_path, "Make sure your file exists.")
334*288bf522SAndroid Build Coastguard Worker
335*288bf522SAndroid Build Coastguard Worker  if args.profiler == "simpleperf":
336*288bf522SAndroid Build Coastguard Worker    args, error = verify_simpleperf_args(args)
337*288bf522SAndroid Build Coastguard Worker    if error is not None:
338*288bf522SAndroid Build Coastguard Worker      return None, error
339*288bf522SAndroid Build Coastguard Worker  else:
340*288bf522SAndroid Build Coastguard Worker    args.scripts_path = None
341*288bf522SAndroid Build Coastguard Worker
342*288bf522SAndroid Build Coastguard Worker  return args, None
343*288bf522SAndroid Build Coastguard Worker
344*288bf522SAndroid Build Coastguard Worker
345*288bf522SAndroid Build Coastguard Workerdef create_profiler_command(args):
346*288bf522SAndroid Build Coastguard Worker  return ProfilerCommand("profiler", args.event, args.profiler, args.out_dir,
347*288bf522SAndroid Build Coastguard Worker                         args.dur_ms,
348*288bf522SAndroid Build Coastguard Worker                         args.app, args.runs, args.simpleperf_event,
349*288bf522SAndroid Build Coastguard Worker                         args.perfetto_config, args.between_dur_ms,
350*288bf522SAndroid Build Coastguard Worker                         args.ui, args.excluded_ftrace_events,
351*288bf522SAndroid Build Coastguard Worker                         args.included_ftrace_events, args.from_user,
352*288bf522SAndroid Build Coastguard Worker                         args.to_user)
353*288bf522SAndroid Build Coastguard Worker
354*288bf522SAndroid Build Coastguard Worker
355*288bf522SAndroid Build Coastguard Workerdef create_config_command(args):
356*288bf522SAndroid Build Coastguard Worker  type = "config " + args.config_subcommand
357*288bf522SAndroid Build Coastguard Worker  config_name = None
358*288bf522SAndroid Build Coastguard Worker  file_path = None
359*288bf522SAndroid Build Coastguard Worker  dur_ms = None
360*288bf522SAndroid Build Coastguard Worker  excluded_ftrace_events = None
361*288bf522SAndroid Build Coastguard Worker  included_ftrace_events = None
362*288bf522SAndroid Build Coastguard Worker  if args.config_subcommand == "pull" or args.config_subcommand == "show":
363*288bf522SAndroid Build Coastguard Worker    config_name = args.config_name
364*288bf522SAndroid Build Coastguard Worker    dur_ms = args.dur_ms
365*288bf522SAndroid Build Coastguard Worker    excluded_ftrace_events = args.excluded_ftrace_events
366*288bf522SAndroid Build Coastguard Worker    included_ftrace_events = args.included_ftrace_events
367*288bf522SAndroid Build Coastguard Worker    if args.config_subcommand == "pull":
368*288bf522SAndroid Build Coastguard Worker      file_path = args.file_path
369*288bf522SAndroid Build Coastguard Worker
370*288bf522SAndroid Build Coastguard Worker  command = ConfigCommand(type, config_name, file_path, dur_ms,
371*288bf522SAndroid Build Coastguard Worker      excluded_ftrace_events, included_ftrace_events)
372*288bf522SAndroid Build Coastguard Worker  return command
373*288bf522SAndroid Build Coastguard Worker
374*288bf522SAndroid Build Coastguard Worker
375*288bf522SAndroid Build Coastguard Workerdef get_command_type(args):
376*288bf522SAndroid Build Coastguard Worker  command = None
377*288bf522SAndroid Build Coastguard Worker  if args.subcommands is None:
378*288bf522SAndroid Build Coastguard Worker    command = create_profiler_command(args)
379*288bf522SAndroid Build Coastguard Worker  if args.subcommands == "config":
380*288bf522SAndroid Build Coastguard Worker    command = create_config_command(args)
381*288bf522SAndroid Build Coastguard Worker  if args.subcommands == "open":
382*288bf522SAndroid Build Coastguard Worker    command = OpenCommand(args.file_path)
383*288bf522SAndroid Build Coastguard Worker  return command
384*288bf522SAndroid Build Coastguard Worker
385*288bf522SAndroid Build Coastguard Worker
386*288bf522SAndroid Build Coastguard Workerdef print_error(error):
387*288bf522SAndroid Build Coastguard Worker  print(error.message)
388*288bf522SAndroid Build Coastguard Worker  if error.suggestion is not None:
389*288bf522SAndroid Build Coastguard Worker    print("Suggestion:\n\t", error.suggestion)
390*288bf522SAndroid Build Coastguard Worker
391*288bf522SAndroid Build Coastguard Worker
392*288bf522SAndroid Build Coastguard Workerdef main():
393*288bf522SAndroid Build Coastguard Worker  parser = create_parser()
394*288bf522SAndroid Build Coastguard Worker  args = parser.parse_args()
395*288bf522SAndroid Build Coastguard Worker  args, error = verify_args(args)
396*288bf522SAndroid Build Coastguard Worker  if error is not None:
397*288bf522SAndroid Build Coastguard Worker    print_error(error)
398*288bf522SAndroid Build Coastguard Worker    return
399*288bf522SAndroid Build Coastguard Worker  command = get_command_type(args)
400*288bf522SAndroid Build Coastguard Worker  device = AdbDevice(args.serial)
401*288bf522SAndroid Build Coastguard Worker  error = command.execute(device)
402*288bf522SAndroid Build Coastguard Worker  if error is not None:
403*288bf522SAndroid Build Coastguard Worker    print_error(error)
404*288bf522SAndroid Build Coastguard Worker    return
405*288bf522SAndroid Build Coastguard Worker
406*288bf522SAndroid Build Coastguard Worker
407*288bf522SAndroid Build Coastguard Workerif __name__ == '__main__':
408*288bf522SAndroid Build Coastguard Worker  main()
409