blob: 01fa968996cdec6aaf1a70ac7fb0f48537ec2af3 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"context"
"os"
"path/filepath"
"testing"
"go.fuchsia.dev/fuchsia/tools/emulator"
"go.fuchsia.dev/fuchsia/tools/emulator/emulatortest"
)
var common_args = []string{
"kernel.lockup-detector.heartbeat-period-ms=50",
// By default, disable all of the thresholds (both fatal and non-fatal). During each of the
// tests, we will re-enable only the specific threshold we want to enable.
"kernel.lockup-detector.heartbeat-age-threshold-ms=0",
"kernel.lockup-detector.critical-section-threshold-ms=0",
"kernel.lockup-detector.heartbeat-age-fatal-threshold-ms=0",
"kernel.lockup-detector.critical-section-fatal-threshold-ms=0",
// Use a huge value to make sure we don't time out.
"kernel.lockup-detector.diagnostic-query-timeout-ms=1000000000",
// Upon booting run "k", which will print a usage message. By waiting for the usage
// message, we can be sure the system has booted and is ready to accept "k" commands.
"zircon.autorun.boot=/boot/bin/sh+-c+k",
}
func testCommon(t *testing.T, ctx context.Context, kernel_arg string) (*emulatortest.Instance, emulator.Arch) {
exDir := execDir(t)
distro := emulatortest.UnpackFrom(t, filepath.Join(exDir, "test_data"), emulator.DistributionParams{
Emulator: emulator.Qemu,
})
arch := distro.TargetCPU()
device := emulator.DefaultVirtualDevice(string(arch))
device.KernelArgs = append(device.KernelArgs, common_args...)
// Enable the lockup detector using the specified kernel_arg.
device.KernelArgs = append(device.KernelArgs, kernel_arg)
i := distro.CreateContext(ctx, device)
// Boot.
i.Start()
// Wait for the system to finish booting.
i.WaitForLogMessage("usage: k <command>")
return i, arch
}
func TestKernelLockupDetectorCriticalSection(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
i, _ := testCommon(t, ctx, "kernel.lockup-detector.critical-section-threshold-ms=200")
// Force a lockup and see that an OOPS is emitted.
i.RunCommand("k lockup test_critical_section 1 600")
i.WaitForLogMessage("locking up CPU")
i.WaitForLogMessage("ZIRCON KERNEL OOPS")
i.WaitForLogMessage("CPU-1 in critical section for")
i.WaitForLogMessage("done")
}
func TestKernelLockupDetectorHeartbeat(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
i, _ := testCommon(t, ctx, "kernel.lockup-detector.heartbeat-age-threshold-ms=200")
// Force a lockup and see that a heartbeat OOPS is emitted.
i.RunCommand("k lockup test_spinlock 1 1000")
i.WaitForLogMessage("locking up CPU")
i.WaitForLogMessage("ZIRCON KERNEL OOPS")
i.WaitForLogMessage("no heartbeat from CPU-1")
// See that the CPU's run queue is printed and contains the thread named "lockup-test", the
// one responsible for the lockup.
i.WaitForLogMessage("lockup-test")
i.WaitForLogMessage("done")
}
func testLockupWithCommand(t *testing.T, i *emulatortest.Instance, arch emulator.Arch, command string) {
// Force a lockup using the given command, and expect a reboot to be the
// result. First look for evidence that the lockedup detector printed
// the context of the unresponsive CPU. Then look for the "welcome to
// Zircon" message as our indication that the system has rebooted.
i.RunCommand(command)
i.WaitForLogMessage("context follows")
i.WaitForLogMessage("{{{bt:0:")
i.WaitForLogMessage("welcome to Zircon")
// TODO(https://fxbug.dev/42161734): Our emulated x64 environment does not currently
// support crashlogs, however our ARM64 environment does. If we are on ARM,
// check the crashlog startup banner to make certain that the system didn't
// just reboot, but that it did so because of a SOFTWARE WATCHDOG event.
if arch == emulator.Arm64 {
i.WaitForLogMessage("SW Reason \"SW WATCHDOG\"")
}
// Wait for the system to finish booting (again).
i.WaitForLogMessage("usage: k <command>")
}
func TestKernelLockupDetectorFatalCriticalSection(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
i, arch := testCommon(t, ctx, "kernel.lockup-detector.critical-section-fatal-threshold-ms=500")
testLockupWithCommand(t, i, arch, "k lockup test_critical_section 1 1000")
}
func TestKernelLockupDetectorFatalHeartbeat(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
i, arch := testCommon(t, ctx, "kernel.lockup-detector.heartbeat-age-fatal-threshold-ms=500")
testLockupWithCommand(t, i, arch, "k lockup test_spinlock 1 1000")
}
func execDir(t *testing.T) string {
ex, err := os.Executable()
if err != nil {
t.Fatal(err)
}
return filepath.Dir(ex)
}