xref: /openbmc/openbmc/poky/meta/lib/oeqa/utils/dump.py (revision 8f840685)
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