1c342db35SBrad Bishop# 292b42cb3SPatrick Williams# Copyright OpenEmbedded Contributors 392b42cb3SPatrick Williams# 4c342db35SBrad Bishop# SPDX-License-Identifier: MIT 5c342db35SBrad Bishop# 6c342db35SBrad Bishop 7eb8dc403SDave Cobbleyimport os 8eb8dc403SDave Cobbleyimport sys 9c926e17cSAndrew Geisslerimport json 10eb8dc403SDave Cobbleyimport errno 11eb8dc403SDave Cobbleyimport datetime 12eb8dc403SDave Cobbleyimport itertools 13eb8dc403SDave Cobbleyfrom .commands import runCmd 14eb8dc403SDave Cobbley 15eb8dc403SDave Cobbleyclass BaseDumper(object): 16eb8dc403SDave Cobbley """ Base class to dump commands from host/target """ 17eb8dc403SDave Cobbley 18eb8dc403SDave Cobbley def __init__(self, cmds, parent_dir): 19eb8dc403SDave Cobbley self.cmds = [] 20eb8dc403SDave Cobbley # Some testing doesn't inherit testimage, so it is needed 21eb8dc403SDave Cobbley # to set some defaults. 22977dc1acSBrad Bishop self.parent_dir = parent_dir 235f35090dSAndrew Geissler self.dump_dir = parent_dir 24eb8dc403SDave Cobbley dft_cmds = """ top -bn1 25eb8dc403SDave Cobbley iostat -x -z -N -d -p ALL 20 2 26eb8dc403SDave Cobbley ps -ef 27eb8dc403SDave Cobbley free 28eb8dc403SDave Cobbley df 29eb8dc403SDave Cobbley memstat 30eb8dc403SDave Cobbley dmesg 31eb8dc403SDave Cobbley ip -s link 32eb8dc403SDave Cobbley netstat -an""" 33eb8dc403SDave Cobbley if not cmds: 34eb8dc403SDave Cobbley cmds = dft_cmds 35eb8dc403SDave Cobbley for cmd in cmds.split('\n'): 36eb8dc403SDave Cobbley cmd = cmd.lstrip() 37eb8dc403SDave Cobbley if not cmd or cmd[0] == '#': 38eb8dc403SDave Cobbley continue 39eb8dc403SDave Cobbley self.cmds.append(cmd) 40eb8dc403SDave Cobbley 41eb8dc403SDave Cobbley def create_dir(self, dir_suffix): 42eb8dc403SDave Cobbley dump_subdir = ("%s_%s" % ( 43eb8dc403SDave Cobbley datetime.datetime.now().strftime('%Y%m%d%H%M'), 44eb8dc403SDave Cobbley dir_suffix)) 45eb8dc403SDave Cobbley dump_dir = os.path.join(self.parent_dir, dump_subdir) 46eb8dc403SDave Cobbley try: 47eb8dc403SDave Cobbley os.makedirs(dump_dir) 48eb8dc403SDave Cobbley except OSError as err: 49eb8dc403SDave Cobbley if err.errno != errno.EEXIST: 50eb8dc403SDave Cobbley raise err 51eb8dc403SDave Cobbley self.dump_dir = dump_dir 52eb8dc403SDave Cobbley 535f35090dSAndrew Geissler def _construct_filename(self, command): 54*8f840685SAndrew Geissler if isinstance(self, TargetDumper): 55eb8dc403SDave Cobbley prefix = "target" 56c926e17cSAndrew Geissler elif isinstance(self, MonitorDumper): 57c926e17cSAndrew Geissler prefix = "qmp" 58eb8dc403SDave Cobbley else: 59eb8dc403SDave Cobbley prefix = "unknown" 60eb8dc403SDave Cobbley for i in itertools.count(): 61eb8dc403SDave Cobbley filename = "%s_%02d_%s" % (prefix, i, command) 62eb8dc403SDave Cobbley fullname = os.path.join(self.dump_dir, filename) 63eb8dc403SDave Cobbley if not os.path.exists(fullname): 64eb8dc403SDave Cobbley break 655f35090dSAndrew Geissler return fullname 665f35090dSAndrew Geissler 675f35090dSAndrew Geissler def _write_dump(self, command, output): 685f35090dSAndrew Geissler fullname = self._construct_filename(command) 697e0e3c0cSAndrew Geissler os.makedirs(os.path.dirname(fullname), exist_ok=True) 70c926e17cSAndrew Geissler if isinstance(self, MonitorDumper): 71c926e17cSAndrew Geissler with open(fullname, 'w') as json_file: 72c926e17cSAndrew Geissler json.dump(output, json_file, indent=4) 73c926e17cSAndrew Geissler else: 74eb8dc403SDave Cobbley with open(fullname, 'w') as dump_file: 75eb8dc403SDave Cobbley dump_file.write(output) 76eb8dc403SDave Cobbley 77eb8dc403SDave Cobbleyclass TargetDumper(BaseDumper): 786aa7eec5SAndrew Geissler """ Class to get dumps from target, it only works with QemuRunner. 796aa7eec5SAndrew Geissler Will give up permanently after 5 errors from running commands over 806aa7eec5SAndrew Geissler serial console. This helps to end testing when target is really dead, hanging 816aa7eec5SAndrew Geissler or unresponsive. 826aa7eec5SAndrew Geissler """ 83eb8dc403SDave Cobbley 84eb8dc403SDave Cobbley def __init__(self, cmds, parent_dir, runner): 85eb8dc403SDave Cobbley super(TargetDumper, self).__init__(cmds, parent_dir) 86eb8dc403SDave Cobbley self.runner = runner 876aa7eec5SAndrew Geissler self.errors = 0 88eb8dc403SDave Cobbley 89eb8dc403SDave Cobbley def dump_target(self, dump_dir=""): 906aa7eec5SAndrew Geissler if self.errors >= 5: 916aa7eec5SAndrew Geissler print("Too many errors when dumping data from target, assuming it is dead! Will not dump data anymore!") 926aa7eec5SAndrew Geissler return 93eb8dc403SDave Cobbley if dump_dir: 94eb8dc403SDave Cobbley self.dump_dir = dump_dir 95eb8dc403SDave Cobbley for cmd in self.cmds: 96eb8dc403SDave Cobbley # We can continue with the testing if serial commands fail 97eb8dc403SDave Cobbley try: 98eb8dc403SDave Cobbley (status, output) = self.runner.run_serial(cmd) 996aa7eec5SAndrew Geissler if status == 0: 1006aa7eec5SAndrew Geissler self.errors = self.errors + 1 101eb8dc403SDave Cobbley self._write_dump(cmd.split()[0], output) 102eb8dc403SDave Cobbley except: 1036aa7eec5SAndrew Geissler self.errors = self.errors + 1 104eb8dc403SDave Cobbley print("Tried to dump info from target but " 105eb8dc403SDave Cobbley "serial console failed") 106c926e17cSAndrew Geissler print("Failed CMD: %s" % (cmd)) 107c926e17cSAndrew Geissler 108c926e17cSAndrew Geisslerclass MonitorDumper(BaseDumper): 1096aa7eec5SAndrew Geissler """ Class to get dumps via the Qemu Monitor, it only works with QemuRunner 1106aa7eec5SAndrew Geissler Will stop completely if there are more than 5 errors when dumping monitor data. 1116aa7eec5SAndrew Geissler This helps to end testing when target is really dead, hanging or unresponsive. 1126aa7eec5SAndrew Geissler """ 113c926e17cSAndrew Geissler 114c926e17cSAndrew Geissler def __init__(self, cmds, parent_dir, runner): 115c926e17cSAndrew Geissler super(MonitorDumper, self).__init__(cmds, parent_dir) 116c926e17cSAndrew Geissler self.runner = runner 1176aa7eec5SAndrew Geissler self.errors = 0 118c926e17cSAndrew Geissler 119c926e17cSAndrew Geissler def dump_monitor(self, dump_dir=""): 120c926e17cSAndrew Geissler if self.runner is None: 121c926e17cSAndrew Geissler return 122c926e17cSAndrew Geissler if dump_dir: 123c926e17cSAndrew Geissler self.dump_dir = dump_dir 1246aa7eec5SAndrew Geissler if self.errors >= 5: 1256aa7eec5SAndrew Geissler print("Too many errors when dumping data from qemu monitor, assuming it is dead! Will not dump data anymore!") 1266aa7eec5SAndrew Geissler return 127c926e17cSAndrew Geissler for cmd in self.cmds: 1285f35090dSAndrew Geissler cmd_name = cmd.split()[0] 129c926e17cSAndrew Geissler try: 1305f35090dSAndrew Geissler if len(cmd.split()) > 1: 1315f35090dSAndrew Geissler cmd_args = cmd.split()[1] 1325f35090dSAndrew Geissler if "%s" in cmd_args: 1335f35090dSAndrew Geissler filename = self._construct_filename(cmd_name) 1345f35090dSAndrew Geissler cmd_data = json.loads(cmd_args % (filename)) 1355f35090dSAndrew Geissler output = self.runner.run_monitor(cmd_name, cmd_data) 1365f35090dSAndrew Geissler else: 1375f35090dSAndrew Geissler output = self.runner.run_monitor(cmd_name) 1385f35090dSAndrew Geissler self._write_dump(cmd_name, output) 1395f35090dSAndrew Geissler except Exception as e: 1406aa7eec5SAndrew Geissler self.errors = self.errors + 1 141595f6308SAndrew Geissler print("Failed to dump QMP CMD: %s with\nException: %s" % (cmd_name, e)) 142