blob: d420b497f936998855ba7bd1f3c5e4ef12680faa [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
Patrick Williams92b42cb2022-09-03 06:53:57 -05002# Copyright OpenEmbedded Contributors
3#
Brad Bishopc342db32019-05-15 21:57:59 -04004# SPDX-License-Identifier: MIT
5#
6
Patrick Williamsc124f4f2015-09-15 14:41:29 -05007import os
8import sys
Andrew Geisslerc926e172021-05-07 16:11:35 -05009import json
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010import errno
11import datetime
12import itertools
Patrick Williamsc0f7c042017-02-23 20:41:17 -060013from .commands import runCmd
Patrick Williamsc124f4f2015-09-15 14:41:29 -050014
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015class BaseDumper(object):
16 """ Base class to dump commands from host/target """
17
18 def __init__(self, cmds, parent_dir):
19 self.cmds = []
Patrick Williamsf1e5d692016-03-30 15:21:19 -050020 # Some testing doesn't inherit testimage, so it is needed
21 # to set some defaults.
Brad Bishop977dc1a2019-02-06 16:01:43 -050022 self.parent_dir = parent_dir
Andrew Geissler5f350902021-07-23 13:09:54 -040023 self.dump_dir = parent_dir
Patrick Williamsf1e5d692016-03-30 15:21:19 -050024 dft_cmds = """ top -bn1
25 iostat -x -z -N -d -p ALL 20 2
26 ps -ef
27 free
28 df
29 memstat
30 dmesg
31 ip -s link
32 netstat -an"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -050033 if not cmds:
Patrick Williamsf1e5d692016-03-30 15:21:19 -050034 cmds = dft_cmds
Patrick Williamsc124f4f2015-09-15 14:41:29 -050035 for cmd in cmds.split('\n'):
36 cmd = cmd.lstrip()
37 if not cmd or cmd[0] == '#':
38 continue
39 self.cmds.append(cmd)
40
41 def create_dir(self, dir_suffix):
42 dump_subdir = ("%s_%s" % (
43 datetime.datetime.now().strftime('%Y%m%d%H%M'),
44 dir_suffix))
45 dump_dir = os.path.join(self.parent_dir, dump_subdir)
46 try:
47 os.makedirs(dump_dir)
48 except OSError as err:
49 if err.errno != errno.EEXIST:
50 raise err
51 self.dump_dir = dump_dir
52
Andrew Geissler5f350902021-07-23 13:09:54 -040053 def _construct_filename(self, command):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050054 if isinstance(self, HostDumper):
55 prefix = "host"
56 elif isinstance(self, TargetDumper):
57 prefix = "target"
Andrew Geisslerc926e172021-05-07 16:11:35 -050058 elif isinstance(self, MonitorDumper):
59 prefix = "qmp"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050060 else:
61 prefix = "unknown"
62 for i in itertools.count():
63 filename = "%s_%02d_%s" % (prefix, i, command)
64 fullname = os.path.join(self.dump_dir, filename)
65 if not os.path.exists(fullname):
66 break
Andrew Geissler5f350902021-07-23 13:09:54 -040067 return fullname
68
69 def _write_dump(self, command, output):
70 fullname = self._construct_filename(command)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +000071 os.makedirs(os.path.dirname(fullname), exist_ok=True)
Andrew Geisslerc926e172021-05-07 16:11:35 -050072 if isinstance(self, MonitorDumper):
73 with open(fullname, 'w') as json_file:
74 json.dump(output, json_file, indent=4)
75 else:
76 with open(fullname, 'w') as dump_file:
77 dump_file.write(output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050078
79class HostDumper(BaseDumper):
80 """ Class to get dumps from the host running the tests """
81
82 def __init__(self, cmds, parent_dir):
83 super(HostDumper, self).__init__(cmds, parent_dir)
84
85 def dump_host(self, dump_dir=""):
86 if dump_dir:
87 self.dump_dir = dump_dir
Andrew Geissler82c905d2020-04-13 13:39:40 -050088 env = os.environ.copy()
89 env['PATH'] = '/usr/sbin:/sbin:/usr/bin:/bin'
90 env['COLUMNS'] = '9999'
Patrick Williamsc124f4f2015-09-15 14:41:29 -050091 for cmd in self.cmds:
Andrew Geissler82c905d2020-04-13 13:39:40 -050092 result = runCmd(cmd, ignore_status=True, env=env)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050093 self._write_dump(cmd.split()[0], result.output)
94
Patrick Williamsc124f4f2015-09-15 14:41:29 -050095class TargetDumper(BaseDumper):
Andrew Geissler6aa7eec2023-03-03 12:41:14 -060096 """ Class to get dumps from target, it only works with QemuRunner.
97 Will give up permanently after 5 errors from running commands over
98 serial console. This helps to end testing when target is really dead, hanging
99 or unresponsive.
100 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500101
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500102 def __init__(self, cmds, parent_dir, runner):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500103 super(TargetDumper, self).__init__(cmds, parent_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500104 self.runner = runner
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600105 self.errors = 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500106
107 def dump_target(self, dump_dir=""):
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600108 if self.errors >= 5:
109 print("Too many errors when dumping data from target, assuming it is dead! Will not dump data anymore!")
110 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111 if dump_dir:
112 self.dump_dir = dump_dir
113 for cmd in self.cmds:
114 # We can continue with the testing if serial commands fail
115 try:
116 (status, output) = self.runner.run_serial(cmd)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600117 if status == 0:
118 self.errors = self.errors + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500119 self._write_dump(cmd.split()[0], output)
120 except:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600121 self.errors = self.errors + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500122 print("Tried to dump info from target but "
123 "serial console failed")
Andrew Geisslerc926e172021-05-07 16:11:35 -0500124 print("Failed CMD: %s" % (cmd))
125
126class MonitorDumper(BaseDumper):
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600127 """ Class to get dumps via the Qemu Monitor, it only works with QemuRunner
128 Will stop completely if there are more than 5 errors when dumping monitor data.
129 This helps to end testing when target is really dead, hanging or unresponsive.
130 """
Andrew Geisslerc926e172021-05-07 16:11:35 -0500131
132 def __init__(self, cmds, parent_dir, runner):
133 super(MonitorDumper, self).__init__(cmds, parent_dir)
134 self.runner = runner
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600135 self.errors = 0
Andrew Geisslerc926e172021-05-07 16:11:35 -0500136
137 def dump_monitor(self, dump_dir=""):
138 if self.runner is None:
139 return
140 if dump_dir:
141 self.dump_dir = dump_dir
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600142 if self.errors >= 5:
143 print("Too many errors when dumping data from qemu monitor, assuming it is dead! Will not dump data anymore!")
144 return
Andrew Geisslerc926e172021-05-07 16:11:35 -0500145 for cmd in self.cmds:
Andrew Geissler5f350902021-07-23 13:09:54 -0400146 cmd_name = cmd.split()[0]
Andrew Geisslerc926e172021-05-07 16:11:35 -0500147 try:
Andrew Geissler5f350902021-07-23 13:09:54 -0400148 if len(cmd.split()) > 1:
149 cmd_args = cmd.split()[1]
150 if "%s" in cmd_args:
151 filename = self._construct_filename(cmd_name)
152 cmd_data = json.loads(cmd_args % (filename))
153 output = self.runner.run_monitor(cmd_name, cmd_data)
154 else:
155 output = self.runner.run_monitor(cmd_name)
156 self._write_dump(cmd_name, output)
157 except Exception as e:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600158 self.errors = self.errors + 1
Andrew Geissler595f6302022-01-24 19:11:47 +0000159 print("Failed to dump QMP CMD: %s with\nException: %s" % (cmd_name, e))