xref: /OK3568_Linux_fs/yocto/poky/meta/lib/oeqa/utils/dump.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1#
2# SPDX-License-Identifier: MIT
3#
4
5import os
6import sys
7import json
8import errno
9import datetime
10import itertools
11from .commands import runCmd
12
13class BaseDumper(object):
14    """ Base class to dump commands from host/target """
15
16    def __init__(self, cmds, parent_dir):
17        self.cmds = []
18        # Some testing doesn't inherit testimage, so it is needed
19        # to set some defaults.
20        self.parent_dir = parent_dir
21        self.dump_dir = parent_dir
22        dft_cmds = """  top -bn1
23                        iostat -x -z -N -d -p ALL 20 2
24                        ps -ef
25                        free
26                        df
27                        memstat
28                        dmesg
29                        ip -s link
30                        netstat -an"""
31        if not cmds:
32            cmds = dft_cmds
33        for cmd in cmds.split('\n'):
34            cmd = cmd.lstrip()
35            if not cmd or cmd[0] == '#':
36                continue
37            self.cmds.append(cmd)
38
39    def create_dir(self, dir_suffix):
40        dump_subdir = ("%s_%s" % (
41                datetime.datetime.now().strftime('%Y%m%d%H%M'),
42                dir_suffix))
43        dump_dir = os.path.join(self.parent_dir, dump_subdir)
44        try:
45            os.makedirs(dump_dir)
46        except OSError as err:
47            if err.errno != errno.EEXIST:
48                raise err
49        self.dump_dir = dump_dir
50
51    def _construct_filename(self, command):
52        if isinstance(self, HostDumper):
53            prefix = "host"
54        elif isinstance(self, TargetDumper):
55            prefix = "target"
56        elif isinstance(self, MonitorDumper):
57            prefix = "qmp"
58        else:
59            prefix = "unknown"
60        for i in itertools.count():
61            filename = "%s_%02d_%s" % (prefix, i, command)
62            fullname = os.path.join(self.dump_dir, filename)
63            if not os.path.exists(fullname):
64                break
65        return fullname
66
67    def _write_dump(self, command, output):
68        fullname = self._construct_filename(command)
69        os.makedirs(os.path.dirname(fullname), exist_ok=True)
70        if isinstance(self, MonitorDumper):
71            with open(fullname, 'w') as json_file:
72                json.dump(output, json_file, indent=4)
73        else:
74            with open(fullname, 'w') as dump_file:
75                dump_file.write(output)
76
77class HostDumper(BaseDumper):
78    """ Class to get dumps from the host running the tests """
79
80    def __init__(self, cmds, parent_dir):
81        super(HostDumper, self).__init__(cmds, parent_dir)
82
83    def dump_host(self, dump_dir=""):
84        if dump_dir:
85            self.dump_dir = dump_dir
86        env = os.environ.copy()
87        env['PATH'] = '/usr/sbin:/sbin:/usr/bin:/bin'
88        env['COLUMNS'] = '9999'
89        for cmd in self.cmds:
90            result = runCmd(cmd, ignore_status=True, env=env)
91            self._write_dump(cmd.split()[0], result.output)
92
93class TargetDumper(BaseDumper):
94    """ Class to get dumps from target, it only works with QemuRunner.
95        Will give up permanently after 5 errors from running commands over
96        serial console. This helps to end testing when target is really dead, hanging
97        or unresponsive.
98    """
99
100    def __init__(self, cmds, parent_dir, runner):
101        super(TargetDumper, self).__init__(cmds, parent_dir)
102        self.runner = runner
103        self.errors = 0
104
105    def dump_target(self, dump_dir=""):
106        if self.errors >= 5:
107                print("Too many errors when dumping data from target, assuming it is dead! Will not dump data anymore!")
108                return
109        if dump_dir:
110            self.dump_dir = dump_dir
111        for cmd in self.cmds:
112            # We can continue with the testing if serial commands fail
113            try:
114                (status, output) = self.runner.run_serial(cmd)
115                if status == 0:
116                    self.errors = self.errors + 1
117                self._write_dump(cmd.split()[0], output)
118            except:
119                self.errors = self.errors + 1
120                print("Tried to dump info from target but "
121                        "serial console failed")
122                print("Failed CMD: %s" % (cmd))
123
124class MonitorDumper(BaseDumper):
125    """ Class to get dumps via the Qemu Monitor, it only works with QemuRunner
126        Will stop completely if there are more than 5 errors when dumping monitor data.
127        This helps to end testing when target is really dead, hanging or unresponsive.
128    """
129
130    def __init__(self, cmds, parent_dir, runner):
131        super(MonitorDumper, self).__init__(cmds, parent_dir)
132        self.runner = runner
133        self.errors = 0
134
135    def dump_monitor(self, dump_dir=""):
136        if self.runner is None:
137            return
138        if dump_dir:
139            self.dump_dir = dump_dir
140        if self.errors >= 5:
141                print("Too many errors when dumping data from qemu monitor, assuming it is dead! Will not dump data anymore!")
142                return
143        for cmd in self.cmds:
144            cmd_name = cmd.split()[0]
145            try:
146                if len(cmd.split()) > 1:
147                    cmd_args = cmd.split()[1]
148                    if "%s" in cmd_args:
149                        filename = self._construct_filename(cmd_name)
150                    cmd_data = json.loads(cmd_args % (filename))
151                    output = self.runner.run_monitor(cmd_name, cmd_data)
152                else:
153                    output = self.runner.run_monitor(cmd_name)
154                self._write_dump(cmd_name, output)
155            except Exception as e:
156                self.errors = self.errors + 1
157                print("Failed to dump QMP CMD: %s with\nException: %s" % (cmd_name, e))
158