1*90c8c64dSAndroid Build Coastguard Worker#!/usr/bin/python3 2*90c8c64dSAndroid Build Coastguard Worker 3*90c8c64dSAndroid Build Coastguard Worker# Copyright (C) 2019 The Android Open Source Project 4*90c8c64dSAndroid Build Coastguard Worker# 5*90c8c64dSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*90c8c64dSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*90c8c64dSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*90c8c64dSAndroid Build Coastguard Worker# 9*90c8c64dSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*90c8c64dSAndroid Build Coastguard Worker# 11*90c8c64dSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*90c8c64dSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*90c8c64dSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*90c8c64dSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*90c8c64dSAndroid Build Coastguard Worker# limitations under the License. 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Worker# 18*90c8c64dSAndroid Build Coastguard Worker# This is an ADB proxy for Winscope. 19*90c8c64dSAndroid Build Coastguard Worker# 20*90c8c64dSAndroid Build Coastguard Worker# Requirements: python3.10 and ADB installed and in system PATH. 21*90c8c64dSAndroid Build Coastguard Worker# 22*90c8c64dSAndroid Build Coastguard Worker# Usage: 23*90c8c64dSAndroid Build Coastguard Worker# run: python3 winscope_proxy.py 24*90c8c64dSAndroid Build Coastguard Worker# 25*90c8c64dSAndroid Build Coastguard Worker 26*90c8c64dSAndroid Build Coastguard Workerimport argparse 27*90c8c64dSAndroid Build Coastguard Workerimport base64 28*90c8c64dSAndroid Build Coastguard Workerimport gzip 29*90c8c64dSAndroid Build Coastguard Workerimport json 30*90c8c64dSAndroid Build Coastguard Workerimport logging 31*90c8c64dSAndroid Build Coastguard Workerimport os 32*90c8c64dSAndroid Build Coastguard Workerimport re 33*90c8c64dSAndroid Build Coastguard Workerimport secrets 34*90c8c64dSAndroid Build Coastguard Workerimport signal 35*90c8c64dSAndroid Build Coastguard Workerimport subprocess 36*90c8c64dSAndroid Build Coastguard Workerimport sys 37*90c8c64dSAndroid Build Coastguard Workerimport threading 38*90c8c64dSAndroid Build Coastguard Workerimport time 39*90c8c64dSAndroid Build Coastguard Workerfrom abc import abstractmethod 40*90c8c64dSAndroid Build Coastguard Workerfrom enum import Enum 41*90c8c64dSAndroid Build Coastguard Workerfrom http import HTTPStatus 42*90c8c64dSAndroid Build Coastguard Workerfrom http.server import HTTPServer, BaseHTTPRequestHandler 43*90c8c64dSAndroid Build Coastguard Workerfrom logging import DEBUG, INFO 44*90c8c64dSAndroid Build Coastguard Workerfrom tempfile import NamedTemporaryFile 45*90c8c64dSAndroid Build Coastguard Workerfrom typing import Callable 46*90c8c64dSAndroid Build Coastguard Worker 47*90c8c64dSAndroid Build Coastguard Worker# GLOBALS # 48*90c8c64dSAndroid Build Coastguard Worker 49*90c8c64dSAndroid Build Coastguard Workerlog = None 50*90c8c64dSAndroid Build Coastguard Workersecret_token = None 51*90c8c64dSAndroid Build Coastguard Worker 52*90c8c64dSAndroid Build Coastguard Worker# CONFIG # 53*90c8c64dSAndroid Build Coastguard Worker 54*90c8c64dSAndroid Build Coastguard Workerdef create_argument_parser() -> argparse.ArgumentParser: 55*90c8c64dSAndroid Build Coastguard Worker parser = argparse.ArgumentParser(description='Proxy for go/winscope', prog='winscope_proxy') 56*90c8c64dSAndroid Build Coastguard Worker 57*90c8c64dSAndroid Build Coastguard Worker parser.add_argument('--info', '-i', dest='loglevel', action='store_const', const=INFO) 58*90c8c64dSAndroid Build Coastguard Worker parser.add_argument('--port', '-p', default=5544, action='store') 59*90c8c64dSAndroid Build Coastguard Worker 60*90c8c64dSAndroid Build Coastguard Worker parser.set_defaults(loglevel=DEBUG) 61*90c8c64dSAndroid Build Coastguard Worker 62*90c8c64dSAndroid Build Coastguard Worker return parser 63*90c8c64dSAndroid Build Coastguard Worker 64*90c8c64dSAndroid Build Coastguard Worker# Keep in sync with ProxyConnection#VERSION in Winscope 65*90c8c64dSAndroid Build Coastguard WorkerVERSION = '4.0.8' 66*90c8c64dSAndroid Build Coastguard Worker 67*90c8c64dSAndroid Build Coastguard WorkerPERFETTO_TRACE_CONFIG_FILE = '/data/misc/perfetto-configs/winscope-proxy-trace.conf' 68*90c8c64dSAndroid Build Coastguard WorkerPERFETTO_DUMP_CONFIG_FILE = '/data/misc/perfetto-configs/winscope-proxy-dump.conf' 69*90c8c64dSAndroid Build Coastguard WorkerPERFETTO_TRACE_FILE = '/data/misc/perfetto-traces/winscope-proxy-trace.perfetto-trace' 70*90c8c64dSAndroid Build Coastguard WorkerPERFETTO_DUMP_FILE = '/data/misc/perfetto-traces/winscope-proxy-dump.perfetto-trace' 71*90c8c64dSAndroid Build Coastguard WorkerPERFETTO_UNIQUE_SESSION_NAME = 'winscope proxy perfetto tracing' 72*90c8c64dSAndroid Build Coastguard WorkerPERFETTO_TRACING_SESSIONS_QUERY_START = """TRACING SESSIONS: 73*90c8c64dSAndroid Build Coastguard Worker 74*90c8c64dSAndroid Build Coastguard WorkerID UID STATE BUF (#) KB DUR (s) #DS STARTED NAME 75*90c8c64dSAndroid Build Coastguard Worker=== === ===== ========== ======= === ======= ====\n""" 76*90c8c64dSAndroid Build Coastguard WorkerPERFETTO_TRACING_SESSIONS_QUERY_END = """\nNOTE: Some tracing sessions are not reported in the list above.""" 77*90c8c64dSAndroid Build Coastguard Worker 78*90c8c64dSAndroid Build Coastguard WorkerWINSCOPE_VERSION_HEADER = "Winscope-Proxy-Version" 79*90c8c64dSAndroid Build Coastguard WorkerWINSCOPE_TOKEN_HEADER = "Winscope-Token" 80*90c8c64dSAndroid Build Coastguard Worker 81*90c8c64dSAndroid Build Coastguard Worker# Location to save the proxy security token 82*90c8c64dSAndroid Build Coastguard WorkerWINSCOPE_TOKEN_LOCATION = os.path.expanduser('~/.config/winscope/.token') 83*90c8c64dSAndroid Build Coastguard Worker 84*90c8c64dSAndroid Build Coastguard Worker# Winscope traces extensions 85*90c8c64dSAndroid Build Coastguard WorkerWINSCOPE_EXT = ".winscope" 86*90c8c64dSAndroid Build Coastguard WorkerWINSCOPE_EXT_LEGACY = ".pb" 87*90c8c64dSAndroid Build Coastguard WorkerWINSCOPE_EXTS = [WINSCOPE_EXT, WINSCOPE_EXT_LEGACY] 88*90c8c64dSAndroid Build Coastguard Worker 89*90c8c64dSAndroid Build Coastguard Worker# Winscope traces directories 90*90c8c64dSAndroid Build Coastguard WorkerWINSCOPE_DIR = "/data/misc/wmtrace/" 91*90c8c64dSAndroid Build Coastguard WorkerWINSCOPE_BACKUP_DIR = "/data/local/tmp/last_winscope_tracing_session/" 92*90c8c64dSAndroid Build Coastguard Worker 93*90c8c64dSAndroid Build Coastguard Worker# Tracing handlers 94*90c8c64dSAndroid Build Coastguard WorkerSIGNAL_HANDLER_LOG = "/data/local/tmp/winscope_signal_handler.log" 95*90c8c64dSAndroid Build Coastguard WorkerWINSCOPE_STATUS = "/data/local/tmp/winscope_status" 96*90c8c64dSAndroid Build Coastguard Worker 97*90c8c64dSAndroid Build Coastguard Worker# Max interval between the client keep-alive requests in seconds 98*90c8c64dSAndroid Build Coastguard WorkerKEEP_ALIVE_INTERVAL_S = 5 99*90c8c64dSAndroid Build Coastguard Worker 100*90c8c64dSAndroid Build Coastguard Worker# Perfetto's default timeout for getting an ACK from producer processes is 5s 101*90c8c64dSAndroid Build Coastguard Worker# We need to be sure that the timeout is longer than that with a good margin. 102*90c8c64dSAndroid Build Coastguard WorkerCOMMAND_TIMEOUT_S = 15 103*90c8c64dSAndroid Build Coastguard Worker 104*90c8c64dSAndroid Build Coastguard Workerclass File: 105*90c8c64dSAndroid Build Coastguard Worker def __init__(self, file, filetype) -> None: 106*90c8c64dSAndroid Build Coastguard Worker self.file = file 107*90c8c64dSAndroid Build Coastguard Worker self.type = filetype 108*90c8c64dSAndroid Build Coastguard Worker 109*90c8c64dSAndroid Build Coastguard Worker def get_filepaths(self, device_id) -> list[str]: 110*90c8c64dSAndroid Build Coastguard Worker return [self.file] 111*90c8c64dSAndroid Build Coastguard Worker 112*90c8c64dSAndroid Build Coastguard Worker def get_filetype(self) -> str: 113*90c8c64dSAndroid Build Coastguard Worker return self.type 114*90c8c64dSAndroid Build Coastguard Worker 115*90c8c64dSAndroid Build Coastguard Worker 116*90c8c64dSAndroid Build Coastguard Workerclass FileMatcher: 117*90c8c64dSAndroid Build Coastguard Worker def __init__(self, path, matcher, filetype) -> None: 118*90c8c64dSAndroid Build Coastguard Worker self.path = path 119*90c8c64dSAndroid Build Coastguard Worker self.matcher = matcher 120*90c8c64dSAndroid Build Coastguard Worker self.type = filetype 121*90c8c64dSAndroid Build Coastguard Worker 122*90c8c64dSAndroid Build Coastguard Worker def get_filepaths(self, device_id) -> list[str]: 123*90c8c64dSAndroid Build Coastguard Worker if len(self.matcher) > 0: 124*90c8c64dSAndroid Build Coastguard Worker matchingFiles = call_adb( 125*90c8c64dSAndroid Build Coastguard Worker f"shell su root find {self.path} -name {self.matcher}", device_id) 126*90c8c64dSAndroid Build Coastguard Worker else: 127*90c8c64dSAndroid Build Coastguard Worker matchingFiles = call_adb( 128*90c8c64dSAndroid Build Coastguard Worker f"shell su root find {self.path}", device_id) 129*90c8c64dSAndroid Build Coastguard Worker 130*90c8c64dSAndroid Build Coastguard Worker files = matchingFiles.split('\n')[:-1] 131*90c8c64dSAndroid Build Coastguard Worker log.debug("Found files %s", files) 132*90c8c64dSAndroid Build Coastguard Worker return files 133*90c8c64dSAndroid Build Coastguard Worker 134*90c8c64dSAndroid Build Coastguard Worker def get_filetype(self) -> str: 135*90c8c64dSAndroid Build Coastguard Worker return self.type 136*90c8c64dSAndroid Build Coastguard Worker 137*90c8c64dSAndroid Build Coastguard Worker 138*90c8c64dSAndroid Build Coastguard Workerclass WinscopeFileMatcher(FileMatcher): 139*90c8c64dSAndroid Build Coastguard Worker def __init__(self, path, matcher, filetype) -> None: 140*90c8c64dSAndroid Build Coastguard Worker self.path = path 141*90c8c64dSAndroid Build Coastguard Worker self.internal_matchers = list(map(lambda ext: FileMatcher(path, f'{matcher}{ext}', filetype), 142*90c8c64dSAndroid Build Coastguard Worker WINSCOPE_EXTS)) 143*90c8c64dSAndroid Build Coastguard Worker self.type = filetype 144*90c8c64dSAndroid Build Coastguard Worker 145*90c8c64dSAndroid Build Coastguard Worker def get_filepaths(self, device_id) -> list[str]: 146*90c8c64dSAndroid Build Coastguard Worker for matcher in self.internal_matchers: 147*90c8c64dSAndroid Build Coastguard Worker files = matcher.get_filepaths(device_id) 148*90c8c64dSAndroid Build Coastguard Worker if len(files) > 0: 149*90c8c64dSAndroid Build Coastguard Worker return files 150*90c8c64dSAndroid Build Coastguard Worker log.debug("No files found") 151*90c8c64dSAndroid Build Coastguard Worker return [] 152*90c8c64dSAndroid Build Coastguard Worker 153*90c8c64dSAndroid Build Coastguard Worker 154*90c8c64dSAndroid Build Coastguard Workerdef get_shell_args(device_id: str, type: str) -> list[str]: 155*90c8c64dSAndroid Build Coastguard Worker shell = ['adb', '-s', device_id, 'shell'] 156*90c8c64dSAndroid Build Coastguard Worker log.debug(f"Starting {type} shell {' '.join(shell)}") 157*90c8c64dSAndroid Build Coastguard Worker return shell 158*90c8c64dSAndroid Build Coastguard Worker 159*90c8c64dSAndroid Build Coastguard Workerdef is_perfetto_data_source_available(name: str, query_result: str) -> bool: 160*90c8c64dSAndroid Build Coastguard Worker return name in query_result 161*90c8c64dSAndroid Build Coastguard Worker 162*90c8c64dSAndroid Build Coastguard Workerdef is_any_perfetto_data_source_available(query_result: str) -> bool: 163*90c8c64dSAndroid Build Coastguard Worker return is_perfetto_data_source_available('android.inputmethod', query_result) or \ 164*90c8c64dSAndroid Build Coastguard Worker is_perfetto_data_source_available('android.protolog', query_result) or \ 165*90c8c64dSAndroid Build Coastguard Worker is_perfetto_data_source_available('android.surfaceflinger.layers', query_result) or \ 166*90c8c64dSAndroid Build Coastguard Worker is_perfetto_data_source_available('android.surfaceflinger.transactions', query_result) or \ 167*90c8c64dSAndroid Build Coastguard Worker is_perfetto_data_source_available('com.android.wm.shell.transition', query_result) or \ 168*90c8c64dSAndroid Build Coastguard Worker is_perfetto_data_source_available('android.viewcapture', query_result) or \ 169*90c8c64dSAndroid Build Coastguard Worker is_perfetto_data_source_available('android.windowmanager', query_result) or \ 170*90c8c64dSAndroid Build Coastguard Worker is_perfetto_data_source_available('android.input.inputevent', query_result) 171*90c8c64dSAndroid Build Coastguard Worker 172*90c8c64dSAndroid Build Coastguard Worker 173*90c8c64dSAndroid Build Coastguard Workerclass TraceConfig: 174*90c8c64dSAndroid Build Coastguard Worker def __init__(self, is_perfetto: bool) -> None: 175*90c8c64dSAndroid Build Coastguard Worker self.is_perfetto = is_perfetto 176*90c8c64dSAndroid Build Coastguard Worker 177*90c8c64dSAndroid Build Coastguard Worker @abstractmethod 178*90c8c64dSAndroid Build Coastguard Worker def add(self, config: str, value: str | list[str] | None) -> None: 179*90c8c64dSAndroid Build Coastguard Worker pass 180*90c8c64dSAndroid Build Coastguard Worker 181*90c8c64dSAndroid Build Coastguard Worker @abstractmethod 182*90c8c64dSAndroid Build Coastguard Worker def is_valid(self, config: str) -> bool: 183*90c8c64dSAndroid Build Coastguard Worker pass 184*90c8c64dSAndroid Build Coastguard Worker 185*90c8c64dSAndroid Build Coastguard Worker @abstractmethod 186*90c8c64dSAndroid Build Coastguard Worker def execute_command(self, server, device_id): 187*90c8c64dSAndroid Build Coastguard Worker pass 188*90c8c64dSAndroid Build Coastguard Worker 189*90c8c64dSAndroid Build Coastguard Worker def get_trace_identifiers(self)-> list[str]: 190*90c8c64dSAndroid Build Coastguard Worker return [""] 191*90c8c64dSAndroid Build Coastguard Worker 192*90c8c64dSAndroid Build Coastguard Worker def get_optional_start_args(self, identifier)-> str: 193*90c8c64dSAndroid Build Coastguard Worker return "" 194*90c8c64dSAndroid Build Coastguard Worker 195*90c8c64dSAndroid Build Coastguard Worker def execute_optional_config_command(self, server, device_id, shell, command, config_key, config_value): 196*90c8c64dSAndroid Build Coastguard Worker process = subprocess.Popen(shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 197*90c8c64dSAndroid Build Coastguard Worker stdin=subprocess.PIPE, start_new_session=True) 198*90c8c64dSAndroid Build Coastguard Worker out, err = process.communicate(command.encode('utf-8')) 199*90c8c64dSAndroid Build Coastguard Worker if process.returncode != 0: 200*90c8c64dSAndroid Build Coastguard Worker raise AdbError( 201*90c8c64dSAndroid Build Coastguard Worker f"Error executing command:\n {command}\n\n### OUTPUT ###{out.decode('utf-8')}\n{err.decode('utf-8')}") 202*90c8c64dSAndroid Build Coastguard Worker log.debug(f"Optional trace config changed on device {device_id} {config_key}:{config_value}") 203*90c8c64dSAndroid Build Coastguard Worker 204*90c8c64dSAndroid Build Coastguard Worker def execute_perfetto_config_command(self, server, shell, command, trace_name): 205*90c8c64dSAndroid Build Coastguard Worker process = subprocess.Popen(shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 206*90c8c64dSAndroid Build Coastguard Worker stdin=subprocess.PIPE, start_new_session=True) 207*90c8c64dSAndroid Build Coastguard Worker out, err = process.communicate(command.encode('utf-8')) 208*90c8c64dSAndroid Build Coastguard Worker if process.returncode != 0: 209*90c8c64dSAndroid Build Coastguard Worker raise AdbError( 210*90c8c64dSAndroid Build Coastguard Worker f"Error executing command:\n {command}\n\n### OUTPUT ###{out.decode('utf-8')}\n{err.decode('utf-8')}") 211*90c8c64dSAndroid Build Coastguard Worker log.info(f'{trace_name} (perfetto) configured to start along the other perfetto traces.') 212*90c8c64dSAndroid Build Coastguard Worker 213*90c8c64dSAndroid Build Coastguard Worker 214*90c8c64dSAndroid Build Coastguard Workerclass SurfaceFlingerTraceConfig(TraceConfig): 215*90c8c64dSAndroid Build Coastguard Worker """Handles optional configuration for Surface Flinger traces. 216*90c8c64dSAndroid Build Coastguard Worker """ 217*90c8c64dSAndroid Build Coastguard Worker LEGACY_FLAGS_MAP = { 218*90c8c64dSAndroid Build Coastguard Worker "input": 1 << 1, 219*90c8c64dSAndroid Build Coastguard Worker "composition": 1 << 2, 220*90c8c64dSAndroid Build Coastguard Worker "metadata": 1 << 3, 221*90c8c64dSAndroid Build Coastguard Worker "hwc": 1 << 4, 222*90c8c64dSAndroid Build Coastguard Worker "tracebuffers": 1 << 5, 223*90c8c64dSAndroid Build Coastguard Worker "virtualdisplays": 1 << 6 224*90c8c64dSAndroid Build Coastguard Worker } 225*90c8c64dSAndroid Build Coastguard Worker 226*90c8c64dSAndroid Build Coastguard Worker PERFETTO_FLAGS_MAP = { 227*90c8c64dSAndroid Build Coastguard Worker "input": "TRACE_FLAG_INPUT", 228*90c8c64dSAndroid Build Coastguard Worker "composition": "TRACE_FLAG_COMPOSITION", 229*90c8c64dSAndroid Build Coastguard Worker "metadata": "TRACE_FLAG_EXTRA", 230*90c8c64dSAndroid Build Coastguard Worker "hwc": "TRACE_FLAG_HWC", 231*90c8c64dSAndroid Build Coastguard Worker "tracebuffers": "TRACE_FLAG_BUFFERS", 232*90c8c64dSAndroid Build Coastguard Worker "virtualdisplays": "TRACE_FLAG_VIRTUAL_DISPLAYS", 233*90c8c64dSAndroid Build Coastguard Worker } 234*90c8c64dSAndroid Build Coastguard Worker 235*90c8c64dSAndroid Build Coastguard Worker def __init__(self, is_perfetto: bool) -> None: 236*90c8c64dSAndroid Build Coastguard Worker super().__init__(is_perfetto) 237*90c8c64dSAndroid Build Coastguard Worker self.flags = [] 238*90c8c64dSAndroid Build Coastguard Worker self.perfetto_flags = [] 239*90c8c64dSAndroid Build Coastguard Worker 240*90c8c64dSAndroid Build Coastguard Worker # defaults set for all configs 241*90c8c64dSAndroid Build Coastguard Worker self.selected_configs = { 242*90c8c64dSAndroid Build Coastguard Worker "sfbuffersize": "16000" 243*90c8c64dSAndroid Build Coastguard Worker } 244*90c8c64dSAndroid Build Coastguard Worker 245*90c8c64dSAndroid Build Coastguard Worker def add(self, config: str, value: str | None) -> None: 246*90c8c64dSAndroid Build Coastguard Worker if config in SurfaceFlingerTraceConfig.LEGACY_FLAGS_MAP: 247*90c8c64dSAndroid Build Coastguard Worker self.flags.append(config) 248*90c8c64dSAndroid Build Coastguard Worker elif config in self.selected_configs: 249*90c8c64dSAndroid Build Coastguard Worker self.selected_configs[config] = value 250*90c8c64dSAndroid Build Coastguard Worker 251*90c8c64dSAndroid Build Coastguard Worker def is_valid(self, config: str) -> bool: 252*90c8c64dSAndroid Build Coastguard Worker return config in SurfaceFlingerTraceConfig.LEGACY_FLAGS_MAP or config in self.selected_configs 253*90c8c64dSAndroid Build Coastguard Worker 254*90c8c64dSAndroid Build Coastguard Worker def execute_command(self, server, device_id): 255*90c8c64dSAndroid Build Coastguard Worker shell = get_shell_args(device_id, "sf config") 256*90c8c64dSAndroid Build Coastguard Worker 257*90c8c64dSAndroid Build Coastguard Worker if self.is_perfetto: 258*90c8c64dSAndroid Build Coastguard Worker self.execute_perfetto_config_command(server, shell, self._perfetto_config_command(), "SurfaceFlinger") 259*90c8c64dSAndroid Build Coastguard Worker else: 260*90c8c64dSAndroid Build Coastguard Worker self.execute_optional_config_command(server, device_id, shell, self._legacy_flags_command(), "sf flags", self.flags) 261*90c8c64dSAndroid Build Coastguard Worker self.execute_optional_config_command(server, device_id, shell, self._legacy_buffer_size_command(), "sf buffer size", self.selected_configs["sfbuffersize"]) 262*90c8c64dSAndroid Build Coastguard Worker 263*90c8c64dSAndroid Build Coastguard Worker def _perfetto_config_command(self) -> str: 264*90c8c64dSAndroid Build Coastguard Worker flags = "\n".join([f"""trace_flags: {SurfaceFlingerTraceConfig.PERFETTO_FLAGS_MAP[flag]}""" for flag in self.flags]) 265*90c8c64dSAndroid Build Coastguard Worker 266*90c8c64dSAndroid Build Coastguard Worker return f""" 267*90c8c64dSAndroid Build Coastguard Workercat << EOF >> {PERFETTO_TRACE_CONFIG_FILE} 268*90c8c64dSAndroid Build Coastguard Workerdata_sources: {{ 269*90c8c64dSAndroid Build Coastguard Worker config {{ 270*90c8c64dSAndroid Build Coastguard Worker name: "android.surfaceflinger.layers" 271*90c8c64dSAndroid Build Coastguard Worker surfaceflinger_layers_config: {{ 272*90c8c64dSAndroid Build Coastguard Worker mode: MODE_ACTIVE 273*90c8c64dSAndroid Build Coastguard Worker {flags} 274*90c8c64dSAndroid Build Coastguard Worker }} 275*90c8c64dSAndroid Build Coastguard Worker }} 276*90c8c64dSAndroid Build Coastguard Worker}} 277*90c8c64dSAndroid Build Coastguard WorkerEOF 278*90c8c64dSAndroid Build Coastguard Worker""" 279*90c8c64dSAndroid Build Coastguard Worker 280*90c8c64dSAndroid Build Coastguard Worker def _legacy_buffer_size_command(self) -> str: 281*90c8c64dSAndroid Build Coastguard Worker return f'su root service call SurfaceFlinger 1029 i32 {self.selected_configs["sfbuffersize"]}' 282*90c8c64dSAndroid Build Coastguard Worker 283*90c8c64dSAndroid Build Coastguard Worker def _legacy_flags_command(self) -> str: 284*90c8c64dSAndroid Build Coastguard Worker flags = 0 285*90c8c64dSAndroid Build Coastguard Worker for flag in self.flags: 286*90c8c64dSAndroid Build Coastguard Worker flags |= SurfaceFlingerTraceConfig.LEGACY_FLAGS_MAP[flag] 287*90c8c64dSAndroid Build Coastguard Worker 288*90c8c64dSAndroid Build Coastguard Worker return f"su root service call SurfaceFlinger 1033 i32 {flags}" 289*90c8c64dSAndroid Build Coastguard Worker 290*90c8c64dSAndroid Build Coastguard Worker 291*90c8c64dSAndroid Build Coastguard Workerclass WindowManagerTraceConfig(TraceConfig): 292*90c8c64dSAndroid Build Coastguard Worker PERFETTO_LOG_LEVEL_MAP = { 293*90c8c64dSAndroid Build Coastguard Worker "verbose": "LOG_LEVEL_VERBOSE", 294*90c8c64dSAndroid Build Coastguard Worker "debug": "LOG_LEVEL_DEBUG", 295*90c8c64dSAndroid Build Coastguard Worker "critical": "LOG_LEVEL_CRITICAL", 296*90c8c64dSAndroid Build Coastguard Worker } 297*90c8c64dSAndroid Build Coastguard Worker 298*90c8c64dSAndroid Build Coastguard Worker PERFETTO_LOG_FREQUENCY_MAP = { 299*90c8c64dSAndroid Build Coastguard Worker "frame": "LOG_FREQUENCY_FRAME", 300*90c8c64dSAndroid Build Coastguard Worker "transaction": "LOG_FREQUENCY_TRANSACTION", 301*90c8c64dSAndroid Build Coastguard Worker } 302*90c8c64dSAndroid Build Coastguard Worker 303*90c8c64dSAndroid Build Coastguard Worker """Handles optional selected configuration for Window Manager traces. 304*90c8c64dSAndroid Build Coastguard Worker """ 305*90c8c64dSAndroid Build Coastguard Worker 306*90c8c64dSAndroid Build Coastguard Worker def __init__(self, is_perfetto: bool) -> None: 307*90c8c64dSAndroid Build Coastguard Worker super().__init__(is_perfetto) 308*90c8c64dSAndroid Build Coastguard Worker # defaults set for all configs 309*90c8c64dSAndroid Build Coastguard Worker self.selected_configs = { 310*90c8c64dSAndroid Build Coastguard Worker "wmbuffersize": "16000", 311*90c8c64dSAndroid Build Coastguard Worker "tracinglevel": "debug", 312*90c8c64dSAndroid Build Coastguard Worker "tracingtype": "frame", 313*90c8c64dSAndroid Build Coastguard Worker } 314*90c8c64dSAndroid Build Coastguard Worker 315*90c8c64dSAndroid Build Coastguard Worker def add(self, config_type: str, config_value: str | None) -> None: 316*90c8c64dSAndroid Build Coastguard Worker self.selected_configs[config_type] = config_value 317*90c8c64dSAndroid Build Coastguard Worker 318*90c8c64dSAndroid Build Coastguard Worker def is_valid(self, config_type: str) -> bool: 319*90c8c64dSAndroid Build Coastguard Worker return config_type in self.selected_configs 320*90c8c64dSAndroid Build Coastguard Worker 321*90c8c64dSAndroid Build Coastguard Worker def execute_command(self, server, device_id): 322*90c8c64dSAndroid Build Coastguard Worker shell = get_shell_args(device_id, "wm config") 323*90c8c64dSAndroid Build Coastguard Worker 324*90c8c64dSAndroid Build Coastguard Worker if self.is_perfetto: 325*90c8c64dSAndroid Build Coastguard Worker self.execute_perfetto_config_command(server, shell, self._perfetto_config_command(), "WM") 326*90c8c64dSAndroid Build Coastguard Worker else: 327*90c8c64dSAndroid Build Coastguard Worker self.execute_optional_config_command(server, device_id, shell, self._legacy_tracing_type_command(), "tracing type", self.selected_configs["tracingtype"]) 328*90c8c64dSAndroid Build Coastguard Worker self.execute_optional_config_command(server, device_id, shell, self._legacy_tracing_level_command(), "tracing level", self.selected_configs["tracinglevel"]) 329*90c8c64dSAndroid Build Coastguard Worker # /!\ buffer size must be configured last 330*90c8c64dSAndroid Build Coastguard Worker # otherwise the other configurations might override it 331*90c8c64dSAndroid Build Coastguard Worker self.execute_optional_config_command(server, device_id, shell, self._legacy_buffer_size_command(), "wm buffer size", self.selected_configs["wmbuffersize"]) 332*90c8c64dSAndroid Build Coastguard Worker 333*90c8c64dSAndroid Build Coastguard Worker def _perfetto_config_command(self) -> str: 334*90c8c64dSAndroid Build Coastguard Worker log_level = WindowManagerTraceConfig.PERFETTO_LOG_LEVEL_MAP[self.selected_configs["tracinglevel"]] 335*90c8c64dSAndroid Build Coastguard Worker log_frequency = WindowManagerTraceConfig.PERFETTO_LOG_FREQUENCY_MAP[self.selected_configs["tracingtype"]] 336*90c8c64dSAndroid Build Coastguard Worker return f""" 337*90c8c64dSAndroid Build Coastguard Workercat << EOF >> {PERFETTO_TRACE_CONFIG_FILE} 338*90c8c64dSAndroid Build Coastguard Workerdata_sources: {{ 339*90c8c64dSAndroid Build Coastguard Worker config {{ 340*90c8c64dSAndroid Build Coastguard Worker name: "android.windowmanager" 341*90c8c64dSAndroid Build Coastguard Worker windowmanager_config: {{ 342*90c8c64dSAndroid Build Coastguard Worker log_level: {log_level} 343*90c8c64dSAndroid Build Coastguard Worker log_frequency: {log_frequency} 344*90c8c64dSAndroid Build Coastguard Worker }} 345*90c8c64dSAndroid Build Coastguard Worker }} 346*90c8c64dSAndroid Build Coastguard Worker}} 347*90c8c64dSAndroid Build Coastguard WorkerEOF 348*90c8c64dSAndroid Build Coastguard Worker""" 349*90c8c64dSAndroid Build Coastguard Worker 350*90c8c64dSAndroid Build Coastguard Worker def _legacy_tracing_type_command(self) -> str: 351*90c8c64dSAndroid Build Coastguard Worker return f'su root cmd window tracing {self.selected_configs["tracingtype"]}' 352*90c8c64dSAndroid Build Coastguard Worker 353*90c8c64dSAndroid Build Coastguard Worker def _legacy_tracing_level_command(self) -> str: 354*90c8c64dSAndroid Build Coastguard Worker return f'su root cmd window tracing level {self.selected_configs["tracinglevel"]}' 355*90c8c64dSAndroid Build Coastguard Worker 356*90c8c64dSAndroid Build Coastguard Worker def _legacy_buffer_size_command(self) -> str: 357*90c8c64dSAndroid Build Coastguard Worker return f'su root cmd window tracing size {self.selected_configs["wmbuffersize"]}' 358*90c8c64dSAndroid Build Coastguard Worker 359*90c8c64dSAndroid Build Coastguard Worker 360*90c8c64dSAndroid Build Coastguard Workerclass ViewCaptureTraceConfig(TraceConfig): 361*90c8c64dSAndroid Build Coastguard Worker """Handles perfetto config for View Capture traces.""" 362*90c8c64dSAndroid Build Coastguard Worker 363*90c8c64dSAndroid Build Coastguard Worker COMMAND = f""" 364*90c8c64dSAndroid Build Coastguard Workercat << EOF >> {PERFETTO_TRACE_CONFIG_FILE} 365*90c8c64dSAndroid Build Coastguard Workerdata_sources: {{ 366*90c8c64dSAndroid Build Coastguard Worker config {{ 367*90c8c64dSAndroid Build Coastguard Worker name: "android.viewcapture" 368*90c8c64dSAndroid Build Coastguard Worker }} 369*90c8c64dSAndroid Build Coastguard Worker}} 370*90c8c64dSAndroid Build Coastguard WorkerEOF 371*90c8c64dSAndroid Build Coastguard Worker """ 372*90c8c64dSAndroid Build Coastguard Worker 373*90c8c64dSAndroid Build Coastguard Worker def execute_command(self, server, device_id): 374*90c8c64dSAndroid Build Coastguard Worker if (self.is_perfetto): 375*90c8c64dSAndroid Build Coastguard Worker shell = get_shell_args(device_id, "perfetto config view capture") 376*90c8c64dSAndroid Build Coastguard Worker self.execute_perfetto_config_command(server, shell, ViewCaptureTraceConfig.COMMAND, "View Capture") 377*90c8c64dSAndroid Build Coastguard Worker 378*90c8c64dSAndroid Build Coastguard Workerclass TransactionsConfig(TraceConfig): 379*90c8c64dSAndroid Build Coastguard Worker """Handles perfetto config for Transactions traces.""" 380*90c8c64dSAndroid Build Coastguard Worker 381*90c8c64dSAndroid Build Coastguard Worker COMMAND = f""" 382*90c8c64dSAndroid Build Coastguard Workercat << EOF >> {PERFETTO_TRACE_CONFIG_FILE} 383*90c8c64dSAndroid Build Coastguard Workerdata_sources: {{ 384*90c8c64dSAndroid Build Coastguard Worker config {{ 385*90c8c64dSAndroid Build Coastguard Worker name: "android.surfaceflinger.transactions" 386*90c8c64dSAndroid Build Coastguard Worker surfaceflinger_transactions_config: {{ 387*90c8c64dSAndroid Build Coastguard Worker mode: MODE_ACTIVE 388*90c8c64dSAndroid Build Coastguard Worker }} 389*90c8c64dSAndroid Build Coastguard Worker }} 390*90c8c64dSAndroid Build Coastguard Worker}} 391*90c8c64dSAndroid Build Coastguard WorkerEOF 392*90c8c64dSAndroid Build Coastguard Worker """ 393*90c8c64dSAndroid Build Coastguard Worker 394*90c8c64dSAndroid Build Coastguard Worker def execute_command(self, server, device_id): 395*90c8c64dSAndroid Build Coastguard Worker if (self.is_perfetto): 396*90c8c64dSAndroid Build Coastguard Worker shell = get_shell_args(device_id, "perfetto config transactions") 397*90c8c64dSAndroid Build Coastguard Worker self.execute_perfetto_config_command(server, shell, TransactionsConfig.COMMAND, "SF transactions") 398*90c8c64dSAndroid Build Coastguard Worker 399*90c8c64dSAndroid Build Coastguard Worker 400*90c8c64dSAndroid Build Coastguard Workerclass ProtoLogConfig(TraceConfig): 401*90c8c64dSAndroid Build Coastguard Worker """Handles perfetto config for ProtoLog traces.""" 402*90c8c64dSAndroid Build Coastguard Worker 403*90c8c64dSAndroid Build Coastguard Worker COMMAND = f""" 404*90c8c64dSAndroid Build Coastguard Workercat << EOF >> {PERFETTO_TRACE_CONFIG_FILE} 405*90c8c64dSAndroid Build Coastguard Workerdata_sources: {{ 406*90c8c64dSAndroid Build Coastguard Worker config {{ 407*90c8c64dSAndroid Build Coastguard Worker name: "android.protolog" 408*90c8c64dSAndroid Build Coastguard Worker protolog_config: {{ 409*90c8c64dSAndroid Build Coastguard Worker tracing_mode: ENABLE_ALL 410*90c8c64dSAndroid Build Coastguard Worker }} 411*90c8c64dSAndroid Build Coastguard Worker }} 412*90c8c64dSAndroid Build Coastguard Worker}} 413*90c8c64dSAndroid Build Coastguard WorkerEOF 414*90c8c64dSAndroid Build Coastguard Worker """ 415*90c8c64dSAndroid Build Coastguard Worker 416*90c8c64dSAndroid Build Coastguard Worker def execute_command(self, server, device_id): 417*90c8c64dSAndroid Build Coastguard Worker if (self.is_perfetto): 418*90c8c64dSAndroid Build Coastguard Worker shell = get_shell_args(device_id, "perfetto config protolog") 419*90c8c64dSAndroid Build Coastguard Worker self.execute_perfetto_config_command(server, shell, ProtoLogConfig.COMMAND, "ProtoLog") 420*90c8c64dSAndroid Build Coastguard Worker 421*90c8c64dSAndroid Build Coastguard Worker 422*90c8c64dSAndroid Build Coastguard Workerclass ImeConfig(TraceConfig): 423*90c8c64dSAndroid Build Coastguard Worker """Handles perfetto config for IME traces.""" 424*90c8c64dSAndroid Build Coastguard Worker 425*90c8c64dSAndroid Build Coastguard Worker COMMAND = f""" 426*90c8c64dSAndroid Build Coastguard Workercat << EOF >> {PERFETTO_TRACE_CONFIG_FILE} 427*90c8c64dSAndroid Build Coastguard Workerdata_sources: {{ 428*90c8c64dSAndroid Build Coastguard Worker config {{ 429*90c8c64dSAndroid Build Coastguard Worker name: "android.inputmethod" 430*90c8c64dSAndroid Build Coastguard Worker }} 431*90c8c64dSAndroid Build Coastguard Worker}} 432*90c8c64dSAndroid Build Coastguard WorkerEOF 433*90c8c64dSAndroid Build Coastguard Worker """ 434*90c8c64dSAndroid Build Coastguard Worker 435*90c8c64dSAndroid Build Coastguard Worker def execute_command(self, server, device_id): 436*90c8c64dSAndroid Build Coastguard Worker if (self.is_perfetto): 437*90c8c64dSAndroid Build Coastguard Worker shell = get_shell_args(device_id, "perfetto config ime") 438*90c8c64dSAndroid Build Coastguard Worker self.execute_perfetto_config_command(server, shell, ImeConfig.COMMAND, "IME tracing") 439*90c8c64dSAndroid Build Coastguard Worker 440*90c8c64dSAndroid Build Coastguard Worker 441*90c8c64dSAndroid Build Coastguard Workerclass TransitionTracesConfig(TraceConfig): 442*90c8c64dSAndroid Build Coastguard Worker """Handles perfetto config for Transition traces.""" 443*90c8c64dSAndroid Build Coastguard Worker 444*90c8c64dSAndroid Build Coastguard Worker COMMAND = f""" 445*90c8c64dSAndroid Build Coastguard Workercat << EOF >> {PERFETTO_TRACE_CONFIG_FILE} 446*90c8c64dSAndroid Build Coastguard Workerdata_sources: {{ 447*90c8c64dSAndroid Build Coastguard Worker config {{ 448*90c8c64dSAndroid Build Coastguard Worker name: "com.android.wm.shell.transition" 449*90c8c64dSAndroid Build Coastguard Worker }} 450*90c8c64dSAndroid Build Coastguard Worker}} 451*90c8c64dSAndroid Build Coastguard WorkerEOF 452*90c8c64dSAndroid Build Coastguard Worker """ 453*90c8c64dSAndroid Build Coastguard Worker 454*90c8c64dSAndroid Build Coastguard Worker def execute_command(self, server, device_id): 455*90c8c64dSAndroid Build Coastguard Worker if (self.is_perfetto): 456*90c8c64dSAndroid Build Coastguard Worker shell = get_shell_args(device_id, "perfetto config transitions") 457*90c8c64dSAndroid Build Coastguard Worker self.execute_perfetto_config_command(server, shell, TransitionTracesConfig.COMMAND, "Transitions") 458*90c8c64dSAndroid Build Coastguard Worker 459*90c8c64dSAndroid Build Coastguard Worker 460*90c8c64dSAndroid Build Coastguard Workerclass InputConfig(TraceConfig): 461*90c8c64dSAndroid Build Coastguard Worker """Handles perfetto config for Input traces.""" 462*90c8c64dSAndroid Build Coastguard Worker 463*90c8c64dSAndroid Build Coastguard Worker COMMAND = f""" 464*90c8c64dSAndroid Build Coastguard Workercat << EOF >> {PERFETTO_TRACE_CONFIG_FILE} 465*90c8c64dSAndroid Build Coastguard Workerdata_sources: {{ 466*90c8c64dSAndroid Build Coastguard Worker config {{ 467*90c8c64dSAndroid Build Coastguard Worker name: "android.input.inputevent" 468*90c8c64dSAndroid Build Coastguard Worker android_input_event_config {{ 469*90c8c64dSAndroid Build Coastguard Worker mode: TRACE_MODE_TRACE_ALL 470*90c8c64dSAndroid Build Coastguard Worker }} 471*90c8c64dSAndroid Build Coastguard Worker }} 472*90c8c64dSAndroid Build Coastguard Worker}} 473*90c8c64dSAndroid Build Coastguard WorkerEOF 474*90c8c64dSAndroid Build Coastguard Worker """ 475*90c8c64dSAndroid Build Coastguard Worker 476*90c8c64dSAndroid Build Coastguard Worker def execute_command(self, server, device_id): 477*90c8c64dSAndroid Build Coastguard Worker if (self.is_perfetto): 478*90c8c64dSAndroid Build Coastguard Worker shell = get_shell_args(device_id, "perfetto config input") 479*90c8c64dSAndroid Build Coastguard Worker self.execute_perfetto_config_command(server, shell, InputConfig.COMMAND, "Input trace") 480*90c8c64dSAndroid Build Coastguard Worker 481*90c8c64dSAndroid Build Coastguard Worker 482*90c8c64dSAndroid Build Coastguard Workerclass MediaBasedConfig(TraceConfig): 483*90c8c64dSAndroid Build Coastguard Worker """Creates trace identifiers for Screen Recording traces and Screenshots.""" 484*90c8c64dSAndroid Build Coastguard Worker trace_identifiers = ["active"] 485*90c8c64dSAndroid Build Coastguard Worker 486*90c8c64dSAndroid Build Coastguard Worker def get_trace_identifiers(self): 487*90c8c64dSAndroid Build Coastguard Worker return self.trace_identifiers 488*90c8c64dSAndroid Build Coastguard Worker 489*90c8c64dSAndroid Build Coastguard Worker def is_valid(self, config_type: str) -> bool: 490*90c8c64dSAndroid Build Coastguard Worker return config_type == "displays" 491*90c8c64dSAndroid Build Coastguard Worker 492*90c8c64dSAndroid Build Coastguard Worker def add(self, config_type: str, config_value: str | list[str] | None): 493*90c8c64dSAndroid Build Coastguard Worker if config_type != "displays": 494*90c8c64dSAndroid Build Coastguard Worker return 495*90c8c64dSAndroid Build Coastguard Worker if config_value and len(config_value) > 0: 496*90c8c64dSAndroid Build Coastguard Worker if type(config_value) == str: 497*90c8c64dSAndroid Build Coastguard Worker self.trace_identifiers = [config_value.split(" ")[0]] 498*90c8c64dSAndroid Build Coastguard Worker else: 499*90c8c64dSAndroid Build Coastguard Worker self.trace_identifiers = list(map(lambda d: d.split(" ")[0], config_value)) 500*90c8c64dSAndroid Build Coastguard Worker 501*90c8c64dSAndroid Build Coastguard Worker def execute_command(self, server, device_id): 502*90c8c64dSAndroid Build Coastguard Worker pass 503*90c8c64dSAndroid Build Coastguard Worker 504*90c8c64dSAndroid Build Coastguard Workerclass ScreenRecordingConfig(MediaBasedConfig): 505*90c8c64dSAndroid Build Coastguard Worker """Creates display args for Screen Recording traces.""" 506*90c8c64dSAndroid Build Coastguard Worker def get_optional_start_args(self, identifier): 507*90c8c64dSAndroid Build Coastguard Worker if identifier == "active": 508*90c8c64dSAndroid Build Coastguard Worker return "" 509*90c8c64dSAndroid Build Coastguard Worker return f"--display-id {identifier}" 510*90c8c64dSAndroid Build Coastguard Worker 511*90c8c64dSAndroid Build Coastguard Workerclass ScreenshotConfig(MediaBasedConfig): 512*90c8c64dSAndroid Build Coastguard Worker """Creates display args for Screenshots.""" 513*90c8c64dSAndroid Build Coastguard Worker def get_optional_start_args(self, identifier): 514*90c8c64dSAndroid Build Coastguard Worker if identifier == "active": 515*90c8c64dSAndroid Build Coastguard Worker return "" 516*90c8c64dSAndroid Build Coastguard Worker return f"-d {identifier}" 517*90c8c64dSAndroid Build Coastguard Worker 518*90c8c64dSAndroid Build Coastguard Worker 519*90c8c64dSAndroid Build Coastguard Workerclass SurfaceFlingerDumpConfig(TraceConfig): 520*90c8c64dSAndroid Build Coastguard Worker """Handles perfetto config for SurfaceFlinger dumps.""" 521*90c8c64dSAndroid Build Coastguard Worker def __init__(self, is_perfetto: bool) -> None: 522*90c8c64dSAndroid Build Coastguard Worker super().__init__(is_perfetto) 523*90c8c64dSAndroid Build Coastguard Worker 524*90c8c64dSAndroid Build Coastguard Worker def add(self, config: str, value: str | None) -> None: 525*90c8c64dSAndroid Build Coastguard Worker pass 526*90c8c64dSAndroid Build Coastguard Worker 527*90c8c64dSAndroid Build Coastguard Worker def is_valid(self, config: str) -> bool: 528*90c8c64dSAndroid Build Coastguard Worker return False 529*90c8c64dSAndroid Build Coastguard Worker 530*90c8c64dSAndroid Build Coastguard Worker def execute_command(self, server, device_id): 531*90c8c64dSAndroid Build Coastguard Worker shell = get_shell_args(device_id, "sf config") 532*90c8c64dSAndroid Build Coastguard Worker 533*90c8c64dSAndroid Build Coastguard Worker if self.is_perfetto: 534*90c8c64dSAndroid Build Coastguard Worker self.execute_perfetto_config_command(server, shell, self._perfetto_config_command(), "SurfaceFlinger") 535*90c8c64dSAndroid Build Coastguard Worker 536*90c8c64dSAndroid Build Coastguard Worker def _perfetto_config_command(self) -> str: 537*90c8c64dSAndroid Build Coastguard Worker return f""" 538*90c8c64dSAndroid Build Coastguard Workercat << EOF >> {PERFETTO_DUMP_CONFIG_FILE} 539*90c8c64dSAndroid Build Coastguard Workerdata_sources: {{ 540*90c8c64dSAndroid Build Coastguard Worker config {{ 541*90c8c64dSAndroid Build Coastguard Worker name: "android.surfaceflinger.layers" 542*90c8c64dSAndroid Build Coastguard Worker surfaceflinger_layers_config: {{ 543*90c8c64dSAndroid Build Coastguard Worker mode: MODE_DUMP 544*90c8c64dSAndroid Build Coastguard Worker trace_flags: TRACE_FLAG_INPUT 545*90c8c64dSAndroid Build Coastguard Worker trace_flags: TRACE_FLAG_COMPOSITION 546*90c8c64dSAndroid Build Coastguard Worker trace_flags: TRACE_FLAG_HWC 547*90c8c64dSAndroid Build Coastguard Worker trace_flags: TRACE_FLAG_BUFFERS 548*90c8c64dSAndroid Build Coastguard Worker trace_flags: TRACE_FLAG_VIRTUAL_DISPLAYS 549*90c8c64dSAndroid Build Coastguard Worker }} 550*90c8c64dSAndroid Build Coastguard Worker }} 551*90c8c64dSAndroid Build Coastguard Worker}} 552*90c8c64dSAndroid Build Coastguard WorkerEOF 553*90c8c64dSAndroid Build Coastguard Worker""" 554*90c8c64dSAndroid Build Coastguard Worker 555*90c8c64dSAndroid Build Coastguard Worker def _legacy_buffer_size_command(self) -> str: 556*90c8c64dSAndroid Build Coastguard Worker return f'su root service call SurfaceFlinger 1029 i32 {self.selected_configs["sfbuffersize"]}' 557*90c8c64dSAndroid Build Coastguard Worker 558*90c8c64dSAndroid Build Coastguard Worker def _legacy_flags_command(self) -> str: 559*90c8c64dSAndroid Build Coastguard Worker flags = 0 560*90c8c64dSAndroid Build Coastguard Worker for flag in self.flags: 561*90c8c64dSAndroid Build Coastguard Worker flags |= SurfaceFlingerTraceConfig.LEGACY_FLAGS_MAP[flag] 562*90c8c64dSAndroid Build Coastguard Worker 563*90c8c64dSAndroid Build Coastguard Worker return f"su root service call SurfaceFlinger 1033 i32 {flags}" 564*90c8c64dSAndroid Build Coastguard Worker 565*90c8c64dSAndroid Build Coastguard Worker 566*90c8c64dSAndroid Build Coastguard Workerclass TraceTarget: 567*90c8c64dSAndroid Build Coastguard Worker """Defines a single parameter to trace. 568*90c8c64dSAndroid Build Coastguard Worker 569*90c8c64dSAndroid Build Coastguard Worker Attributes: 570*90c8c64dSAndroid Build Coastguard Worker trace_name: used as a key to access config and log statements during Start/End endpoints. 571*90c8c64dSAndroid Build Coastguard Worker files: the matchers used to identify the paths on the device the trace results are saved to. 572*90c8c64dSAndroid Build Coastguard Worker is_perfetto_available: callback to determine if perfetto tracing is available for target. 573*90c8c64dSAndroid Build Coastguard Worker trace_start: command to start the trace from adb shell, must not block. 574*90c8c64dSAndroid Build Coastguard Worker trace_stop: command to stop the trace, should block until the trace is stopped. 575*90c8c64dSAndroid Build Coastguard Worker get_trace_config: getter for optional setup to execute pre-tracing adb commands and define start command arguments. 576*90c8c64dSAndroid Build Coastguard Worker """ 577*90c8c64dSAndroid Build Coastguard Worker 578*90c8c64dSAndroid Build Coastguard Worker def __init__( 579*90c8c64dSAndroid Build Coastguard Worker self, 580*90c8c64dSAndroid Build Coastguard Worker trace_name: str, 581*90c8c64dSAndroid Build Coastguard Worker files: list[File | FileMatcher], 582*90c8c64dSAndroid Build Coastguard Worker is_perfetto_available: Callable[[str], bool], 583*90c8c64dSAndroid Build Coastguard Worker trace_start: str, 584*90c8c64dSAndroid Build Coastguard Worker trace_stop: str, 585*90c8c64dSAndroid Build Coastguard Worker get_trace_config: Callable[[bool], TraceConfig] | None = None 586*90c8c64dSAndroid Build Coastguard Worker ) -> None: 587*90c8c64dSAndroid Build Coastguard Worker self.trace_name = trace_name 588*90c8c64dSAndroid Build Coastguard Worker if type(files) is not list: 589*90c8c64dSAndroid Build Coastguard Worker files = [files] 590*90c8c64dSAndroid Build Coastguard Worker self.files = files 591*90c8c64dSAndroid Build Coastguard Worker self.is_perfetto_available = is_perfetto_available 592*90c8c64dSAndroid Build Coastguard Worker self.trace_start = trace_start 593*90c8c64dSAndroid Build Coastguard Worker self.trace_stop = trace_stop 594*90c8c64dSAndroid Build Coastguard Worker self.get_trace_config = get_trace_config 595*90c8c64dSAndroid Build Coastguard Worker self.status_filename = WINSCOPE_STATUS + "_" + trace_name 596*90c8c64dSAndroid Build Coastguard Worker 597*90c8c64dSAndroid Build Coastguard Worker 598*90c8c64dSAndroid Build Coastguard Worker# Order of files matters as they will be expected in that order and decoded in that order 599*90c8c64dSAndroid Build Coastguard WorkerTRACE_TARGETS = { 600*90c8c64dSAndroid Build Coastguard Worker "view_capture_trace": TraceTarget( 601*90c8c64dSAndroid Build Coastguard Worker "view_capture_trace", 602*90c8c64dSAndroid Build Coastguard Worker File('/data/misc/wmtrace/view_capture_trace.zip', "view_capture_trace.zip"), 603*90c8c64dSAndroid Build Coastguard Worker lambda res: is_perfetto_data_source_available("android.viewcapture", res), 604*90c8c64dSAndroid Build Coastguard Worker """ 605*90c8c64dSAndroid Build Coastguard Workersu root settings put global view_capture_enabled 1 606*90c8c64dSAndroid Build Coastguard Workerecho 'ViewCapture tracing (legacy) started.' 607*90c8c64dSAndroid Build Coastguard Worker """, 608*90c8c64dSAndroid Build Coastguard Worker """ 609*90c8c64dSAndroid Build Coastguard Workersu root sh -c 'cmd launcherapps dump-view-hierarchies >/data/misc/wmtrace/view_capture_trace.zip' 610*90c8c64dSAndroid Build Coastguard Workersu root settings put global view_capture_enabled 0 611*90c8c64dSAndroid Build Coastguard Workerecho 'ViewCapture tracing (legacy) stopped.' 612*90c8c64dSAndroid Build Coastguard Worker """, 613*90c8c64dSAndroid Build Coastguard Worker lambda is_perfetto: ViewCaptureTraceConfig(is_perfetto) 614*90c8c64dSAndroid Build Coastguard Worker ), 615*90c8c64dSAndroid Build Coastguard Worker "window_trace": TraceTarget( 616*90c8c64dSAndroid Build Coastguard Worker "window_trace", 617*90c8c64dSAndroid Build Coastguard Worker WinscopeFileMatcher(WINSCOPE_DIR, "wm_trace", "window_trace"), 618*90c8c64dSAndroid Build Coastguard Worker lambda res: is_perfetto_data_source_available('android.windowmanager', res), 619*90c8c64dSAndroid Build Coastguard Worker """ 620*90c8c64dSAndroid Build Coastguard Workersu root cmd window tracing start 621*90c8c64dSAndroid Build Coastguard Workerecho 'WM trace (legacy) started.' 622*90c8c64dSAndroid Build Coastguard Worker """, 623*90c8c64dSAndroid Build Coastguard Worker """ 624*90c8c64dSAndroid Build Coastguard Workersu root cmd window tracing stop >/dev/null 2>&1 625*90c8c64dSAndroid Build Coastguard Workerecho 'WM trace (legacy) stopped.' 626*90c8c64dSAndroid Build Coastguard Worker """, 627*90c8c64dSAndroid Build Coastguard Worker lambda is_perfetto: WindowManagerTraceConfig(is_perfetto) 628*90c8c64dSAndroid Build Coastguard Worker ), 629*90c8c64dSAndroid Build Coastguard Worker "layers_trace": TraceTarget( 630*90c8c64dSAndroid Build Coastguard Worker "layers_trace", 631*90c8c64dSAndroid Build Coastguard Worker WinscopeFileMatcher(WINSCOPE_DIR, "layers_trace", "layers_trace"), 632*90c8c64dSAndroid Build Coastguard Worker lambda res: is_perfetto_data_source_available('android.surfaceflinger.layers', res), 633*90c8c64dSAndroid Build Coastguard Worker """ 634*90c8c64dSAndroid Build Coastguard Workersu root service call SurfaceFlinger 1025 i32 1 635*90c8c64dSAndroid Build Coastguard Workerecho 'SF layers trace (legacy) started.' 636*90c8c64dSAndroid Build Coastguard Worker """, 637*90c8c64dSAndroid Build Coastguard Worker """ 638*90c8c64dSAndroid Build Coastguard Workersu root service call SurfaceFlinger 1025 i32 0 >/dev/null 2>&1 639*90c8c64dSAndroid Build Coastguard Workerecho 'SF layers trace (legacy) stopped.' 640*90c8c64dSAndroid Build Coastguard Worker """, 641*90c8c64dSAndroid Build Coastguard Worker lambda is_perfetto: SurfaceFlingerTraceConfig(is_perfetto) 642*90c8c64dSAndroid Build Coastguard Worker ), 643*90c8c64dSAndroid Build Coastguard Worker "screen_recording": TraceTarget( 644*90c8c64dSAndroid Build Coastguard Worker "screen_recording", 645*90c8c64dSAndroid Build Coastguard Worker File( 646*90c8c64dSAndroid Build Coastguard Worker '/data/local/tmp/screen_{trace_identifier}.mp4', 647*90c8c64dSAndroid Build Coastguard Worker "screen_recording_{trace_identifier}"), 648*90c8c64dSAndroid Build Coastguard Worker lambda res: False, 649*90c8c64dSAndroid Build Coastguard Worker ''' 650*90c8c64dSAndroid Build Coastguard Worker settings put system show_touches 1 && \ 651*90c8c64dSAndroid Build Coastguard Worker settings put system pointer_location 1 && \ 652*90c8c64dSAndroid Build Coastguard Worker screenrecord --bugreport --bit-rate 8M {options} /data/local/tmp/screen_{trace_identifier}.mp4 & \ 653*90c8c64dSAndroid Build Coastguard Worker echo "ScreenRecorder started." 654*90c8c64dSAndroid Build Coastguard Worker ''', 655*90c8c64dSAndroid Build Coastguard Worker '''settings put system pointer_location 0 && \ 656*90c8c64dSAndroid Build Coastguard Worker settings put system show_touches 0 && \ 657*90c8c64dSAndroid Build Coastguard Worker pkill -l SIGINT screenrecord >/dev/null 2>&1 658*90c8c64dSAndroid Build Coastguard Worker '''.strip(), 659*90c8c64dSAndroid Build Coastguard Worker lambda is_perfetto: ScreenRecordingConfig(is_perfetto) 660*90c8c64dSAndroid Build Coastguard Worker ), 661*90c8c64dSAndroid Build Coastguard Worker "transactions": TraceTarget( 662*90c8c64dSAndroid Build Coastguard Worker "transactions", 663*90c8c64dSAndroid Build Coastguard Worker WinscopeFileMatcher(WINSCOPE_DIR, "transactions_trace", "transactions"), 664*90c8c64dSAndroid Build Coastguard Worker lambda res: is_perfetto_data_source_available('android.surfaceflinger.transactions', res), 665*90c8c64dSAndroid Build Coastguard Worker """ 666*90c8c64dSAndroid Build Coastguard Workersu root service call SurfaceFlinger 1041 i32 1 667*90c8c64dSAndroid Build Coastguard Workerecho 'SF transactions trace (legacy) started.' 668*90c8c64dSAndroid Build Coastguard Worker """, 669*90c8c64dSAndroid Build Coastguard Worker "su root service call SurfaceFlinger 1041 i32 0 >/dev/null 2>&1", 670*90c8c64dSAndroid Build Coastguard Worker lambda is_perfetto: TransactionsConfig(is_perfetto) 671*90c8c64dSAndroid Build Coastguard Worker ), 672*90c8c64dSAndroid Build Coastguard Worker "transactions_legacy": TraceTarget( 673*90c8c64dSAndroid Build Coastguard Worker "transactions_legacy", 674*90c8c64dSAndroid Build Coastguard Worker [ 675*90c8c64dSAndroid Build Coastguard Worker WinscopeFileMatcher(WINSCOPE_DIR, "transaction_trace", "transactions_legacy"), 676*90c8c64dSAndroid Build Coastguard Worker FileMatcher(WINSCOPE_DIR, f'transaction_merges_*', "transaction_merges"), 677*90c8c64dSAndroid Build Coastguard Worker ], 678*90c8c64dSAndroid Build Coastguard Worker lambda res: False, 679*90c8c64dSAndroid Build Coastguard Worker 'su root service call SurfaceFlinger 1020 i32 1\necho "SF transactions recording started."', 680*90c8c64dSAndroid Build Coastguard Worker 'su root service call SurfaceFlinger 1020 i32 0 >/dev/null 2>&1', 681*90c8c64dSAndroid Build Coastguard Worker ), 682*90c8c64dSAndroid Build Coastguard Worker "proto_log": TraceTarget( 683*90c8c64dSAndroid Build Coastguard Worker "proto_log", 684*90c8c64dSAndroid Build Coastguard Worker WinscopeFileMatcher(WINSCOPE_DIR, "wm_log", "proto_log"), 685*90c8c64dSAndroid Build Coastguard Worker lambda res: is_perfetto_data_source_available('android.protolog', res), 686*90c8c64dSAndroid Build Coastguard Worker """ 687*90c8c64dSAndroid Build Coastguard Workersu root cmd window logging start 688*90c8c64dSAndroid Build Coastguard Workerecho "ProtoLog (legacy) started." 689*90c8c64dSAndroid Build Coastguard Worker """, 690*90c8c64dSAndroid Build Coastguard Worker """ 691*90c8c64dSAndroid Build Coastguard Workersu root cmd window logging stop >/dev/null 2>&1 692*90c8c64dSAndroid Build Coastguard Workerecho "ProtoLog (legacy) stopped." 693*90c8c64dSAndroid Build Coastguard Worker """, 694*90c8c64dSAndroid Build Coastguard Worker lambda is_perfetto: ProtoLogConfig(is_perfetto) 695*90c8c64dSAndroid Build Coastguard Worker ), 696*90c8c64dSAndroid Build Coastguard Worker "ime": TraceTarget( 697*90c8c64dSAndroid Build Coastguard Worker "ime", 698*90c8c64dSAndroid Build Coastguard Worker [WinscopeFileMatcher(WINSCOPE_DIR, "ime_trace_clients", "ime_trace_clients"), 699*90c8c64dSAndroid Build Coastguard Worker WinscopeFileMatcher(WINSCOPE_DIR, "ime_trace_service", "ime_trace_service"), 700*90c8c64dSAndroid Build Coastguard Worker WinscopeFileMatcher(WINSCOPE_DIR, "ime_trace_managerservice", "ime_trace_managerservice")], 701*90c8c64dSAndroid Build Coastguard Worker lambda res: is_perfetto_data_source_available('android.inputmethod', res), 702*90c8c64dSAndroid Build Coastguard Worker """ 703*90c8c64dSAndroid Build Coastguard Workersu root ime tracing start 704*90c8c64dSAndroid Build Coastguard Workerecho "IME tracing (legacy) started." 705*90c8c64dSAndroid Build Coastguard Worker """, 706*90c8c64dSAndroid Build Coastguard Worker """ 707*90c8c64dSAndroid Build Coastguard Workersu root ime tracing stop >/dev/null 2>&1 708*90c8c64dSAndroid Build Coastguard Workerecho "IME tracing (legacy) stopped." 709*90c8c64dSAndroid Build Coastguard Worker """, 710*90c8c64dSAndroid Build Coastguard Worker lambda is_perfetto: ImeConfig(is_perfetto) 711*90c8c64dSAndroid Build Coastguard Worker ), 712*90c8c64dSAndroid Build Coastguard Worker "wayland_trace": TraceTarget( 713*90c8c64dSAndroid Build Coastguard Worker "wayland_trace", 714*90c8c64dSAndroid Build Coastguard Worker WinscopeFileMatcher("/data/misc/wltrace", "wl_trace", "wl_trace"), 715*90c8c64dSAndroid Build Coastguard Worker lambda res: False, 716*90c8c64dSAndroid Build Coastguard Worker 'su root service call Wayland 26 i32 1 >/dev/null\necho "Wayland trace started."', 717*90c8c64dSAndroid Build Coastguard Worker 'su root service call Wayland 26 i32 0 >/dev/null' 718*90c8c64dSAndroid Build Coastguard Worker ), 719*90c8c64dSAndroid Build Coastguard Worker "eventlog": TraceTarget( 720*90c8c64dSAndroid Build Coastguard Worker "eventlog", 721*90c8c64dSAndroid Build Coastguard Worker WinscopeFileMatcher("/data/local/tmp", "eventlog", "eventlog"), 722*90c8c64dSAndroid Build Coastguard Worker lambda res: False, 723*90c8c64dSAndroid Build Coastguard Worker 'rm -f /data/local/tmp/eventlog.winscope && EVENT_LOG_TRACING_START_TIME=$EPOCHREALTIME\necho "Event Log trace started."', 724*90c8c64dSAndroid Build Coastguard Worker 'echo "EventLog\\n" > /data/local/tmp/eventlog.winscope && su root logcat -b events -v threadtime -v printable -v uid -v nsec -v epoch -b events -t $EVENT_LOG_TRACING_START_TIME >> /data/local/tmp/eventlog.winscope', 725*90c8c64dSAndroid Build Coastguard Worker ), 726*90c8c64dSAndroid Build Coastguard Worker "transition_traces": TraceTarget( 727*90c8c64dSAndroid Build Coastguard Worker "transition_traces", 728*90c8c64dSAndroid Build Coastguard Worker [WinscopeFileMatcher(WINSCOPE_DIR, "wm_transition_trace", "wm_transition_trace"), 729*90c8c64dSAndroid Build Coastguard Worker WinscopeFileMatcher(WINSCOPE_DIR, "shell_transition_trace", "shell_transition_trace")], 730*90c8c64dSAndroid Build Coastguard Worker lambda res: is_perfetto_data_source_available('com.android.wm.shell.transition', res), 731*90c8c64dSAndroid Build Coastguard Worker """ 732*90c8c64dSAndroid Build Coastguard Workersu root cmd window shell tracing start && su root dumpsys activity service SystemUIService WMShell transitions tracing start 733*90c8c64dSAndroid Build Coastguard Workerecho "Transition traces (legacy) started." 734*90c8c64dSAndroid Build Coastguard Worker """, 735*90c8c64dSAndroid Build Coastguard Worker """ 736*90c8c64dSAndroid Build Coastguard Workersu root cmd window shell tracing stop && su root dumpsys activity service SystemUIService WMShell transitions tracing stop >/dev/null 2>&1 737*90c8c64dSAndroid Build Coastguard Workerecho 'Transition traces (legacy) stopped.' 738*90c8c64dSAndroid Build Coastguard Worker """, 739*90c8c64dSAndroid Build Coastguard Worker lambda is_perfetto: TransitionTracesConfig(is_perfetto) 740*90c8c64dSAndroid Build Coastguard Worker ), 741*90c8c64dSAndroid Build Coastguard Worker "input": TraceTarget( 742*90c8c64dSAndroid Build Coastguard Worker "input", 743*90c8c64dSAndroid Build Coastguard Worker [WinscopeFileMatcher(WINSCOPE_DIR, "input_trace", "input_trace")], 744*90c8c64dSAndroid Build Coastguard Worker lambda res: is_perfetto_data_source_available('android.input.inputevent', res), 745*90c8c64dSAndroid Build Coastguard Worker "", 746*90c8c64dSAndroid Build Coastguard Worker "", 747*90c8c64dSAndroid Build Coastguard Worker lambda is_perfetto: InputConfig(is_perfetto) 748*90c8c64dSAndroid Build Coastguard Worker ), 749*90c8c64dSAndroid Build Coastguard Worker "perfetto_trace": TraceTarget( 750*90c8c64dSAndroid Build Coastguard Worker "perfetto_trace", 751*90c8c64dSAndroid Build Coastguard Worker File(PERFETTO_TRACE_FILE, "trace.perfetto-trace"), 752*90c8c64dSAndroid Build Coastguard Worker lambda res: is_any_perfetto_data_source_available(res), 753*90c8c64dSAndroid Build Coastguard Worker f""" 754*90c8c64dSAndroid Build Coastguard Workercat << EOF >> {PERFETTO_TRACE_CONFIG_FILE} 755*90c8c64dSAndroid Build Coastguard Workerbuffers: {{ 756*90c8c64dSAndroid Build Coastguard Worker size_kb: 500000 757*90c8c64dSAndroid Build Coastguard Worker fill_policy: RING_BUFFER 758*90c8c64dSAndroid Build Coastguard Worker}} 759*90c8c64dSAndroid Build Coastguard Workerduration_ms: 0 760*90c8c64dSAndroid Build Coastguard Workerfile_write_period_ms: 999999999 761*90c8c64dSAndroid Build Coastguard Workerwrite_into_file: true 762*90c8c64dSAndroid Build Coastguard Workerunique_session_name: "{PERFETTO_UNIQUE_SESSION_NAME}" 763*90c8c64dSAndroid Build Coastguard WorkerEOF 764*90c8c64dSAndroid Build Coastguard Worker 765*90c8c64dSAndroid Build Coastguard Workerrm -f {PERFETTO_TRACE_FILE} 766*90c8c64dSAndroid Build Coastguard Workerperfetto --out {PERFETTO_TRACE_FILE} --txt --config {PERFETTO_TRACE_CONFIG_FILE} --detach=WINSCOPE-PROXY-TRACING-SESSION 767*90c8c64dSAndroid Build Coastguard Workerecho 'Started perfetto trace.' 768*90c8c64dSAndroid Build Coastguard Worker""", 769*90c8c64dSAndroid Build Coastguard Worker """ 770*90c8c64dSAndroid Build Coastguard Workerperfetto --attach=WINSCOPE-PROXY-TRACING-SESSION --stop 771*90c8c64dSAndroid Build Coastguard Workerecho 'Stopped perfetto trace.' 772*90c8c64dSAndroid Build Coastguard Worker""", 773*90c8c64dSAndroid Build Coastguard Worker ), 774*90c8c64dSAndroid Build Coastguard Worker} 775*90c8c64dSAndroid Build Coastguard Worker 776*90c8c64dSAndroid Build Coastguard Worker 777*90c8c64dSAndroid Build Coastguard Workerclass DumpTarget: 778*90c8c64dSAndroid Build Coastguard Worker """Defines a single parameter to dump. 779*90c8c64dSAndroid Build Coastguard Worker 780*90c8c64dSAndroid Build Coastguard Worker Attributes: 781*90c8c64dSAndroid Build Coastguard Worker trace_name: used as a key to access config and log statements during Dump endpoint. 782*90c8c64dSAndroid Build Coastguard Worker files: the matchers used to identify the paths on the device the dump results are saved to. 783*90c8c64dSAndroid Build Coastguard Worker dump_command: command to dump state to file. 784*90c8c64dSAndroid Build Coastguard Worker get_trace_config: getter for optional setup to execute pre-tracing adb commands and define start command arguments. 785*90c8c64dSAndroid Build Coastguard Worker """ 786*90c8c64dSAndroid Build Coastguard Worker 787*90c8c64dSAndroid Build Coastguard Worker def __init__( 788*90c8c64dSAndroid Build Coastguard Worker self, 789*90c8c64dSAndroid Build Coastguard Worker trace_name: str, 790*90c8c64dSAndroid Build Coastguard Worker files: list[File | FileMatcher], 791*90c8c64dSAndroid Build Coastguard Worker is_perfetto_available: Callable[[str], bool], 792*90c8c64dSAndroid Build Coastguard Worker dump_command: str, 793*90c8c64dSAndroid Build Coastguard Worker get_trace_config: Callable[[bool], TraceConfig] | None = None 794*90c8c64dSAndroid Build Coastguard Worker ) -> None: 795*90c8c64dSAndroid Build Coastguard Worker self.trace_name = trace_name 796*90c8c64dSAndroid Build Coastguard Worker if type(files) is not list: 797*90c8c64dSAndroid Build Coastguard Worker files = [files] 798*90c8c64dSAndroid Build Coastguard Worker self.files = files 799*90c8c64dSAndroid Build Coastguard Worker self.is_perfetto_available = is_perfetto_available 800*90c8c64dSAndroid Build Coastguard Worker self.dump_command = dump_command 801*90c8c64dSAndroid Build Coastguard Worker self.get_trace_config = get_trace_config 802*90c8c64dSAndroid Build Coastguard Worker 803*90c8c64dSAndroid Build Coastguard Worker 804*90c8c64dSAndroid Build Coastguard WorkerDUMP_TARGETS = { 805*90c8c64dSAndroid Build Coastguard Worker "window_dump": DumpTarget( 806*90c8c64dSAndroid Build Coastguard Worker "window_dump", 807*90c8c64dSAndroid Build Coastguard Worker File(f'/data/local/tmp/wm_dump{WINSCOPE_EXT}', "window_dump"), 808*90c8c64dSAndroid Build Coastguard Worker lambda res: False, 809*90c8c64dSAndroid Build Coastguard Worker f'su root dumpsys window --proto > /data/local/tmp/wm_dump{WINSCOPE_EXT}' 810*90c8c64dSAndroid Build Coastguard Worker ), 811*90c8c64dSAndroid Build Coastguard Worker 812*90c8c64dSAndroid Build Coastguard Worker "layers_dump": DumpTarget( 813*90c8c64dSAndroid Build Coastguard Worker "layers_dump", 814*90c8c64dSAndroid Build Coastguard Worker File(f'/data/local/tmp/sf_dump{WINSCOPE_EXT}', "layers_dump"), 815*90c8c64dSAndroid Build Coastguard Worker lambda res: is_perfetto_data_source_available('android.surfaceflinger.layers', res), 816*90c8c64dSAndroid Build Coastguard Worker f""" 817*90c8c64dSAndroid Build Coastguard Workersu root dumpsys SurfaceFlinger --proto > /data/local/tmp/sf_dump{WINSCOPE_EXT} 818*90c8c64dSAndroid Build Coastguard Worker """, 819*90c8c64dSAndroid Build Coastguard Worker lambda is_perfetto: SurfaceFlingerDumpConfig(is_perfetto) 820*90c8c64dSAndroid Build Coastguard Worker ), 821*90c8c64dSAndroid Build Coastguard Worker 822*90c8c64dSAndroid Build Coastguard Worker "screenshot": DumpTarget( 823*90c8c64dSAndroid Build Coastguard Worker "screenshot", 824*90c8c64dSAndroid Build Coastguard Worker File("/data/local/tmp/screenshot_{trace_identifier}.png", "screenshot_{trace_identifier}.png"), 825*90c8c64dSAndroid Build Coastguard Worker lambda res: False, 826*90c8c64dSAndroid Build Coastguard Worker "screencap -p {options}> /data/local/tmp/screenshot_{trace_identifier}.png", 827*90c8c64dSAndroid Build Coastguard Worker lambda is_perfetto: ScreenshotConfig(is_perfetto) 828*90c8c64dSAndroid Build Coastguard Worker ), 829*90c8c64dSAndroid Build Coastguard Worker 830*90c8c64dSAndroid Build Coastguard Worker "perfetto_dump": DumpTarget( 831*90c8c64dSAndroid Build Coastguard Worker "perfetto_dump", 832*90c8c64dSAndroid Build Coastguard Worker File(PERFETTO_DUMP_FILE, "dump.perfetto-trace"), 833*90c8c64dSAndroid Build Coastguard Worker lambda res: is_any_perfetto_data_source_available(res), 834*90c8c64dSAndroid Build Coastguard Worker f""" 835*90c8c64dSAndroid Build Coastguard Workercat << EOF >> {PERFETTO_DUMP_CONFIG_FILE} 836*90c8c64dSAndroid Build Coastguard Workerbuffers: {{ 837*90c8c64dSAndroid Build Coastguard Worker size_kb: 500000 838*90c8c64dSAndroid Build Coastguard Worker fill_policy: RING_BUFFER 839*90c8c64dSAndroid Build Coastguard Worker}} 840*90c8c64dSAndroid Build Coastguard Workerduration_ms: 1 841*90c8c64dSAndroid Build Coastguard WorkerEOF 842*90c8c64dSAndroid Build Coastguard Worker 843*90c8c64dSAndroid Build Coastguard Workerrm -f {PERFETTO_DUMP_FILE} 844*90c8c64dSAndroid Build Coastguard Workerperfetto --out {PERFETTO_DUMP_FILE} --txt --config {PERFETTO_DUMP_CONFIG_FILE} 845*90c8c64dSAndroid Build Coastguard Workerecho 'Recorded perfetto dump.' 846*90c8c64dSAndroid Build Coastguard Worker """ 847*90c8c64dSAndroid Build Coastguard Worker ) 848*90c8c64dSAndroid Build Coastguard Worker} 849*90c8c64dSAndroid Build Coastguard Worker 850*90c8c64dSAndroid Build Coastguard Worker 851*90c8c64dSAndroid Build Coastguard Worker# END OF CONFIG # 852*90c8c64dSAndroid Build Coastguard Worker 853*90c8c64dSAndroid Build Coastguard Worker 854*90c8c64dSAndroid Build Coastguard Workerdef get_token() -> str: 855*90c8c64dSAndroid Build Coastguard Worker """Returns saved proxy security token or creates new one""" 856*90c8c64dSAndroid Build Coastguard Worker try: 857*90c8c64dSAndroid Build Coastguard Worker with open(WINSCOPE_TOKEN_LOCATION, 'r') as token_file: 858*90c8c64dSAndroid Build Coastguard Worker token = token_file.readline() 859*90c8c64dSAndroid Build Coastguard Worker log.debug("Loaded token {} from {}".format( 860*90c8c64dSAndroid Build Coastguard Worker token, WINSCOPE_TOKEN_LOCATION)) 861*90c8c64dSAndroid Build Coastguard Worker return token 862*90c8c64dSAndroid Build Coastguard Worker except IOError: 863*90c8c64dSAndroid Build Coastguard Worker token = secrets.token_hex(32) 864*90c8c64dSAndroid Build Coastguard Worker os.makedirs(os.path.dirname(WINSCOPE_TOKEN_LOCATION), exist_ok=True) 865*90c8c64dSAndroid Build Coastguard Worker try: 866*90c8c64dSAndroid Build Coastguard Worker with open(WINSCOPE_TOKEN_LOCATION, 'w') as token_file: 867*90c8c64dSAndroid Build Coastguard Worker log.debug("Created and saved token {} to {}".format( 868*90c8c64dSAndroid Build Coastguard Worker token, WINSCOPE_TOKEN_LOCATION)) 869*90c8c64dSAndroid Build Coastguard Worker token_file.write(token) 870*90c8c64dSAndroid Build Coastguard Worker os.chmod(WINSCOPE_TOKEN_LOCATION, 0o600) 871*90c8c64dSAndroid Build Coastguard Worker except IOError: 872*90c8c64dSAndroid Build Coastguard Worker log.error("Unable to save persistent token {} to {}".format( 873*90c8c64dSAndroid Build Coastguard Worker token, WINSCOPE_TOKEN_LOCATION)) 874*90c8c64dSAndroid Build Coastguard Worker return token 875*90c8c64dSAndroid Build Coastguard Worker 876*90c8c64dSAndroid Build Coastguard Worker 877*90c8c64dSAndroid Build Coastguard Workerclass RequestType(Enum): 878*90c8c64dSAndroid Build Coastguard Worker GET = 1 879*90c8c64dSAndroid Build Coastguard Worker POST = 2 880*90c8c64dSAndroid Build Coastguard Worker HEAD = 3 881*90c8c64dSAndroid Build Coastguard Worker 882*90c8c64dSAndroid Build Coastguard Worker 883*90c8c64dSAndroid Build Coastguard Workerdef add_standard_headers(server): 884*90c8c64dSAndroid Build Coastguard Worker server.send_header('Cache-Control', 'no-cache, no-store, must-revalidate') 885*90c8c64dSAndroid Build Coastguard Worker server.send_header('Access-Control-Allow-Origin', '*') 886*90c8c64dSAndroid Build Coastguard Worker server.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS') 887*90c8c64dSAndroid Build Coastguard Worker server.send_header('Access-Control-Allow-Headers', 888*90c8c64dSAndroid Build Coastguard Worker WINSCOPE_TOKEN_HEADER + ', Content-Type, Content-Length') 889*90c8c64dSAndroid Build Coastguard Worker server.send_header('Access-Control-Expose-Headers', 890*90c8c64dSAndroid Build Coastguard Worker 'Winscope-Proxy-Version') 891*90c8c64dSAndroid Build Coastguard Worker server.send_header(WINSCOPE_VERSION_HEADER, VERSION) 892*90c8c64dSAndroid Build Coastguard Worker server.end_headers() 893*90c8c64dSAndroid Build Coastguard Worker 894*90c8c64dSAndroid Build Coastguard Worker 895*90c8c64dSAndroid Build Coastguard Workerclass RequestEndpoint: 896*90c8c64dSAndroid Build Coastguard Worker """Request endpoint to use with the RequestRouter.""" 897*90c8c64dSAndroid Build Coastguard Worker 898*90c8c64dSAndroid Build Coastguard Worker @abstractmethod 899*90c8c64dSAndroid Build Coastguard Worker def process(self, server, path): 900*90c8c64dSAndroid Build Coastguard Worker pass 901*90c8c64dSAndroid Build Coastguard Worker 902*90c8c64dSAndroid Build Coastguard Worker 903*90c8c64dSAndroid Build Coastguard Workerclass AdbError(Exception): 904*90c8c64dSAndroid Build Coastguard Worker """Unsuccessful ADB operation""" 905*90c8c64dSAndroid Build Coastguard Worker pass 906*90c8c64dSAndroid Build Coastguard Worker 907*90c8c64dSAndroid Build Coastguard Worker 908*90c8c64dSAndroid Build Coastguard Workerclass BadRequest(Exception): 909*90c8c64dSAndroid Build Coastguard Worker """Invalid client request""" 910*90c8c64dSAndroid Build Coastguard Worker pass 911*90c8c64dSAndroid Build Coastguard Worker 912*90c8c64dSAndroid Build Coastguard Worker 913*90c8c64dSAndroid Build Coastguard Workerclass RequestRouter: 914*90c8c64dSAndroid Build Coastguard Worker """Handles HTTP request authentication and routing""" 915*90c8c64dSAndroid Build Coastguard Worker 916*90c8c64dSAndroid Build Coastguard Worker def __init__(self, handler): 917*90c8c64dSAndroid Build Coastguard Worker self.request = handler 918*90c8c64dSAndroid Build Coastguard Worker self.endpoints = {} 919*90c8c64dSAndroid Build Coastguard Worker 920*90c8c64dSAndroid Build Coastguard Worker def register_endpoint(self, method: RequestType, name: str, endpoint: RequestEndpoint): 921*90c8c64dSAndroid Build Coastguard Worker self.endpoints[(method, name)] = endpoint 922*90c8c64dSAndroid Build Coastguard Worker 923*90c8c64dSAndroid Build Coastguard Worker def _bad_request(self, error: str): 924*90c8c64dSAndroid Build Coastguard Worker log.warning("Bad request: " + error) 925*90c8c64dSAndroid Build Coastguard Worker self.request.respond(HTTPStatus.BAD_REQUEST, b"Bad request!\nThis is Winscope ADB proxy.\n\n" 926*90c8c64dSAndroid Build Coastguard Worker + error.encode("utf-8"), 'text/txt') 927*90c8c64dSAndroid Build Coastguard Worker 928*90c8c64dSAndroid Build Coastguard Worker def _internal_error(self, error: str): 929*90c8c64dSAndroid Build Coastguard Worker log.error("Internal error: " + error) 930*90c8c64dSAndroid Build Coastguard Worker self.request.respond(HTTPStatus.INTERNAL_SERVER_ERROR, 931*90c8c64dSAndroid Build Coastguard Worker error.encode("utf-8"), 'text/txt') 932*90c8c64dSAndroid Build Coastguard Worker 933*90c8c64dSAndroid Build Coastguard Worker def _bad_token(self): 934*90c8c64dSAndroid Build Coastguard Worker log.warning("Bad token") 935*90c8c64dSAndroid Build Coastguard Worker self.request.respond(HTTPStatus.FORBIDDEN, b"Bad Winscope authorization token!\nThis is Winscope ADB proxy.\n", 936*90c8c64dSAndroid Build Coastguard Worker 'text/txt') 937*90c8c64dSAndroid Build Coastguard Worker 938*90c8c64dSAndroid Build Coastguard Worker def process(self, method: RequestType): 939*90c8c64dSAndroid Build Coastguard Worker token = self.request.headers[WINSCOPE_TOKEN_HEADER] 940*90c8c64dSAndroid Build Coastguard Worker if not token or token != secret_token: 941*90c8c64dSAndroid Build Coastguard Worker return self._bad_token() 942*90c8c64dSAndroid Build Coastguard Worker path = self.request.path.strip('/').split('/') 943*90c8c64dSAndroid Build Coastguard Worker if path and len(path) > 0: 944*90c8c64dSAndroid Build Coastguard Worker endpoint_name = path[0] 945*90c8c64dSAndroid Build Coastguard Worker try: 946*90c8c64dSAndroid Build Coastguard Worker return self.endpoints[(method, endpoint_name)].process(self.request, path[1:]) 947*90c8c64dSAndroid Build Coastguard Worker except KeyError as ex: 948*90c8c64dSAndroid Build Coastguard Worker if "RequestType" in repr(ex): 949*90c8c64dSAndroid Build Coastguard Worker return self._bad_request("Unknown endpoint /{}/".format(endpoint_name)) 950*90c8c64dSAndroid Build Coastguard Worker return self._internal_error(repr(ex)) 951*90c8c64dSAndroid Build Coastguard Worker except AdbError as ex: 952*90c8c64dSAndroid Build Coastguard Worker return self._internal_error(str(ex)) 953*90c8c64dSAndroid Build Coastguard Worker except BadRequest as ex: 954*90c8c64dSAndroid Build Coastguard Worker return self._bad_request(str(ex)) 955*90c8c64dSAndroid Build Coastguard Worker except Exception as ex: 956*90c8c64dSAndroid Build Coastguard Worker return self._internal_error(repr(ex)) 957*90c8c64dSAndroid Build Coastguard Worker self._bad_request("No endpoint specified") 958*90c8c64dSAndroid Build Coastguard Worker 959*90c8c64dSAndroid Build Coastguard Worker 960*90c8c64dSAndroid Build Coastguard Workerdef call_adb(params: str, device: str = None, stdin: bytes = None): 961*90c8c64dSAndroid Build Coastguard Worker command = ['adb'] + (['-s', device] if device else []) + params.split(' ') 962*90c8c64dSAndroid Build Coastguard Worker try: 963*90c8c64dSAndroid Build Coastguard Worker log.debug("Call: " + ' '.join(command)) 964*90c8c64dSAndroid Build Coastguard Worker return subprocess.check_output(command, stderr=subprocess.STDOUT, input=stdin).decode('utf-8') 965*90c8c64dSAndroid Build Coastguard Worker except OSError as ex: 966*90c8c64dSAndroid Build Coastguard Worker raise AdbError('Error executing adb command: {}\n{}'.format( 967*90c8c64dSAndroid Build Coastguard Worker ' '.join(command), repr(ex))) 968*90c8c64dSAndroid Build Coastguard Worker except subprocess.CalledProcessError as ex: 969*90c8c64dSAndroid Build Coastguard Worker raise AdbError('Error executing adb command: adb {}\n{}'.format( 970*90c8c64dSAndroid Build Coastguard Worker params, ex.output.decode("utf-8"))) 971*90c8c64dSAndroid Build Coastguard Worker 972*90c8c64dSAndroid Build Coastguard Worker 973*90c8c64dSAndroid Build Coastguard Workerdef call_adb_outfile(params: str, outfile, device: str = None, stdin: bytes = None): 974*90c8c64dSAndroid Build Coastguard Worker try: 975*90c8c64dSAndroid Build Coastguard Worker process = subprocess.Popen(['adb'] + (['-s', device] if device else []) + params.split(' '), stdout=outfile, 976*90c8c64dSAndroid Build Coastguard Worker stderr=subprocess.PIPE) 977*90c8c64dSAndroid Build Coastguard Worker _, err = process.communicate(stdin) 978*90c8c64dSAndroid Build Coastguard Worker outfile.seek(0) 979*90c8c64dSAndroid Build Coastguard Worker if process.returncode != 0: 980*90c8c64dSAndroid Build Coastguard Worker raise AdbError('Error executing adb command: adb {}\n'.format(params) + err.decode( 981*90c8c64dSAndroid Build Coastguard Worker 'utf-8') + '\n' + outfile.read().decode('utf-8')) 982*90c8c64dSAndroid Build Coastguard Worker except OSError as ex: 983*90c8c64dSAndroid Build Coastguard Worker raise AdbError( 984*90c8c64dSAndroid Build Coastguard Worker 'Error executing adb command: adb {}\n{}'.format(params, repr(ex))) 985*90c8c64dSAndroid Build Coastguard Worker 986*90c8c64dSAndroid Build Coastguard Worker 987*90c8c64dSAndroid Build Coastguard Workerclass ListDevicesEndpoint(RequestEndpoint): 988*90c8c64dSAndroid Build Coastguard Worker ADB_INFO_RE = re.compile("^([A-Za-z0-9._:\\-]+)\\s+(\\w+)(.*model:(\\w+))?") 989*90c8c64dSAndroid Build Coastguard Worker foundDevices: dict[str | int, dict[str, bool | str]] = {} 990*90c8c64dSAndroid Build Coastguard Worker 991*90c8c64dSAndroid Build Coastguard Worker def process(self, server, path): 992*90c8c64dSAndroid Build Coastguard Worker lines = list(filter(None, call_adb('devices -l').split('\n'))) 993*90c8c64dSAndroid Build Coastguard Worker devices = {} 994*90c8c64dSAndroid Build Coastguard Worker for m in [ListDevicesEndpoint.ADB_INFO_RE.match(d) for d in lines[1:]]: 995*90c8c64dSAndroid Build Coastguard Worker if m: 996*90c8c64dSAndroid Build Coastguard Worker authorized = str(m.group(2)) != 'unauthorized' 997*90c8c64dSAndroid Build Coastguard Worker try: 998*90c8c64dSAndroid Build Coastguard Worker screen_record_version = call_adb('shell screenrecord --version', m.group(1)) if authorized else '0' 999*90c8c64dSAndroid Build Coastguard Worker except AdbError: 1000*90c8c64dSAndroid Build Coastguard Worker try: 1001*90c8c64dSAndroid Build Coastguard Worker help_text = call_adb('shell screenrecord --help', m.group(1)) 1002*90c8c64dSAndroid Build Coastguard Worker version_start_index = help_text.find('v') + 1 1003*90c8c64dSAndroid Build Coastguard Worker screen_record_version = help_text[version_start_index:version_start_index + 3] 1004*90c8c64dSAndroid Build Coastguard Worker except AdbError: 1005*90c8c64dSAndroid Build Coastguard Worker screen_record_version = '0' 1006*90c8c64dSAndroid Build Coastguard Worker 1007*90c8c64dSAndroid Build Coastguard Worker try: 1008*90c8c64dSAndroid Build Coastguard Worker displays = list(filter(None, call_adb('shell su root dumpsys SurfaceFlinger --display-id', 1009*90c8c64dSAndroid Build Coastguard Worker m.group(1)).split('\n'))) if authorized else [] 1010*90c8c64dSAndroid Build Coastguard Worker except AdbError: 1011*90c8c64dSAndroid Build Coastguard Worker displays = [] 1012*90c8c64dSAndroid Build Coastguard Worker 1013*90c8c64dSAndroid Build Coastguard Worker devices[m.group(1)] = { 1014*90c8c64dSAndroid Build Coastguard Worker 'authorized': authorized, 1015*90c8c64dSAndroid Build Coastguard Worker 'model': m.group(4).replace('_', ' ') if m.group(4) else '', 1016*90c8c64dSAndroid Build Coastguard Worker 'displays': displays, 1017*90c8c64dSAndroid Build Coastguard Worker 'screenrecord_version': screen_record_version, 1018*90c8c64dSAndroid Build Coastguard Worker } 1019*90c8c64dSAndroid Build Coastguard Worker self.foundDevices = devices 1020*90c8c64dSAndroid Build Coastguard Worker j = json.dumps(devices) 1021*90c8c64dSAndroid Build Coastguard Worker log.info("Detected devices: " + j) 1022*90c8c64dSAndroid Build Coastguard Worker server.respond(HTTPStatus.OK, j.encode("utf-8"), "text/json") 1023*90c8c64dSAndroid Build Coastguard Worker 1024*90c8c64dSAndroid Build Coastguard Worker 1025*90c8c64dSAndroid Build Coastguard Worker 1026*90c8c64dSAndroid Build Coastguard Workerclass CheckWaylandServiceEndpoint(RequestEndpoint): 1027*90c8c64dSAndroid Build Coastguard Worker def __init__(self, listDevicesEndpoint: ListDevicesEndpoint): 1028*90c8c64dSAndroid Build Coastguard Worker self._listDevicesEndpoint = listDevicesEndpoint 1029*90c8c64dSAndroid Build Coastguard Worker 1030*90c8c64dSAndroid Build Coastguard Worker def process(self, server, path): 1031*90c8c64dSAndroid Build Coastguard Worker self._listDevicesEndpoint.process(server, path) 1032*90c8c64dSAndroid Build Coastguard Worker foundDevices = self._listDevicesEndpoint.foundDevices 1033*90c8c64dSAndroid Build Coastguard Worker 1034*90c8c64dSAndroid Build Coastguard Worker if len(foundDevices) != 1: 1035*90c8c64dSAndroid Build Coastguard Worker res = 'false' 1036*90c8c64dSAndroid Build Coastguard Worker else: 1037*90c8c64dSAndroid Build Coastguard Worker device = list(foundDevices.values())[0] 1038*90c8c64dSAndroid Build Coastguard Worker if not device.get('authorized') or not device.get('model'): 1039*90c8c64dSAndroid Build Coastguard Worker res = 'false' 1040*90c8c64dSAndroid Build Coastguard Worker else: 1041*90c8c64dSAndroid Build Coastguard Worker raw_res = call_adb('shell service check Wayland') 1042*90c8c64dSAndroid Build Coastguard Worker res = 'false' if 'not found' in raw_res else 'true' 1043*90c8c64dSAndroid Build Coastguard Worker server.respond(HTTPStatus.OK, res.encode("utf-8"), "text/json") 1044*90c8c64dSAndroid Build Coastguard Worker 1045*90c8c64dSAndroid Build Coastguard Worker 1046*90c8c64dSAndroid Build Coastguard Worker 1047*90c8c64dSAndroid Build Coastguard Workerclass DeviceRequestEndpoint(RequestEndpoint): 1048*90c8c64dSAndroid Build Coastguard Worker def process(self, server, path): 1049*90c8c64dSAndroid Build Coastguard Worker if len(path) > 0 and re.fullmatch("[A-Za-z0-9._:\\-]+", path[0]): 1050*90c8c64dSAndroid Build Coastguard Worker self.process_with_device(server, path[1:], path[0]) 1051*90c8c64dSAndroid Build Coastguard Worker else: 1052*90c8c64dSAndroid Build Coastguard Worker raise BadRequest("Device id not specified") 1053*90c8c64dSAndroid Build Coastguard Worker 1054*90c8c64dSAndroid Build Coastguard Worker @abstractmethod 1055*90c8c64dSAndroid Build Coastguard Worker def process_with_device(self, server, path, device_id): 1056*90c8c64dSAndroid Build Coastguard Worker pass 1057*90c8c64dSAndroid Build Coastguard Worker 1058*90c8c64dSAndroid Build Coastguard Worker def get_request(self, server) -> str: 1059*90c8c64dSAndroid Build Coastguard Worker try: 1060*90c8c64dSAndroid Build Coastguard Worker length = int(server.headers["Content-Length"]) 1061*90c8c64dSAndroid Build Coastguard Worker except KeyError as err: 1062*90c8c64dSAndroid Build Coastguard Worker raise BadRequest("Missing Content-Length header\n" + str(err)) 1063*90c8c64dSAndroid Build Coastguard Worker except ValueError as err: 1064*90c8c64dSAndroid Build Coastguard Worker raise BadRequest("Content length unreadable\n" + str(err)) 1065*90c8c64dSAndroid Build Coastguard Worker return json.loads(server.rfile.read(length).decode("utf-8")) 1066*90c8c64dSAndroid Build Coastguard Worker 1067*90c8c64dSAndroid Build Coastguard Worker def get_targets_and_prepare_for_tracing(self, server, device_id, perfetto_config_file, targets_map: dict[str, TraceTarget | DumpTarget], perfetto_name): 1068*90c8c64dSAndroid Build Coastguard Worker warnings: list[str] = [] 1069*90c8c64dSAndroid Build Coastguard Worker 1070*90c8c64dSAndroid Build Coastguard Worker call_adb(f"shell su root rm -f {perfetto_config_file}", device_id) 1071*90c8c64dSAndroid Build Coastguard Worker log.debug("Cleared perfetto config file for previous tracing session") 1072*90c8c64dSAndroid Build Coastguard Worker 1073*90c8c64dSAndroid Build Coastguard Worker trace_requests: list[dict] = self.get_request(server) 1074*90c8c64dSAndroid Build Coastguard Worker trace_types = [t.get("name") for t in trace_requests] 1075*90c8c64dSAndroid Build Coastguard Worker log.debug(f"Received client request of {trace_types} for {device_id}") 1076*90c8c64dSAndroid Build Coastguard Worker 1077*90c8c64dSAndroid Build Coastguard Worker perfetto_query_result = call_adb("shell perfetto --query", device_id) 1078*90c8c64dSAndroid Build Coastguard Worker 1079*90c8c64dSAndroid Build Coastguard Worker too_many_perfetto_sessions = self.too_many_perfetto_sessions(perfetto_query_result) 1080*90c8c64dSAndroid Build Coastguard Worker if (too_many_perfetto_sessions): 1081*90c8c64dSAndroid Build Coastguard Worker warnings.append("Limit of 5 Perfetto sessions reached on device. Will attempt to collect legacy traces.") 1082*90c8c64dSAndroid Build Coastguard Worker log.warning(warnings[0]) 1083*90c8c64dSAndroid Build Coastguard Worker 1084*90c8c64dSAndroid Build Coastguard Worker trace_targets: list[tuple[DumpTarget | TraceTarget, TraceConfig | None]] = [] 1085*90c8c64dSAndroid Build Coastguard Worker for t in trace_requests: 1086*90c8c64dSAndroid Build Coastguard Worker try: 1087*90c8c64dSAndroid Build Coastguard Worker trace_name = t.get("name") 1088*90c8c64dSAndroid Build Coastguard Worker target = targets_map[trace_name] 1089*90c8c64dSAndroid Build Coastguard Worker is_perfetto = (not too_many_perfetto_sessions) and target.is_perfetto_available(perfetto_query_result) 1090*90c8c64dSAndroid Build Coastguard Worker config = None 1091*90c8c64dSAndroid Build Coastguard Worker if target.get_trace_config is not None: 1092*90c8c64dSAndroid Build Coastguard Worker config = target.get_trace_config(is_perfetto) 1093*90c8c64dSAndroid Build Coastguard Worker self.apply_config(config, t.get("config"), server, device_id) 1094*90c8c64dSAndroid Build Coastguard Worker is_valid_perfetto_target = trace_name == perfetto_name and not too_many_perfetto_sessions 1095*90c8c64dSAndroid Build Coastguard Worker is_valid_trace_target = trace_name != perfetto_name and not is_perfetto 1096*90c8c64dSAndroid Build Coastguard Worker if is_valid_perfetto_target or is_valid_trace_target: 1097*90c8c64dSAndroid Build Coastguard Worker trace_targets.append((target, config)) 1098*90c8c64dSAndroid Build Coastguard Worker 1099*90c8c64dSAndroid Build Coastguard Worker except KeyError as err: 1100*90c8c64dSAndroid Build Coastguard Worker log.warning("Unsupported trace target\n" + str(err)) 1101*90c8c64dSAndroid Build Coastguard Worker trace_targets = self.move_perfetto_target_to_end_of_list(trace_targets) 1102*90c8c64dSAndroid Build Coastguard Worker 1103*90c8c64dSAndroid Build Coastguard Worker self.check_device_and_permissions(server, device_id) 1104*90c8c64dSAndroid Build Coastguard Worker self.clear_last_tracing_session(device_id) 1105*90c8c64dSAndroid Build Coastguard Worker 1106*90c8c64dSAndroid Build Coastguard Worker log.debug("Trace requested for {} with targets {}".format( 1107*90c8c64dSAndroid Build Coastguard Worker device_id, ','.join([target.trace_name for target, config in trace_targets]))) 1108*90c8c64dSAndroid Build Coastguard Worker 1109*90c8c64dSAndroid Build Coastguard Worker return trace_targets, warnings 1110*90c8c64dSAndroid Build Coastguard Worker 1111*90c8c64dSAndroid Build Coastguard Worker def too_many_perfetto_sessions(self, perfetto_query_result: str): 1112*90c8c64dSAndroid Build Coastguard Worker concurrent_sessions_start = perfetto_query_result.find(PERFETTO_TRACING_SESSIONS_QUERY_START) 1113*90c8c64dSAndroid Build Coastguard Worker if concurrent_sessions_start != -1: 1114*90c8c64dSAndroid Build Coastguard Worker concurrent_sessions = perfetto_query_result[concurrent_sessions_start + len(PERFETTO_TRACING_SESSIONS_QUERY_START):] 1115*90c8c64dSAndroid Build Coastguard Worker log.info(f"Concurrent sessions:\n{concurrent_sessions}\n") 1116*90c8c64dSAndroid Build Coastguard Worker concurrent_sessions_end = concurrent_sessions.find(PERFETTO_TRACING_SESSIONS_QUERY_END) 1117*90c8c64dSAndroid Build Coastguard Worker if concurrent_sessions_end > 0: 1118*90c8c64dSAndroid Build Coastguard Worker concurrent_sessions_end -= 1 1119*90c8c64dSAndroid Build Coastguard Worker concurrent_sessions = concurrent_sessions[:concurrent_sessions_end] 1120*90c8c64dSAndroid Build Coastguard Worker number_of_concurrent_sessions = len(concurrent_sessions.split("\n")) if len(concurrent_sessions) > 0 else 0 1121*90c8c64dSAndroid Build Coastguard Worker 1122*90c8c64dSAndroid Build Coastguard Worker if PERFETTO_UNIQUE_SESSION_NAME in concurrent_sessions: 1123*90c8c64dSAndroid Build Coastguard Worker call_adb("shell perfetto --attach=WINSCOPE-PROXY-TRACING-SESSION --stop") 1124*90c8c64dSAndroid Build Coastguard Worker log.debug("Stopped already-running winscope perfetto session.") 1125*90c8c64dSAndroid Build Coastguard Worker number_of_concurrent_sessions -= 1 1126*90c8c64dSAndroid Build Coastguard Worker return number_of_concurrent_sessions >= 5 1127*90c8c64dSAndroid Build Coastguard Worker return False 1128*90c8c64dSAndroid Build Coastguard Worker 1129*90c8c64dSAndroid Build Coastguard Worker def apply_config(self, trace_config: TraceConfig, requested_configs: list[dict], server, device_id): 1130*90c8c64dSAndroid Build Coastguard Worker for requested_config in requested_configs: 1131*90c8c64dSAndroid Build Coastguard Worker config_key = requested_config.get("key") 1132*90c8c64dSAndroid Build Coastguard Worker if not trace_config.is_valid(config_key): 1133*90c8c64dSAndroid Build Coastguard Worker raise BadRequest( 1134*90c8c64dSAndroid Build Coastguard Worker f"Unsupported config {config_key}\n") 1135*90c8c64dSAndroid Build Coastguard Worker trace_config.add(config_key, requested_config.get("value")) 1136*90c8c64dSAndroid Build Coastguard Worker 1137*90c8c64dSAndroid Build Coastguard Worker if device_id in TRACE_THREADS: 1138*90c8c64dSAndroid Build Coastguard Worker BadRequest(f"Trace in progress for {device_id}") 1139*90c8c64dSAndroid Build Coastguard Worker if not self.check_root(device_id): 1140*90c8c64dSAndroid Build Coastguard Worker raise AdbError( 1141*90c8c64dSAndroid Build Coastguard Worker f"Unable to acquire root privileges on the device - check the output of 'adb -s {device_id} shell su root id'") 1142*90c8c64dSAndroid Build Coastguard Worker trace_config.execute_command(server, device_id) 1143*90c8c64dSAndroid Build Coastguard Worker 1144*90c8c64dSAndroid Build Coastguard Worker def check_root(self, device_id): 1145*90c8c64dSAndroid Build Coastguard Worker log.debug("Checking root access on {}".format(device_id)) 1146*90c8c64dSAndroid Build Coastguard Worker return int(call_adb('shell su root id -u', device_id)) == 0 1147*90c8c64dSAndroid Build Coastguard Worker 1148*90c8c64dSAndroid Build Coastguard Worker def move_perfetto_target_to_end_of_list(self, targets: list[tuple[TraceTarget, TraceConfig | None]]) -> list[tuple[TraceTarget, TraceConfig | None]]: 1149*90c8c64dSAndroid Build Coastguard Worker # Make sure a perfetto target (if present) comes last in the list of targets, i.e. will 1150*90c8c64dSAndroid Build Coastguard Worker # be processed last. 1151*90c8c64dSAndroid Build Coastguard Worker # A perfetto target must be processed last, so that perfetto tracing is started only after 1152*90c8c64dSAndroid Build Coastguard Worker # the other targets have been processed and have configured the perfetto config file. 1153*90c8c64dSAndroid Build Coastguard Worker def is_perfetto_target(target: TraceTarget): 1154*90c8c64dSAndroid Build Coastguard Worker return target == TRACE_TARGETS["perfetto_trace"] or target == DUMP_TARGETS["perfetto_dump"] 1155*90c8c64dSAndroid Build Coastguard Worker non_perfetto_targets = [t for t in targets if not is_perfetto_target(t[0])] 1156*90c8c64dSAndroid Build Coastguard Worker perfetto_targets = [t for t in targets if is_perfetto_target(t[0])] 1157*90c8c64dSAndroid Build Coastguard Worker return non_perfetto_targets + perfetto_targets 1158*90c8c64dSAndroid Build Coastguard Worker 1159*90c8c64dSAndroid Build Coastguard Worker def check_device_and_permissions(self, server, device_id): 1160*90c8c64dSAndroid Build Coastguard Worker if device_id in TRACE_THREADS: 1161*90c8c64dSAndroid Build Coastguard Worker log.warning("Trace already in progress for {}", device_id) 1162*90c8c64dSAndroid Build Coastguard Worker server.respond(HTTPStatus.OK, b'', "text/plain") 1163*90c8c64dSAndroid Build Coastguard Worker if not self.check_root(device_id): 1164*90c8c64dSAndroid Build Coastguard Worker raise AdbError( 1165*90c8c64dSAndroid Build Coastguard Worker "Unable to acquire root privileges on the device - check the output of 'adb -s {} shell su root id'" 1166*90c8c64dSAndroid Build Coastguard Worker .format( device_id)) 1167*90c8c64dSAndroid Build Coastguard Worker 1168*90c8c64dSAndroid Build Coastguard Worker def clear_last_tracing_session(self, device_id): 1169*90c8c64dSAndroid Build Coastguard Worker call_adb(f"shell su root rm -rf {WINSCOPE_BACKUP_DIR}", device_id) 1170*90c8c64dSAndroid Build Coastguard Worker call_adb(f"shell su root mkdir {WINSCOPE_BACKUP_DIR}", device_id) 1171*90c8c64dSAndroid Build Coastguard Worker log.debug("Cleared previous tracing session files from device") 1172*90c8c64dSAndroid Build Coastguard Worker 1173*90c8c64dSAndroid Build Coastguard Worker 1174*90c8c64dSAndroid Build Coastguard Workerclass FetchFilesEndpoint(DeviceRequestEndpoint): 1175*90c8c64dSAndroid Build Coastguard Worker def process_with_device(self, server, path, device_id): 1176*90c8c64dSAndroid Build Coastguard Worker file_buffers = self.fetch_existing_files(device_id) 1177*90c8c64dSAndroid Build Coastguard Worker 1178*90c8c64dSAndroid Build Coastguard Worker j = json.dumps(file_buffers) 1179*90c8c64dSAndroid Build Coastguard Worker server.respond(HTTPStatus.OK, j.encode("utf-8"), "text/json") 1180*90c8c64dSAndroid Build Coastguard Worker 1181*90c8c64dSAndroid Build Coastguard Worker def fetch_existing_files(self, device_id): 1182*90c8c64dSAndroid Build Coastguard Worker file_buffers = dict() 1183*90c8c64dSAndroid Build Coastguard Worker file = FileMatcher(f"{WINSCOPE_BACKUP_DIR}*", "", "") 1184*90c8c64dSAndroid Build Coastguard Worker try: 1185*90c8c64dSAndroid Build Coastguard Worker file_paths = file.get_filepaths(device_id) 1186*90c8c64dSAndroid Build Coastguard Worker for file_path in file_paths: 1187*90c8c64dSAndroid Build Coastguard Worker with NamedTemporaryFile() as tmp: 1188*90c8c64dSAndroid Build Coastguard Worker file_name = file_path.split('/')[-1] + ".gz" 1189*90c8c64dSAndroid Build Coastguard Worker log.debug( 1190*90c8c64dSAndroid Build Coastguard Worker f"Fetching file {file_path} from device to {tmp.name}") 1191*90c8c64dSAndroid Build Coastguard Worker try: 1192*90c8c64dSAndroid Build Coastguard Worker call_adb_outfile('exec-out su root cat ' + 1193*90c8c64dSAndroid Build Coastguard Worker file_path, tmp, device_id) 1194*90c8c64dSAndroid Build Coastguard Worker except AdbError as ex: 1195*90c8c64dSAndroid Build Coastguard Worker log.warning(f"Unable to fetch file {file_path} - {repr(ex)}") 1196*90c8c64dSAndroid Build Coastguard Worker return 1197*90c8c64dSAndroid Build Coastguard Worker log.debug(f"Uploading file {tmp.name}") 1198*90c8c64dSAndroid Build Coastguard Worker if file_name not in file_buffers: 1199*90c8c64dSAndroid Build Coastguard Worker file_buffers[file_name] = [] 1200*90c8c64dSAndroid Build Coastguard Worker buf = base64.encodebytes(gzip.compress(tmp.read())).decode("utf-8") 1201*90c8c64dSAndroid Build Coastguard Worker file_buffers[file_name].append(buf) 1202*90c8c64dSAndroid Build Coastguard Worker except: 1203*90c8c64dSAndroid Build Coastguard Worker self.log_no_files_warning() 1204*90c8c64dSAndroid Build Coastguard Worker return file_buffers 1205*90c8c64dSAndroid Build Coastguard Worker 1206*90c8c64dSAndroid Build Coastguard Worker def log_no_files_warning(self): 1207*90c8c64dSAndroid Build Coastguard Worker log.warning("Proxy didn't find any file to fetch") 1208*90c8c64dSAndroid Build Coastguard Worker 1209*90c8c64dSAndroid Build Coastguard Worker 1210*90c8c64dSAndroid Build Coastguard WorkerTRACE_THREADS = {} 1211*90c8c64dSAndroid Build Coastguard Worker 1212*90c8c64dSAndroid Build Coastguard Workerclass TraceThread(threading.Thread): 1213*90c8c64dSAndroid Build Coastguard Worker def __init__(self, trace_name: str, device_id: str, command: str, trace_identifier: str, status_filename: str): 1214*90c8c64dSAndroid Build Coastguard Worker self.trace_command = command 1215*90c8c64dSAndroid Build Coastguard Worker self.trace_name = trace_name 1216*90c8c64dSAndroid Build Coastguard Worker self.trace_identifier = trace_identifier 1217*90c8c64dSAndroid Build Coastguard Worker self.status_filename = status_filename 1218*90c8c64dSAndroid Build Coastguard Worker self._device_id = device_id 1219*90c8c64dSAndroid Build Coastguard Worker self._keep_alive_timer = None 1220*90c8c64dSAndroid Build Coastguard Worker self.out = None, 1221*90c8c64dSAndroid Build Coastguard Worker self.err = None, 1222*90c8c64dSAndroid Build Coastguard Worker self._command_timed_out = False 1223*90c8c64dSAndroid Build Coastguard Worker self._success = False 1224*90c8c64dSAndroid Build Coastguard Worker try: 1225*90c8c64dSAndroid Build Coastguard Worker shell = get_shell_args(self._device_id, "trace") 1226*90c8c64dSAndroid Build Coastguard Worker self.process = subprocess.Popen(shell, stdout=subprocess.PIPE, 1227*90c8c64dSAndroid Build Coastguard Worker stderr=subprocess.PIPE, stdin=subprocess.PIPE, start_new_session=True) 1228*90c8c64dSAndroid Build Coastguard Worker except OSError as ex: 1229*90c8c64dSAndroid Build Coastguard Worker raise AdbError( 1230*90c8c64dSAndroid Build Coastguard Worker 'Error executing adb command for trace {}: adb shell\n{}'.format(trace_name, repr(ex))) 1231*90c8c64dSAndroid Build Coastguard Worker 1232*90c8c64dSAndroid Build Coastguard Worker super().__init__() 1233*90c8c64dSAndroid Build Coastguard Worker 1234*90c8c64dSAndroid Build Coastguard Worker def timeout(self): 1235*90c8c64dSAndroid Build Coastguard Worker if self.is_alive(): 1236*90c8c64dSAndroid Build Coastguard Worker log.warning("Keep-alive timeout for {} trace on {}".format(self.trace_name, self._device_id)) 1237*90c8c64dSAndroid Build Coastguard Worker self.end_trace() 1238*90c8c64dSAndroid Build Coastguard Worker 1239*90c8c64dSAndroid Build Coastguard Worker def reset_timer(self): 1240*90c8c64dSAndroid Build Coastguard Worker log.info( 1241*90c8c64dSAndroid Build Coastguard Worker "Resetting keep-alive clock for {} trace on {}".format(self.trace_name, self._device_id)) 1242*90c8c64dSAndroid Build Coastguard Worker if self._keep_alive_timer: 1243*90c8c64dSAndroid Build Coastguard Worker self._keep_alive_timer.cancel() 1244*90c8c64dSAndroid Build Coastguard Worker self._keep_alive_timer = threading.Timer( 1245*90c8c64dSAndroid Build Coastguard Worker KEEP_ALIVE_INTERVAL_S, self.timeout) 1246*90c8c64dSAndroid Build Coastguard Worker self._keep_alive_timer.start() 1247*90c8c64dSAndroid Build Coastguard Worker 1248*90c8c64dSAndroid Build Coastguard Worker def end_trace(self): 1249*90c8c64dSAndroid Build Coastguard Worker if self._keep_alive_timer: 1250*90c8c64dSAndroid Build Coastguard Worker self._keep_alive_timer.cancel() 1251*90c8c64dSAndroid Build Coastguard Worker log.info("Sending SIGINT to the {} process on {}".format( 1252*90c8c64dSAndroid Build Coastguard Worker self.trace_name, 1253*90c8c64dSAndroid Build Coastguard Worker self._device_id)) 1254*90c8c64dSAndroid Build Coastguard Worker self.process.send_signal(signal.SIGINT) 1255*90c8c64dSAndroid Build Coastguard Worker try: 1256*90c8c64dSAndroid Build Coastguard Worker log.debug("Waiting for {} trace shell to exit for {}".format( 1257*90c8c64dSAndroid Build Coastguard Worker self.trace_name, 1258*90c8c64dSAndroid Build Coastguard Worker self._device_id)) 1259*90c8c64dSAndroid Build Coastguard Worker self.process.wait(timeout=COMMAND_TIMEOUT_S) 1260*90c8c64dSAndroid Build Coastguard Worker except TimeoutError: 1261*90c8c64dSAndroid Build Coastguard Worker log.error( 1262*90c8c64dSAndroid Build Coastguard Worker "TIMEOUT - sending SIGKILL to the {} trace process on {}".format(self.trace_name, self._device_id)) 1263*90c8c64dSAndroid Build Coastguard Worker self.process.kill() 1264*90c8c64dSAndroid Build Coastguard Worker self.join() 1265*90c8c64dSAndroid Build Coastguard Worker 1266*90c8c64dSAndroid Build Coastguard Worker def run(self): 1267*90c8c64dSAndroid Build Coastguard Worker retry_interval = 0.1 1268*90c8c64dSAndroid Build Coastguard Worker log.info("Trace {} started on {}".format(self.trace_name, self._device_id)) 1269*90c8c64dSAndroid Build Coastguard Worker self.reset_timer() 1270*90c8c64dSAndroid Build Coastguard Worker self.out, self.err = self.process.communicate(self.trace_command) 1271*90c8c64dSAndroid Build Coastguard Worker log.info("Trace {} ended on {}, waiting for cleanup".format(self.trace_name, self._device_id)) 1272*90c8c64dSAndroid Build Coastguard Worker time.sleep(0.2) 1273*90c8c64dSAndroid Build Coastguard Worker for i in range(int(COMMAND_TIMEOUT_S / retry_interval)): 1274*90c8c64dSAndroid Build Coastguard Worker if call_adb(f"shell su root cat {self.status_filename}", device=self._device_id) == 'TRACE_OK\n': 1275*90c8c64dSAndroid Build Coastguard Worker log.info("Trace {} finished on {}".format( 1276*90c8c64dSAndroid Build Coastguard Worker self.trace_name, 1277*90c8c64dSAndroid Build Coastguard Worker self._device_id)) 1278*90c8c64dSAndroid Build Coastguard Worker if self.trace_name == "perfetto_trace": 1279*90c8c64dSAndroid Build Coastguard Worker self._success = True 1280*90c8c64dSAndroid Build Coastguard Worker else: 1281*90c8c64dSAndroid Build Coastguard Worker self._success = len(self.err) == 0 1282*90c8c64dSAndroid Build Coastguard Worker return 1283*90c8c64dSAndroid Build Coastguard Worker log.debug("Still waiting for cleanup on {} for {}".format(self._device_id, self.trace_name)) 1284*90c8c64dSAndroid Build Coastguard Worker time.sleep(retry_interval) 1285*90c8c64dSAndroid Build Coastguard Worker 1286*90c8c64dSAndroid Build Coastguard Worker self._command_timed_out = True 1287*90c8c64dSAndroid Build Coastguard Worker 1288*90c8c64dSAndroid Build Coastguard Worker def success(self): 1289*90c8c64dSAndroid Build Coastguard Worker return self._success 1290*90c8c64dSAndroid Build Coastguard Worker 1291*90c8c64dSAndroid Build Coastguard Worker def timed_out(self): 1292*90c8c64dSAndroid Build Coastguard Worker return self._command_timed_out 1293*90c8c64dSAndroid Build Coastguard Worker 1294*90c8c64dSAndroid Build Coastguard Workerclass StartTraceEndpoint(DeviceRequestEndpoint): 1295*90c8c64dSAndroid Build Coastguard Worker TRACE_COMMAND = """ 1296*90c8c64dSAndroid Build Coastguard Workerset -e 1297*90c8c64dSAndroid Build Coastguard Worker 1298*90c8c64dSAndroid Build Coastguard Workerecho "Starting trace..." 1299*90c8c64dSAndroid Build Coastguard Workerecho "TRACE_START" > {winscope_status} 1300*90c8c64dSAndroid Build Coastguard Worker 1301*90c8c64dSAndroid Build Coastguard Worker# Do not print anything to stdout/stderr in the handler 1302*90c8c64dSAndroid Build Coastguard Workerfunction stop_trace() {{ 1303*90c8c64dSAndroid Build Coastguard Worker echo "start" >{signal_handler_log} 1304*90c8c64dSAndroid Build Coastguard Worker 1305*90c8c64dSAndroid Build Coastguard Worker # redirect stdout/stderr to log file 1306*90c8c64dSAndroid Build Coastguard Worker exec 1>>{signal_handler_log} 1307*90c8c64dSAndroid Build Coastguard Worker exec 2>>{signal_handler_log} 1308*90c8c64dSAndroid Build Coastguard Worker 1309*90c8c64dSAndroid Build Coastguard Worker set -x 1310*90c8c64dSAndroid Build Coastguard Worker trap - EXIT HUP INT 1311*90c8c64dSAndroid Build Coastguard Worker {stop_commands} 1312*90c8c64dSAndroid Build Coastguard Worker echo "TRACE_OK" > {winscope_status} 1313*90c8c64dSAndroid Build Coastguard Worker}} 1314*90c8c64dSAndroid Build Coastguard Worker 1315*90c8c64dSAndroid Build Coastguard Workertrap stop_trace EXIT HUP INT 1316*90c8c64dSAndroid Build Coastguard Workerecho "Signal handler registered." 1317*90c8c64dSAndroid Build Coastguard Worker 1318*90c8c64dSAndroid Build Coastguard Worker{start_commands} 1319*90c8c64dSAndroid Build Coastguard Worker 1320*90c8c64dSAndroid Build Coastguard Worker# ADB shell does not handle hung up well and does not call HUP handler when a child is active in foreground, 1321*90c8c64dSAndroid Build Coastguard Worker# as a workaround we sleep for short intervals in a loop so the handler is called after a sleep interval. 1322*90c8c64dSAndroid Build Coastguard Workerwhile true; do sleep 0.1; done 1323*90c8c64dSAndroid Build Coastguard Worker""" 1324*90c8c64dSAndroid Build Coastguard Worker 1325*90c8c64dSAndroid Build Coastguard Worker def process_with_device(self, server, path, device_id): 1326*90c8c64dSAndroid Build Coastguard Worker trace_targets, warnings = self.get_targets_and_prepare_for_tracing( 1327*90c8c64dSAndroid Build Coastguard Worker server, device_id, PERFETTO_TRACE_CONFIG_FILE, TRACE_TARGETS, "perfetto_trace") 1328*90c8c64dSAndroid Build Coastguard Worker 1329*90c8c64dSAndroid Build Coastguard Worker for t, config in trace_targets: 1330*90c8c64dSAndroid Build Coastguard Worker if config: 1331*90c8c64dSAndroid Build Coastguard Worker trace_identifiers = config.get_trace_identifiers() 1332*90c8c64dSAndroid Build Coastguard Worker else: 1333*90c8c64dSAndroid Build Coastguard Worker trace_identifiers = [""] 1334*90c8c64dSAndroid Build Coastguard Worker 1335*90c8c64dSAndroid Build Coastguard Worker for trace_identifier in trace_identifiers: 1336*90c8c64dSAndroid Build Coastguard Worker if trace_identifier: 1337*90c8c64dSAndroid Build Coastguard Worker if config: 1338*90c8c64dSAndroid Build Coastguard Worker options = config.get_optional_start_args(trace_identifier) 1339*90c8c64dSAndroid Build Coastguard Worker else: 1340*90c8c64dSAndroid Build Coastguard Worker options = "" 1341*90c8c64dSAndroid Build Coastguard Worker start_cmd = t.trace_start.format(trace_identifier=trace_identifier, options=options) 1342*90c8c64dSAndroid Build Coastguard Worker else: 1343*90c8c64dSAndroid Build Coastguard Worker start_cmd = t.trace_start 1344*90c8c64dSAndroid Build Coastguard Worker 1345*90c8c64dSAndroid Build Coastguard Worker command = StartTraceEndpoint.TRACE_COMMAND.format( 1346*90c8c64dSAndroid Build Coastguard Worker winscope_status=t.status_filename, 1347*90c8c64dSAndroid Build Coastguard Worker signal_handler_log=SIGNAL_HANDLER_LOG, 1348*90c8c64dSAndroid Build Coastguard Worker stop_commands=t.trace_stop, 1349*90c8c64dSAndroid Build Coastguard Worker perfetto_config_file=PERFETTO_TRACE_CONFIG_FILE, 1350*90c8c64dSAndroid Build Coastguard Worker start_commands=start_cmd, 1351*90c8c64dSAndroid Build Coastguard Worker ) 1352*90c8c64dSAndroid Build Coastguard Worker log.debug(f"Executing start command for {t.trace_name} on {device_id}...") 1353*90c8c64dSAndroid Build Coastguard Worker thread = TraceThread(t.trace_name, device_id, command.encode('utf-8'), trace_identifier, t.status_filename) 1354*90c8c64dSAndroid Build Coastguard Worker if device_id not in TRACE_THREADS: 1355*90c8c64dSAndroid Build Coastguard Worker TRACE_THREADS[device_id] = [thread] 1356*90c8c64dSAndroid Build Coastguard Worker else: 1357*90c8c64dSAndroid Build Coastguard Worker TRACE_THREADS[device_id].append(thread) 1358*90c8c64dSAndroid Build Coastguard Worker thread.start() 1359*90c8c64dSAndroid Build Coastguard Worker 1360*90c8c64dSAndroid Build Coastguard Worker server.respond(HTTPStatus.OK, json.dumps(warnings).encode("utf-8"), "text/json") 1361*90c8c64dSAndroid Build Coastguard Worker 1362*90c8c64dSAndroid Build Coastguard Worker 1363*90c8c64dSAndroid Build Coastguard Workerdef move_collected_files(files: list[File | FileMatcher], device_id, trace_identifier): 1364*90c8c64dSAndroid Build Coastguard Worker for f in files: 1365*90c8c64dSAndroid Build Coastguard Worker file_paths = f.get_filepaths(device_id) 1366*90c8c64dSAndroid Build Coastguard Worker file_type = f.get_filetype().format(trace_identifier=trace_identifier) 1367*90c8c64dSAndroid Build Coastguard Worker 1368*90c8c64dSAndroid Build Coastguard Worker for file_path in file_paths: 1369*90c8c64dSAndroid Build Coastguard Worker formatted_path = file_path.format(trace_identifier=trace_identifier) 1370*90c8c64dSAndroid Build Coastguard Worker log.debug(f"Moving file {formatted_path} to {WINSCOPE_BACKUP_DIR}{file_type} on device") 1371*90c8c64dSAndroid Build Coastguard Worker try: 1372*90c8c64dSAndroid Build Coastguard Worker call_adb( 1373*90c8c64dSAndroid Build Coastguard Worker f"shell su root [ ! -f {formatted_path} ] || su root mv {formatted_path} {WINSCOPE_BACKUP_DIR}{file_type}", 1374*90c8c64dSAndroid Build Coastguard Worker device_id) 1375*90c8c64dSAndroid Build Coastguard Worker except AdbError as ex: 1376*90c8c64dSAndroid Build Coastguard Worker log.warning(f"Unable to move file {formatted_path} - {repr(ex)}") 1377*90c8c64dSAndroid Build Coastguard Worker 1378*90c8c64dSAndroid Build Coastguard Worker 1379*90c8c64dSAndroid Build Coastguard Workerclass EndTraceEndpoint(DeviceRequestEndpoint): 1380*90c8c64dSAndroid Build Coastguard Worker def process_with_device(self, server, path, device_id): 1381*90c8c64dSAndroid Build Coastguard Worker if device_id not in TRACE_THREADS: 1382*90c8c64dSAndroid Build Coastguard Worker raise BadRequest("No trace in progress for {}".format(device_id)) 1383*90c8c64dSAndroid Build Coastguard Worker 1384*90c8c64dSAndroid Build Coastguard Worker errors: list[str] = [] 1385*90c8c64dSAndroid Build Coastguard Worker 1386*90c8c64dSAndroid Build Coastguard Worker for thread in TRACE_THREADS[device_id]: 1387*90c8c64dSAndroid Build Coastguard Worker if thread.is_alive(): 1388*90c8c64dSAndroid Build Coastguard Worker thread.end_trace() 1389*90c8c64dSAndroid Build Coastguard Worker success = thread.success() 1390*90c8c64dSAndroid Build Coastguard Worker signal_handler_log = call_adb(f"shell su root cat {SIGNAL_HANDLER_LOG}", device=device_id).encode('utf-8') 1391*90c8c64dSAndroid Build Coastguard Worker 1392*90c8c64dSAndroid Build Coastguard Worker if (thread.timed_out()): 1393*90c8c64dSAndroid Build Coastguard Worker timeout_message = "Trace {} timed out during cleanup".format(thread.trace_name) 1394*90c8c64dSAndroid Build Coastguard Worker errors.append(timeout_message) 1395*90c8c64dSAndroid Build Coastguard Worker log.error(timeout_message) 1396*90c8c64dSAndroid Build Coastguard Worker 1397*90c8c64dSAndroid Build Coastguard Worker if not success: 1398*90c8c64dSAndroid Build Coastguard Worker log.error("Error ending trace {} on the device".format(thread.trace_name)) 1399*90c8c64dSAndroid Build Coastguard Worker errors.append("Error ending trace {} on the device: {}".format(thread.trace_name, thread.err)) 1400*90c8c64dSAndroid Build Coastguard Worker 1401*90c8c64dSAndroid Build Coastguard Worker out = b"### Shell script's stdout ###\n" + \ 1402*90c8c64dSAndroid Build Coastguard Worker (thread.out if thread.out else b'<no stdout>') + \ 1403*90c8c64dSAndroid Build Coastguard Worker b"\n### Shell script's stderr ###\n" + \ 1404*90c8c64dSAndroid Build Coastguard Worker (thread.err if thread.err else b'<no stderr>') + \ 1405*90c8c64dSAndroid Build Coastguard Worker b"\n### Signal handler log ###\n" + \ 1406*90c8c64dSAndroid Build Coastguard Worker (signal_handler_log if signal_handler_log else b'<no signal handler logs>') + \ 1407*90c8c64dSAndroid Build Coastguard Worker b"\n" 1408*90c8c64dSAndroid Build Coastguard Worker log.debug("### Output ###\n".format(thread.trace_name) + out.decode("utf-8")) 1409*90c8c64dSAndroid Build Coastguard Worker if thread.trace_name in TRACE_TARGETS: 1410*90c8c64dSAndroid Build Coastguard Worker files = TRACE_TARGETS[thread.trace_name].files 1411*90c8c64dSAndroid Build Coastguard Worker move_collected_files(files, device_id, thread.trace_identifier) 1412*90c8c64dSAndroid Build Coastguard Worker else: 1413*90c8c64dSAndroid Build Coastguard Worker errors.append(f"File location unknown for {thread.trace_name}") 1414*90c8c64dSAndroid Build Coastguard Worker 1415*90c8c64dSAndroid Build Coastguard Worker call_adb(f"shell su root rm {thread.status_filename}", device=device_id) 1416*90c8c64dSAndroid Build Coastguard Worker TRACE_THREADS.pop(device_id) 1417*90c8c64dSAndroid Build Coastguard Worker server.respond(HTTPStatus.OK, json.dumps(errors).encode("utf-8"), "text/plain") 1418*90c8c64dSAndroid Build Coastguard Worker 1419*90c8c64dSAndroid Build Coastguard Worker 1420*90c8c64dSAndroid Build Coastguard Workerclass StatusEndpoint(DeviceRequestEndpoint): 1421*90c8c64dSAndroid Build Coastguard Worker def process_with_device(self, server, path, device_id): 1422*90c8c64dSAndroid Build Coastguard Worker if device_id not in TRACE_THREADS: 1423*90c8c64dSAndroid Build Coastguard Worker raise BadRequest("No trace in progress for {}".format(device_id)) 1424*90c8c64dSAndroid Build Coastguard Worker for thread in TRACE_THREADS[device_id]: 1425*90c8c64dSAndroid Build Coastguard Worker thread.reset_timer() 1426*90c8c64dSAndroid Build Coastguard Worker server.respond(HTTPStatus.OK, str(TRACE_THREADS[device_id][0].is_alive()).encode("utf-8"), "text/plain") 1427*90c8c64dSAndroid Build Coastguard Worker 1428*90c8c64dSAndroid Build Coastguard Worker 1429*90c8c64dSAndroid Build Coastguard Workerclass DumpEndpoint(DeviceRequestEndpoint): 1430*90c8c64dSAndroid Build Coastguard Worker def process_with_device(self, server, path, device_id): 1431*90c8c64dSAndroid Build Coastguard Worker dump_targets, warnings = self.get_targets_and_prepare_for_tracing( 1432*90c8c64dSAndroid Build Coastguard Worker server, device_id, PERFETTO_DUMP_CONFIG_FILE, DUMP_TARGETS, "perfetto_dump") 1433*90c8c64dSAndroid Build Coastguard Worker 1434*90c8c64dSAndroid Build Coastguard Worker dump_commands = [] 1435*90c8c64dSAndroid Build Coastguard Worker for t, config in dump_targets: 1436*90c8c64dSAndroid Build Coastguard Worker if config: 1437*90c8c64dSAndroid Build Coastguard Worker for trace_identifier in config.get_trace_identifiers(): 1438*90c8c64dSAndroid Build Coastguard Worker dump_commands.append( 1439*90c8c64dSAndroid Build Coastguard Worker t.dump_command.format(trace_identifier=trace_identifier, options=config.get_optional_start_args(trace_identifier))) 1440*90c8c64dSAndroid Build Coastguard Worker else: 1441*90c8c64dSAndroid Build Coastguard Worker dump_commands.append(t.dump_command) 1442*90c8c64dSAndroid Build Coastguard Worker 1443*90c8c64dSAndroid Build Coastguard Worker dump_commands = '\n'.join(dump_commands) 1444*90c8c64dSAndroid Build Coastguard Worker 1445*90c8c64dSAndroid Build Coastguard Worker shell = get_shell_args(device_id, "dump") 1446*90c8c64dSAndroid Build Coastguard Worker process = subprocess.Popen(shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 1447*90c8c64dSAndroid Build Coastguard Worker stdin=subprocess.PIPE, start_new_session=True) 1448*90c8c64dSAndroid Build Coastguard Worker log.info("Starting dump on device {}".format(device_id)) 1449*90c8c64dSAndroid Build Coastguard Worker out, err = process.communicate(dump_commands.encode('utf-8')) 1450*90c8c64dSAndroid Build Coastguard Worker if process.returncode != 0: 1451*90c8c64dSAndroid Build Coastguard Worker raise AdbError("Error executing dump command." + "\n\n### OUTPUT ###" + out.decode('utf-8') + "\n" 1452*90c8c64dSAndroid Build Coastguard Worker + err.decode('utf-8')) 1453*90c8c64dSAndroid Build Coastguard Worker log.info("Dump finished on device {}".format(device_id)) 1454*90c8c64dSAndroid Build Coastguard Worker 1455*90c8c64dSAndroid Build Coastguard Worker for target, config in dump_targets: 1456*90c8c64dSAndroid Build Coastguard Worker if config: 1457*90c8c64dSAndroid Build Coastguard Worker trace_identifiers = config.get_trace_identifiers() 1458*90c8c64dSAndroid Build Coastguard Worker for trace_identifier in trace_identifiers: 1459*90c8c64dSAndroid Build Coastguard Worker move_collected_files(target.files, device_id, trace_identifier) 1460*90c8c64dSAndroid Build Coastguard Worker else: 1461*90c8c64dSAndroid Build Coastguard Worker move_collected_files(target.files, device_id, "") 1462*90c8c64dSAndroid Build Coastguard Worker 1463*90c8c64dSAndroid Build Coastguard Worker server.respond(HTTPStatus.OK, json.dumps(warnings).encode("utf-8"), "text/plain") 1464*90c8c64dSAndroid Build Coastguard Worker 1465*90c8c64dSAndroid Build Coastguard Worker 1466*90c8c64dSAndroid Build Coastguard Workerclass ADBWinscopeProxy(BaseHTTPRequestHandler): 1467*90c8c64dSAndroid Build Coastguard Worker def __init__(self, request, client_address, server): 1468*90c8c64dSAndroid Build Coastguard Worker self.router = RequestRouter(self) 1469*90c8c64dSAndroid Build Coastguard Worker listDevicesEndpoint = ListDevicesEndpoint() 1470*90c8c64dSAndroid Build Coastguard Worker self.router.register_endpoint( 1471*90c8c64dSAndroid Build Coastguard Worker RequestType.GET, "devices", listDevicesEndpoint) 1472*90c8c64dSAndroid Build Coastguard Worker self.router.register_endpoint( 1473*90c8c64dSAndroid Build Coastguard Worker RequestType.GET, "status", StatusEndpoint()) 1474*90c8c64dSAndroid Build Coastguard Worker self.router.register_endpoint( 1475*90c8c64dSAndroid Build Coastguard Worker RequestType.GET, "fetch", FetchFilesEndpoint()) 1476*90c8c64dSAndroid Build Coastguard Worker self.router.register_endpoint(RequestType.POST, "start", StartTraceEndpoint()) 1477*90c8c64dSAndroid Build Coastguard Worker self.router.register_endpoint(RequestType.POST, "end", EndTraceEndpoint()) 1478*90c8c64dSAndroid Build Coastguard Worker self.router.register_endpoint(RequestType.POST, "dump", DumpEndpoint()) 1479*90c8c64dSAndroid Build Coastguard Worker self.router.register_endpoint( 1480*90c8c64dSAndroid Build Coastguard Worker RequestType.GET, "checkwayland", CheckWaylandServiceEndpoint(listDevicesEndpoint)) 1481*90c8c64dSAndroid Build Coastguard Worker super().__init__(request, client_address, server) 1482*90c8c64dSAndroid Build Coastguard Worker 1483*90c8c64dSAndroid Build Coastguard Worker def respond(self, code: int, data: bytes, mime: str) -> None: 1484*90c8c64dSAndroid Build Coastguard Worker self.send_response(code) 1485*90c8c64dSAndroid Build Coastguard Worker self.send_header('Content-type', mime) 1486*90c8c64dSAndroid Build Coastguard Worker add_standard_headers(self) 1487*90c8c64dSAndroid Build Coastguard Worker self.wfile.write(data) 1488*90c8c64dSAndroid Build Coastguard Worker 1489*90c8c64dSAndroid Build Coastguard Worker def do_GET(self): 1490*90c8c64dSAndroid Build Coastguard Worker self.router.process(RequestType.GET) 1491*90c8c64dSAndroid Build Coastguard Worker 1492*90c8c64dSAndroid Build Coastguard Worker def do_POST(self): 1493*90c8c64dSAndroid Build Coastguard Worker self.router.process(RequestType.POST) 1494*90c8c64dSAndroid Build Coastguard Worker 1495*90c8c64dSAndroid Build Coastguard Worker def do_OPTIONS(self): 1496*90c8c64dSAndroid Build Coastguard Worker self.send_response(HTTPStatus.OK) 1497*90c8c64dSAndroid Build Coastguard Worker self.send_header('Allow', 'GET,POST') 1498*90c8c64dSAndroid Build Coastguard Worker add_standard_headers(self) 1499*90c8c64dSAndroid Build Coastguard Worker self.end_headers() 1500*90c8c64dSAndroid Build Coastguard Worker self.wfile.write(b'GET,POST') 1501*90c8c64dSAndroid Build Coastguard Worker 1502*90c8c64dSAndroid Build Coastguard Worker def log_request(self, code='-', size='-'): 1503*90c8c64dSAndroid Build Coastguard Worker log.info('{} {} {}'.format(self.requestline, str(code), str(size))) 1504*90c8c64dSAndroid Build Coastguard Worker 1505*90c8c64dSAndroid Build Coastguard Worker 1506*90c8c64dSAndroid Build Coastguard Workerif __name__ == '__main__': 1507*90c8c64dSAndroid Build Coastguard Worker args = create_argument_parser().parse_args() 1508*90c8c64dSAndroid Build Coastguard Worker 1509*90c8c64dSAndroid Build Coastguard Worker logging.basicConfig(stream=sys.stderr, level=args.loglevel, 1510*90c8c64dSAndroid Build Coastguard Worker format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') 1511*90c8c64dSAndroid Build Coastguard Worker 1512*90c8c64dSAndroid Build Coastguard Worker log = logging.getLogger("ADBProxy") 1513*90c8c64dSAndroid Build Coastguard Worker secret_token = get_token() 1514*90c8c64dSAndroid Build Coastguard Worker 1515*90c8c64dSAndroid Build Coastguard Worker print("Winscope ADB Connect proxy version: " + VERSION) 1516*90c8c64dSAndroid Build Coastguard Worker print('Winscope token: ' + secret_token) 1517*90c8c64dSAndroid Build Coastguard Worker 1518*90c8c64dSAndroid Build Coastguard Worker httpd = HTTPServer(('localhost', args.port), ADBWinscopeProxy) 1519*90c8c64dSAndroid Build Coastguard Worker try: 1520*90c8c64dSAndroid Build Coastguard Worker httpd.serve_forever() 1521*90c8c64dSAndroid Build Coastguard Worker except KeyboardInterrupt: 1522*90c8c64dSAndroid Build Coastguard Worker log.info("Shutting down") 1523