blob: a4c1fb6c1c58e5d15b40f0e5cc783ff47c27cd50 [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 (
"os"
"path/filepath"
"testing"
"go.fuchsia.dev/fuchsia/tools/emulator"
)
// Boots an instance, |crash_cmd|, waits for the system to reboot, prints the
// recovered crash report.
func testCommon(t *testing.T, crash_cmd string) *emulator.Instance {
exDir := execDir(t)
distro, err := emulator.UnpackFrom(filepath.Join(exDir, "test_data"), emulator.DistributionParams{
Emulator: emulator.Qemu,
})
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err = distro.Delete(); err != nil {
t.Error(err)
}
})
arch, err := distro.TargetCPU()
if err != nil {
t.Fatal(err)
}
if arch != emulator.Arm64 {
// TODO(maniscalco): Flesh out the qemu/x64 support for stowing/retrieving a
// crashlog.
t.Skipf("Skipping test. This test only supports arm64 targets.\n")
return nil
}
device := emulator.DefaultVirtualDevice(string(arch))
device.Hw.EnableKvm = false
// Be sure to reboot instead of halt so the newly booted kernel instance can retrieve
// the previous instance's crashlog.
//
// 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.
device.KernelArgs = append(device.KernelArgs, "kernel.halt-on-panic=false", "zircon.autorun.boot=/boot/bin/sh+-c+k")
i, err := distro.Create(device)
if err != nil {
t.Fatal(err)
}
// Boot.
if err = i.Start(); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err = i.Kill(); err != nil {
t.Error(err)
}
})
// Wait for the system to finish booting.
if err = i.WaitForLogMessage("usage: k <command>"); err != nil {
t.Fatal(err)
}
// Crash the kernel.
if err = i.RunCommand(crash_cmd); err != nil {
t.Fatal(err)
}
if err = i.WaitForLogMessage("ZIRCON KERNEL PANIC"); err != nil {
t.Fatal(err)
}
// Now that the kernel has panicked, it should reboot. Wait for it to come back up.
if err = i.WaitForLogMessage("welcome to Zircon"); err != nil {
t.Fatal(err)
}
if err = i.WaitForLogMessage("usage: k <command>"); err != nil {
t.Fatal(err)
}
// Early in boot, the system should have recovered the stowed crashlog and stored it in
// last-panic.txt. We're dumping that file using dd instead of cat because dd is part of
// the system image and cat is not.
if err = i.RunCommand("dd if=/boot/log/last-panic.txt"); err != nil {
t.Fatal(err)
}
// See that the crashlog looks reasonable.
if err = i.WaitForLogMessage("ZIRCON REBOOT REASON (KERNEL PANIC)"); err != nil {
t.Fatal(err)
}
return i
}
// See that the kernel stows a crashlog upon panicking.
func TestKernelCrashlog(t *testing.T) {
i := testCommon(t, "k crash")
// See that the crash report contains ESR and FAR.
//
// This is a regression test for fxbug.dev/52182.
if err := i.WaitForLogMessage("esr: 0x96000045"); err != nil {
t.Fatal(err)
}
if err := i.WaitForLogMessage("far: 0x1"); err != nil {
t.Fatal(err)
}
// And a backtrace and counters.
if err := i.WaitForLogMessage("BACKTRACE"); err != nil {
t.Fatal(err)
}
if err := i.WaitForLogMessage("{{{bt:0"); err != nil {
t.Fatal(err)
}
if err := i.WaitForLogMessage("counters: "); err != nil {
t.Fatal(err)
}
}
// See that when the kernel crashes because of an assert failure the crashlog contains the assert
// message.
func TestKernelCrashlogAssert(t *testing.T) {
i := testCommon(t, "k crash_assert")
// See that there's a backtrace, followed by some counters, and finally the assert
// message.
if err := i.WaitForLogMessage("BACKTRACE"); err != nil {
t.Fatal(err)
}
if err := i.WaitForLogMessage("{{{bt:0"); err != nil {
t.Fatal(err)
}
if err := i.WaitForLogMessage("counters: "); err != nil {
t.Fatal(err)
}
if err := i.WaitForLogMessage("panic buffer: "); err != nil {
t.Fatal(err)
}
if err := i.WaitForLogMessage("KERNEL PANIC"); err != nil {
t.Fatal(err)
}
if err := i.WaitForLogMessage("ASSERT FAILED"); err != nil {
t.Fatal(err)
}
if err := i.WaitForLogMessage("value 42"); err != nil {
t.Fatal(err)
}
}
func execDir(t *testing.T) string {
ex, err := os.Executable()
if err != nil {
t.Fatal(err)
return ""
}
return filepath.Dir(ex)
}