• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2019, The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Class to collector perfetto trace."""
16import datetime
17import os
18import re
19import sys
20import time
21from datetime import timedelta
22from typing import Optional, List, Tuple
23
24# global variables
25DIR = os.path.abspath(os.path.dirname(__file__))
26
27sys.path.append(os.path.dirname(os.path.dirname(DIR)))
28
29import app_startup.lib.adb_utils as adb_utils
30from app_startup.lib.app_runner import AppRunner, AppRunnerListener
31import lib.print_utils as print_utils
32import lib.logcat_utils as logcat_utils
33import iorap.lib.iorapd_utils as iorapd_utils
34
35class PerfettoTraceCollector(AppRunnerListener):
36  """ Class to collect perfetto trace.
37
38      To set trace duration of perfetto, change the 'trace_duration_ms'.
39      To pull the generated perfetto trace on device, set the 'output'.
40  """
41  TRACE_FILE_SUFFIX = 'perfetto_trace.pb'
42  TRACE_DURATION_PROP = 'iorapd.perfetto.trace_duration_ms'
43  MS_PER_SEC  = 1000
44  DEFAULT_TRACE_DURATION = timedelta(milliseconds=5000) # 5 seconds
45  _COLLECTOR_TIMEOUT_MULTIPLIER = 10  # take the regular timeout and multiply
46
47  def __init__(self,
48               package: str,
49               activity: Optional[str],
50               compiler_filter: Optional[str],
51               timeout: Optional[int],
52               simulate: bool,
53               trace_duration: timedelta = DEFAULT_TRACE_DURATION,
54               save_destination_file_path: Optional[str] = None):
55    """ Initialize the perfetto trace collector. """
56    self.app_runner = AppRunner(package,
57                                activity,
58                                compiler_filter,
59                                timeout,
60                                simulate)
61    self.app_runner.add_callbacks(self)
62
63    self.trace_duration = trace_duration
64    self.save_destination_file_path = save_destination_file_path
65
66  def purge_file(self, suffix: str) -> None:
67    print_utils.debug_print('iorapd-perfetto: purge file in ' +
68                            self._get_remote_path())
69    adb_utils.delete_file_on_device(self._get_remote_path())
70
71  def run(self) -> Optional[List[Tuple[str]]]:
72    """Runs an app.
73
74    Returns:
75      A list of (metric, value) tuples.
76    """
77    return self.app_runner.run()
78
79  def preprocess(self):
80    # Sets up adb environment.
81    adb_utils.root()
82    adb_utils.disable_selinux()
83    time.sleep(1)
84
85    # Kill any existing process of this app
86    adb_utils.pkill(self.app_runner.package)
87
88    # Remove existing trace and compiler files
89    self.purge_file(PerfettoTraceCollector.TRACE_FILE_SUFFIX)
90
91    # Set perfetto trace duration prop to milliseconds.
92    adb_utils.set_prop(PerfettoTraceCollector.TRACE_DURATION_PROP,
93                       int(self.trace_duration.total_seconds()*
94                           PerfettoTraceCollector.MS_PER_SEC))
95
96    if not iorapd_utils.stop_iorapd():
97      raise RuntimeError('Cannot stop iorapd!')
98
99    if not iorapd_utils.enable_iorapd_perfetto():
100      raise RuntimeError('Cannot enable perfetto!')
101
102    if not iorapd_utils.disable_iorapd_readahead():
103      raise RuntimeError('Cannot disable readahead!')
104
105    if not iorapd_utils.start_iorapd():
106      raise RuntimeError('Cannot start iorapd!')
107
108    # Drop all caches to get cold starts.
109    adb_utils.vm_drop_cache()
110
111  def postprocess(self, pre_launch_timestamp: str):
112    # Kill any existing process of this app
113    adb_utils.pkill(self.app_runner.package)
114
115    iorapd_utils.disable_iorapd_perfetto()
116
117    if self.save_destination_file_path is not None:
118      adb_utils.pull_file(self._get_remote_path(),
119                          self.save_destination_file_path)
120
121  def metrics_selector(self, am_start_output: str,
122                       pre_launch_timestamp: str) -> str:
123    """Parses the metric after app startup by reading from logcat in a blocking
124    manner until all metrics have been found".
125
126    Returns:
127      An empty string because the metric needs no further parsing.
128    """
129    if not self._wait_for_perfetto_trace(pre_launch_timestamp):
130      raise RuntimeError('Could not save perfetto app trace file!')
131
132    return ''
133
134  def _wait_for_perfetto_trace(self, pre_launch_timestamp) -> Optional[str]:
135    """ Waits for the perfetto trace being saved to file.
136
137    The string is in the format of r".*Perfetto TraceBuffer saved to file:
138    <file path>.*"
139
140    Returns:
141      the string what the program waits for. If the string doesn't show up,
142      return None.
143    """
144    pattern = re.compile(r'.*Perfetto TraceBuffer saved to file: {}.*'.
145                         format(self._get_remote_path()))
146
147    # The pre_launch_timestamp is longer than what the datetime can parse. Trim
148    # last three digits to make them align. For example:
149    # 2019-07-02 23:20:06.972674825999 -> 2019-07-02 23:20:06.972674825
150    assert len(pre_launch_timestamp) == len('2019-07-02 23:20:06.972674825')
151    timestamp = datetime.datetime.strptime(pre_launch_timestamp[:-3],
152                                           '%Y-%m-%d %H:%M:%S.%f')
153
154    # The timeout of perfetto trace is longer than the normal app run timeout.
155    timeout_dt = self.app_runner.timeout * PerfettoTraceCollector._COLLECTOR_TIMEOUT_MULTIPLIER
156    timeout_end = timestamp + datetime.timedelta(seconds=timeout_dt)
157
158    return logcat_utils.blocking_wait_for_logcat_pattern(timestamp,
159                                                         pattern,
160                                                         timeout_end)
161
162  def _get_remote_path(self):
163    # For example: android.music%2Fmusic.TopLevelActivity.perfetto_trace.pb
164    return iorapd_utils._iorapd_path_to_data_file(self.app_runner.package,
165                                                  self.app_runner.activity,
166                                                  PerfettoTraceCollector.TRACE_FILE_SUFFIX)
167