| // This file is part of Eigen, a lightweight C++ template library |
| // for linear algebra. |
| // |
| // This Source Code Form is subject to the terms of the Mozilla |
| // Public License v. 2.0. If a copy of the MPL was not distributed |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| // SPDX-FileCopyrightText: The Eigen Authors |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| // A failed runtime call or kernel launch inside GpuDevice has to reach EIGEN_GPU_RUNTIME_CHECK, in a release build |
| // as much as in a debug one (tensor_gpu_runtime_check_ndebug.cu compiles this file with EIGEN_NO_DEBUG). The macro |
| // is overridden here to record the failure instead of stopping the process. |
| |
| #define EIGEN_TEST_NO_LONGDOUBLE |
| #define EIGEN_TEST_NO_COMPLEX |
| |
| #define EIGEN_USE_GPU |
| |
| #include <string> |
| |
| struct RecordedGpuError { |
| int code = 0; |
| std::string expression; |
| std::string file; |
| int line = 0; |
| }; |
| static RecordedGpuError g_last_gpu_error; |
| static int g_num_gpu_errors = 0; |
| |
| // cudaSuccess and hipSuccess are both 0. |
| template <typename Error> |
| __host__ void record_gpu_runtime_error(Error error, const char* expression, const char* file, int line) { |
| if (static_cast<int>(error) == 0) return; |
| ++g_num_gpu_errors; |
| g_last_gpu_error.code = static_cast<int>(error); |
| g_last_gpu_error.expression = expression; |
| g_last_gpu_error.file = file; |
| g_last_gpu_error.line = line; |
| } |
| #define EIGEN_GPU_RUNTIME_CHECK(expr) ::record_gpu_runtime_error((expr), #expr, __FILE__, __LINE__) |
| |
| #include "main.h" |
| #include <contrib/Eigen/Tensor> |
| |
| #if defined(EIGEN_USE_HIP) |
| static const int kInvalidConfiguration = static_cast<int>(hipErrorInvalidConfiguration); |
| static const int kInvalidValue = static_cast<int>(hipErrorInvalidValue); |
| #else |
| static const int kInvalidConfiguration = static_cast<int>(cudaErrorInvalidConfiguration); |
| static const int kInvalidValue = static_cast<int>(cudaErrorInvalidValue); |
| #endif |
| |
| __global__ void write_one_kernel(int* out) { |
| if (blockIdx.x == 0 && threadIdx.x == 0) *out = 1; |
| } |
| |
| static bool ends_with(const std::string& s, const std::string& suffix) { |
| return s.size() >= suffix.size() && s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0; |
| } |
| |
| // A valid launch through LAUNCH_GPU_KERNEL runs and records nothing; one with more threads per block than any |
| // device allows is refused at enqueue time, and the refusal is reported from gpu_launch's own check. |
| static void test_launch_check() { |
| Eigen::GpuStreamDevice stream; |
| Eigen::GpuDevice device(&stream); |
| int* d_out = static_cast<int*>(device.allocate(sizeof(int))); |
| device.memset(d_out, 0, sizeof(int)); |
| g_num_gpu_errors = 0; |
| |
| LAUNCH_GPU_KERNEL(write_one_kernel, 1, 32, 0, device, d_out); |
| int out = 0; |
| device.memcpyDeviceToHost(&out, d_out, sizeof(int)); |
| device.synchronize(); |
| VERIFY_IS_EQUAL(out, 1); |
| VERIFY_IS_EQUAL(g_num_gpu_errors, 0); |
| |
| const int too_many_threads = 2 * device.maxGpuThreadsPerBlock(); |
| LAUNCH_GPU_KERNEL(write_one_kernel, 1, too_many_threads, 0, device, d_out); |
| VERIFY_IS_EQUAL(g_num_gpu_errors, 1); |
| // Which of the two codes an oversize block gets depends on the toolkit: CUDA 13.3 reports invalid value. |
| VERIFY(g_last_gpu_error.code == kInvalidConfiguration || g_last_gpu_error.code == kInvalidValue); |
| VERIFY_IS_EQUAL(g_last_gpu_error.expression, std::string("gpuGetLastError()")); |
| VERIFY(ends_with(g_last_gpu_error.file, "GpuRuntime.h")); |
| |
| device.deallocate(d_out); |
| VERIFY_IS_EQUAL(g_num_gpu_errors, 1); |
| } |
| |
| // A runtime call that fails inside GpuDevice is reported with the call's own text and location. |
| static void test_runtime_call_check() { |
| Eigen::GpuStreamDevice stream; |
| Eigen::GpuDevice device(&stream); |
| g_num_gpu_errors = 0; |
| |
| int host_dst = 0; |
| // HIP can accept a host source here; a null source with a nonzero size is invalid on both runtimes. |
| device.memcpyDeviceToHost(&host_dst, nullptr, sizeof(int)); |
| VERIFY_IS_EQUAL(g_num_gpu_errors, 1); |
| VERIFY_IS_EQUAL(g_last_gpu_error.code, kInvalidValue); |
| VERIFY(g_last_gpu_error.expression.find("gpuMemcpyAsync") == 0); |
| VERIFY(ends_with(g_last_gpu_error.file, "TensorDeviceGpu.h")); |
| VERIFY(g_last_gpu_error.line > 0); |
| } |
| |
| EIGEN_DECLARE_TEST(tensor_gpu_runtime_check) { |
| CALL_SUBTEST(test_launch_check()); |
| CALL_SUBTEST(test_runtime_call_check()); |
| } |