Add new kernel_MemoryRamoop test
This test verify that /dev/pstore/console-ramoops is preserved correctly
after system reboot/kernel crash and also verify integrity of that log.
BUG=chromium:344659
TEST=Tested in peach_pit. Test passed.
Change-Id: I963d2d3e4a9a077975718100fe58632005239545
Reviewed-on: https://chromium-review.googlesource.com/189076
Reviewed-by: Grant Grundler <grundler@chromium.org>
Tested-by: Puthikorn Voravootivat <puthik@chromium.org>
Commit-Queue: Puthikorn Voravootivat <puthik@chromium.org>
diff --git a/server/site_tests/kernel_MemoryRamoop/control b/server/site_tests/kernel_MemoryRamoop/control
new file mode 100644
index 0000000..8511a78
--- /dev/null
+++ b/server/site_tests/kernel_MemoryRamoop/control
@@ -0,0 +1,21 @@
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+NAME = 'kernel_MemoryRamoop'
+AUTHOR = 'puthik'
+PURPOSE = 'Check that kernel log preserve correctly in RAM'
+TIME = 'SHORT'
+TEST_CLASS = 'kernel'
+TEST_TYPE = 'server'
+
+DOC = """
+This test verify that /dev/pstore/console-ramoops is preserved correctly
+after system reboot/kernel crash and also verify integrity of that log.
+"""
+
+def run_kernel_MemoryRamoop(machine):
+ job.run_test('kernel_MemoryRamoop', client_ip=machine)
+
+job.parallel_simple(run_kernel_MemoryRamoop, machines)
+
diff --git a/server/site_tests/kernel_MemoryRamoop/kernel_MemoryRamoop.py b/server/site_tests/kernel_MemoryRamoop/kernel_MemoryRamoop.py
new file mode 100644
index 0000000..12f4a25
--- /dev/null
+++ b/server/site_tests/kernel_MemoryRamoop/kernel_MemoryRamoop.py
@@ -0,0 +1,199 @@
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging, random, re, string, traceback
+from autotest_lib.client.common_lib import error
+from autotest_lib.server import autotest
+from autotest_lib.server import hosts
+from autotest_lib.server import test
+
+class kernel_MemoryRamoop(test.test):
+ """
+ This test verify that /dev/pstore/console-ramoops is preserved correctly
+ after system reboot/kernel crash and also verify that there is no memory
+ corrupt in that log.
+
+ There is also platform_KernelErrorPaths test that test kernel crash. But
+ this test focuses on verify that kernel create console-ramoops file
+ correctly and its content is not corrupt. Contrary to the other test
+ that test at the bigger scope, i.e. the whole error reporting mechanism.
+ """
+ version = 1
+
+ _RAMOOP_PATH = '/dev/pstore/console-ramoops'
+ _KMSG_PATH = '/dev/kmsg'
+ _LKDTM_PATH = '/sys/kernel/debug/provoke-crash/DIRECT'
+
+ # ramopp have size of 128K so we will generate about 100K of random message
+ _MSG_LINE_COUNT = 1000
+ _MSG_LINE_LENGTH = 80
+ _MSG_MAGIC = 'ramoop_test'
+
+
+ def run_once(self, client_ip):
+ """
+ Run the test.
+ """
+ if not client_ip:
+ error.TestError("Must provide client's IP address to test")
+
+ self._client = hosts.create_host(client_ip)
+ self._client_at = autotest.Autotest(self._client)
+
+ self._run_test(self._do_reboot, '.*Restarting system.$')
+ self._run_test(self._do_kernel_panic, '.*lkdtm:.*PANIC$')
+ self._run_test(self._do_kernel_bug, '.*lkdtm:.*BUG$')
+ self._run_test(self._do_reboot_with_suspend, '.*Restarting system.$')
+
+ def _run_test(self, test_function, sig_pattern):
+ """
+ Run the test using by write random message to kernel log. Then
+ restart/crash the kernel and then verify integrity of console-ramoop
+
+ @param test_function: fuction to call to reboot / crash DUT
+ @param sig_pattern: regex of kernel log message generate when reboot
+ or crash by test_function
+ """
+
+ msg = self._generate_random_msg()
+
+ for line in msg:
+ cmd = 'echo "%s" > %s' % (line, self._KMSG_PATH)
+ self._client.run(cmd)
+
+ test_function()
+
+ cmd = 'cat %s' % self._RAMOOP_PATH
+ ramoop = self._client.run(cmd).stdout
+
+ self._verify_random_msg(ramoop, msg, sig_pattern)
+
+ def _do_reboot(self):
+ """
+ Reboot host machine
+ """
+ logging.info('Server: reboot client')
+ try:
+ self._client.reboot()
+ except error.AutoservRebootError as e:
+ raise error.TestFail('%s.\nTest failed with error %s' % (
+ traceback.format_exc(), str(e)))
+
+ def _do_reboot_with_suspend(self):
+ """
+ Reboot host machine after suspend once
+ """
+ self._client_at.run_test('power_Resume')
+
+ logging.info('Server: reboot client')
+ try:
+ self._client.reboot()
+ except error.AutoservRebootError as e:
+ raise error.TestFail('%s.\nTest failed with error %s' % (
+ traceback.format_exc(), str(e)))
+
+ def _do_kernel_panic(self):
+ """
+ Cause kernel panic using kernel dump test module
+ """
+ logging.info('Server: make client kernel panic')
+
+ cmd = 'echo PANIC > %s' % self._LKDTM_PATH
+ boot_id = self._client.get_boot_id()
+ self._client.run(cmd, ignore_status=True)
+ self._client.wait_for_restart(old_boot_id=boot_id)
+
+ def _do_kernel_bug(self):
+ """
+ Cause kernel bug using kernel dump test module
+ """
+ logging.info('Server: make client kernel bug')
+
+ cmd = 'echo BUG > %s' % self._LKDTM_PATH
+ boot_id = self._client.get_boot_id()
+ self._client.run(cmd, ignore_status=True)
+ self._client.wait_for_restart(old_boot_id=boot_id)
+
+
+ def _generate_random_msg(self):
+ """
+ Generate random message to put in kernel log
+ The message format is [magic string]: [3 digit id] [random char/digit]
+ """
+ valid_char = string.letters + string.digits
+ ret = []
+ for i in range(self._MSG_LINE_COUNT):
+ line = '%s: %03d ' % (self._MSG_MAGIC, i)
+ for _ in range(self._MSG_LINE_LENGTH):
+ line += random.choice(valid_char)
+ ret += [line]
+ return ret
+
+ def _verify_random_msg(self, ramoop, src_msg, sig_pattern):
+ """
+ Verify random message generated by _generate_random_msg
+
+ There are 3 things to verify.
+ 1. At least one random message exist. (earlier random message may be
+ cutoff because console-ramoops has limited size.
+ 2. Integrity of random message.
+ 3. Signature of reboot / kernel crash
+
+ @param ramoop: console-ramoops file in DUT
+ @param src_msg: message write to kernel log
+ @param sig_patterm: regex of kernel log to verify
+ """
+ # time stamp magic id random
+ pattern = str("\\[ *(\\d+\\.\\d+)\\] (%s: (\\d{3}) \\w{%d})$" %
+ (self._MSG_MAGIC, self._MSG_LINE_LENGTH))
+ matcher = re.compile(pattern)
+
+ logging.info('%s', pattern)
+
+ state = 'find_rand_msg'
+
+ last_timestamp = 0
+ for line in ramoop.split('\n'):
+ if state == 'find_rand_msg':
+ if not matcher.match(line):
+ continue
+ last_id = int(matcher.split(line)[3]) - 1
+ state = 'match_rand_pattern'
+ logging.info("%s: %s", state, line)
+
+ if state == 'match_rand_pattern':
+ if not matcher.match(line):
+ continue
+ components = matcher.split(line)
+ timestamp = float(components[1])
+ msg = components[2]
+ id = int(components[3])
+
+ if timestamp < last_timestamp:
+ logging.info("last_timestamp: %f, timestamp: %d",
+ last_timestamp, timestamp)
+ raise error.TestFail('Found reverse time stamp.')
+ last_timestamp = timestamp
+
+ if id != last_id + 1:
+ logging.info("last_id: %d, id: %d", last_id, id)
+ raise error.TestFail('Found missing message.')
+ last_id = id
+
+ if msg != src_msg[id]:
+ logging.info("cur_msg: '%s'", msg)
+ logging.info("src_msg: '%s'", src_msg[id])
+ raise error.TestFail('Found corrupt message.')
+
+ if id == self._MSG_LINE_COUNT - 1:
+ state = 'find_signature'
+
+ if state == 'find_signature':
+ if re.match(sig_pattern, line):
+ logging.info("%s: %s", state, line)
+ break
+
+ # error case: successful run must break in find_sigature state
+ else:
+ raise error.TestFail(str('Verify failed at state %s' % state))