xref: /openbmc/qemu/tests/avocado/replay_linux.py (revision 2eefd4fcec4b8fe41ceee2a8f00cdec1fe81b75c)
1 # Record/replay test that boots a complete Linux system via a cloud image
2 #
3 # Copyright (c) 2020 ISP RAS
4 #
5 # Author:
6 #  Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru>
7 #
8 # This work is licensed under the terms of the GNU GPL, version 2 or
9 # later.  See the COPYING file in the top-level directory.
10 
11 import os
12 import logging
13 import time
14 
15 from avocado import skipUnless
16 from avocado_qemu import BUILD_DIR
17 from avocado.utils import cloudinit
18 from avocado.utils import network
19 from avocado.utils import vmimage
20 from avocado.utils import datadrainer
21 from avocado.utils.path import find_command
22 from avocado_qemu.linuxtest import LinuxTest
23 
24 class ReplayLinux(LinuxTest):
25     """
26     Boots a Linux system, checking for a successful initialization
27     """
28 
29     timeout = 1800
30     chksum = None
31     hdd = 'ide-hd'
32     cd = 'ide-cd'
33     bus = 'ide'
34 
35     def setUp(self):
36         # LinuxTest does many replay-incompatible things, but includes
37         # useful methods. Do not setup LinuxTest here and just
38         # call some functions.
39         super(LinuxTest, self).setUp()
40         self._set_distro()
41         self.boot_path = self.download_boot()
42         self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0),
43                                                       self.name)
44         ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys()
45         self.cloudinit_path = self.prepare_cloudinit(ssh_pubkey)
46 
47     def vm_add_disk(self, vm, path, id, device):
48         bus_string = ''
49         if self.bus:
50             bus_string = ',bus=%s.%d' % (self.bus, id,)
51         vm.add_args('-drive', 'file=%s,snapshot=on,id=disk%s,if=none' % (path, id))
52         vm.add_args('-drive',
53             'driver=blkreplay,id=disk%s-rr,if=none,image=disk%s' % (id, id))
54         vm.add_args('-device',
55             '%s,drive=disk%s-rr%s' % (device, id, bus_string))
56 
57     def vm_add_cdrom(self, vm, path, id, device):
58         vm.add_args('-drive', 'file=%s,id=disk%s,if=none,media=cdrom' % (path, id))
59 
60     def launch_and_wait(self, record, args, shift):
61         self.require_netdev('user')
62         vm = self.get_vm()
63         vm.add_args('-smp', '1')
64         vm.add_args('-m', '1024')
65         vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22',
66                     '-device', 'virtio-net,netdev=vnet')
67         vm.add_args('-object', 'filter-replay,id=replay,netdev=vnet')
68         if args:
69             vm.add_args(*args)
70         self.vm_add_disk(vm, self.boot_path, 0, self.hdd)
71         self.vm_add_cdrom(vm, self.cloudinit_path, 1, self.cd)
72         logger = logging.getLogger('replay')
73         if record:
74             logger.info('recording the execution...')
75             mode = 'record'
76         else:
77             logger.info('replaying the execution...')
78             mode = 'replay'
79         replay_path = os.path.join(self.workdir, 'replay.bin')
80         vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' %
81                     (shift, mode, replay_path))
82 
83         start_time = time.time()
84 
85         vm.set_console()
86         vm.launch()
87         console_drainer = datadrainer.LineLogger(vm.console_socket.fileno(),
88                                     logger=self.log.getChild('console'),
89                                     stop_check=(lambda : not vm.is_running()))
90         console_drainer.start()
91         if record:
92             while not self.phone_server.instance_phoned_back:
93                 self.phone_server.handle_request()
94             vm.shutdown()
95             logger.info('finished the recording with log size %s bytes'
96                 % os.path.getsize(replay_path))
97             self.run_replay_dump(replay_path)
98             logger.info('successfully tested replay-dump.py')
99         else:
100             vm.event_wait('SHUTDOWN', self.timeout)
101             vm.wait()
102             logger.info('successfully finished the replay')
103         elapsed = time.time() - start_time
104         logger.info('elapsed time %.2f sec' % elapsed)
105         return elapsed
106 
107     def run_rr(self, args=None, shift=7):
108         t1 = self.launch_and_wait(True, args, shift)
109         t2 = self.launch_and_wait(False, args, shift)
110         logger = logging.getLogger('replay')
111         logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1))
112 
113     def run_replay_dump(self, replay_path):
114         try:
115             subprocess.check_call(["./scripts/replay-dump.py",
116                                    "-f", replay_path],
117                                   stdout=subprocess.DEVNULL)
118         except subprocess.CalledProcessError:
119             self.fail('replay-dump.py failed')
120 
121 @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
122 class ReplayLinuxX8664(ReplayLinux):
123     """
124     :avocado: tags=arch:x86_64
125     :avocado: tags=accel:tcg
126     """
127 
128     chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0'
129 
130     def test_pc_i440fx(self):
131         """
132         :avocado: tags=machine:pc
133         """
134         self.run_rr(shift=1)
135 
136     def test_pc_q35(self):
137         """
138         :avocado: tags=machine:q35
139         """
140         self.run_rr(shift=3)
141 
142 @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
143 class ReplayLinuxX8664Virtio(ReplayLinux):
144     """
145     :avocado: tags=arch:x86_64
146     :avocado: tags=virtio
147     :avocado: tags=accel:tcg
148     """
149 
150     hdd = 'virtio-blk-pci'
151     cd = 'virtio-blk-pci'
152     bus = None
153 
154     chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0'
155 
156     def test_pc_i440fx(self):
157         """
158         :avocado: tags=machine:pc
159         """
160         self.run_rr(shift=1)
161 
162     def test_pc_q35(self):
163         """
164         :avocado: tags=machine:q35
165         """
166         self.run_rr(shift=3)
167 
168 @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
169 class ReplayLinuxAarch64(ReplayLinux):
170     """
171     :avocado: tags=accel:tcg
172     :avocado: tags=arch:aarch64
173     :avocado: tags=machine:virt
174     :avocado: tags=cpu:max
175     """
176 
177     chksum = '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49'
178 
179     hdd = 'virtio-blk-device'
180     cd = 'virtio-blk-device'
181     bus = None
182 
183     def get_common_args(self):
184         return ('-bios',
185                 os.path.join(BUILD_DIR, 'pc-bios', 'edk2-aarch64-code.fd'),
186                 "-cpu", "max,lpa2=off",
187                 '-device', 'virtio-rng-pci,rng=rng0',
188                 '-object', 'rng-builtin,id=rng0')
189 
190     def test_virt_gicv2(self):
191         """
192         :avocado: tags=machine:gic-version=2
193         """
194 
195         self.run_rr(shift=3,
196                     args=(*self.get_common_args(),
197                           "-machine", "virt,gic-version=2"))
198 
199     def test_virt_gicv3(self):
200         """
201         :avocado: tags=machine:gic-version=3
202         """
203 
204         self.run_rr(shift=3,
205                     args=(*self.get_common_args(),
206                           "-machine", "virt,gic-version=3"))
207