| 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 | |
| 11 | import os |
| 12 | import logging |
| 13 | import time |
| 14 | import subprocess |
| 15 | |
| 16 | from qemu_test.linuxkernel import LinuxKernelTest |
| 17 | |
| 18 | class ReplayKernelBase(LinuxKernelTest): |
| 19 | """ |
| 20 | Boots a Linux kernel in record mode and checks that the console |
| 21 | is operational and the kernel command line is properly passed |
| 22 | from QEMU to the kernel. |
| 23 | Then replays the same scenario and verifies, that QEMU correctly |
| 24 | terminates. |
| 25 | """ |
| 26 | |
| 27 | timeout = 180 |
| 28 | REPLAY_KERNEL_COMMAND_LINE = 'printk.time=1 panic=-1 ' |
| 29 | |
| 30 | def run_vm(self, kernel_path, kernel_command_line, console_pattern, |
| 31 | record, shift, args, replay_path): |
| 32 | # icount requires TCG to be available |
| 33 | self.require_accelerator('tcg') |
| 34 | |
| 35 | start_time = time.time() |
| 36 | vm = self.get_vm(name='recording' if record else 'replay') |
| 37 | vm.set_console() |
| 38 | if record: |
| 39 | self.log.info('recording the execution...') |
| 40 | mode = 'record' |
| 41 | else: |
| 42 | self.log.info('replaying the execution...') |
| 43 | mode = 'replay' |
| 44 | vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % |
| 45 | (shift, mode, replay_path), |
| 46 | '-kernel', kernel_path, |
| 47 | '-append', kernel_command_line, |
| 48 | '-net', 'none', |
| 49 | '-no-reboot') |
| 50 | if args: |
| 51 | vm.add_args(*args) |
| 52 | vm.launch() |
| 53 | self.wait_for_console_pattern(console_pattern, vm) |
| 54 | if record: |
| 55 | vm.shutdown() |
| 56 | self.log.info('finished the recording with log size %s bytes' |
| 57 | % os.path.getsize(replay_path)) |
| 58 | self.run_replay_dump(replay_path) |
| 59 | self.log.info('successfully tested replay-dump.py') |
| 60 | else: |
| 61 | vm.wait() |
| 62 | self.log.info('successfully finished the replay') |
| 63 | elapsed = time.time() - start_time |
| 64 | self.log.info('elapsed time %.2f sec' % elapsed) |
| 65 | return elapsed |
| 66 | |
| 67 | def run_replay_dump(self, replay_path): |
| 68 | try: |
| 69 | subprocess.check_call(["./scripts/replay-dump.py", |
| 70 | "-f", replay_path], |
| 71 | stdout=subprocess.DEVNULL) |
| 72 | except subprocess.CalledProcessError: |
| 73 | self.fail('replay-dump.py failed') |
| 74 | |
| 75 | def run_rr(self, kernel_path, kernel_command_line, console_pattern, |
| 76 | shift=7, args=None): |
| 77 | replay_path = os.path.join(self.workdir, 'replay.bin') |
| 78 | t1 = self.run_vm(kernel_path, kernel_command_line, console_pattern, |
| 79 | True, shift, args, replay_path) |
| 80 | t2 = self.run_vm(kernel_path, kernel_command_line, console_pattern, |
| 81 | False, shift, args, replay_path) |
| 82 | self.log.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) |