blob: 6c5b3b10a97c20c679bfe4e06b024beb4fcb5315 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/crashlog.h>
#include <lib/crashlog/panic_buffer.h>
#include <lib/unittest/unittest.h>
#include <ktl/string_view.h>
#include <ktl/unique_ptr.h>
#include <ktl/enforce.h>
extern FILE gSerialFile;
namespace {
constexpr size_t kLargeBufferSize = 8 * 1024;
constexpr size_t kTooSmallBufferSize = 64;
bool NoCrashTest() {
BEGIN_TEST;
fbl::AllocChecker ac;
auto buffer = ktl::make_unique<char[]>(&ac, kLargeBufferSize);
ASSERT_TRUE(ac.check());
// NoCrash should currently produce no crash payload at all.
size_t len = crashlog_to_string({buffer.get(), kLargeBufferSize}, ZirconCrashReason::NoCrash);
EXPECT_EQ(0u, len);
END_TEST;
}
bool OomRootJobTest() {
BEGIN_TEST;
fbl::AllocChecker ac;
auto buffer = ktl::make_unique<char[]>(&ac, kLargeBufferSize);
ASSERT_TRUE(ac.check());
// OOM and UserspaceRootJobTermination reasons should produce all of the
// sections, except for the debugging context and panic buffer.
constexpr ktl::array kReasons = {
ZirconCrashReason::Oom,
ZirconCrashReason::UserspaceRootJobTermination,
};
for (auto reason : kReasons) {
memset(buffer.get(), 0, kLargeBufferSize);
size_t len = crashlog_to_string({buffer.get(), kLargeBufferSize}, reason);
ASSERT_LE(len, kLargeBufferSize);
ASSERT_GT(len, 0u);
ktl::string_view text{buffer.get(), len};
// Banner.
EXPECT_TRUE(text.find("ZIRCON REBOOT REASON"sv) != ktl::string_view::npos);
EXPECT_TRUE(text.find("UPTIME"sv) != ktl::string_view::npos);
EXPECT_TRUE(text.find("VERSION"sv) != ktl::string_view::npos);
// No debug info.
EXPECT_FALSE(text.find("{{{reset}}}"sv) != ktl::string_view::npos);
EXPECT_FALSE(text.find("REGISTERS"sv) != ktl::string_view::npos);
EXPECT_FALSE(text.find("BACKTRACE"sv) != ktl::string_view::npos);
// Critical counters.
EXPECT_TRUE(text.find("counters: "sv) != ktl::string_view::npos);
// No panic buffer.
EXPECT_FALSE(text.find("panic buffer: "sv) != ktl::string_view::npos);
// Debug log.
EXPECT_TRUE(text.find("--- BEGIN DLOG DUMP ---"sv) != ktl::string_view::npos);
EXPECT_TRUE(text.find("--- END DLOG DUMP ---"sv) != ktl::string_view::npos);
}
END_TEST;
}
bool PanicWdtTest() {
BEGIN_TEST;
fbl::AllocChecker ac;
auto buffer = ktl::make_unique<char[]>(&ac, kLargeBufferSize);
ASSERT_TRUE(ac.check());
// Panic and SoftwareWatchdog reasons should produce all of the sections.
constexpr ktl::array kReasons = {ZirconCrashReason::Panic, ZirconCrashReason::SoftwareWatchdog};
for (auto reason : kReasons) {
memset(buffer.get(), 0, kLargeBufferSize);
size_t len = crashlog_to_string({buffer.get(), kLargeBufferSize}, reason);
ASSERT_LE(len, kLargeBufferSize);
ASSERT_GT(len, 0u);
ktl::string_view text{buffer.get(), len};
// Banner.
EXPECT_TRUE(text.find("ZIRCON REBOOT REASON"sv) != ktl::string_view::npos);
EXPECT_TRUE(text.find("UPTIME"sv) != ktl::string_view::npos);
EXPECT_TRUE(text.find("VERSION"sv) != ktl::string_view::npos);
// Debug info.
EXPECT_TRUE(text.find("{{{reset}}}"sv) != ktl::string_view::npos);
EXPECT_TRUE(text.find("REGISTERS"sv) != ktl::string_view::npos);
EXPECT_TRUE(text.find("BACKTRACE"sv) != ktl::string_view::npos);
// Critical counters.
EXPECT_TRUE(text.find("counters: "sv) != ktl::string_view::npos);
// Panic buffer.
EXPECT_TRUE(text.find("panic buffer: "sv) != ktl::string_view::npos);
// Debug log.
EXPECT_TRUE(text.find("--- BEGIN DLOG DUMP ---"sv) != ktl::string_view::npos);
EXPECT_TRUE(text.find("--- END DLOG DUMP ---"sv) != ktl::string_view::npos);
}
END_TEST;
}
bool UnknownTest() {
BEGIN_TEST;
fbl::AllocChecker ac;
auto buffer = ktl::make_unique<char[]>(&ac, kLargeBufferSize);
ASSERT_TRUE(ac.check());
// Unknown reasons should include just the banner.
constexpr ktl::array kReasons = {
ZirconCrashReason::Unknown,
static_cast<ZirconCrashReason>(0xbaadf00d),
};
for (auto reason : kReasons) {
memset(buffer.get(), 0, kLargeBufferSize);
size_t len = crashlog_to_string({buffer.get(), kLargeBufferSize}, reason);
ASSERT_LE(len, kLargeBufferSize);
ASSERT_GT(len, 0u);
ktl::string_view text{buffer.get(), len};
// Banner.
EXPECT_TRUE(text.find("ZIRCON REBOOT REASON"sv) != ktl::string_view::npos);
EXPECT_TRUE(text.find("UPTIME"sv) != ktl::string_view::npos);
EXPECT_TRUE(text.find("VERSION"sv) != ktl::string_view::npos);
// No debug info.
EXPECT_FALSE(text.find("{{{reset}}}"sv) != ktl::string_view::npos);
EXPECT_FALSE(text.find("REGISTERS"sv) != ktl::string_view::npos);
EXPECT_FALSE(text.find("BACKTRACE"sv) != ktl::string_view::npos);
// No critical counters.
EXPECT_FALSE(text.find("counters: "sv) != ktl::string_view::npos);
// No panic buffer.
EXPECT_FALSE(text.find("panic buffer: "sv) != ktl::string_view::npos);
// No debug log.
EXPECT_FALSE(text.find("--- BEGIN DLOG DUMP ---"sv) != ktl::string_view::npos);
EXPECT_FALSE(text.find("--- END DLOG DUMP ---"sv) != ktl::string_view::npos);
}
END_TEST;
}
bool TruncationTest() {
BEGIN_TEST;
constexpr char kCanaryValue = '\xa5';
char buffer[kTooSmallBufferSize + 1];
buffer[sizeof(buffer) - 1] = kCanaryValue;
size_t len = crashlog_to_string({buffer, sizeof(buffer) - 1}, ZirconCrashReason::Panic);
EXPECT_LT(len, sizeof(buffer));
EXPECT_GT(len, 0u);
EXPECT_EQ(buffer[sizeof(buffer) - 1], kCanaryValue);
END_TEST;
}
bool PanicBufferTest() {
BEGIN_TEST;
PanicBuffer b;
ASSERT_EQ(0u, strlen(b.c_str()));
ASSERT_EQ(0u, b.size());
const char kMsg[] = "hello";
const size_t kMsgLen = ::strlen(kMsg);
b.Append(kMsg);
ASSERT_FALSE(strcmp(b.c_str(), kMsg));
ASSERT_EQ(kMsgLen, b.size());
// Grossly over append.
for (unsigned i = 0; i < PanicBuffer::kMaxSize; ++i) {
b.Append(kMsg);
const size_t expected_size = ktl::min((i + 2) * kMsgLen, PanicBuffer::kMaxSize - 1);
ASSERT_EQ(expected_size, b.size());
}
ASSERT_EQ(PanicBuffer::kMaxSize - 1, strlen(b.c_str()));
END_TEST;
}
} // namespace
UNITTEST_START_TESTCASE(crashlog_tests)
UNITTEST("NoCrash", NoCrashTest)
UNITTEST("Panic/SW WDT", PanicWdtTest)
UNITTEST("OOM/RootJob", OomRootJobTest)
UNITTEST("UnknownReason", UnknownTest)
UNITTEST("Truncation", TruncationTest)
UNITTEST("PanicBuffer", PanicBufferTest)
UNITTEST_END_TESTCASE(crashlog_tests, "crashlog", "crashlog tests")