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
254 lines
9.7 KiB
C++
254 lines
9.7 KiB
C++
/*
|
|
* Copyright © Advanced Micro Devices, Inc., or its affiliates.
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include "suites/functional/signal_allocation_validation.h"
|
|
#include "common/base_rocr_utils.h"
|
|
#include "common/common.h"
|
|
#include "common/helper_funcs.h"
|
|
#include "gtest/gtest.h"
|
|
#include "hsa/hsa.h"
|
|
|
|
// Target number of signals to allocate for stress testing
|
|
static const uint32_t TARGET_SIGNAL_COUNT = 100000;
|
|
|
|
SignalAllocationValidationTest::SignalAllocationValidationTest()
|
|
: TestBase() {
|
|
set_num_iteration(10); // Number of iterations to execute of the main test;
|
|
// This is a default value which can be overridden
|
|
// on the command line.
|
|
set_title("RocR Signal Allocation Validation Test");
|
|
set_description("This test verifies that hsa_signal_create returns valid signal handles when HSA_STATUS_SUCCESS is returned, even under resource pressure");
|
|
}
|
|
|
|
SignalAllocationValidationTest::~SignalAllocationValidationTest(void) {
|
|
}
|
|
|
|
// Any 1-time setup involving member variables used in the rest of the test
|
|
// should be done here.
|
|
void SignalAllocationValidationTest::SetUp(void) {
|
|
hsa_status_t err;
|
|
|
|
TestBase::SetUp();
|
|
if (test_skipped_) return;
|
|
|
|
err = rocrtst::SetDefaultAgents(this);
|
|
ASSERT_EQ(HSA_STATUS_SUCCESS, err);
|
|
|
|
err = rocrtst::SetPoolsTypical(this);
|
|
ASSERT_EQ(HSA_STATUS_SUCCESS, err);
|
|
return;
|
|
}
|
|
|
|
void SignalAllocationValidationTest::Run(void) {
|
|
if (!rocrtst::CheckProfile(this)) {
|
|
return;
|
|
}
|
|
|
|
TestBase::Run();
|
|
}
|
|
|
|
void SignalAllocationValidationTest::DisplayTestInfo(void) {
|
|
TestBase::DisplayTestInfo();
|
|
}
|
|
|
|
void SignalAllocationValidationTest::DisplayResults(void) const {
|
|
// Compare required profile for this test case with what we're actually
|
|
// running on
|
|
if (!rocrtst::CheckProfile(this)) {
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void SignalAllocationValidationTest::Close() {
|
|
// This will close handles opened within rocrtst utility calls and call
|
|
// hsa_shut_down(), so it should be done after other hsa cleanup
|
|
TestBase::Close();
|
|
}
|
|
|
|
/*
|
|
* Test Name: TestSignalAllocationValidation
|
|
* Scope: Conformance
|
|
*
|
|
* Purpose: Verifies that hsa_signal_create never returns HSA_STATUS_SUCCESS
|
|
* with an invalid (zero) signal handle, even when allocating a very large
|
|
* number of signals.
|
|
*
|
|
* Test Description:
|
|
* 1) Allocate a very large number of signals (approximately 100,000)
|
|
* 2) For each allocation attempt:
|
|
* a) If hsa_signal_create returns HSA_STATUS_SUCCESS, verify that
|
|
* the returned signal handle is valid (non-zero)
|
|
* b) If hsa_signal_create returns HSA_STATUS_ERROR_OUT_OF_RESOURCES,
|
|
* this is expected behavior when resources are exhausted - stop allocating
|
|
* c) Track all successfully allocated signals for cleanup
|
|
* 3) Report any cases where HSA_STATUS_SUCCESS was returned with an invalid handle
|
|
* 4) Destroy all successfully allocated signals
|
|
*
|
|
* Expected Results: If hsa_signal_create returns HSA_STATUS_SUCCESS, the signal
|
|
* handle must be non-zero.
|
|
*/
|
|
void SignalAllocationValidationTest::TestSignalAllocationValidation(void) {
|
|
hsa_status_t status;
|
|
std::vector<hsa_signal_t> allocated_signals;
|
|
uint32_t invalid_handle_count = 0;
|
|
uint32_t success_count = 0;
|
|
uint32_t failure_count = 0;
|
|
uint32_t i;
|
|
|
|
std::cout << "Attempting to allocate " << TARGET_SIGNAL_COUNT << " signals..." << std::endl;
|
|
|
|
// Reserve space to avoid reallocations during the test
|
|
allocated_signals.reserve(TARGET_SIGNAL_COUNT);
|
|
|
|
// Attempt to allocate TARGET_SIGNAL_COUNT signals
|
|
for (i = 0; i < TARGET_SIGNAL_COUNT; ++i) {
|
|
hsa_signal_t signal;
|
|
status = hsa_signal_create(1, 0, NULL, &signal);
|
|
|
|
if (status == HSA_STATUS_SUCCESS) {
|
|
success_count++;
|
|
|
|
// This is the critical check: if HSA_STATUS_SUCCESS is returned,
|
|
// the signal handle MUST be valid (non-zero)
|
|
if (signal.handle == 0) {
|
|
invalid_handle_count++;
|
|
std::cout << "ERROR: hsa_signal_create returned HSA_STATUS_SUCCESS but signal handle is 0 at allocation "
|
|
<< i << std::endl;
|
|
// This is the bug we're testing for - should never happen after the fix
|
|
FAIL() << "Signal allocation returned success with invalid (zero) handle";
|
|
} else {
|
|
// Valid signal, add to our tracking list
|
|
allocated_signals.push_back(signal);
|
|
}
|
|
} else if (status == HSA_STATUS_ERROR_OUT_OF_RESOURCES) {
|
|
// This is expected when we run out of signal resources
|
|
failure_count++;
|
|
std::cout << "Signal allocation failed with HSA_STATUS_ERROR_OUT_OF_RESOURCES after "
|
|
<< success_count << " successful allocations (expected under resource pressure)" << std::endl;
|
|
break;
|
|
} else {
|
|
// Unexpected error code
|
|
failure_count++;
|
|
std::cout << "ERROR: Unexpected status code " << status << " at allocation " << i << std::endl;
|
|
FAIL() << "hsa_signal_create returned unexpected status code: " << status;
|
|
}
|
|
|
|
// Print progress every 10,000 signals
|
|
if ((i + 1) % 10000 == 0) {
|
|
if(verbosity() > 0) {
|
|
std::cout << "Allocated " << (i + 1) << " signals..." << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::cout << "\nAllocation Summary:" << std::endl;
|
|
std::cout << " Successful allocations: " << success_count << std::endl;
|
|
std::cout << " Failed allocations: " << failure_count << std::endl;
|
|
std::cout << " Invalid handles on success: " << invalid_handle_count << std::endl;
|
|
|
|
// The main assertion: we should never have invalid handles when success is returned
|
|
ASSERT_EQ(invalid_handle_count, 0) << "Found " << invalid_handle_count
|
|
<< " cases where HSA_STATUS_SUCCESS was returned with invalid handle";
|
|
|
|
// Cleanup: destroy all successfully allocated signals
|
|
std::cout << "Cleaning up " << allocated_signals.size() << " signals..." << std::endl;
|
|
for (i = 0; i < allocated_signals.size(); ++i) {
|
|
status = hsa_signal_destroy(allocated_signals[i]);
|
|
ASSERT_EQ(HSA_STATUS_SUCCESS, status) << "Failed to destroy signal " << i;
|
|
|
|
// Print cleanup progress every 10,000 signals
|
|
if ((i + 1) % 10000 == 0) {
|
|
if(verbosity() > 0) {
|
|
std::cout << "Destroyed " << (i + 1) << " signals..." << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Second pass: re-allocate signals to verify resource reclamation.
|
|
// After destroying all signals from the first pass, we should be able
|
|
// to allocate a similar number of signals again. This validates that
|
|
// the runtime properly reclaims signal resources on destruction.
|
|
const uint32_t first_pass_success_count = success_count;
|
|
allocated_signals.clear();
|
|
uint32_t second_pass_success_count = 0;
|
|
uint32_t second_pass_failure_count = 0;
|
|
uint32_t second_pass_invalid_handle_count = 0;
|
|
|
|
std::cout << "\nSecond pass: re-allocating signals to test resource reclamation..." << std::endl;
|
|
|
|
for (i = 0; i < TARGET_SIGNAL_COUNT; ++i) {
|
|
hsa_signal_t signal;
|
|
status = hsa_signal_create(1, 0, NULL, &signal);
|
|
|
|
if (status == HSA_STATUS_SUCCESS) {
|
|
second_pass_success_count++;
|
|
|
|
if (signal.handle == 0) {
|
|
second_pass_invalid_handle_count++;
|
|
std::cout << "ERROR: hsa_signal_create returned HSA_STATUS_SUCCESS but signal handle is 0 at allocation "
|
|
<< i << " (second pass)" << std::endl;
|
|
FAIL() << "Signal allocation returned success with invalid (zero) handle (second pass)";
|
|
} else {
|
|
allocated_signals.push_back(signal);
|
|
}
|
|
} else if (status == HSA_STATUS_ERROR_OUT_OF_RESOURCES) {
|
|
second_pass_failure_count++;
|
|
std::cout << "Signal allocation failed with HSA_STATUS_ERROR_OUT_OF_RESOURCES after "
|
|
<< second_pass_success_count << " successful allocations on second pass" << std::endl;
|
|
break;
|
|
} else {
|
|
second_pass_failure_count++;
|
|
std::cout << "ERROR: Unexpected status code " << status << " at allocation " << i
|
|
<< " (second pass)" << std::endl;
|
|
FAIL() << "hsa_signal_create returned unexpected status code on second pass: " << status;
|
|
}
|
|
|
|
if ((i + 1) % 10000 == 0) {
|
|
if(verbosity() > 0) {
|
|
std::cout << "Allocated " << (i + 1) << " signals (second pass)..." << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::cout << "\nSecond Pass Allocation Summary:" << std::endl;
|
|
std::cout << " Successful allocations: " << second_pass_success_count << std::endl;
|
|
std::cout << " Failed allocations: " << second_pass_failure_count << std::endl;
|
|
std::cout << " Invalid handles on success: " << second_pass_invalid_handle_count << std::endl;
|
|
|
|
ASSERT_EQ(second_pass_invalid_handle_count, 0u)
|
|
<< "Found invalid handles on second pass";
|
|
|
|
// Verify that resource reclamation worked: the second pass should be able
|
|
// to allocate at least as many signals as the first pass (allowing for some
|
|
// small variation due to other runtime activity).
|
|
EXPECT_GE(second_pass_success_count, first_pass_success_count)
|
|
<< "Second pass allocated fewer signals than first pass ("
|
|
<< second_pass_success_count << " vs " << first_pass_success_count
|
|
<< "), indicating possible resource leak or incomplete reclamation";
|
|
|
|
// Cleanup: destroy all signals allocated in the second pass
|
|
std::cout << "Cleaning up " << allocated_signals.size()
|
|
<< " signals from second pass..." << std::endl;
|
|
for (i = 0; i < allocated_signals.size(); ++i) {
|
|
status = hsa_signal_destroy(allocated_signals[i]);
|
|
ASSERT_EQ(HSA_STATUS_SUCCESS, status)
|
|
<< "Failed to destroy signal " << i << " (second pass)";
|
|
|
|
if ((i + 1) % 10000 == 0) {
|
|
if(verbosity() > 0) {
|
|
std::cout << "Destroyed " << (i + 1) << " signals (second pass)..." << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::cout << "Test completed successfully - all signal allocations returned valid handles" << std::endl;
|
|
}
|