blob: d35039c715150da3110631a7ce2abdc2baeffc60 [file] [log] [blame]
// Copyright 2019 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 "gather_threads_and_cpu.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "dockyard_proxy_fake.h"
#include "info_resource.h"
using ::testing::_;
using ::testing::Eq;
using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::ReturnRef;
class MockTaskTree : public harvester::TaskTree {
public:
MOCK_METHOD(void, Gather, (), (override));
MOCK_METHOD(const std::vector<Task>&, Jobs, (), (const, override));
MOCK_METHOD(const std::vector<Task>&, Processes, (), (const, override));
MOCK_METHOD(const std::vector<Task>&, Threads, (), (const, override));
};
class MockOS : public harvester::OS {
public:
MOCK_METHOD(zx_duration_t, HighResolutionNow, (), (override));
MOCK_METHOD(zx_status_t, GetInfo,
(zx_handle_t parent, unsigned int children_kind, void* out_buffer,
size_t buffer_size, size_t* actual, size_t* avail),
(override));
};
class GatherThreadsAndCpuTest : public ::testing::Test {
public:
void SetUp() override {
EXPECT_CALL(task_tree_, Gather()).Times(1);
jobs_ = {{1, 1, 0}};
EXPECT_CALL(task_tree_, Jobs()).WillOnce(ReturnRef(jobs_));
threads_ = {{2, 2, 1}};
EXPECT_CALL(task_tree_, Processes()).WillOnce(ReturnRef(processes_));
threads_ = {{101, 101, 1}, {102, 102, 1}};
EXPECT_CALL(task_tree_, Threads())
.Times(2)
.WillRepeatedly(ReturnRef(threads_));
EXPECT_CALL(os_, GetInfo(_, Eq(ZX_INFO_CPU_STATS), IsNull(), _, _, _))
.WillOnce(Invoke(this, &GatherThreadsAndCpuTest::GetCpuStatsCount));
EXPECT_CALL(os_,
GetInfo(_, Eq(ZX_INFO_CPU_STATS), NotNull(), _, NotNull(), _))
.WillOnce(Invoke(this, &GatherThreadsAndCpuTest::GetCpuStatsInfo));
EXPECT_CALL(os_, GetInfo(_, Eq(ZX_INFO_THREAD), NotNull(), _, _, _))
.WillRepeatedly(Invoke(this, &GatherThreadsAndCpuTest::GetThreadInfo));
EXPECT_CALL(os_, GetInfo(_, Eq(ZX_INFO_THREAD_STATS), NotNull(), _, _, _))
.WillRepeatedly(
Invoke(this, &GatherThreadsAndCpuTest::GetThreadStatsInfo));
}
zx_status_t GetCpuStatsCount(zx_handle_t parent, int children_kind,
void* out_buffer, size_t buffer_size,
size_t* actual, size_t* avail) {
if (parent != info_resource_) {
ADD_FAILURE() << "Passed a handle that was not the info resource.";
return ZX_ERR_BAD_HANDLE;
}
*avail = cpu_stats_.size();
return ZX_OK;
}
zx_status_t GetCpuStatsInfo(zx_handle_t parent, int children_kind,
void* out_buffer, size_t buffer_size,
size_t* actual, size_t* avail) {
size_t capacity = buffer_size / sizeof(zx_info_cpu_stats_t);
if (parent != info_resource_) {
ADD_FAILURE() << "Passed a handle that was not the info resource.";
return ZX_ERR_BAD_HANDLE;
}
*actual = std::min(cpu_stats_.size(), capacity);
*avail = cpu_stats_.size();
memcpy(out_buffer, (void*)cpu_stats_.data(),
*actual * sizeof(zx_info_cpu_stats_t));
return ZX_OK;
}
zx_status_t GetThreadInfo(zx_handle_t parent, int children_kind,
void* out_buffer, size_t buffer_size,
size_t* actual, size_t* avail) {
EXPECT_EQ(buffer_size, sizeof(zx_info_thread_t));
if (thread_info_.count(parent) == 0) {
ADD_FAILURE() << "Warning: unexpected handle " << parent;
return ZX_ERR_BAD_HANDLE;
}
memcpy(out_buffer, (void*)&thread_info_.at(parent),
sizeof(zx_info_thread_t));
return ZX_OK;
}
zx_status_t GetThreadStatsInfo(zx_handle_t parent, int children_kind,
void* out_buffer, size_t buffer_size,
size_t* actual, size_t* avail) {
EXPECT_EQ(buffer_size, sizeof(zx_info_thread_stats_t));
if (thread_stats_.count(parent) == 0) {
ADD_FAILURE() << "Warning: unexpected handle " << parent;
return ZX_ERR_BAD_HANDLE;
}
memcpy(out_buffer, (void*)&thread_stats_.at(parent),
sizeof(zx_info_thread_stats_t));
return ZX_OK;
}
// Get a dockyard path for the given |koid| and |suffix| key.
std::string KoidPath(uint64_t koid, const std::string& suffix) {
std::ostringstream out;
out << "koid:" << koid << ":" << suffix;
return out.str();
}
// Get a dockyard path for the given |koid| and |suffix| key.
std::string CpuPath(uint64_t cpu, const std::string& suffix) {
std::ostringstream out;
out << "cpu:" << cpu << ":" << suffix;
return out.str();
}
uint64_t GetValueForPath(std::string path) {
uint64_t value;
EXPECT_TRUE(dockyard_proxy_.CheckValueSent(path, &value));
return value;
}
protected:
// Mocks.
MockTaskTree task_tree_;
MockOS os_;
// Test data.
std::vector<zx_info_cpu_stats_t> cpu_stats_;
harvester::DockyardProxyFake dockyard_proxy_;
zx_handle_t info_resource_;
std::vector<harvester::TaskTree::Task> jobs_;
std::vector<harvester::TaskTree::Task> processes_;
std::vector<harvester::TaskTree::Task> threads_;
std::map<zx_handle_t, zx_info_thread_t> thread_info_;
std::map<zx_handle_t, zx_info_thread_stats_t> thread_stats_;
};
TEST_F(GatherThreadsAndCpuTest, GetsThreadAndCpuInfo) {
harvester::GatherThreadsAndCpu gatherer(info_resource_, &dockyard_proxy_,
task_tree_, &os_);
EXPECT_CALL(os_, HighResolutionNow()).WillOnce(Return(100UL));
cpu_stats_ = {{
.idle_time = 1,
.reschedules = 2,
.context_switches = 3,
.irq_preempts = 4,
.preempts = 5,
.yields = 6,
.ints = 7,
.timer_ints = 8,
.timers = 9,
.syscalls = 10,
.reschedule_ipis = 11,
.generic_ipis = 12
}};
thread_info_[101] = { .state = ZX_THREAD_STATE_RUNNING, };
thread_stats_[101] = { .total_runtime = 0, };
thread_info_[102] = { .state = ZX_THREAD_STATE_DEAD, };
thread_stats_[102] = { .total_runtime = 1200, };
gatherer.Gather();
EXPECT_EQ(GetValueForPath(CpuPath(0, "reschedules")), 2UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "context_switches")), 3UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "meaningful_irq_preempts")), 4UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "preempts")), 5UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "yields")), 6UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "busy_time")), 99UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "idle_time")), 1UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "external_hardware_interrupts")), 7UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "timer_interrupts")), 8UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "timer_callbacks")), 9UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "syscalls")), 10UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "reschedule_ipis")), 11UL);
EXPECT_EQ(GetValueForPath(CpuPath(0, "generic_ipis")), 12UL);
EXPECT_EQ(GetValueForPath(KoidPath(101, "thread_state")),
ZX_THREAD_STATE_RUNNING);
EXPECT_EQ(GetValueForPath(KoidPath(101, "cpu_total")), 0UL);
EXPECT_EQ(GetValueForPath(KoidPath(102, "thread_state")),
ZX_THREAD_STATE_DEAD);
EXPECT_EQ(GetValueForPath(KoidPath(102, "cpu_total")), 1200UL);
}