1# Copyright 2024 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from __future__ import annotations 6 7from typing import TYPE_CHECKING, Iterable, Optional, Tuple, cast 8 9from crossbench.plt.android_adb import AndroidAdbPlatform 10from crossbench.probes.probe import (Probe, ProbeConfigParser, ProbeContext, 11 ProbeIncompatibleBrowser) 12from crossbench.probes.result_location import ResultLocation 13from crossbench.probes.results import LocalProbeResult, ProbeResult 14 15if TYPE_CHECKING: 16 from crossbench.browsers.browser import Browser 17 from crossbench.env import HostEnvironment 18 from crossbench.runner.run import Run 19 20 21class AndroidLogcatProbe(Probe): 22 """ 23 Android-only probe to collect logcat traces. 24 """ 25 NAME = "logcat" 26 RESULT_LOCATION = ResultLocation.LOCAL 27 28 IS_GENERAL_PURPOSE = True 29 30 @classmethod 31 def config_parser(cls) -> ProbeConfigParser: 32 parser = super().config_parser() 33 parser.add_argument( 34 "filterspec", 35 type=str, 36 is_list=True, 37 default=tuple(), 38 help="Filter specifications are a series of <tag>[:priority]") 39 return parser 40 41 def __init__(self, filterspec: Iterable[str]): 42 super().__init__() 43 self._filterspec = tuple(filterspec) 44 45 @property 46 def filterspec(self) -> Tuple[str, ...]: 47 return self._filterspec 48 49 def validate_browser(self, env: HostEnvironment, browser: Browser) -> None: 50 super().validate_browser(env, browser) 51 if not browser.platform.is_android: 52 raise ProbeIncompatibleBrowser(self, browser, "Only supported on android") 53 54 def get_context(self, run: Run) -> AndroidLogcatProbeContext: 55 return AndroidLogcatProbeContext(self, run) 56 57 58class AndroidLogcatProbeContext(ProbeContext[AndroidLogcatProbe]): 59 60 def __init__(self, probe: AndroidLogcatProbe, run: Run) -> None: 61 super().__init__(probe, run) 62 self._logcat_start_time: Optional[str] = None 63 64 def _get_browser_platform_time(self) -> str: 65 return self.browser_platform.sh_stdout("date", 66 "+%Y-%m-%d %H:%M:%S").rstrip() 67 68 def _log_to_logcat(self, msg: str): 69 self.browser_platform.sh("log", "-t", "crossbench", msg) 70 71 @property 72 def browser_platform(self) -> AndroidAdbPlatform: 73 browser_platform = super().browser_platform 74 assert isinstance(browser_platform, AndroidAdbPlatform) 75 return cast(AndroidAdbPlatform, browser_platform) 76 77 def start(self) -> None: 78 self._logcat_start_time = self._get_browser_platform_time() 79 self._log_to_logcat("logcat probe start") 80 81 def stop(self) -> None: 82 self._log_to_logcat("logcat probe end") 83 84 def teardown(self) -> ProbeResult: 85 assert self._logcat_start_time 86 file = self.local_result_path.with_suffix(".txt") 87 with file.open("w", encoding="utf-8") as f: 88 self.host_platform.sh( 89 "adb", 90 "logcat", 91 "-t", 92 self._logcat_start_time + ".000", 93 *self.probe.filterspec, 94 stdout=f) 95 96 return LocalProbeResult(trace=(file,)) 97