-
Notifications
You must be signed in to change notification settings - Fork 286
Implement cudax::demangle
#4996
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of CUDA Experimental in CUDA C++ Core Libraries, | ||
| // under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef _CUDAX___BINUTILS_DEMANGLE_CUH | ||
| #define _CUDAX___BINUTILS_DEMANGLE_CUH | ||
|
|
||
| #include <cuda/std/detail/__config> | ||
|
|
||
| #if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) | ||
| # pragma GCC system_header | ||
| #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) | ||
| # pragma clang system_header | ||
| #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) | ||
| # pragma system_header | ||
| #endif // no system header | ||
|
|
||
| #if !_CCCL_COMPILER(NVRTC) | ||
|
|
||
| # include <cuda/std/__cstddef/types.h> | ||
| # include <cuda/std/__exception/throw_error.h> | ||
| # include <cuda/std/__new/bad_alloc.h> | ||
| # include <cuda/std/__type_traits/always_false.h> | ||
| # include <cuda/std/cstdlib> | ||
| # include <cuda/std/string_view> | ||
|
|
||
| # include <string> | ||
|
|
||
| # include <cuda/std/__cccl/prologue.h> | ||
|
|
||
| // Forward declaration of __cu_demangle function from <nv_decode.h> (cuxxfilt package from CUDA Toolkit) | ||
| extern "C" char* __cu_demangle(const char* id, char* output_buffer, ::cuda::std::size_t* length, int* status); | ||
|
|
||
| namespace cuda::experimental | ||
| { | ||
|
|
||
| // todo: make this function take cuda::std::cstring_view once P3655 is merged to C++29 and implemented in libcu++ | ||
|
|
||
| //! @brief Demangles a CUDA C++ mangled name. | ||
| //! | ||
| //! @param __name The mangled name to demangle. | ||
| //! | ||
| //! @return A `std::string` containing the demangled name. | ||
| //! | ||
| //! @throws std::bad_alloc if memory allocation fails. | ||
| //! @throws std::runtime_error if the passed \c __name is not a valid mangled symbol. | ||
| template <class _Dummy = void> | ||
| [[nodiscard]] _CCCL_HOST_API ::std::string demangle([[maybe_unused]] ::cuda::std::string_view __name) | ||
| { | ||
| # if !_CCCL_HAS_INCLUDE(<nv_decode.h>) | ||
| static_assert(::cuda::std::__always_false_v<_Dummy>, | ||
| "cuda::demangle requires the `cuxxfilt` package from the CUDA Toolkit."); | ||
| return {}; | ||
| # else // ^^^ no cuxxfilt ^^^ / vvv has cuxxfilt vvv | ||
| // input must be zero-terminated, so we convert string_view to std::string | ||
| ::std::string __name_in{__name.begin(), __name.end()}; | ||
|
|
||
| int __status{}; | ||
| char* __dname = ::__cu_demangle(__name_in.c_str(), nullptr, nullptr, &__status); | ||
|
|
||
| try | ||
| { | ||
| switch (__status) | ||
| { | ||
| case 0: { | ||
| ::std::string __ret{__dname}; | ||
| ::cuda::std::free(__dname); | ||
| return __ret; | ||
| } | ||
| case -1: | ||
| ::cuda::std::__throw_bad_alloc(); | ||
| case -2: | ||
| ::cuda::std::__throw_runtime_error("invalid mangled name passed to cuda::demangle function"); | ||
| case -3: | ||
| _CCCL_ASSERT(false, "cccl internal error - invalid argument passed to __cu_demangle"); | ||
| [[fallthrough]]; | ||
| default: | ||
| _CCCL_UNREACHABLE(); | ||
| } | ||
| } | ||
| catch (...) | ||
| { | ||
| // If an exception is thrown, free the allocated memory and rethrow the exception | ||
| ::cuda::std::free(__dname); | ||
| throw; | ||
| } | ||
| # endif // ^^^ has cuxxfilt ^^^ | ||
| } | ||
|
|
||
| } // namespace cuda::experimental | ||
|
|
||
| # include <cuda/std/__cccl/epilogue.h> | ||
|
|
||
| #endif // !_CCCL_COMPILER(NVRTC) | ||
|
|
||
| #endif // _CUDAX___BINUTILS_DEMANGLE_CUH |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of CUDA Experimental in CUDA C++ Core Libraries, | ||
| // under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef _CUDAX_BINUTILS | ||
| #define _CUDAX_BINUTILS | ||
|
|
||
| #include <cuda/std/detail/__config> | ||
|
|
||
| #if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) | ||
| # pragma GCC system_header | ||
| #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) | ||
| # pragma clang system_header | ||
| #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) | ||
| # pragma system_header | ||
| #endif // no system header | ||
|
|
||
| #include <cuda/experimental/__binutils/demangle.cuh> | ||
|
|
||
| #endif // _CUDAX_BINUTILS |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| find_package(cudax REQUIRED) | ||
| find_package(CUDAToolkit REQUIRED) | ||
|
|
||
| # Find the cu++filt library. | ||
| find_library(cudax_cufilt_lib "cufilt" PATHS "${CUDAToolkit_LIBRARY_DIR}" NO_DEFAULT_PATH) | ||
| if (NOT cudax_cufilt_lib) | ||
| message(FATAL_ERROR "cudax: cu++filt library (libcufilt.a) not found.") | ||
| endif() | ||
|
|
||
| foreach(cn_target IN LISTS cudax_TARGETS) | ||
| cudax_get_target_property(config_prefix ${cn_target} PREFIX) | ||
| set(config_meta_target ${config_prefix}.tests) | ||
| cudax_get_target_property(config_dialect ${cn_target} DIALECT) | ||
|
|
||
| # 1. positive case (pass) | ||
| set(test_target ${config_prefix}.test.binutils_demangle) | ||
| add_executable(${test_target} demangle.pass.cpp) | ||
| target_compile_options(${test_target} PRIVATE "$<$<CXX_COMPILER_ID:MSVC>:/MT>") | ||
| target_link_libraries(${test_target} PRIVATE | ||
| ${cn_target} | ||
| CUDA::cudart_static | ||
| ${cudax_cufilt_lib} | ||
| ) | ||
| cccl_configure_target(${test_target} DIALECT ${config_dialect}) | ||
| cudax_clone_target_properties(${test_target} ${cn_target}) | ||
| add_dependencies(${config_meta_target} ${test_target}) | ||
| add_test(NAME ${test_target} COMMAND "$<TARGET_FILE:${test_target}>") | ||
|
|
||
| # 2. missing <nvdecode.h> include but the function is not used (pass) | ||
| set(test_target ${config_prefix}.test.binutils_demangle_no_nvdecode_pass) | ||
| add_executable(${test_target} demangle_no_nvdecode.pass.cpp) | ||
| target_compile_options(${test_target} PRIVATE "$<$<CXX_COMPILER_ID:MSVC>:/MT>") | ||
| target_link_libraries(${test_target} PRIVATE ${cn_target}) | ||
| cccl_configure_target(${test_target} DIALECT ${config_dialect}) | ||
| cudax_clone_target_properties(${test_target} ${cn_target}) | ||
| add_dependencies(${config_meta_target} ${test_target}) | ||
| add_test(NAME ${test_target} COMMAND "$<TARGET_FILE:${test_target}>") | ||
|
|
||
| # 3. missing <nvdecode.h> and the function is used (fail) | ||
| set(test_target ${config_prefix}.test.binutils_demangle_no_nvdecode_fail) | ||
| add_test(NAME ${test_target} | ||
| COMMAND ${CMAKE_CTEST_COMMAND} | ||
| --build-and-test | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Check out what we do for CUB "fail" tests: https://github.com/NVIDIA/cccl/blob/main/cub/test/CMakeLists.txt#L174-L199 Basically:
...or for more robustness, instead check for output that confirms the expected failure mode, if a cross-platform regex exists for this: (Remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For example, the regex approach is preferred because the test is currently passing without actually testing what it's supposed to: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See #6434. Repeat the steps for creating the executable target like you did for (2), but pass it to |
||
| ${CMAKE_CURRENT_LIST_DIR}/binutils_demangle_no_nvdecode_fail | ||
| ${CMAKE_CURRENT_BINARY_DIR}/binutils_demangle_no_nvdecode_fail | ||
| --build-generator ${CMAKE_GENERATOR} | ||
| --test-command ${CMAKE_CTEST_COMMAND} | ||
| ) | ||
| set_tests_properties(${test_target} PROPERTIES WILL_FAIL true) | ||
| endforeach() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of CUDA Experimental in CUDA C++ Core Libraries, | ||
| // under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // This test uses assert(...) for checking results | ||
| #undef NDEBUG | ||
|
|
||
| #include <cuda/std/cassert> | ||
| #include <cuda/std/string_view> | ||
| #include <cuda/std/type_traits> | ||
|
|
||
| #include <cuda/experimental/binutils.cuh> | ||
|
|
||
| #include <stdexcept> | ||
| #include <string> | ||
|
|
||
| namespace cudax = cuda::experimental; | ||
|
|
||
| bool test() | ||
| { | ||
| // 1. Test signature | ||
| static_assert(cuda::std::is_same_v<std::string, decltype(cudax::demangle(cuda::std::string_view{}))>); | ||
| static_assert(!noexcept(cudax::demangle(cuda::std::string_view{}))); | ||
|
|
||
| // 2. Test positive case | ||
| { | ||
| constexpr auto real_mangled_name = "_ZN8clstmp01I5cls01E13clstmp01_mf01Ev"; | ||
| const auto demangled = cudax::demangle(real_mangled_name); | ||
| assert(demangled == "clstmp01<cls01>::clstmp01_mf01()"); | ||
| } | ||
|
|
||
| // 3. Test error case | ||
| { | ||
| #if _CCCL_HAS_EXCEPTIONS() | ||
| constexpr auto fake_mangled_name = "B@d_iDentiFier"; | ||
| try | ||
| { | ||
| auto demangled = cudax::demangle(fake_mangled_name); | ||
| assert(false); | ||
| } | ||
| catch (const std::runtime_error&) | ||
| { | ||
| assert(true); | ||
| } | ||
| catch (...) | ||
| { | ||
| assert(false); | ||
| } | ||
| #endif // _CCCL_HAS_EXCEPTIONS() | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| int main(int, char**) | ||
| { | ||
| NV_IF_TARGET(NV_IS_HOST, (test();)) | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of CUDA Experimental in CUDA C++ Core Libraries, | ||
| // under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // This test uses assert(...) for checking results | ||
| #undef NDEBUG | ||
|
|
||
| #include <cuda/std/cassert> | ||
|
|
||
| #include <cuda/experimental/binutils.cuh> | ||
|
|
||
| #if _CCCL_HAS_INCLUDE(<nv_decode.h>) | ||
| # error "This test requires <nv_decode.h> not to be findable in PATH." | ||
| #endif | ||
|
|
||
| namespace cudax = cuda::experimental; | ||
|
|
||
| bool test() | ||
| { | ||
| constexpr auto real_mangled_name = "_ZN8clstmp01I5cls01E13clstmp01_mf01Ev"; | ||
| const auto demangled = cudax::demangle(real_mangled_name); | ||
| assert(demangled == "clstmp01<cls01>::clstmp01_mf01()"); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| int main(int, char**) | ||
| { | ||
| NV_IF_TARGET(NV_IS_HOST, (test();)) | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of CUDA Experimental in CUDA C++ Core Libraries, | ||
| // under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include <cuda/experimental/binutils.cuh> | ||
|
|
||
| #if _CCCL_HAS_INCLUDE(<nv_decode.h>) | ||
| # error "This test requires <nv_decode.h> not to be findable in PATH." | ||
| #endif | ||
|
|
||
| int main(int, char**) | ||
| { | ||
| return 0; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: replace
cn_targetwithcudax_targetin this new code. I'm making this change for existing code in #6346 -- cudax used to be called "cuda next" and this prefix is a legacy artifact that no longer makes sense.