# # Copyright OpenEmbedded Contributors # # SPDX-License-Identifier: MIT # import os import sys import json import errno import datetime import itertools from .commands import runCmd class BaseDumper(object): """ Base class to dump commands from host/target """ def __init__(self, cmds, parent_dir): self.cmds = [] # Some testing doesn't inherit testimage, so it is needed # to set some defaults. self.parent_dir = parent_dir self.dump_dir = parent_dir dft_cmds = """ top -bn1 iostat -x -z -N -d -p ALL 20 2 ps -ef free df memstat dmesg ip -s link netstat -an""" if not cmds: cmds = dft_cmds for cmd in cmds.split('\n'): cmd = cmd.lstrip() if not cmd or cmd[0] == '#': continue self.cmds.append(cmd) def create_dir(self, dir_suffix): dump_subdir = ("%s_%s" % ( datetime.datetime.now().strftime('%Y%m%d%H%M'), dir_suffix)) dump_dir = os.path.join(self.parent_dir, dump_subdir) try: os.makedirs(dump_dir) except OSError as err: if err.errno != errno.EEXIST: raise err self.dump_dir = dump_dir def _construct_filename(self, command): if isinstance(self, TargetDumper): prefix = "target" elif isinstance(self, MonitorDumper): prefix = "qmp" else: prefix = "unknown" for i in itertools.count(): filename = "%s_%02d_%s" % (prefix, i, command) fullname = os.path.join(self.dump_dir, filename) if not os.path.exists(fullname): break return fullname def _write_dump(self, command, output): fullname = self._construct_filename(command) os.makedirs(os.path.dirname(fullname), exist_ok=True) if isinstance(self, MonitorDumper): with open(fullname, 'w') as json_file: json.dump(output, json_file, indent=4) else: with open(fullname, 'w') as dump_file: dump_file.write(output) class TargetDumper(BaseDumper): """ Class to get dumps from target, it only works with QemuRunner. Will give up permanently after 5 errors from running commands over serial console. This helps to end testing when target is really dead, hanging or unresponsive. """ def __init__(self, cmds, parent_dir, runner): super(TargetDumper, self).__init__(cmds, parent_dir) self.runner = runner self.errors = 0 def dump_target(self, dump_dir=""): if self.errors >= 5: print("Too many errors when dumping data from target, assuming it is dead! Will not dump data anymore!") return if dump_dir: self.dump_dir = dump_dir for cmd in self.cmds: # We can continue with the testing if serial commands fail try: (status, output) = self.runner.run_serial(cmd) if status == 0: self.errors = self.errors + 1 self._write_dump(cmd.split()[0], output) except: self.errors = self.errors + 1 print("Tried to dump info from target but " "serial console failed") print("Failed CMD: %s" % (cmd)) class MonitorDumper(BaseDumper): """ Class to get dumps via the Qemu Monitor, it only works with QemuRunner Will stop completely if there are more than 5 errors when dumping monitor data. This helps to end testing when target is really dead, hanging or unresponsive. """ def __init__(self, cmds, parent_dir, runner): super(MonitorDumper, self).__init__(cmds, parent_dir) self.runner = runner self.errors = 0 def dump_monitor(self, dump_dir=""): if self.runner is None: return if dump_dir: self.dump_dir = dump_dir if self.errors >= 5: print("Too many errors when dumping data from qemu monitor, assuming it is dead! Will not dump data anymore!") return for cmd in self.cmds: cmd_name = cmd.split()[0] try: if len(cmd.split()) > 1: cmd_args = cmd.split()[1] if "%s" in cmd_args: filename = self._construct_filename(cmd_name) cmd_data = json.loads(cmd_args % (filename)) output = self.runner.run_monitor(cmd_name, cmd_data) else: output = self.runner.run_monitor(cmd_name) self._write_dump(cmd_name, output) except Exception as e: self.errors = self.errors + 1 print("Failed to dump QMP CMD: %s with\nException: %s" % (cmd_name, e))