Files
TheRock/rocm-systems/projects/hipfile/test/amd_detail/stats.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

333 lines
14 KiB
C++

/* Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
*
* SPDX-License-Identifier: MIT
*/
#include "hipfile-test.h"
#include "mconfiguration.h"
#include "mhip.h"
#include "mstats.h"
#include "stats.h"
#include "msys.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <sstream>
using namespace hipFile;
using ::testing::StrictMock;
// Put tests inside the macros to suppress the global constructor
// warnings
HIPFILE_WARN_NO_GLOBAL_CTOR_OFF
struct HipFileStats : public HipFileUnopened {};
TEST_F(HipFileStats, StatsCollectionAddIo)
{
Stats stats{};
StrictMock<MStatsServer> mstats{};
StrictMock<MHip> mhip{};
EXPECT_CALL(mstats, getStats).WillRepeatedly(testing::Return(&stats));
EXPECT_CALL(mhip, hipGetDevice).WillRepeatedly(testing::Return(0));
stats.setLevel(StatsLevel::Basic);
Context<StatsCollection>::get()->addIo(IoType::Read, StatsBackend::Fastpath, 10, 0, true);
ASSERT_EQ(10, stats.getPerGpuStats(0, StatsBackend::Fastpath)
->ioSizeBytes[static_cast<size_t>(IoType::Read)]
.buckets[0]
.load());
Context<StatsCollection>::get()->addIo(IoType::Write, StatsBackend::Fallback, 10, 0, true);
ASSERT_EQ(10, stats.getPerGpuStats(0, StatsBackend::Fallback)
->ioSizeBytes[static_cast<size_t>(IoType::Write)]
.buckets[0]
.load());
Context<StatsCollection>::get()->addIo(IoType::Read, StatsBackend::Fastpath, 10, 0, true);
ASSERT_EQ(20, stats.getPerGpuStats(0, StatsBackend::Fastpath)
->ioSizeBytes[static_cast<size_t>(IoType::Read)]
.buckets[0]
.load());
stats.setLevel(StatsLevel::Disabled);
Context<StatsCollection>::get()->addIo(IoType::Write, StatsBackend::Fallback, 10, 0, true);
ASSERT_EQ(10, stats.getPerGpuStats(0, StatsBackend::Fallback)
->ioSizeBytes[static_cast<size_t>(IoType::Write)]
.buckets[0]
.load());
}
TEST_F(HipFileStats, StatsCollectionAddIoUnaligned)
{
Stats stats{};
StrictMock<MStatsServer> mstats{};
StrictMock<MHip> mhip{};
EXPECT_CALL(mstats, getStats).WillRepeatedly(testing::Return(&stats));
EXPECT_CALL(mhip, hipGetDevice).WillRepeatedly(testing::Return(0));
stats.setLevel(StatsLevel::Basic);
Context<StatsCollection>::get()->addIo(IoType::Read, StatsBackend::Fastpath, 10, 0, false);
ASSERT_EQ(1, stats.getPerGpuStats(0, StatsBackend::Fastpath)
->unalignedCount[static_cast<size_t>(IoType::Read)]
.load());
Context<StatsCollection>::get()->addIo(IoType::Read, StatsBackend::Fastpath, 10, 0, true);
ASSERT_EQ(1, stats.getPerGpuStats(0, StatsBackend::Fastpath)
->unalignedCount[static_cast<size_t>(IoType::Read)]
.load());
}
TEST_F(HipFileStats, StatsCollectionError)
{
Stats stats{};
StrictMock<MStatsServer> mstats{};
StrictMock<MHip> mhip{};
EXPECT_CALL(mstats, getStats).WillRepeatedly(testing::Return(&stats));
EXPECT_CALL(mhip, hipGetDevice).WillRepeatedly(testing::Return(0));
stats.setLevel(StatsLevel::Basic);
Context<StatsCollection>::get()->error(IoType::Read, StatsBackend::Fastpath, 10);
ASSERT_EQ(1, stats.getPerGpuStats(0, StatsBackend::Fastpath)
->errorCount[static_cast<size_t>(IoType::Read)]
.buckets[0]
.load());
Context<StatsCollection>::get()->error(IoType::Write, StatsBackend::Fallback, 10);
ASSERT_EQ(1, stats.getPerGpuStats(0, StatsBackend::Fallback)
->errorCount[static_cast<size_t>(IoType::Write)]
.buckets[0]
.load());
Context<StatsCollection>::get()->error(IoType::Read, StatsBackend::Fastpath, 10);
ASSERT_EQ(2, stats.getPerGpuStats(0, StatsBackend::Fastpath)
->errorCount[static_cast<size_t>(IoType::Read)]
.buckets[0]
.load());
stats.setLevel(StatsLevel::Disabled);
Context<StatsCollection>::get()->error(IoType::Write, StatsBackend::Fallback, 10);
ASSERT_EQ(1, stats.getPerGpuStats(0, StatsBackend::Fallback)
->errorCount[static_cast<size_t>(IoType::Write)]
.buckets[0]
.load());
}
TEST_F(HipFileStats, StatsCollectionFileRegistration)
{
Stats stats{};
StrictMock<MStatsServer> mstats{};
EXPECT_CALL(mstats, getStats).WillRepeatedly(testing::Return(&stats));
stats.setLevel(StatsLevel::Basic);
Context<StatsCollection>::get()->fileRegistration();
ASSERT_EQ(1, stats.getFileRegistrations().load());
Context<StatsCollection>::get()->fileRegistration();
ASSERT_EQ(2, stats.getFileRegistrations().load());
stats.setLevel(StatsLevel::Disabled);
Context<StatsCollection>::get()->fileRegistration();
ASSERT_EQ(2, stats.getFileRegistrations().load());
}
TEST_F(HipFileStats, StatsCollectionBufferRegistration)
{
Stats stats{};
StrictMock<MStatsServer> mstats{};
EXPECT_CALL(mstats, getStats).WillRepeatedly(testing::Return(&stats));
stats.setLevel(StatsLevel::Basic);
Context<StatsCollection>::get()->bufferRegistration();
ASSERT_EQ(1, stats.getBufferRegistrations().load());
Context<StatsCollection>::get()->bufferRegistration();
ASSERT_EQ(2, stats.getBufferRegistrations().load());
stats.setLevel(StatsLevel::Disabled);
Context<StatsCollection>::get()->bufferRegistration();
ASSERT_EQ(2, stats.getBufferRegistrations().load());
}
TEST_F(HipFileStats, StatsCollectionFastpathRejection)
{
Stats stats{};
StrictMock<MStatsServer> mstats{};
EXPECT_CALL(mstats, getStats).WillRepeatedly(testing::Return(&stats));
stats.setLevel(StatsLevel::Basic);
Context<StatsCollection>::get()->fastpathRejection();
ASSERT_EQ(1, stats.getFastpathRejections().load());
Context<StatsCollection>::get()->fastpathRejection();
ASSERT_EQ(2, stats.getFastpathRejections().load());
stats.setLevel(StatsLevel::Disabled);
Context<StatsCollection>::get()->fastpathRejection();
ASSERT_EQ(2, stats.getFastpathRejections().load());
}
TEST_F(HipFileStats, StatsContainer)
{
StrictMock<MSys> msys{};
StrictMock<MConfiguration> mcfg{};
alignas(Stats) char buff[sizeof(Stats)];
EXPECT_CALL(msys, memfd_create).WillOnce(testing::Return(10));
EXPECT_CALL(msys, fcntl).WillOnce(testing::Return(0));
EXPECT_CALL(msys, ftruncate);
EXPECT_CALL(msys, mmap).WillOnce(testing::Return(&buff));
EXPECT_CALL(msys, munmap).Times(1);
EXPECT_CALL(msys, close).Times(1);
EXPECT_CALL(mcfg, statsLevel()).WillOnce(testing::Return(1));
StatsContainer sc{};
ASSERT_EQ(reinterpret_cast<Stats *>(&buff), sc.getStats());
ASSERT_EQ(10, sc.getFd());
}
TEST_F(HipFileStats, StatsContainerNoF_SEAL_FUTURE_WRITE)
{
StrictMock<MSys> msys{};
StrictMock<MConfiguration> mcfg{};
EXPECT_CALL(msys, memfd_create).WillOnce(testing::Return(10));
EXPECT_CALL(mcfg, statsLevel()).WillOnce(testing::Return(1));
EXPECT_CALL(msys, ftruncate);
EXPECT_CALL(msys, mmap).WillOnce(testing::Return(nullptr));
EXPECT_CALL(msys, fcntl).WillOnce(testing::Throw(std::system_error(EINVAL, std::generic_category())));
EXPECT_CALL(msys, munmap).Times(1);
EXPECT_CALL(msys, close).Times(1);
StatsContainer sc{};
ASSERT_EQ(nullptr, sc.getStats());
}
TEST_F(HipFileStats, GenerateReportV1)
{
Stats stats{};
std::ostringstream os{};
stats.getPerGpuStats(0, StatsBackend::Fastpath)
->ioSizeBytes[static_cast<size_t>(IoType::Read)]
.buckets[0] = 2;
stats.getPerGpuStats(0, StatsBackend::Fastpath)
->ioSizeBytes[static_cast<size_t>(IoType::Write)]
.buckets[0] = 4;
stats.getPerGpuStats(0, StatsBackend::Fallback)
->ioSizeBytes[static_cast<size_t>(IoType::Read)]
.buckets[0] = 6;
stats.getPerGpuStats(0, StatsBackend::Fallback)
->ioSizeBytes[static_cast<size_t>(IoType::Write)]
.buckets[0] = 8;
stats.getPerGpuStats(0, StatsBackend::Fastpath)
->errorCount[static_cast<size_t>(IoType::Read)]
.buckets[0] = 1;
stats.getPerGpuStats(0, StatsBackend::Fastpath)
->errorCount[static_cast<size_t>(IoType::Write)]
.buckets[0] = 2;
stats.getPerGpuStats(0, StatsBackend::Fallback)
->errorCount[static_cast<size_t>(IoType::Read)]
.buckets[0] = 3;
stats.getPerGpuStats(0, StatsBackend::Fallback)
->errorCount[static_cast<size_t>(IoType::Write)]
.buckets[0] = 4;
stats.getBufferRegistrations() = 10;
stats.getFileRegistrations() = 20;
stats.getFastpathRejections() = 30;
stats.getPerGpuStats(0, StatsBackend::Fallback)->unalignedCount[static_cast<size_t>(IoType::Read)] = 40;
stats.getPerGpuStats(0, StatsBackend::Fallback)->unalignedCount[static_cast<size_t>(IoType::Write)] = 50;
StatsClient::generateReportV1(os, &stats);
std::string str{os.str()};
ASSERT_GT(std::string::npos, str.find("Total Fastpath Read Size (B): 2"));
ASSERT_GT(std::string::npos, str.find("Total Fastpath Write Size (B): 4"));
ASSERT_GT(std::string::npos, str.find("Total Fallback Read Size (B): 6"));
ASSERT_GT(std::string::npos, str.find("Total Fallback Write Size (B): 8"));
ASSERT_GT(std::string::npos, str.find("Total Fastpath Read Errors: 1"));
ASSERT_GT(std::string::npos, str.find("Total Fastpath Write Errors: 2"));
ASSERT_GT(std::string::npos, str.find("Total Fallback Read Errors: 3"));
ASSERT_GT(std::string::npos, str.find("Total Fallback Write Errors: 4"));
ASSERT_GT(std::string::npos, str.find("Buffer Registrations: 10"));
ASSERT_GT(std::string::npos, str.find("File Handle Registrations: 20"));
ASSERT_GT(std::string::npos, str.find("Fastpath Rejections: 30"));
ASSERT_GT(std::string::npos, str.find("Total Fallback Read Unaligned: 40"));
ASSERT_GT(std::string::npos, str.find("Total Fallback Write Unaligned: 50"));
}
TEST_F(HipFileStats, GenerateReportV1TwoGpus)
{
Stats stats{};
std::ostringstream os{};
stats.getPerGpuStats(0, StatsBackend::Fastpath)
->ioSizeBytes[static_cast<size_t>(IoType::Read)]
.buckets[0] = 2;
stats.getPerGpuStats(1, StatsBackend::Fastpath)
->ioSizeBytes[static_cast<size_t>(IoType::Read)]
.buckets[0] = 4;
stats.getPerGpuStats(0, StatsBackend::Fastpath)
->errorCount[static_cast<size_t>(IoType::Read)]
.buckets[0] = 1;
stats.getPerGpuStats(1, StatsBackend::Fastpath)
->errorCount[static_cast<size_t>(IoType::Read)]
.buckets[0] = 2;
StatsClient::generateReportV1(os, &stats);
std::string str{os.str()};
ASSERT_GT(std::string::npos, str.find("Total Fastpath Read Size (B): 6"));
ASSERT_GT(std::string::npos, str.find("Total Fastpath Read Errors: 3"));
}
struct HipFileStatsHistogram : public HipFileUnopened {};
TEST_F(HipFileStatsHistogram, ToHistogramBucket)
{
ASSERT_EQ(0, StatsHistogram::toHistogramBucket(0));
ASSERT_EQ(0, StatsHistogram::toHistogramBucket(1));
ASSERT_EQ(0, StatsHistogram::toHistogramBucket(4095));
ASSERT_EQ(1, StatsHistogram::toHistogramBucket(4096));
ASSERT_EQ(1, StatsHistogram::toHistogramBucket(8191));
ASSERT_EQ(2, StatsHistogram::toHistogramBucket(8192));
ASSERT_EQ(2, StatsHistogram::toHistogramBucket(16383));
ASSERT_EQ(3, StatsHistogram::toHistogramBucket(16384));
ASSERT_EQ(14, StatsHistogram::toHistogramBucket(uint64_t{1} << 25));
ASSERT_EQ(15, StatsHistogram::toHistogramBucket(uint64_t{1} << 30));
ASSERT_EQ(15, StatsHistogram::toHistogramBucket(uint64_t{1} << 40));
}
TEST_F(HipFileStatsHistogram, BucketRange)
{
auto [lower0, upper0] = StatsHistogram::bucketRange(0);
ASSERT_EQ(0, lower0);
ASSERT_EQ(4096, upper0);
auto [lower1, upper1] = StatsHistogram::bucketRange(1);
ASSERT_EQ(4096, lower1);
ASSERT_EQ(8192, upper1);
auto [lower2, upper2] = StatsHistogram::bucketRange(2);
ASSERT_EQ(8192, lower2);
ASSERT_EQ(16384, upper2);
auto [lower3, upper3] = StatsHistogram::bucketRange(3);
ASSERT_EQ(16384, lower3);
ASSERT_EQ(32768, upper3);
auto [lower14, upper14] = StatsHistogram::bucketRange(14);
ASSERT_EQ(uint64_t{1} << 25, lower14);
ASSERT_EQ(uint64_t{1} << 26, upper14);
auto [lower15, upper15] = StatsHistogram::bucketRange(15);
ASSERT_EQ(uint64_t{1} << 26, lower15);
ASSERT_EQ(UINT64_MAX, upper15);
}
struct HipFileStatsPerGpuStatsV1 : public HipFileUnopened {};
TEST_F(HipFileStatsPerGpuStatsV1, GetHistograms)
{
PerGpuStatsV1 stats{};
auto [readSize, readCount, readTime, readError] = stats.getHistograms(IoType::Read);
auto [writeSize, writeCount, writeTime, writeError] = stats.getHistograms(IoType::Write);
auto [invalidSize, invalidCount, invalidTime, invalidError] =
// NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange): invalid value tests rejection
stats.getHistograms(static_cast<IoType>(-1));
ASSERT_EQ(&stats.ioSizeBytes[0], readSize);
ASSERT_EQ(&stats.ioCount[0], readCount);
ASSERT_EQ(&stats.ioTimeUs[0], readTime);
ASSERT_EQ(&stats.errorCount[0], readError);
ASSERT_EQ(&stats.ioSizeBytes[1], writeSize);
ASSERT_EQ(&stats.ioCount[1], writeCount);
ASSERT_EQ(&stats.ioTimeUs[1], writeTime);
ASSERT_EQ(&stats.errorCount[1], writeError);
ASSERT_EQ(nullptr, invalidSize);
ASSERT_EQ(nullptr, invalidCount);
ASSERT_EQ(nullptr, invalidTime);
ASSERT_EQ(nullptr, invalidError);
}
struct HipFileStatsV1 : public HipFileUnopened {};
TEST_F(HipFileStatsV1, getPerGpuStats)
{
StatsV1 stats{};
ASSERT_EQ(nullptr, stats.getPerGpuStats(StatsV1::MaxGpus, StatsBackend::Fastpath));
// NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange): invalid value tests rejection
ASSERT_EQ(nullptr, stats.getPerGpuStats(0, static_cast<StatsBackend>(-1)));
}
HIPFILE_WARN_NO_GLOBAL_CTOR_ON