blob: 98b711d579698062bf32ecaa014994fb715990a5 [file] [log] [blame]
#!/usr/bin/env vpython3
# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Reads log data from a device."""
import argparse
import os
import subprocess
import sys
import time
from contextlib import AbstractContextManager
from typing import Iterable, Optional, TextIO
from common import catch_sigterm, read_package_paths, register_common_args, \
register_device_args, run_continuous_ffx_command, \
run_ffx_command
from ffx_integration import ScopedFfxConfig, run_symbolizer
class LogManager(AbstractContextManager):
"""Handles opening and closing file streams for logging purposes."""
def __init__(self, logs_dir: Optional[str]) -> None:
self._logs_dir = logs_dir
# A dictionary with the log file path as the key and a file stream as
# value.
self._log_files = {}
self._log_procs = []
self._scoped_ffx_log = None
if self._logs_dir:
self._scoped_ffx_log = ScopedFfxConfig('log.dir', self._logs_dir)
def __enter__(self):
if self._scoped_ffx_log:
self._scoped_ffx_log.__enter__()
run_ffx_command(('daemon', 'stop'), check=False)
return self
def is_logging_enabled(self) -> bool:
"""Check whether logging is turned on."""
return self._logs_dir is not None
def add_log_process(self, process: subprocess.Popen) -> None:
"""Register a logging process to LogManager to be killed at LogManager
teardown."""
self._log_procs.append(process)
def open_log_file(self, log_file_name: str) -> TextIO:
"""Open a file stream with log_file_name in the logs directory."""
if not self._logs_dir:
raise Exception('Logging directory is not specified.')
log_file_path = os.path.join(self._logs_dir, log_file_name)
log_file = open(log_file_path, 'w', buffering=1)
self._log_files[log_file_path] = log_file
return log_file
def stop(self):
"""Stop all active logging instances."""
for proc in self._log_procs:
proc.kill()
for log in self._log_files.values():
log.close()
def __exit__(self, exc_type, exc_value, traceback):
self.stop()
if self._scoped_ffx_log:
self._scoped_ffx_log.__exit__(exc_type, exc_value, traceback)
# Allow command to fail while ffx team investigates the issue.
run_ffx_command(('daemon', 'stop'), check=False)
def start_system_log(log_manager: LogManager,
log_to_stdout: bool,
pkg_paths: Optional[Iterable[str]] = None,
log_args: Optional[Iterable[str]] = None,
target_id: Optional[str] = None) -> None:
"""
Start system logging.
Args:
log_manager: A LogManager class that manages the log file and process.
log_to_stdout: If set to True, print logs directly to stdout.
pkg_paths: Path to the packages
log_args: Arguments forwarded to `ffx log` command.
target_id: Specify a target to use.
"""
if not log_manager.is_logging_enabled() and not log_to_stdout:
return
symbol_paths = None
if pkg_paths:
symbol_paths = []
# Locate debug symbols for each package.
for pkg_path in pkg_paths:
assert os.path.isfile(pkg_path), '%s does not exist' % pkg_path
symbol_paths.append(
os.path.join(os.path.dirname(pkg_path), 'ids.txt'))
if log_to_stdout:
system_log = sys.stdout
else:
system_log = log_manager.open_log_file('system_log')
log_cmd = ['log', '--raw']
if log_args:
log_cmd.extend(log_args)
if symbol_paths:
log_proc = run_continuous_ffx_command(log_cmd,
target_id,
stdout=subprocess.PIPE)
log_manager.add_log_process(log_proc)
log_manager.add_log_process(
run_symbolizer(symbol_paths, log_proc.stdout, system_log))
else:
log_manager.add_log_process(
run_continuous_ffx_command(log_cmd, target_id, stdout=system_log))
def main():
"""Stand-alone function for fetching system logs and print to terminal.
Runs until the process is killed or interrupted (i.e. user presses CTRL-C).
"""
catch_sigterm()
parser = argparse.ArgumentParser()
register_common_args(parser)
register_device_args(parser)
parser.add_argument('--packages',
action='append',
help='Name of the packages to symbolize.')
manager_args, system_log_args = parser.parse_known_args()
if manager_args.packages and not manager_args.out_dir:
raise ValueError('--out-dir must be specified to symbolize packages.')
package_paths = []
if manager_args.packages:
for package in manager_args.packages:
package_paths.extend(
read_package_paths(manager_args.out_dir, package))
with LogManager(None) as log_manager:
try:
start_system_log(log_manager, True, package_paths, system_log_args,
manager_args.target_id)
while True:
time.sleep(10000)
except (KeyboardInterrupt, SystemExit):
pass
if __name__ == '__main__':
sys.exit(main())