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