From 027a91610778c3d1d528f878ca679d00a5ed76ce Mon Sep 17 00:00:00 2001 From: Mayank Raghuwanshi Date: Thu, 25 Mar 2021 10:50:39 +0530 Subject: [PATCH] Improve code coverage for Pmu Interface Signed-off-by: Mayank Raghuwanshi --- .../tools/source/sysman/linux/pmu/pmu_imp.cpp | 12 +- .../tools/source/sysman/linux/pmu/pmu_imp.h | 2 + .../sources/sysman/linux/pmu/CMakeLists.txt | 15 +++ .../sources/sysman/linux/pmu/mock_pmu.h | 89 +++++++++++++ .../sources/sysman/linux/pmu/test_pmu.cpp | 126 ++++++++++++++++++ 5 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/CMakeLists.txt create mode 100644 level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/mock_pmu.h create mode 100644 level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/test_pmu.cpp diff --git a/level_zero/tools/source/sysman/linux/pmu/pmu_imp.cpp b/level_zero/tools/source/sysman/linux/pmu/pmu_imp.cpp index b5e1626b8d..f4589c7cd7 100644 --- a/level_zero/tools/source/sysman/linux/pmu/pmu_imp.cpp +++ b/level_zero/tools/source/sysman/linux/pmu/pmu_imp.cpp @@ -42,6 +42,14 @@ uint32_t PmuInterfaceImp::getEventType() { return eventTypeVal; } +ssize_t PmuInterfaceImp::readCounters(int fd, uint64_t *data, ssize_t sizeOfdata) { + return read(fd, data, sizeOfdata); +} + +int PmuInterfaceImp::getErrorNo() { + return errno; +} + inline int64_t PmuInterfaceImp::perfEventOpen(perf_event_attr *attr, pid_t pid, int cpu, int groupFd, uint64_t flags) { attr->size = sizeof(*attr); return syscall(perfEventOpenSyscallNumber, attr, pid, cpu, groupFd, flags); @@ -67,14 +75,14 @@ int64_t PmuInterfaceImp::pmuInterfaceOpen(uint64_t config, int group, uint32_t f do { ret = perfEventOpen(&attr, -1, cpu++, group, 0); - } while ((ret < 0 && errno == EINVAL) && (cpu < nrCpus)); + } while ((ret < 0 && getErrorNo() == EINVAL) && (cpu < nrCpus)); return ret; } int PmuInterfaceImp::pmuRead(int fd, uint64_t *data, ssize_t sizeOfdata) { ssize_t len; - len = read(fd, data, sizeOfdata); + len = readCounters(fd, data, sizeOfdata); if (len != sizeOfdata) { return -1; } diff --git a/level_zero/tools/source/sysman/linux/pmu/pmu_imp.h b/level_zero/tools/source/sysman/linux/pmu/pmu_imp.h index ec3c0835c4..b31569bbda 100644 --- a/level_zero/tools/source/sysman/linux/pmu/pmu_imp.h +++ b/level_zero/tools/source/sysman/linux/pmu/pmu_imp.h @@ -25,6 +25,8 @@ class PmuInterfaceImp : public PmuInterface, NEO::NonCopyableOrMovableClass { protected: MOCKABLE_VIRTUAL int64_t perfEventOpen(perf_event_attr *attr, pid_t pid, int cpu, int groupFd, uint64_t flags); + MOCKABLE_VIRTUAL ssize_t readCounters(int fd, uint64_t *data, ssize_t sizeOfdata); + MOCKABLE_VIRTUAL int getErrorNo(); private: uint32_t getEventType(); diff --git a/level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/CMakeLists.txt b/level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/CMakeLists.txt new file mode 100644 index 0000000000..9709499be1 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/CMakeLists.txt @@ -0,0 +1,15 @@ +# +# Copyright (C) 2021 Intel Corporation +# +# SPDX-License-Identifier: MIT +# + +if(UNIX) + target_sources(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt + ${CMAKE_CURRENT_SOURCE_DIR}/test_pmu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mock_pmu.h + ) +endif() + +add_subdirectories() diff --git a/level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/mock_pmu.h b/level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/mock_pmu.h new file mode 100644 index 0000000000..a452691cda --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/mock_pmu.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#pragma once + +#include "level_zero/core/test/unit_tests/mock.h" +#include "level_zero/tools/source/sysman/linux/pmu/pmu_imp.h" + +using namespace NEO; +namespace L0 { +namespace ult { +constexpr uint64_t mockEventVal = 2u; +constexpr uint64_t mockTimeStamp = 100u; +constexpr int64_t mockPmuFd = 5; +constexpr uint64_t mockEventCount = 2u; +constexpr uint64_t mockEvent1Val = 100u; +constexpr uint64_t mockEvent2Val = 150u; +class MockPmuInterfaceImpForSysman : public PmuInterfaceImp { + public: + using PmuInterfaceImp::getErrorNo; + using PmuInterfaceImp::perfEventOpen; + using PmuInterfaceImp::readCounters; + MockPmuInterfaceImpForSysman(LinuxSysmanImp *pLinuxSysmanImp) : PmuInterfaceImp(pLinuxSysmanImp) {} +}; +template <> +struct Mock : public MockPmuInterfaceImpForSysman { + Mock(LinuxSysmanImp *pLinuxSysmanImp) : MockPmuInterfaceImpForSysman(pLinuxSysmanImp) {} + int64_t mockedPerfEventOpenAndSuccessReturn(perf_event_attr *attr, pid_t pid, int cpu, int groupFd, uint64_t flags) { + return mockPmuFd; + } + + int64_t mockedPerfEventOpenAndFailureReturn(perf_event_attr *attr, pid_t pid, int cpu, int groupFd, uint64_t flags) { + return -1; + } + + int mockedPmuReadAfterClearAndSuccessReturn(int fd, uint64_t *data, ssize_t sizeOfdata) { + memset(data, 0, sizeOfdata); + return 0; + } + + int mockedPmuReadAndFailureReturn(int fd, uint64_t *data, ssize_t sizeOfdata) { + return -1; + } + + ssize_t mockReadCounterSuccess(int fd, uint64_t *data, ssize_t sizeOfdata) { + data[0] = mockEventVal; + data[1] = mockTimeStamp; + return sizeOfdata; + } + + ssize_t mockedReadCountersForGroupSuccess(int fd, uint64_t *data, ssize_t sizeOfdata) { + data[0] = mockEventCount; + data[1] = mockTimeStamp; + data[2] = mockEvent1Val; + data[3] = mockEvent2Val; + return sizeOfdata; + } + + ssize_t mockReadCounterFailure(int fd, uint64_t *data, ssize_t sizeOfdata) { + return -1; + } + int mockGetErrorNoSuccess() { + return EINVAL; + } + int mockGetErrorNoFailure() { + return EBADF; + } + + MOCK_METHOD(int64_t, perfEventOpen, (perf_event_attr * attr, pid_t pid, int cpu, int groupFd, uint64_t flags), (override)); + MOCK_METHOD(ssize_t, readCounters, (int fd, uint64_t *data, ssize_t sizeOfdata), (override)); + MOCK_METHOD(int, getErrorNo, (), (override)); +}; + +class PmuFsAccess : public FsAccess {}; + +template <> +struct Mock : public PmuFsAccess { + MOCK_METHOD(ze_result_t, read, (const std::string file, uint32_t &val), (override)); + ze_result_t readValSuccess(const std::string file, uint32_t &val) { + val = 18; + return ZE_RESULT_SUCCESS; + } +}; +} // namespace ult +} // namespace L0 \ No newline at end of file diff --git a/level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/test_pmu.cpp b/level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/test_pmu.cpp new file mode 100644 index 0000000000..9097efe802 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/test_pmu.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2021 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "level_zero/tools/test/unit_tests/sources/sysman/linux/mock_sysman_fixture.h" +#include "level_zero/tools/test/unit_tests/sources/sysman/linux/pmu/mock_pmu.h" + +using ::testing::Matcher; +using ::testing::Return; +namespace L0 { +namespace ult { +struct SysmanPmuFixture : public SysmanDeviceFixture { + protected: + std::unique_ptr> pPmuInterface; + PmuInterface *pOriginalPmuInterface = nullptr; + std::unique_ptr> pFsAccess; + FsAccess *pFsAccessOriginal = nullptr; + + void SetUp() override { + SysmanDeviceFixture::SetUp(); + pFsAccessOriginal = pLinuxSysmanImp->pFsAccess; + pFsAccess = std::make_unique>>(); + pLinuxSysmanImp->pFsAccess = pFsAccess.get(); + pOriginalPmuInterface = pLinuxSysmanImp->pPmuInterface; + pPmuInterface = std::make_unique>>(pLinuxSysmanImp); + pLinuxSysmanImp->pPmuInterface = pPmuInterface.get(); + ON_CALL(*pFsAccess.get(), read(_, _)) + .WillByDefault(::testing::Invoke(pFsAccess.get(), &Mock::readValSuccess)); + ON_CALL(*pPmuInterface.get(), perfEventOpen(_, _, _, _, _)) + .WillByDefault(::testing::Invoke(pPmuInterface.get(), &Mock::mockedPerfEventOpenAndSuccessReturn)); + ON_CALL(*pPmuInterface.get(), getErrorNo()) + .WillByDefault(::testing::Invoke(pPmuInterface.get(), &Mock::mockGetErrorNoSuccess)); + ON_CALL(*pPmuInterface.get(), readCounters(_, _, _)) + .WillByDefault(::testing::Invoke(pPmuInterface.get(), &Mock::mockReadCounterSuccess)); + } + void TearDown() override { + SysmanDeviceFixture::TearDown(); + pLinuxSysmanImp->pPmuInterface = pOriginalPmuInterface; + pLinuxSysmanImp->pFsAccess = pFsAccessOriginal; + } +}; + +TEST_F(SysmanPmuFixture, GivenValidPmuHandleWhenCallingPmuReadAndReadCountersFunctionReturnsSuccessThenSuccessIsReturned) { + uint64_t data[2]; + int validFd = 10; + EXPECT_EQ(0, pLinuxSysmanImp->pPmuInterface->pmuRead(validFd, data, sizeof(data))); + EXPECT_EQ(mockEventVal, data[0]); + EXPECT_EQ(mockTimeStamp, data[1]); +} + +TEST_F(SysmanPmuFixture, GivenValidPmuHandleWhenCallingPmuReadAndReadCountersFunctionReturnsFailureThenFailureIsReturned) { + ON_CALL(*pPmuInterface.get(), readCounters(_, _, _)) + .WillByDefault(::testing::Invoke(pPmuInterface.get(), &Mock::mockReadCounterFailure)); + int validFd = 10; + uint64_t data[2]; + EXPECT_EQ(-1, pLinuxSysmanImp->pPmuInterface->pmuRead(validFd, data, sizeof(data))); +} + +TEST_F(SysmanPmuFixture, GivenValidPmuHandleWhenCallingReadCountersAndInvalidFdIsPassedToReadSyscallThenFailureIsReturned) { + MockPmuInterfaceImpForSysman *pmuInterface = new MockPmuInterfaceImpForSysman(pLinuxSysmanImp); + uint64_t data[2]; + int invalidFd = -1; + EXPECT_EQ(-1, pmuInterface->readCounters(invalidFd, data, sizeof(data))); + delete pmuInterface; +} + +TEST_F(SysmanPmuFixture, GivenValidPmuHandleWhenCallingPerfEventOpenAndInvalidConfigIsPassedThenFailureIsReturned) { + MockPmuInterfaceImpForSysman *pmuInterface = new MockPmuInterfaceImpForSysman(pLinuxSysmanImp); + struct perf_event_attr attr = {}; + int cpu = 0; + attr.type = 0; + attr.read_format = static_cast(PERF_FORMAT_TOTAL_TIME_ENABLED); + attr.config = 0; + EXPECT_LT(pmuInterface->perfEventOpen(&attr, -1, cpu, -1, 0), 0); + delete pmuInterface; +} + +TEST_F(SysmanPmuFixture, GivenValidPmuHandleWhenCallingPmuInterfaceOpenAndPerfEventOpenSucceedsThenVaildFdIsReturned) { + uint64_t config = 10; + EXPECT_EQ(mockPmuFd, pLinuxSysmanImp->pPmuInterface->pmuInterfaceOpen(config, -1, PERF_FORMAT_TOTAL_TIME_ENABLED)); +} + +TEST_F(SysmanPmuFixture, GivenValidPmuHandleWhenReadingGroupOfEventsUsingGroupFdThenSuccessIsReturned) { + ON_CALL(*pPmuInterface.get(), readCounters(_, _, _)) + .WillByDefault(::testing::Invoke(pPmuInterface.get(), &Mock::mockedReadCountersForGroupSuccess)); + uint64_t configForEvent1 = 10; + int64_t groupFd = pLinuxSysmanImp->pPmuInterface->pmuInterfaceOpen(configForEvent1, -1, PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_GROUP); // To get group leader + uint64_t configForEvent2 = 15; + pLinuxSysmanImp->pPmuInterface->pmuInterfaceOpen(configForEvent2, static_cast(groupFd), PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_GROUP); + uint64_t data[4]; + EXPECT_EQ(0, pLinuxSysmanImp->pPmuInterface->pmuRead(static_cast(groupFd), data, sizeof(data))); + EXPECT_EQ(mockEventCount, data[0]); + EXPECT_EQ(mockTimeStamp, data[1]); + EXPECT_EQ(mockEvent1Val, data[2]); + EXPECT_EQ(mockEvent2Val, data[3]); +} + +TEST_F(SysmanPmuFixture, GivenValidPmuHandleWhenCallingPmuInterfaceOpenAndPerfEventOpenFailsThenFailureIsReturned) { + ON_CALL(*pPmuInterface.get(), perfEventOpen(_, _, _, _, _)) + .WillByDefault(::testing::Invoke(pPmuInterface.get(), &Mock::mockedPerfEventOpenAndFailureReturn)); + uint64_t config = 10; + EXPECT_EQ(-1, pLinuxSysmanImp->pPmuInterface->pmuInterfaceOpen(config, -1, PERF_FORMAT_TOTAL_TIME_ENABLED)); +} + +TEST_F(SysmanPmuFixture, GivenValidPmuHandleWhenCallingPmuInterfaceOpenAndPerfEventOpenFailsAndErrNoSetBySyscallIsNotInvalidArgumentThenFailureIsReturned) { + ON_CALL(*pPmuInterface.get(), perfEventOpen(_, _, _, _, _)) + .WillByDefault(::testing::Invoke(pPmuInterface.get(), &Mock::mockedPerfEventOpenAndFailureReturn)); + ON_CALL(*pPmuInterface.get(), getErrorNo()) + .WillByDefault(::testing::Invoke(pPmuInterface.get(), &Mock::mockGetErrorNoFailure)); + uint64_t config = 10; + EXPECT_EQ(-1, pLinuxSysmanImp->pPmuInterface->pmuInterfaceOpen(config, -1, PERF_FORMAT_TOTAL_TIME_ENABLED)); +} + +TEST_F(SysmanPmuFixture, GivenValidPmuHandleWhenCallingReadCountersAndInvalidFdIsPassedToReadSyscallThenErrorNoInavlidFileDescriptorIsSet) { + MockPmuInterfaceImpForSysman *pmuInterface = new MockPmuInterfaceImpForSysman(pLinuxSysmanImp); + uint64_t data[2]; + int invalidFd = -1; + EXPECT_EQ(-1, pmuInterface->readCounters(invalidFd, data, sizeof(data))); + EXPECT_EQ(EBADF, pmuInterface->getErrorNo()); + delete pmuInterface; +} +} // namespace ult +} // namespace L0