blob: f00af50bbf99e9d62494223b8a4d1df304f515e4 [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.
#include <lib/zx/bti.h>
#include <lib/zx/job.h>
#include <lib/zx/pager.h>
#include <lib/zx/vmo.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/iommu.h>
#include <zircon/syscalls/object.h>
#include <vector>
#include <zxtest/zxtest.h>
namespace object_info_test {
namespace {
extern "C" __WEAK zx_handle_t get_root_resource();
class KernelStatsGetInfoTest : public zxtest::Test {
public:
void SetUp() override {
if (!get_root_resource) {
return;
}
root_resource_ = zx::unowned_resource(get_root_resource());
num_cpus_ = zx_system_get_num_cpus();
}
protected:
uint32_t num_cpus_ = 0;
zx::unowned_resource root_resource_ = zx::unowned_resource(ZX_HANDLE_INVALID);
};
TEST_F(KernelStatsGetInfoTest, KmemStats) {
if (!root_resource_->is_valid()) {
printf("Root resource not available, skipping\n");
return;
}
// Commit (and pin) some pages in regular and pager-backed VMOs, to check for non-zero vmo counts
// returned by zx_object_get_info().
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(ZX_PAGE_SIZE, 0, &vmo));
uint64_t buf = 17;
vmo.write(&buf, 0, sizeof(buf));
zx::pager pager;
ASSERT_OK(zx::pager::create(0, &pager));
zx::port port;
ASSERT_OK(zx::port::create(0, &port));
zx::vmo pager_vmo;
ASSERT_OK(zx_pager_create_vmo(pager.get(), 0, port.get(), 0, ZX_PAGE_SIZE,
pager_vmo.reset_and_get_address()));
ASSERT_OK(zx_pager_supply_pages(pager.get(), pager_vmo.get(), 0, ZX_PAGE_SIZE, vmo.get(), 0));
zx::iommu iommu;
zx::bti bti;
zx::pmt pmt;
zx_iommu_desc_dummy_t desc;
ASSERT_OK(zx_iommu_create(root_resource_->get(), ZX_IOMMU_TYPE_DUMMY, &desc, sizeof(desc),
iommu.reset_and_get_address()));
ASSERT_OK(zx::bti::create(iommu, 0, 0xdeadbeef, &bti));
zx_paddr_t addr;
ASSERT_OK(bti.pin(ZX_BTI_PERM_READ, vmo, 0, ZX_PAGE_SIZE, &addr, 1, &pmt));
zx_info_kmem_stats_t buffer;
size_t actual, avail;
ASSERT_OK(zx_object_get_info(root_resource_->get(), ZX_INFO_KMEM_STATS, &buffer, sizeof(buffer),
&actual, &avail));
EXPECT_EQ(actual, 1);
EXPECT_EQ(avail, 1);
// Perform some basic sanity checks.
EXPECT_GT(buffer.total_bytes, 0);
EXPECT_LT(buffer.free_bytes, buffer.total_bytes);
// We pinned a page.
EXPECT_GT(buffer.wired_bytes, 0);
EXPECT_LT(buffer.wired_bytes, buffer.total_bytes);
EXPECT_LT(buffer.total_heap_bytes, buffer.total_bytes);
EXPECT_LT(buffer.free_heap_bytes, buffer.total_bytes);
// We committed some pages in VMOs.
EXPECT_GT(buffer.vmo_bytes, 0);
EXPECT_LT(buffer.vmo_bytes, buffer.total_bytes);
EXPECT_LT(buffer.mmu_overhead_bytes, buffer.total_bytes);
EXPECT_LT(buffer.ipc_bytes, buffer.total_bytes);
EXPECT_LT(buffer.other_bytes, buffer.total_bytes);
ASSERT_OK(pmt.unpin());
}
TEST_F(KernelStatsGetInfoTest, KmemStatsInvalidHandle) {
zx_info_kmem_stats_t buffer;
size_t actual, avail;
ASSERT_EQ(zx_object_get_info(ZX_HANDLE_INVALID, ZX_INFO_KMEM_STATS, &buffer, sizeof(buffer),
&actual, &avail),
ZX_ERR_BAD_HANDLE);
}
TEST_F(KernelStatsGetInfoTest, KmemStatsBadHandleType) {
zx_info_kmem_stats_t buffer;
size_t actual, avail;
ASSERT_EQ(zx_object_get_info(zx::job::default_job()->get(), ZX_INFO_KMEM_STATS, &buffer,
sizeof(buffer), &actual, &avail),
ZX_ERR_WRONG_TYPE);
}
TEST_F(KernelStatsGetInfoTest, KmemStatsNullBuffer) {
if (!root_resource_->is_valid()) {
printf("Root resource not available, skipping\n");
return;
}
size_t actual, avail;
ASSERT_EQ(zx_object_get_info(root_resource_->get(), ZX_INFO_KMEM_STATS, nullptr,
sizeof(zx_info_kmem_stats_t), &actual, &avail),
ZX_ERR_INVALID_ARGS);
}
TEST_F(KernelStatsGetInfoTest, KmemStatsSmallBuffer) {
if (!root_resource_->is_valid()) {
printf("Root resource not available, skipping\n");
return;
}
size_t actual, avail;
zx_info_kmem_stats_t buffer;
ASSERT_EQ(
zx_object_get_info(root_resource_->get(), ZX_INFO_KMEM_STATS, &buffer, 0, &actual, &avail),
ZX_ERR_BUFFER_TOO_SMALL);
EXPECT_EQ(actual, 0);
EXPECT_EQ(avail, 1);
}
TEST_F(KernelStatsGetInfoTest, KmemStatsExtended) {
if (!root_resource_->is_valid()) {
printf("Root resource not available, skipping\n");
return;
}
// Commit (and pin) some pages in regular and pager-backed VMOs, to check for non-zero vmo counts
// returned by zx_object_get_info().
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(ZX_PAGE_SIZE, 0, &vmo));
uint64_t buf = 17;
vmo.write(&buf, 0, sizeof(buf));
zx::pager pager;
ASSERT_OK(zx::pager::create(0, &pager));
zx::port port;
ASSERT_OK(zx::port::create(0, &port));
zx::vmo pager_vmo;
ASSERT_OK(zx_pager_create_vmo(pager.get(), 0, port.get(), 0, ZX_PAGE_SIZE,
pager_vmo.reset_and_get_address()));
ASSERT_OK(zx_pager_supply_pages(pager.get(), pager_vmo.get(), 0, ZX_PAGE_SIZE, vmo.get(), 0));
zx::iommu iommu;
zx::bti bti;
zx::pmt pmt;
zx_iommu_desc_dummy_t desc;
ASSERT_OK(zx_iommu_create(root_resource_->get(), ZX_IOMMU_TYPE_DUMMY, &desc, sizeof(desc),
iommu.reset_and_get_address()));
ASSERT_OK(zx::bti::create(iommu, 0, 0xdeadbeef, &bti));
zx_paddr_t addr;
ASSERT_OK(bti.pin(ZX_BTI_PERM_READ, vmo, 0, ZX_PAGE_SIZE, &addr, 1, &pmt));
zx_info_kmem_stats_extended_t buffer;
size_t actual, avail;
ASSERT_OK(zx_object_get_info(root_resource_->get(), ZX_INFO_KMEM_STATS_EXTENDED, &buffer,
sizeof(buffer), &actual, &avail));
EXPECT_EQ(actual, 1);
EXPECT_EQ(avail, 1);
// Perform some basic sanity checks.
EXPECT_GT(buffer.total_bytes, 0);
EXPECT_LT(buffer.free_bytes, buffer.total_bytes);
// We pinned a page.
EXPECT_GT(buffer.wired_bytes, 0);
EXPECT_LT(buffer.wired_bytes, buffer.total_bytes);
EXPECT_LT(buffer.total_heap_bytes, buffer.total_bytes);
EXPECT_LT(buffer.free_heap_bytes, buffer.total_bytes);
// We committed some pages in VMOs.
EXPECT_GT(buffer.vmo_bytes, 0);
EXPECT_LT(buffer.vmo_bytes, buffer.total_bytes);
EXPECT_LT(buffer.mmu_overhead_bytes, buffer.total_bytes);
EXPECT_LT(buffer.ipc_bytes, buffer.total_bytes);
EXPECT_LT(buffer.other_bytes, buffer.total_bytes);
// We created a pager-backed VMO and committed pages.
EXPECT_GT(buffer.vmo_pager_total_bytes, 0);
// Pager backed VMO memory must be <= total VMO memory.
EXPECT_LE(buffer.vmo_pager_total_bytes, buffer.vmo_bytes);
// Newest and oldest pager-backed memory must be <= total pager-backed memory.
EXPECT_LE(buffer.vmo_pager_newest_bytes, buffer.vmo_pager_total_bytes);
EXPECT_LE(buffer.vmo_pager_oldest_bytes, buffer.vmo_pager_total_bytes);
EXPECT_LE(buffer.vmo_pager_oldest_bytes + buffer.vmo_pager_newest_bytes,
buffer.vmo_pager_total_bytes);
// Discardable counters are currently unimplemented.
EXPECT_EQ(buffer.vmo_discardable_locked_bytes, 0);
EXPECT_EQ(buffer.vmo_discardable_unlocked_bytes, 0);
ASSERT_OK(pmt.unpin());
}
TEST_F(KernelStatsGetInfoTest, KmemStatsExtendedInvalidHandle) {
zx_info_kmem_stats_extended_t buffer;
size_t actual, avail;
ASSERT_EQ(zx_object_get_info(ZX_HANDLE_INVALID, ZX_INFO_KMEM_STATS_EXTENDED, &buffer,
sizeof(buffer), &actual, &avail),
ZX_ERR_BAD_HANDLE);
}
TEST_F(KernelStatsGetInfoTest, KmemStatsExtendedBadHandleType) {
zx_info_kmem_stats_extended_t buffer;
size_t actual, avail;
ASSERT_EQ(zx_object_get_info(zx::job::default_job()->get(), ZX_INFO_KMEM_STATS_EXTENDED, &buffer,
sizeof(buffer), &actual, &avail),
ZX_ERR_WRONG_TYPE);
}
TEST_F(KernelStatsGetInfoTest, KmemStatsExtendedNullBuffer) {
if (!root_resource_->is_valid()) {
printf("Root resource not available, skipping\n");
return;
}
size_t actual, avail;
ASSERT_EQ(zx_object_get_info(root_resource_->get(), ZX_INFO_KMEM_STATS_EXTENDED, nullptr,
sizeof(zx_info_kmem_stats_extended_t), &actual, &avail),
ZX_ERR_INVALID_ARGS);
}
TEST_F(KernelStatsGetInfoTest, KmemStatsExtendedSmallBuffer) {
if (!root_resource_->is_valid()) {
printf("Root resource not available, skipping\n");
return;
}
size_t actual, avail;
zx_info_kmem_stats_extended_t buffer;
ASSERT_EQ(zx_object_get_info(root_resource_->get(), ZX_INFO_KMEM_STATS_EXTENDED, &buffer, 0,
&actual, &avail),
ZX_ERR_BUFFER_TOO_SMALL);
EXPECT_EQ(actual, 0);
EXPECT_EQ(avail, 1);
}
TEST_F(KernelStatsGetInfoTest, CpuStats) {
if (!root_resource_->is_valid()) {
printf("Root resource not available, skipping\n");
return;
}
zx_info_cpu_stats_t buffer;
size_t actual, avail;
// Read a single record.
ASSERT_OK(zx_object_get_info(root_resource_->get(), ZX_INFO_CPU_STATS, &buffer, sizeof(buffer),
&actual, &avail));
EXPECT_EQ(actual, 1);
EXPECT_EQ(avail, num_cpus_);
std::vector<zx_info_cpu_stats_t> buf(num_cpus_);
// Read all records.
ASSERT_OK(zx_object_get_info(root_resource_->get(), ZX_INFO_CPU_STATS, buf.data(),
buf.size() * sizeof(zx_info_cpu_stats_t), &actual, &avail));
EXPECT_EQ(actual, num_cpus_);
EXPECT_EQ(avail, num_cpus_);
for (uint32_t i = 0; i < num_cpus_; i++) {
EXPECT_EQ(buf[i].cpu_number, i);
}
}
TEST_F(KernelStatsGetInfoTest, CpuStatsInvalidHandle) {
zx_info_cpu_stats_t buffer;
size_t actual, avail;
ASSERT_EQ(zx_object_get_info(ZX_HANDLE_INVALID, ZX_INFO_CPU_STATS, &buffer, sizeof(buffer),
&actual, &avail),
ZX_ERR_BAD_HANDLE);
}
TEST_F(KernelStatsGetInfoTest, CpuStatsBadHandleType) {
zx_info_cpu_stats_t buffer;
size_t actual, avail;
ASSERT_EQ(zx_object_get_info(zx::job::default_job()->get(), ZX_INFO_CPU_STATS, &buffer,
sizeof(buffer), &actual, &avail),
ZX_ERR_WRONG_TYPE);
}
TEST_F(KernelStatsGetInfoTest, CpuStatsNullBuffer) {
if (!root_resource_->is_valid()) {
printf("Root resource not available, skipping\n");
return;
}
size_t actual, avail;
ASSERT_OK(zx_object_get_info(root_resource_->get(), ZX_INFO_CPU_STATS, nullptr,
sizeof(zx_info_kmem_stats_t), &actual, &avail));
EXPECT_EQ(actual, 0);
EXPECT_EQ(avail, num_cpus_);
}
} // namespace
} // namespace object_info_test