xref: /aosp_15_r20/external/cronet/build/fuchsia/test/log_manager.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env vpython3
2*6777b538SAndroid Build Coastguard Worker# Copyright 2022 The Chromium Authors
3*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file.
5*6777b538SAndroid Build Coastguard Worker"""Reads log data from a device."""
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Workerimport argparse
8*6777b538SAndroid Build Coastguard Workerimport os
9*6777b538SAndroid Build Coastguard Workerimport subprocess
10*6777b538SAndroid Build Coastguard Workerimport sys
11*6777b538SAndroid Build Coastguard Worker
12*6777b538SAndroid Build Coastguard Workerfrom contextlib import AbstractContextManager
13*6777b538SAndroid Build Coastguard Workerfrom typing import Iterable, Optional, TextIO
14*6777b538SAndroid Build Coastguard Worker
15*6777b538SAndroid Build Coastguard Workerfrom common import catch_sigterm, read_package_paths, register_common_args, \
16*6777b538SAndroid Build Coastguard Worker                   register_device_args, run_continuous_ffx_command, \
17*6777b538SAndroid Build Coastguard Worker                   stop_ffx_daemon, wait_for_sigterm
18*6777b538SAndroid Build Coastguard Workerfrom compatible_utils import running_unattended
19*6777b538SAndroid Build Coastguard Workerfrom ffx_integration import ScopedFfxConfig, run_symbolizer
20*6777b538SAndroid Build Coastguard Worker
21*6777b538SAndroid Build Coastguard Worker
22*6777b538SAndroid Build Coastguard Workerclass LogManager(AbstractContextManager):
23*6777b538SAndroid Build Coastguard Worker    """Handles opening and closing file streams for logging purposes."""
24*6777b538SAndroid Build Coastguard Worker
25*6777b538SAndroid Build Coastguard Worker    def __init__(self, logs_dir: Optional[str]) -> None:
26*6777b538SAndroid Build Coastguard Worker        self._logs_dir = logs_dir
27*6777b538SAndroid Build Coastguard Worker
28*6777b538SAndroid Build Coastguard Worker        # A dictionary with the log file path as the key and a file stream as
29*6777b538SAndroid Build Coastguard Worker        # value.
30*6777b538SAndroid Build Coastguard Worker        self._log_files = {}
31*6777b538SAndroid Build Coastguard Worker        self._log_procs = []
32*6777b538SAndroid Build Coastguard Worker        self._scoped_ffx_log = None
33*6777b538SAndroid Build Coastguard Worker
34*6777b538SAndroid Build Coastguard Worker        if self._logs_dir:
35*6777b538SAndroid Build Coastguard Worker            self._scoped_ffx_log = ScopedFfxConfig('log.dir', self._logs_dir)
36*6777b538SAndroid Build Coastguard Worker
37*6777b538SAndroid Build Coastguard Worker    def __enter__(self):
38*6777b538SAndroid Build Coastguard Worker        if self._scoped_ffx_log:
39*6777b538SAndroid Build Coastguard Worker            self._scoped_ffx_log.__enter__()
40*6777b538SAndroid Build Coastguard Worker            # log.dir change always requires the restarting of the daemon.
41*6777b538SAndroid Build Coastguard Worker            # In the test fleet with running_unattended being true, we
42*6777b538SAndroid Build Coastguard Worker            # explicitly disallow the daemon from automatically starting, and
43*6777b538SAndroid Build Coastguard Worker            # do all the configuration before starting the daemon.
44*6777b538SAndroid Build Coastguard Worker            # But in local development workflow, we help the developers to
45*6777b538SAndroid Build Coastguard Worker            # restart the daemon to ensure the change of log.dir taking effect.
46*6777b538SAndroid Build Coastguard Worker            if not running_unattended():
47*6777b538SAndroid Build Coastguard Worker                stop_ffx_daemon()
48*6777b538SAndroid Build Coastguard Worker
49*6777b538SAndroid Build Coastguard Worker        return self
50*6777b538SAndroid Build Coastguard Worker
51*6777b538SAndroid Build Coastguard Worker    def is_logging_enabled(self) -> bool:
52*6777b538SAndroid Build Coastguard Worker        """Check whether logging is turned on."""
53*6777b538SAndroid Build Coastguard Worker
54*6777b538SAndroid Build Coastguard Worker        return self._logs_dir is not None
55*6777b538SAndroid Build Coastguard Worker
56*6777b538SAndroid Build Coastguard Worker    def add_log_process(self, process: subprocess.Popen) -> None:
57*6777b538SAndroid Build Coastguard Worker        """Register a logging process to LogManager to be killed at LogManager
58*6777b538SAndroid Build Coastguard Worker        teardown."""
59*6777b538SAndroid Build Coastguard Worker
60*6777b538SAndroid Build Coastguard Worker        self._log_procs.append(process)
61*6777b538SAndroid Build Coastguard Worker
62*6777b538SAndroid Build Coastguard Worker    def open_log_file(self, log_file_name: str) -> TextIO:
63*6777b538SAndroid Build Coastguard Worker        """Open a file stream with log_file_name in the logs directory."""
64*6777b538SAndroid Build Coastguard Worker
65*6777b538SAndroid Build Coastguard Worker        if not self._logs_dir:
66*6777b538SAndroid Build Coastguard Worker            raise Exception('Logging directory is not specified.')
67*6777b538SAndroid Build Coastguard Worker        log_file_path = os.path.join(self._logs_dir, log_file_name)
68*6777b538SAndroid Build Coastguard Worker        log_file = open(log_file_path, 'w', buffering=1)
69*6777b538SAndroid Build Coastguard Worker        self._log_files[log_file_path] = log_file
70*6777b538SAndroid Build Coastguard Worker        return log_file
71*6777b538SAndroid Build Coastguard Worker
72*6777b538SAndroid Build Coastguard Worker    def stop(self):
73*6777b538SAndroid Build Coastguard Worker        """Stop all active logging instances."""
74*6777b538SAndroid Build Coastguard Worker
75*6777b538SAndroid Build Coastguard Worker        for proc in self._log_procs:
76*6777b538SAndroid Build Coastguard Worker            proc.kill()
77*6777b538SAndroid Build Coastguard Worker        for log in self._log_files.values():
78*6777b538SAndroid Build Coastguard Worker            log.close()
79*6777b538SAndroid Build Coastguard Worker
80*6777b538SAndroid Build Coastguard Worker    def __exit__(self, exc_type, exc_value, traceback):
81*6777b538SAndroid Build Coastguard Worker        self.stop()
82*6777b538SAndroid Build Coastguard Worker        if self._scoped_ffx_log:
83*6777b538SAndroid Build Coastguard Worker            self._scoped_ffx_log.__exit__(exc_type, exc_value, traceback)
84*6777b538SAndroid Build Coastguard Worker            if not running_unattended():
85*6777b538SAndroid Build Coastguard Worker                stop_ffx_daemon()
86*6777b538SAndroid Build Coastguard Worker
87*6777b538SAndroid Build Coastguard Worker
88*6777b538SAndroid Build Coastguard Workerdef start_system_log(log_manager: LogManager,
89*6777b538SAndroid Build Coastguard Worker                     log_to_stdout: bool,
90*6777b538SAndroid Build Coastguard Worker                     pkg_paths: Optional[Iterable[str]] = None,
91*6777b538SAndroid Build Coastguard Worker                     log_args: Optional[Iterable[str]] = None,
92*6777b538SAndroid Build Coastguard Worker                     target_id: Optional[str] = None) -> None:
93*6777b538SAndroid Build Coastguard Worker    """
94*6777b538SAndroid Build Coastguard Worker    Start system logging.
95*6777b538SAndroid Build Coastguard Worker
96*6777b538SAndroid Build Coastguard Worker    Args:
97*6777b538SAndroid Build Coastguard Worker        log_manager: A LogManager class that manages the log file and process.
98*6777b538SAndroid Build Coastguard Worker        log_to_stdout: If set to True, print logs directly to stdout.
99*6777b538SAndroid Build Coastguard Worker        pkg_paths: Path to the packages
100*6777b538SAndroid Build Coastguard Worker        log_args: Arguments forwarded to `ffx log` command.
101*6777b538SAndroid Build Coastguard Worker        target_id: Specify a target to use.
102*6777b538SAndroid Build Coastguard Worker    """
103*6777b538SAndroid Build Coastguard Worker
104*6777b538SAndroid Build Coastguard Worker    if not log_manager.is_logging_enabled() and not log_to_stdout:
105*6777b538SAndroid Build Coastguard Worker        return
106*6777b538SAndroid Build Coastguard Worker    symbol_paths = None
107*6777b538SAndroid Build Coastguard Worker    if pkg_paths:
108*6777b538SAndroid Build Coastguard Worker        symbol_paths = []
109*6777b538SAndroid Build Coastguard Worker
110*6777b538SAndroid Build Coastguard Worker        # Locate debug symbols for each package.
111*6777b538SAndroid Build Coastguard Worker        for pkg_path in pkg_paths:
112*6777b538SAndroid Build Coastguard Worker            assert os.path.isfile(pkg_path), '%s does not exist' % pkg_path
113*6777b538SAndroid Build Coastguard Worker            symbol_paths.append(
114*6777b538SAndroid Build Coastguard Worker                os.path.join(os.path.dirname(pkg_path), 'ids.txt'))
115*6777b538SAndroid Build Coastguard Worker
116*6777b538SAndroid Build Coastguard Worker    if log_to_stdout:
117*6777b538SAndroid Build Coastguard Worker        system_log = sys.stdout
118*6777b538SAndroid Build Coastguard Worker    else:
119*6777b538SAndroid Build Coastguard Worker        system_log = log_manager.open_log_file('system_log')
120*6777b538SAndroid Build Coastguard Worker    log_cmd = ['log', '--symbolize', 'off', '--no-color']
121*6777b538SAndroid Build Coastguard Worker    if log_args:
122*6777b538SAndroid Build Coastguard Worker        log_cmd.extend(log_args)
123*6777b538SAndroid Build Coastguard Worker    if symbol_paths:
124*6777b538SAndroid Build Coastguard Worker        log_proc = run_continuous_ffx_command(log_cmd,
125*6777b538SAndroid Build Coastguard Worker                                              target_id,
126*6777b538SAndroid Build Coastguard Worker                                              stdout=subprocess.PIPE)
127*6777b538SAndroid Build Coastguard Worker        log_manager.add_log_process(log_proc)
128*6777b538SAndroid Build Coastguard Worker        log_manager.add_log_process(
129*6777b538SAndroid Build Coastguard Worker            run_symbolizer(symbol_paths, log_proc.stdout, system_log))
130*6777b538SAndroid Build Coastguard Worker    else:
131*6777b538SAndroid Build Coastguard Worker        log_manager.add_log_process(
132*6777b538SAndroid Build Coastguard Worker            run_continuous_ffx_command(log_cmd, target_id, stdout=system_log))
133*6777b538SAndroid Build Coastguard Worker
134*6777b538SAndroid Build Coastguard Worker
135*6777b538SAndroid Build Coastguard Workerdef main():
136*6777b538SAndroid Build Coastguard Worker    """Stand-alone function for fetching system logs and print to terminal.
137*6777b538SAndroid Build Coastguard Worker    Runs until the process is killed or interrupted (i.e. user presses CTRL-C).
138*6777b538SAndroid Build Coastguard Worker    """
139*6777b538SAndroid Build Coastguard Worker
140*6777b538SAndroid Build Coastguard Worker    catch_sigterm()
141*6777b538SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser()
142*6777b538SAndroid Build Coastguard Worker    register_common_args(parser)
143*6777b538SAndroid Build Coastguard Worker    register_device_args(parser)
144*6777b538SAndroid Build Coastguard Worker    parser.add_argument('--packages',
145*6777b538SAndroid Build Coastguard Worker                        action='append',
146*6777b538SAndroid Build Coastguard Worker                        help='Name of the packages to symbolize.')
147*6777b538SAndroid Build Coastguard Worker    manager_args, system_log_args = parser.parse_known_args()
148*6777b538SAndroid Build Coastguard Worker    if manager_args.packages and not manager_args.out_dir:
149*6777b538SAndroid Build Coastguard Worker        raise ValueError('--out-dir must be specified to symbolize packages.')
150*6777b538SAndroid Build Coastguard Worker    package_paths = []
151*6777b538SAndroid Build Coastguard Worker    if manager_args.packages:
152*6777b538SAndroid Build Coastguard Worker        for package in manager_args.packages:
153*6777b538SAndroid Build Coastguard Worker            package_paths.extend(
154*6777b538SAndroid Build Coastguard Worker                read_package_paths(manager_args.out_dir, package))
155*6777b538SAndroid Build Coastguard Worker    with LogManager(None) as log_manager:
156*6777b538SAndroid Build Coastguard Worker        start_system_log(log_manager, True, package_paths, system_log_args,
157*6777b538SAndroid Build Coastguard Worker                         manager_args.target_id)
158*6777b538SAndroid Build Coastguard Worker        wait_for_sigterm()
159*6777b538SAndroid Build Coastguard Worker
160*6777b538SAndroid Build Coastguard Worker
161*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__':
162*6777b538SAndroid Build Coastguard Worker    sys.exit(main())
163