1*8975f5c5SAndroid Build Coastguard Worker# Copyright 2022 The Chromium Authors 2*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 3*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file. 4*8975f5c5SAndroid Build Coastguard Worker"""Reads log data from a device.""" 5*8975f5c5SAndroid Build Coastguard Worker 6*8975f5c5SAndroid Build Coastguard Workerimport os 7*8975f5c5SAndroid Build Coastguard Workerimport subprocess 8*8975f5c5SAndroid Build Coastguard Workerimport sys 9*8975f5c5SAndroid Build Coastguard Worker 10*8975f5c5SAndroid Build Coastguard Workerfrom contextlib import AbstractContextManager 11*8975f5c5SAndroid Build Coastguard Workerfrom typing import Iterable, Optional, TextIO 12*8975f5c5SAndroid Build Coastguard Worker 13*8975f5c5SAndroid Build Coastguard Workerfrom common import run_continuous_ffx_command 14*8975f5c5SAndroid Build Coastguard Workerfrom ffx_integration import run_symbolizer 15*8975f5c5SAndroid Build Coastguard Worker 16*8975f5c5SAndroid Build Coastguard Worker 17*8975f5c5SAndroid Build Coastguard Workerclass LogManager(AbstractContextManager): 18*8975f5c5SAndroid Build Coastguard Worker """Handles opening and closing file streams for logging purposes.""" 19*8975f5c5SAndroid Build Coastguard Worker 20*8975f5c5SAndroid Build Coastguard Worker def __init__(self, logs_dir: Optional[str]) -> None: 21*8975f5c5SAndroid Build Coastguard Worker self._logs_dir = logs_dir 22*8975f5c5SAndroid Build Coastguard Worker 23*8975f5c5SAndroid Build Coastguard Worker # A dictionary with the log file path as the key and a file stream as 24*8975f5c5SAndroid Build Coastguard Worker # value. 25*8975f5c5SAndroid Build Coastguard Worker self._log_files = {} 26*8975f5c5SAndroid Build Coastguard Worker self._log_procs = [] 27*8975f5c5SAndroid Build Coastguard Worker self._scoped_ffx_log = None 28*8975f5c5SAndroid Build Coastguard Worker 29*8975f5c5SAndroid Build Coastguard Worker def __enter__(self): 30*8975f5c5SAndroid Build Coastguard Worker return self 31*8975f5c5SAndroid Build Coastguard Worker 32*8975f5c5SAndroid Build Coastguard Worker def is_logging_enabled(self) -> bool: 33*8975f5c5SAndroid Build Coastguard Worker """Check whether logging is turned on.""" 34*8975f5c5SAndroid Build Coastguard Worker 35*8975f5c5SAndroid Build Coastguard Worker return self._logs_dir is not None 36*8975f5c5SAndroid Build Coastguard Worker 37*8975f5c5SAndroid Build Coastguard Worker def add_log_process(self, process: subprocess.Popen) -> None: 38*8975f5c5SAndroid Build Coastguard Worker """Register a logging process to LogManager to be killed at LogManager 39*8975f5c5SAndroid Build Coastguard Worker teardown.""" 40*8975f5c5SAndroid Build Coastguard Worker 41*8975f5c5SAndroid Build Coastguard Worker self._log_procs.append(process) 42*8975f5c5SAndroid Build Coastguard Worker 43*8975f5c5SAndroid Build Coastguard Worker def open_log_file(self, log_file_name: str) -> TextIO: 44*8975f5c5SAndroid Build Coastguard Worker """Open a file stream with log_file_name in the logs directory.""" 45*8975f5c5SAndroid Build Coastguard Worker 46*8975f5c5SAndroid Build Coastguard Worker assert self._logs_dir, 'Logging directory is not specified.' 47*8975f5c5SAndroid Build Coastguard Worker log_file_path = os.path.join(self._logs_dir, log_file_name) 48*8975f5c5SAndroid Build Coastguard Worker log_file = open(log_file_path, 'w', buffering=1) 49*8975f5c5SAndroid Build Coastguard Worker self._log_files[log_file_path] = log_file 50*8975f5c5SAndroid Build Coastguard Worker return log_file 51*8975f5c5SAndroid Build Coastguard Worker 52*8975f5c5SAndroid Build Coastguard Worker def __exit__(self, exc_type, exc_value, traceback): 53*8975f5c5SAndroid Build Coastguard Worker """Stop all active logging instances.""" 54*8975f5c5SAndroid Build Coastguard Worker for proc in self._log_procs: 55*8975f5c5SAndroid Build Coastguard Worker proc.kill() 56*8975f5c5SAndroid Build Coastguard Worker for log in self._log_files.values(): 57*8975f5c5SAndroid Build Coastguard Worker log.close() 58*8975f5c5SAndroid Build Coastguard Worker return False 59*8975f5c5SAndroid Build Coastguard Worker 60*8975f5c5SAndroid Build Coastguard Worker 61*8975f5c5SAndroid Build Coastguard Workerdef start_system_log(log_manager: LogManager, 62*8975f5c5SAndroid Build Coastguard Worker log_to_stdout: bool, 63*8975f5c5SAndroid Build Coastguard Worker pkg_paths: Optional[Iterable[str]] = None, 64*8975f5c5SAndroid Build Coastguard Worker log_args: Optional[Iterable[str]] = None, 65*8975f5c5SAndroid Build Coastguard Worker target_id: Optional[str] = None) -> None: 66*8975f5c5SAndroid Build Coastguard Worker """ 67*8975f5c5SAndroid Build Coastguard Worker Start system logging. 68*8975f5c5SAndroid Build Coastguard Worker 69*8975f5c5SAndroid Build Coastguard Worker Args: 70*8975f5c5SAndroid Build Coastguard Worker log_manager: A LogManager class that manages the log file and process. 71*8975f5c5SAndroid Build Coastguard Worker log_to_stdout: If set to True, print logs directly to stdout. 72*8975f5c5SAndroid Build Coastguard Worker pkg_paths: Path to the packages 73*8975f5c5SAndroid Build Coastguard Worker log_args: Arguments forwarded to `ffx log` command. 74*8975f5c5SAndroid Build Coastguard Worker target_id: Specify a target to use. 75*8975f5c5SAndroid Build Coastguard Worker """ 76*8975f5c5SAndroid Build Coastguard Worker 77*8975f5c5SAndroid Build Coastguard Worker if not log_manager.is_logging_enabled() and not log_to_stdout: 78*8975f5c5SAndroid Build Coastguard Worker return 79*8975f5c5SAndroid Build Coastguard Worker symbol_paths = None 80*8975f5c5SAndroid Build Coastguard Worker if pkg_paths: 81*8975f5c5SAndroid Build Coastguard Worker symbol_paths = [] 82*8975f5c5SAndroid Build Coastguard Worker 83*8975f5c5SAndroid Build Coastguard Worker # Locate debug symbols for each package. 84*8975f5c5SAndroid Build Coastguard Worker for pkg_path in pkg_paths: 85*8975f5c5SAndroid Build Coastguard Worker assert os.path.isfile(pkg_path), '%s does not exist' % pkg_path 86*8975f5c5SAndroid Build Coastguard Worker symbol_paths.append( 87*8975f5c5SAndroid Build Coastguard Worker os.path.join(os.path.dirname(pkg_path), 'ids.txt')) 88*8975f5c5SAndroid Build Coastguard Worker 89*8975f5c5SAndroid Build Coastguard Worker if log_to_stdout: 90*8975f5c5SAndroid Build Coastguard Worker system_log = sys.stdout 91*8975f5c5SAndroid Build Coastguard Worker else: 92*8975f5c5SAndroid Build Coastguard Worker system_log = log_manager.open_log_file('system_log') 93*8975f5c5SAndroid Build Coastguard Worker log_cmd = ['log', '--symbolize', 'off', '--no-color'] 94*8975f5c5SAndroid Build Coastguard Worker if log_args: 95*8975f5c5SAndroid Build Coastguard Worker log_cmd.extend(log_args) 96*8975f5c5SAndroid Build Coastguard Worker if symbol_paths: 97*8975f5c5SAndroid Build Coastguard Worker log_proc = run_continuous_ffx_command(log_cmd, 98*8975f5c5SAndroid Build Coastguard Worker target_id, 99*8975f5c5SAndroid Build Coastguard Worker stdout=subprocess.PIPE) 100*8975f5c5SAndroid Build Coastguard Worker log_manager.add_log_process(log_proc) 101*8975f5c5SAndroid Build Coastguard Worker log_manager.add_log_process( 102*8975f5c5SAndroid Build Coastguard Worker run_symbolizer(symbol_paths, log_proc.stdout, system_log)) 103*8975f5c5SAndroid Build Coastguard Worker else: 104*8975f5c5SAndroid Build Coastguard Worker log_manager.add_log_process( 105*8975f5c5SAndroid Build Coastguard Worker run_continuous_ffx_command(log_cmd, target_id, stdout=system_log)) 106