blob: 7ea891fe03623eb0e362332399668d8535ac413f [file] [log] [blame]
// Copyright 2022 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.
#include "src/sys/early_boot_instrumentation/coverage_source.h"
#include <fcntl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/namespace.h>
#include <lib/zx/channel.h>
#include <lib/zx/vmo.h>
#include <sys/stat.h>
#include <memory>
#include <fbl/unique_fd.h>
#include <gtest/gtest.h>
#include <sdk/lib/vfs/cpp/flags.h>
#include <sdk/lib/vfs/cpp/pseudo_dir.h>
#include <sdk/lib/vfs/cpp/vmo_file.h>
namespace early_boot_instrumentation {
namespace {
constexpr auto kFlags = fuchsia::io::OpenFlags::RIGHT_READABLE;
zx_koid_t GetKoid(zx_handle_t handle) {
zx_info_handle_basic_t info;
zx_status_t status =
zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
return status == ZX_OK ? info.koid : ZX_KOID_INVALID;
}
// Serve the vmos from /somepath/kernel/vmofile.name.
class FakeBootItemsFixture : public testing::Test {
public:
void Serve(const std::string& path) {
zx::channel dir_server, dir_client;
ASSERT_EQ(zx::channel::create(0, &dir_server, &dir_client), 0);
fdio_ns_t* root_ns = nullptr;
path_ = path;
ASSERT_EQ(fdio_ns_get_installed(&root_ns), ZX_OK);
ASSERT_EQ(fdio_ns_bind(root_ns, path.c_str(), dir_client.release()), ZX_OK);
ASSERT_EQ(kernel_dir_.Serve(kFlags, std::move(dir_server), loop_.dispatcher()), ZX_OK);
loop_.StartThread("kernel_data_dir");
}
void BindFile(std::string_view path) {
zx::vmo path_vmo;
ASSERT_EQ(zx::vmo::create(4096, 0, &path_vmo), 0);
zx_koid_t koid = GetKoid(path_vmo.get());
ASSERT_NE(koid, ZX_KOID_INVALID);
auto str_path = std::string(path);
path_to_koid_[str_path] = koid;
auto file = std::make_unique<vfs::VmoFile>(std::move(path_vmo), 0, 4096);
ASSERT_EQ(kernel_dir_.AddEntry(str_path, std::move(file)), ZX_OK);
}
void TearDown() override {
// Best effort.
fdio_ns_t* root_ns = nullptr;
ASSERT_EQ(fdio_ns_get_installed(&root_ns), ZX_OK);
fdio_ns_unbind(root_ns, path_.c_str());
loop_.Shutdown();
}
private:
async::Loop loop_ = async::Loop(&kAsyncLoopConfigNoAttachToCurrentThread);
vfs::PseudoDir kernel_dir_;
std::map<std::string, zx_koid_t> path_to_koid_;
std::string path_;
};
using ExposeKernelProfileDataTest = FakeBootItemsFixture;
using ExposePhysbootProfileDataTest = FakeBootItemsFixture;
TEST_F(ExposeKernelProfileDataTest, WithSymbolizerLogExposesBoth) {
BindFile("zircon.elf.profraw");
BindFile("symbolizer.log");
ASSERT_NO_FATAL_FAILURE(Serve("/boot/kernel/data"));
fbl::unique_fd kernel_data_dir(open("/boot/kernel/data", O_RDONLY));
ASSERT_TRUE(kernel_data_dir) << strerror(errno);
vfs::PseudoDir out_dir;
ASSERT_TRUE(ExposeKernelProfileData(kernel_data_dir, out_dir).is_ok());
ASSERT_FALSE(out_dir.IsEmpty());
std::string kernel_file(kKernelFile);
vfs::internal::Node* node = nullptr;
ASSERT_EQ(out_dir.Lookup(kernel_file, &node), ZX_OK);
ASSERT_NE(node, nullptr);
node = nullptr;
std::string symbolizer_file(kKernelSymbolizerFile);
ASSERT_EQ(out_dir.Lookup(symbolizer_file, &node), ZX_OK);
ASSERT_NE(node, nullptr);
}
TEST_F(ExposeKernelProfileDataTest, OnlyKernelFileIsOk) {
// Dispatcher
BindFile("zircon.elf.profraw");
ASSERT_NO_FATAL_FAILURE(Serve("/boot/kernel/data"));
fbl::unique_fd kernel_data_dir(open("/boot/kernel/data", O_RDONLY));
ASSERT_TRUE(kernel_data_dir) << strerror(errno);
vfs::PseudoDir out_dir;
ASSERT_TRUE(ExposeKernelProfileData(kernel_data_dir, out_dir).is_ok());
ASSERT_FALSE(out_dir.IsEmpty());
std::string kernel_file(kKernelFile);
vfs::internal::Node* node = nullptr;
ASSERT_EQ(out_dir.Lookup(kernel_file, &node), ZX_OK);
ASSERT_NE(node, nullptr);
node = nullptr;
std::string symbolizer_file(kKernelSymbolizerFile);
ASSERT_NE(out_dir.Lookup(symbolizer_file, &node), ZX_OK);
}
TEST_F(ExposePhysbootProfileDataTest, WithSymbolizerFileIsOk) {
// Dispatcher
BindFile("physboot.profraw");
BindFile("symbolizer.log");
ASSERT_NO_FATAL_FAILURE(Serve("/boot/kernel/data/phys"));
fbl::unique_fd kernel_data_dir(open("/boot/kernel/data/phys", O_RDONLY));
ASSERT_TRUE(kernel_data_dir) << strerror(errno);
vfs::PseudoDir out_dir;
ASSERT_TRUE(ExposePhysbootProfileData(kernel_data_dir, out_dir).is_ok());
ASSERT_FALSE(out_dir.IsEmpty());
std::string phys_file(kPhysFile);
vfs::internal::Node* node = nullptr;
ASSERT_EQ(out_dir.Lookup(phys_file, &node), ZX_OK);
ASSERT_NE(node, nullptr);
node = nullptr;
std::string symbolizer_file(kPhysSymbolizerFile);
ASSERT_EQ(out_dir.Lookup(symbolizer_file, &node), ZX_OK);
ASSERT_NE(node, nullptr);
}
TEST_F(ExposePhysbootProfileDataTest, OnlyProfrawFileIsOk) {
// Dispatcher
BindFile("physboot.profraw");
ASSERT_NO_FATAL_FAILURE(Serve("/boot/kernel/data/phys"));
fbl::unique_fd kernel_data_dir(open("/boot/kernel/data/phys", O_RDONLY));
ASSERT_TRUE(kernel_data_dir) << strerror(errno);
vfs::PseudoDir out_dir;
ASSERT_TRUE(ExposePhysbootProfileData(kernel_data_dir, out_dir).is_ok());
ASSERT_FALSE(out_dir.IsEmpty());
std::string phys_file(kPhysFile);
vfs::internal::Node* node = nullptr;
ASSERT_EQ(out_dir.Lookup(phys_file, &node), ZX_OK);
ASSERT_NE(node, nullptr);
node = nullptr;
std::string symbolizer_file(kPhysSymbolizerFile);
ASSERT_NE(out_dir.Lookup(symbolizer_file, &node), ZX_OK);
}
} // namespace
} // namespace early_boot_instrumentation