• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#
2# Copyright (C) 2024 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17from abc import ABC, abstractmethod
18from .command_executor import ProfilerCommandExecutor, \
19  UserSwitchCommandExecutor, BootCommandExecutor, AppStartupCommandExecutor, \
20  ConfigCommandExecutor, WEB_UI_ADDRESS
21from .validation_error import ValidationError
22from .open_ui import open_trace
23
24ANDROID_SDK_VERSION_T = 33
25
26class Command(ABC):
27  """
28  Abstract base class representing a command.
29  """
30  def __init__(self, type):
31    self.type = type
32    self.command_executor = None
33
34  def get_type(self):
35    return self.type
36
37  def execute(self, device):
38    return self.command_executor.execute(self, device)
39
40  @abstractmethod
41  def validate(self, device):
42    raise NotImplementedError
43
44
45class ProfilerCommand(Command):
46  """
47  Represents commands which profile and trace the system.
48  """
49  def __init__(self, type, event, profiler, out_dir, dur_ms, app, runs,
50      simpleperf_event, perfetto_config, between_dur_ms, ui,
51      excluded_ftrace_events, included_ftrace_events, from_user, to_user,
52      scripts_path, symbols):
53    super().__init__(type)
54    self.event = event
55    self.profiler = profiler
56    self.out_dir = out_dir
57    self.dur_ms = dur_ms
58    self.app = app
59    self.runs = runs
60    self.simpleperf_event = simpleperf_event
61    self.perfetto_config = perfetto_config
62    self.between_dur_ms = between_dur_ms
63    self.use_ui = ui
64    self.excluded_ftrace_events = excluded_ftrace_events
65    self.included_ftrace_events = included_ftrace_events
66    self.from_user = from_user
67    self.to_user = to_user
68    self.scripts_path = scripts_path
69    self.symbols = symbols
70    match event:
71      case "custom":
72        self.command_executor = ProfilerCommandExecutor()
73      case "user-switch":
74        self.original_user = None
75        self.command_executor = UserSwitchCommandExecutor()
76      case "boot":
77        self.command_executor = BootCommandExecutor()
78      case "app-startup":
79        self.command_executor = AppStartupCommandExecutor()
80      case _:
81        raise ValueError("Invalid event name was used.")
82
83  def validate(self, device):
84    print("Further validating arguments of ProfilerCommand.")
85    if self.simpleperf_event is not None:
86      error = device.simpleperf_event_exists(self.simpleperf_event)
87      if error is not None:
88        return error
89    match self.event:
90      case "user-switch":
91        return self.validate_user_switch(device)
92      case "boot":
93        return self.validate_boot(device)
94      case "app-startup":
95        return self.validate_app_startup(device)
96
97  def validate_user_switch(self, device):
98    error = device.user_exists(self.to_user)
99    if error is not None:
100      return error
101    self.original_user = device.get_current_user()
102    if self.from_user is None:
103      self.from_user = self.original_user
104    else:
105      error = device.user_exists(self.from_user)
106      if error is not None:
107        return error
108    if self.from_user == self.to_user:
109      return ValidationError("Cannot perform user-switch to user %s because"
110                             " the current user on device %s is already %s."
111                             % (self.to_user, device.serial, self.from_user),
112                             "Choose a --to-user ID that is different than"
113                             " the --from-user ID.")
114    return None
115
116  @staticmethod
117  def validate_boot(device):
118    if device.get_android_sdk_version() < ANDROID_SDK_VERSION_T:
119      return ValidationError(
120          ("Cannot perform trace on boot because only devices with version Android 13"
121           " (T) or newer can be configured to automatically start recording traces on"
122           " boot."), ("Update your device or use a different device with"
123                      " Android 13 (T) or newer."))
124    return None
125
126  def validate_app_startup(self, device):
127    packages = device.get_packages()
128    if self.app not in packages:
129      return ValidationError(("Package %s does not exist on device with serial"
130                              " %s." % (self.app, device.serial)),
131                             ("Select from one of the following packages on"
132                              " device with serial %s: \n\t %s"
133                              % (device.serial, (",\n\t ".join(packages)))))
134    if device.is_package_running(self.app):
135      return ValidationError(("Package %s is already running on device with"
136                              " serial %s." % (self.app, device.serial)),
137                             ("Run 'adb -s %s shell am force-stop %s' to close"
138                              " the package %s before trying to start it."
139                              % (device.serial, self.app, self.app)))
140    return None
141
142
143class ConfigCommand(Command):
144  """
145  Represents commands which get information about the predefined configs.
146  """
147  def __init__(self, type, config_name, file_path, dur_ms,
148      excluded_ftrace_events, included_ftrace_events):
149    super().__init__(type)
150    self.config_name = config_name
151    self.file_path = file_path
152    self.dur_ms = dur_ms
153    self.excluded_ftrace_events = excluded_ftrace_events
154    self.included_ftrace_events = included_ftrace_events
155    self.command_executor = ConfigCommandExecutor()
156
157  def validate(self, device):
158    raise NotImplementedError
159
160
161class OpenCommand(Command):
162  """
163  Represents commands which open traces.
164  """
165  def __init__(self, file_path, use_trace_processor):
166    super().__init__(type)
167    self.file_path = file_path
168    self.use_trace_processor = use_trace_processor
169
170  def validate(self, device):
171    raise NotImplementedError
172
173  def execute(self, device):
174    return open_trace(self.file_path, WEB_UI_ADDRESS, self.use_trace_processor)
175