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
397 lines
13 KiB
C++
397 lines
13 KiB
C++
/* ************************************************************************
|
|
* Copyright (C) 2018-2026 Advanced Micro Devices, Inc. All rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*
|
|
* ************************************************************************ */
|
|
|
|
#include "utility.hpp"
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <hip/hip_runtime_api.h>
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
#include "hipsparse_parse_data.hpp"
|
|
#include "program_options.hpp"
|
|
|
|
#include <csignal>
|
|
|
|
using testing::InitGoogleTest;
|
|
using testing::TestCase;
|
|
using testing::TestEventListener;
|
|
using testing::TestInfo;
|
|
using testing::TestPartResult;
|
|
using testing::UnitTest;
|
|
|
|
// Log device VRAM usage. A failed hipMemGetInfo is a strong signal that the
|
|
// device has been lost (rather than merely out of memory).
|
|
static bool hipsparse_log_device_memory(const char* context)
|
|
{
|
|
size_t free_mem = 0;
|
|
size_t total_mem = 0;
|
|
hipError_t status = hipMemGetInfo(&free_mem, &total_mem);
|
|
if(status == hipSuccess)
|
|
{
|
|
fprintf(stderr,
|
|
"[ MEMORY ] %s: device free/total = %zu/%zu MB\n",
|
|
context,
|
|
free_mem >> 20,
|
|
total_mem >> 20);
|
|
return true;
|
|
}
|
|
|
|
fprintf(stderr,
|
|
"[ MEMORY ] %s: hipMemGetInfo failed: hip error %d (%s) -- device may be lost\n",
|
|
context,
|
|
status,
|
|
hipGetErrorString(status));
|
|
return false;
|
|
}
|
|
|
|
// Print the currently running test on a fatal signal so a single crashed CI run
|
|
// is diagnosable without a rerun, then re-raise with the default handler.
|
|
extern "C" void hipsparse_fatal_signal_handler(int sig)
|
|
{
|
|
const char* sig_name = (sig == SIGSEGV) ? "SIGSEGV" : (sig == SIGABRT) ? "SIGABRT" : "signal";
|
|
|
|
const TestInfo* info = UnitTest::GetInstance()->current_test_info();
|
|
if(info != nullptr)
|
|
{
|
|
fprintf(stderr,
|
|
"\n[ FATAL ] hipsparse-test TERMINATED BY %s during test: %s.%s\n",
|
|
sig_name,
|
|
info->test_case_name(),
|
|
info->name());
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr,
|
|
"\n[ FATAL ] hipsparse-test TERMINATED BY %s (no test currently running)\n",
|
|
sig_name);
|
|
}
|
|
fflush(stderr);
|
|
|
|
// Restore the default disposition and re-raise so the process exit status
|
|
// still reflects the original signal.
|
|
signal(sig, SIG_DFL);
|
|
raise(sig);
|
|
}
|
|
|
|
class ConfigurableEventListener : public TestEventListener
|
|
{
|
|
TestEventListener* eventListener;
|
|
|
|
public:
|
|
bool showTestCases; // Show the names of each test case.
|
|
bool showTestNames; // Show the names of each test.
|
|
bool showSuccesses; // Show each success.
|
|
bool showInlineFailures; // Show each failure as it occurs.
|
|
bool showEnvironment; // Show the setup of the global environment.
|
|
|
|
explicit ConfigurableEventListener(TestEventListener* theEventListener)
|
|
: eventListener(theEventListener)
|
|
, showTestCases(true)
|
|
, showTestNames(true)
|
|
, showSuccesses(true)
|
|
, showInlineFailures(true)
|
|
, showEnvironment(true)
|
|
{
|
|
}
|
|
|
|
~ConfigurableEventListener() override
|
|
{
|
|
delete eventListener;
|
|
}
|
|
|
|
void OnTestProgramStart(const UnitTest& unit_test) override
|
|
{
|
|
eventListener->OnTestProgramStart(unit_test);
|
|
hipsparse_log_device_memory("test program start");
|
|
}
|
|
|
|
void OnTestIterationStart(const UnitTest& unit_test, int iteration) override
|
|
{
|
|
eventListener->OnTestIterationStart(unit_test, iteration);
|
|
}
|
|
|
|
void OnEnvironmentsSetUpStart(const UnitTest& unit_test) override
|
|
{
|
|
if(showEnvironment)
|
|
{
|
|
eventListener->OnEnvironmentsSetUpStart(unit_test);
|
|
}
|
|
}
|
|
|
|
void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) override
|
|
{
|
|
if(showEnvironment)
|
|
{
|
|
eventListener->OnEnvironmentsSetUpEnd(unit_test);
|
|
}
|
|
}
|
|
|
|
void OnTestCaseStart(const TestCase& test_case) override
|
|
{
|
|
if(showTestCases)
|
|
{
|
|
eventListener->OnTestCaseStart(test_case);
|
|
}
|
|
}
|
|
|
|
void OnTestStart(const TestInfo& test_info) override
|
|
{
|
|
if(showTestNames)
|
|
{
|
|
eventListener->OnTestStart(test_info);
|
|
}
|
|
}
|
|
|
|
void OnTestPartResult(const TestPartResult& result) override
|
|
{
|
|
eventListener->OnTestPartResult(result);
|
|
|
|
// On the first failure, capture device memory state. This both proves
|
|
// the tests themselves are not the memory consumer and detects a device
|
|
// that has collapsed mid-run. If the device is lost, abort the whole run
|
|
// so the log shows one clear fault instead of a long cascade of
|
|
// dependent failures (and a likely SIGSEGV) on the dead device.
|
|
if(result.failed())
|
|
{
|
|
const bool device_alive = hipsparse_log_device_memory("on test failure");
|
|
if(!device_alive)
|
|
{
|
|
fprintf(stderr,
|
|
"[ DEVICE ] Device appears lost after a test failure; aborting the test "
|
|
"run to avoid a cascade of dependent failures.\n");
|
|
fflush(stderr);
|
|
// _Exit avoids running static destructors that may themselves
|
|
// fault while the device is in a bad state.
|
|
std::_Exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnTestEnd(const TestInfo& test_info) override
|
|
{
|
|
if(test_info.result()->Failed() ? showInlineFailures : showSuccesses)
|
|
{
|
|
eventListener->OnTestEnd(test_info);
|
|
}
|
|
}
|
|
|
|
void OnTestCaseEnd(const TestCase& test_case) override
|
|
{
|
|
if(showTestCases)
|
|
{
|
|
eventListener->OnTestCaseEnd(test_case);
|
|
}
|
|
}
|
|
|
|
void OnEnvironmentsTearDownStart(const UnitTest& unit_test) override
|
|
{
|
|
if(showEnvironment)
|
|
{
|
|
eventListener->OnEnvironmentsTearDownStart(unit_test);
|
|
}
|
|
}
|
|
|
|
void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) override
|
|
{
|
|
if(showEnvironment)
|
|
{
|
|
eventListener->OnEnvironmentsTearDownEnd(unit_test);
|
|
}
|
|
}
|
|
|
|
void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override
|
|
{
|
|
eventListener->OnTestIterationEnd(unit_test, iteration);
|
|
}
|
|
|
|
void OnTestProgramEnd(const UnitTest& unit_test) override
|
|
{
|
|
eventListener->OnTestProgramEnd(unit_test);
|
|
}
|
|
};
|
|
|
|
hipsparseStatus_t hipsparse_record_output_legend(const std::string& s)
|
|
{
|
|
return HIPSPARSE_STATUS_SUCCESS;
|
|
}
|
|
|
|
hipsparseStatus_t hipsparse_record_output(const std::string& s)
|
|
{
|
|
return HIPSPARSE_STATUS_SUCCESS;
|
|
}
|
|
|
|
hipsparseStatus_t hipsparse_record_timing(double msec, double gflops, double gbs)
|
|
{
|
|
return HIPSPARSE_STATUS_SUCCESS;
|
|
}
|
|
|
|
bool display_timing_info_is_stdout_disabled()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* =====================================================================
|
|
Main function:
|
|
=================================================================== */
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
// Parse hipsparse-test options. Unknown options (e.g. --gtest_*) are
|
|
// ignored here so they can be forwarded to InitGoogleTest below. The
|
|
// backing storage for matrices_dir is a function-scope static so that
|
|
// s_hipsparse_clients_matrices_dir (a const char*) remains valid for the
|
|
// entire program lifetime.
|
|
std::string matrices_dir;
|
|
int device_id = 0;
|
|
|
|
options_description desc("hipSPARSE test command line options");
|
|
// clang-format off
|
|
desc.add_options()
|
|
("help,h",
|
|
"Produces this help message and exits.")
|
|
("version,v",
|
|
"Prints the hipSPARSE version and exits.")
|
|
("device,d",
|
|
value<int>(&device_id)->default_value(0),
|
|
"Set the default device to use for the tests.")
|
|
("matrices-dir",
|
|
value<std::string>(&matrices_dir),
|
|
"Path to the directory containing the test matrix input files. "
|
|
"Overrides the HIPSPARSE_CLIENTS_MATRICES_DIR environment variable "
|
|
"when both are specified.");
|
|
// clang-format on
|
|
|
|
variables_map vm;
|
|
try
|
|
{
|
|
store(parse_command_line(argc, argv, desc, /*ignoreUnknown=*/true), vm);
|
|
notify(vm);
|
|
}
|
|
catch(const std::exception& e)
|
|
{
|
|
fprintf(stderr, "Error parsing command line: %s\n", e.what());
|
|
return -1;
|
|
}
|
|
|
|
if(vm.count("help"))
|
|
{
|
|
std::cout << "Usage: " << argv[0] << " [hipsparse-test options] [GoogleTest options]\n\n"
|
|
<< desc
|
|
<< "\nAny options not listed above (e.g. --gtest_filter, "
|
|
"--gtest_list_tests) are forwarded to GoogleTest.\n"
|
|
"To specify the directory of matrix input files, the user can either "
|
|
"export the environment variable HIPSPARSE_CLIENTS_MATRICES_DIR or "
|
|
"use the command line option '--matrices-dir'. If '--matrices-dir' "
|
|
"is used then the environment variable is ignored."
|
|
<< std::endl;
|
|
return 0;
|
|
}
|
|
|
|
// Set matrix directory
|
|
if(!matrices_dir.empty())
|
|
{
|
|
s_hipsparse_clients_matrices_dir = matrices_dir.c_str();
|
|
}
|
|
|
|
// Validate and select the requested HIP device before any subsequent
|
|
// hipSPARSE / HIP API call.
|
|
int device_count = query_device_property();
|
|
|
|
if(device_id < 0 || device_count <= device_id)
|
|
{
|
|
fprintf(stderr,
|
|
"Error: invalid device ID %d (detected %d device(s)). Will exit.\n",
|
|
device_id,
|
|
device_count);
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
set_device(device_id);
|
|
}
|
|
|
|
// Query the hipSPARSE version on the selected device.
|
|
char version[512];
|
|
query_version(version);
|
|
|
|
if(vm.count("version"))
|
|
{
|
|
printf("hipSPARSE version: %s\n", version);
|
|
return 0;
|
|
}
|
|
|
|
printf("hipSPARSE version: %s\n", version);
|
|
|
|
std::string datapath = hipsparse_datapath();
|
|
|
|
// Print test data path being used
|
|
std::cout << "hipSPARSE data path: " << datapath << std::endl;
|
|
|
|
// Set data file path
|
|
hipsparse_parse_data(datapath + "hipsparse_test.data");
|
|
|
|
// Initialize google test
|
|
InitGoogleTest(&argc, argv);
|
|
|
|
// Remove the default listener
|
|
auto& listeners = UnitTest::GetInstance()->listeners();
|
|
auto default_printer = listeners.Release(listeners.default_result_printer());
|
|
|
|
// Add our listener, by default everything is on (the same as using the default listener)
|
|
// Here turning everything off so only the 3 lines for the result are visible
|
|
// (plus any failures at the end), like:
|
|
|
|
// [==========] Running 149 tests from 53 test cases.
|
|
// [==========] 149 tests from 53 test cases ran. (1 ms total)
|
|
// [ PASSED ] 149 tests.
|
|
//
|
|
auto listener = new ConfigurableEventListener(default_printer);
|
|
auto gtest_listener = getenv("GTEST_LISTENER");
|
|
|
|
if(gtest_listener && !strcmp(gtest_listener, "NO_PASS_LINE_IN_LOG"))
|
|
{
|
|
listener->showTestNames = listener->showSuccesses = listener->showInlineFailures = false;
|
|
}
|
|
|
|
listeners.Append(listener);
|
|
|
|
// Install fatal-signal handlers so a crash (e.g. a dereference of an
|
|
// un-initialized device buffer after an allocation failure) still records
|
|
// the test that was running before the process dies.
|
|
signal(SIGSEGV, hipsparse_fatal_signal_handler);
|
|
signal(SIGABRT, hipsparse_fatal_signal_handler);
|
|
|
|
// Run all tests
|
|
int ret = RUN_ALL_TESTS();
|
|
|
|
// Reset HIP device
|
|
(void)hipDeviceReset();
|
|
|
|
return ret;
|
|
}
|