blob: 07b251fa82afa59a26ffc4b63ae746e4c2089f14 [file]
#
# Copyright (C) 2025 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.
#
import os
import subprocess
from .base import ANDROID_SDK_VERSION_T, Command, ValidationError
from .config_builder import PREDEFINED_PERFETTO_CONFIGS
def add_config_parser(subparsers):
config_parser = subparsers.add_parser(
'config',
help=('The config subcommand used'
' to list and show the'
' predefined perfetto configs.'))
config_subparsers = config_parser.add_subparsers(
dest='config_subcommand', help=('torq config'
' subcommands'))
config_subparsers.add_parser(
'list', help=('Command to list the predefined'
' perfetto configs'))
config_show_parser = config_subparsers.add_parser(
'show',
help=('Command to print'
' the '
' perfetto config'
' in the terminal.'))
config_show_parser.add_argument(
'config_name',
choices=['lightweight', 'default', 'memory'],
help=('Name of the predefined perfetto'
' config to print.'))
config_show_parser.add_argument(
'-d',
'--dur-ms',
type=int,
help=('The duration (ms) of the event. Determines when'
' to stop collecting performance data.'))
config_show_parser.add_argument(
'--excluded-ftrace-events',
action='append',
help=('Excludes specified ftrace event from the perfetto'
' config events.'))
config_show_parser.add_argument(
'--included-ftrace-events',
action='append',
help=('Includes specified ftrace event in the perfetto'
' config events.'))
config_pull_parser = config_subparsers.add_parser(
'pull',
help=('Command to copy'
' a predefined config'
' to the specified'
' file path.'))
config_pull_parser.add_argument(
'config_name',
choices=['lightweight', 'default', 'memory'],
help='Name of the predefined config to copy')
config_pull_parser.add_argument(
'file_path',
nargs='?',
help=('File path to copy the predefined'
' config to'))
config_pull_parser.add_argument(
'-d',
'--dur-ms',
type=int,
help=('The duration (ms) of the event. Determines when'
' to stop collecting performance data.'))
config_pull_parser.add_argument(
'--excluded-ftrace-events',
action='append',
help=('Excludes specified ftrace event from the perfetto'
' config events.'))
config_pull_parser.add_argument(
'--included-ftrace-events',
action='append',
help=('Includes specified ftrace event in the perfetto'
' config events.'))
def verify_config_args(args):
if args.config_subcommand is None:
return None, ValidationError(
("Command is invalid because torq config cannot be called"
" without a subcommand."), ("Use one of the following subcommands:\n"
"\t torq config list\n"
"\t torq config show\n"
"\t torq config pull\n"))
if args.config_subcommand == "pull":
if args.file_path is None:
args.file_path = "./" + args.config_name + ".pbtxt"
elif not os.path.isfile(args.file_path):
return None, ValidationError(
("Command is invalid because %s is not a valid filepath." %
args.file_path),
("A default filepath can be used if you do not specify a file-path:\n"
"\t torq pull default to copy to ./default.pbtxt\n"
"\t torq pull lightweight to copy to ./lightweight.pbtxt\n"
"\t torq pull memory to copy to ./memory.pbtxt"))
return args, None
def create_config_command(args):
type = "config " + args.config_subcommand
config_name = None
file_path = None
dur_ms = None
excluded_ftrace_events = None
included_ftrace_events = None
if args.config_subcommand == "pull" or args.config_subcommand == "show":
config_name = args.config_name
dur_ms = args.dur_ms
excluded_ftrace_events = args.excluded_ftrace_events
included_ftrace_events = args.included_ftrace_events
if args.config_subcommand == "pull":
file_path = args.file_path
command = ConfigCommand(type, config_name, file_path, dur_ms,
excluded_ftrace_events, included_ftrace_events)
return command
def execute_show_or_pull_command(command, device):
android_sdk_version = ANDROID_SDK_VERSION_T
error = device.check_device_connection()
if error is None:
device.root_device()
android_sdk_version = device.get_android_sdk_version()
config, error = PREDEFINED_PERFETTO_CONFIGS[command.config_name](
command, android_sdk_version)
if error is not None:
return error
if command.get_type() == "config pull":
subprocess.run(("cat > %s %s" % (command.file_path, config)), shell=True)
else:
print("\n".join(config.strip().split("\n")[2:-2]))
return None
def execute_config_command(args, device):
command = create_config_command(args)
match command.get_type():
case "config list":
print("\n".join(list(PREDEFINED_PERFETTO_CONFIGS.keys())))
return None
case "config show" | "config pull":
return execute_show_or_pull_command(command, device)
case _:
raise ValueError("Invalid config subcommand was used.")
class ConfigCommand(Command):
"""
Represents commands which get information about the predefined configs.
"""
def __init__(self, type, config_name, file_path, dur_ms,
excluded_ftrace_events, included_ftrace_events):
super().__init__(type)
self.config_name = config_name
self.file_path = file_path
self.dur_ms = dur_ms
self.excluded_ftrace_events = excluded_ftrace_events
self.included_ftrace_events = included_ftrace_events
def validate(self, device):
raise NotImplementedError