1# Record/replay test that boots a Linux kernel 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 11import os 12import lzma 13import shutil 14import logging 15import time 16import subprocess 17 18from avocado import skip 19from avocado import skipUnless 20from avocado import skipUnless 21from avocado_qemu import wait_for_console_pattern 22from avocado.utils import archive 23from avocado.utils import process 24from boot_linux_console import LinuxKernelTest 25 26class ReplayKernelBase(LinuxKernelTest): 27 """ 28 Boots a Linux kernel in record mode and checks that the console 29 is operational and the kernel command line is properly passed 30 from QEMU to the kernel. 31 Then replays the same scenario and verifies, that QEMU correctly 32 terminates. 33 """ 34 35 timeout = 180 36 KERNEL_COMMON_COMMAND_LINE = 'printk.time=1 panic=-1 ' 37 38 def run_vm(self, kernel_path, kernel_command_line, console_pattern, 39 record, shift, args, replay_path): 40 # icount requires TCG to be available 41 self.require_accelerator('tcg') 42 43 logger = logging.getLogger('replay') 44 start_time = time.time() 45 vm = self.get_vm() 46 vm.set_console() 47 if record: 48 logger.info('recording the execution...') 49 mode = 'record' 50 else: 51 logger.info('replaying the execution...') 52 mode = 'replay' 53 vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % 54 (shift, mode, replay_path), 55 '-kernel', kernel_path, 56 '-append', kernel_command_line, 57 '-net', 'none', 58 '-no-reboot') 59 if args: 60 vm.add_args(*args) 61 vm.launch() 62 self.wait_for_console_pattern(console_pattern, vm) 63 if record: 64 vm.shutdown() 65 logger.info('finished the recording with log size %s bytes' 66 % os.path.getsize(replay_path)) 67 self.run_replay_dump(replay_path) 68 logger.info('successfully tested replay-dump.py') 69 else: 70 vm.wait() 71 logger.info('successfully finished the replay') 72 elapsed = time.time() - start_time 73 logger.info('elapsed time %.2f sec' % elapsed) 74 return elapsed 75 76 def run_replay_dump(self, replay_path): 77 try: 78 subprocess.check_call(["./scripts/replay-dump.py", 79 "-f", replay_path], 80 stdout=subprocess.DEVNULL) 81 except subprocess.CalledProcessError: 82 self.fail('replay-dump.py failed') 83 84 def run_rr(self, kernel_path, kernel_command_line, console_pattern, 85 shift=7, args=None): 86 replay_path = os.path.join(self.workdir, 'replay.bin') 87 t1 = self.run_vm(kernel_path, kernel_command_line, console_pattern, 88 True, shift, args, replay_path) 89 t2 = self.run_vm(kernel_path, kernel_command_line, console_pattern, 90 False, shift, args, replay_path) 91 logger = logging.getLogger('replay') 92 logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) 93 94class ReplayKernelNormal(ReplayKernelBase): 95 96 def test_i386_pc(self): 97 """ 98 :avocado: tags=arch:i386 99 :avocado: tags=machine:pc 100 """ 101 kernel_url = ('https://storage.tuxboot.com/20230331/i386/bzImage') 102 kernel_hash = 'a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956' 103 kernel_path = self.fetch_asset(kernel_url, 104 asset_hash=kernel_hash, 105 algorithm = "sha256") 106 107 kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' 108 console_pattern = 'VFS: Cannot open root device' 109 110 self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) 111