From 4dacc69b8692f9f3f04342d25c41d1106b98ba70 Mon Sep 17 00:00:00 2001 From: "romin.tomasetti" Date: Thu, 21 Dec 2023 12:38:12 +0000 Subject: [PATCH] space-time-stack: allow demangled names --- CMakeLists.txt | 11 +++ CMakePresets.json | 3 +- common/utils/demangle.hpp | 74 +++++++++++++++ .../simple-kernel-timer/kp_json_writer.cpp | 2 +- .../simple-kernel-timer/kp_kernel_info.h | 59 +++--------- profiling/simple-kernel-timer/kp_reader.cpp | 10 +- profiling/simple-kernel-timer/kp_shared.h | 6 +- .../space-time-stack/kp_space_time_stack.cpp | 4 +- tests/CMakeLists.txt | 1 + tests/space-time-stack/CMakeLists.txt | 18 ++++ tests/space-time-stack/test_demangling.cpp | 93 +++++++++++++++++++ 11 files changed, 224 insertions(+), 57 deletions(-) create mode 100644 common/utils/demangle.hpp create mode 100644 tests/CMakeLists.txt create mode 100644 tests/space-time-stack/CMakeLists.txt create mode 100644 tests/space-time-stack/test_demangling.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e7699414..64206d56f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,8 @@ option(KokkosTools_ENABLE_MPI "Enable MPI support" OFF) option(KokkosTools_ENABLE_CALIPER "Enable building Caliper library" OFF) option(KokkosTools_ENABLE_APEX "Enable building Apex library" OFF) option(KokkosTools_ENABLE_EXAMPLES "Build examples" OFF) +option(KokkosTools_ENABLE_TESTS "Enable tests" OFF) + # Advanced settings option(KokkosTools_REUSE_KOKKOS_COMPILER "Set the compiler and flags based on installed Kokkos settings" OFF) mark_as_advanced(KokkosTools_REUSE_KOKKOS_COMPILER) @@ -116,6 +118,9 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/profiling/all) set(COMMON_HEADERS_PATH ${CMAKE_CURRENT_BINARY_DIR}/common) include_directories(${COMMON_HEADERS_PATH}) +# Allow all tools to include any file. +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + set(SINGLELIB_PROFILERS "" CACHE STRING "" FORCE) # Export settings @@ -264,6 +269,12 @@ if(KokkosTools_ENABLE_EXAMPLES) endif() endif() +# Tests +if(KokkosTools_ENABLE_TESTS) + enable_testing() + add_subdirectory(tests) +endif() + # Install exports install(TARGETS ${EXPORT_TARGETS} EXPORT ${EXPORT_NAME}) install(EXPORT ${EXPORT_NAME} diff --git a/CMakePresets.json b/CMakePresets.json index 667854c95..8d76097d8 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -10,7 +10,8 @@ "KokkosTools_ENABLE_EXAMPLES" : "ON", "KokkosTools_ENABLE_SINGLE" : "ON", "KokkosTools_ENABLE_MPI" : "ON", - "KokkosTools_ENABLE_PAPI" : "ON" + "KokkosTools_ENABLE_PAPI" : "ON", + "KokkosTools_ENABLE_TESTS" : "ON" } }, { diff --git a/common/utils/demangle.hpp b/common/utils/demangle.hpp new file mode 100644 index 000000000..662d8a6fc --- /dev/null +++ b/common/utils/demangle.hpp @@ -0,0 +1,74 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#ifndef KOKKOSTOOLS_COMMON_UTILS_DEMANGLE_HPP +#define KOKKOSTOOLS_COMMON_UTILS_DEMANGLE_HPP + +#include + +#if defined(__GXX_ABI_VERSION) +#define HAVE_GCC_ABI_DEMANGLE +#endif + +#if defined(HAVE_GCC_ABI_DEMANGLE) +#include +#endif // HAVE_GCC_ABI_DEMANGLE + +namespace KokkosTools { + +//! Demangle @p mangled_name. +inline std::string demangleName(const std::string_view mangled_name) { +#if defined(HAVE_GCC_ABI_DEMANGLE) + int status = 0; + + char* demangled_name = + abi::__cxa_demangle(mangled_name.data(), nullptr, nullptr, &status); + + if (demangled_name) { + std::string ret(demangled_name); + std::free(demangled_name); + return ret; + } +#endif + return std::string(mangled_name); +} + +/** + * @brief Demangle @p mangled_name. + * + * This function supports @c Kokkos convention from + * @c Kokkos::Impl::ParallelConstructName. + * + * For instance, a kernel launched with a tag would appear as + * "/". + */ +inline std::string demangleNameKokkos(const std::string_view mangled_name) { + if (size_t pos = mangled_name.find('/', 0); + pos != std::string_view::npos && pos > 0) { + /// An explicit copy of the first part of the string is needed, because + /// @c abi::__cxa_demangle will parse the pointer until its NULL-terminated. + return demangleName(std::string(mangled_name.substr(0, pos))) + .append("/") + .append( + demangleName(mangled_name.substr(pos + 1, mangled_name.size()))); + } else { + return demangleName(mangled_name); + } +} + +} // namespace KokkosTools + +#endif // KOKKOSTOOLS_COMMON_UTILS_DEMANGLE_HPP diff --git a/profiling/simple-kernel-timer/kp_json_writer.cpp b/profiling/simple-kernel-timer/kp_json_writer.cpp index b552218c0..3d58b260a 100644 --- a/profiling/simple-kernel-timer/kp_json_writer.cpp +++ b/profiling/simple-kernel-timer/kp_json_writer.cpp @@ -83,7 +83,7 @@ int main(int argc, char* argv[]) { KernelPerformanceInfo* new_kernel = new KernelPerformanceInfo("", PARALLEL_FOR); if (new_kernel->readFromFile(the_file)) { - if (strlen(new_kernel->getName()) > 0) { + if (!new_kernel->getName().empty()) { int kernelIndex = find_index(kernelInfo, new_kernel->getName()); if (kernelIndex > -1) { diff --git a/profiling/simple-kernel-timer/kp_kernel_info.h b/profiling/simple-kernel-timer/kp_kernel_info.h index 93b7871eb..bb6cff6b6 100644 --- a/profiling/simple-kernel-timer/kp_kernel_info.h +++ b/profiling/simple-kernel-timer/kp_kernel_info.h @@ -22,29 +22,10 @@ #include #include -#if defined(__GXX_ABI_VERSION) -#define HAVE_GCC_ABI_DEMANGLE -#endif - -#if defined(HAVE_GCC_ABI_DEMANGLE) -#include -#endif // HAVE_GCC_ABI_DEMANGLE +#include "common/utils/demangle.hpp" namespace KokkosTools::KernelTimer { -inline char* demangleName(char* kernelName) { -#if defined(HAVE_GCC_ABI_DEMANGLE) - int status = -1; - char* demangledKernelName = - abi::__cxa_demangle(kernelName, NULL, NULL, &status); - if (status == 0) { - free(kernelName); - kernelName = demangledKernelName; - } -#endif // HAVE_GCC_ABI_DEMANGLE - return kernelName; -} - inline double seconds() { struct timeval now; gettimeofday(&now, NULL); @@ -62,15 +43,7 @@ enum KernelExecutionType { class KernelPerformanceInfo { public: KernelPerformanceInfo(std::string kName, KernelExecutionType kernelType) - : kType(kernelType) { - kernelName = (char*)malloc(sizeof(char) * (kName.size() + 1)); - strcpy(kernelName, kName.c_str()); - - callCount = 0; - time = 0; - } - - ~KernelPerformanceInfo() { free(kernelName); } + : kernelName(std::move(kName)), kType(kernelType) {} KernelExecutionType getKernelType() const { return kType; } @@ -95,7 +68,7 @@ class KernelPerformanceInfo { double getTimeSq() { return timeSq; } - char* getName() const { return kernelName; } + const std::string& getName() const { return kernelName; } void addCallCount(const uint64_t newCalls) { callCount += newCalls; } @@ -112,15 +85,9 @@ class KernelPerformanceInfo { copy((char*)&kernelNameLength, &entry[nextIndex], sizeof(kernelNameLength)); nextIndex += sizeof(kernelNameLength); - if (strlen(kernelName) > 0) { - free(kernelName); - } - - kernelName = (char*)malloc(sizeof(char) * (kernelNameLength + 1)); - copy(kernelName, &entry[nextIndex], kernelNameLength); - kernelName[kernelNameLength] = '\0'; + this->kernelName = std::string(&entry[nextIndex], kernelNameLength); - kernelName = demangleName(kernelName); + kernelName = demangleNameKokkos(kernelName); nextIndex += kernelNameLength; @@ -152,7 +119,7 @@ class KernelPerformanceInfo { } void writeToBinaryFile(FILE* output) { - const uint32_t kernelNameLen = (uint32_t)strlen(kernelName); + const uint32_t kernelNameLen = kernelName.size(); const uint32_t recordLen = sizeof(uint32_t) + sizeof(char) * kernelNameLen + sizeof(uint64_t) + sizeof(double) + sizeof(double) + sizeof(uint32_t); @@ -163,7 +130,7 @@ class KernelPerformanceInfo { copy(&entry[nextIndex], (char*)&kernelNameLen, sizeof(kernelNameLen)); nextIndex += sizeof(kernelNameLen); - copy(&entry[nextIndex], kernelName, kernelNameLen); + copy(&entry[nextIndex], kernelName.c_str(), kernelNameLen); nextIndex += kernelNameLen; copy(&entry[nextIndex], (char*)&callCount, sizeof(callCount)); @@ -191,7 +158,7 @@ class KernelPerformanceInfo { snprintf(indentBuffer, 256, "%s ", indent); fprintf(output, "%s\"kernel-name\" : \"%s\",\n", indentBuffer, - kernelName); + kernelName.c_str()); // fprintf(output, "%s\"region\" : \"%s\",\n", indentBuffer, // regionName); fprintf(output, "%s\"call-count\" : %llu,\n", indentBuffer, @@ -216,12 +183,12 @@ class KernelPerformanceInfo { } } - char* kernelName; + std::string kernelName; // const char* regionName; - uint64_t callCount; - double time; - double timeSq; - double startTime; + uint64_t callCount = 0; + double time = 0; + double timeSq = 0; + double startTime = 0; KernelExecutionType kType; }; diff --git a/profiling/simple-kernel-timer/kp_reader.cpp b/profiling/simple-kernel-timer/kp_reader.cpp index 45493445f..551f33d67 100644 --- a/profiling/simple-kernel-timer/kp_reader.cpp +++ b/profiling/simple-kernel-timer/kp_reader.cpp @@ -64,7 +64,7 @@ int main(int argc, char* argv[]) { KernelPerformanceInfo* new_kernel = new KernelPerformanceInfo("", PARALLEL_FOR); if (new_kernel->readFromFile(the_file)) { - if (strlen(new_kernel->getName()) > 0) { + if (!new_kernel->getName().empty()) { int kernelIndex = find_index(kernelInfo, new_kernel->getName()); if (kernelIndex > -1) { @@ -97,7 +97,7 @@ int main(int argc, char* argv[]) { if (kernelInfo[i]->getKernelType() != REGION) continue; if (fixed_width) printf("- %100s\n%11s%c%15.5f%c%12" PRIu64 "%c%15.5f%c%7.3f%c%7.3f\n", - kernelInfo[i]->getName(), + kernelInfo[i]->getName().c_str(), (kernelInfo[i]->getKernelType() == PARALLEL_FOR) ? (" (ParFor) ") : ((kernelInfo[i]->getKernelType() == PARALLEL_REDUCE) @@ -112,7 +112,7 @@ int main(int argc, char* argv[]) { (kernelInfo[i]->getTime() / totalExecuteTime) * 100.0); else printf("- %s\n%s%c%f%c%" PRIu64 "%c%f%c%f%c%f\n", - kernelInfo[i]->getName(), + kernelInfo[i]->getName().c_str(), (kernelInfo[i]->getKernelType() == PARALLEL_FOR) ? (" (ParFor) ") : ((kernelInfo[i]->getKernelType() == PARALLEL_REDUCE) @@ -139,7 +139,7 @@ int main(int argc, char* argv[]) { if (kernelInfo[i]->getKernelType() == REGION) continue; if (fixed_width) printf("- %100s\n%11s%c%15.5f%c%12" PRIu64 "%c%15.5f%c%7.3f%c%7.3f\n", - kernelInfo[i]->getName(), + kernelInfo[i]->getName().c_str(), (kernelInfo[i]->getKernelType() == PARALLEL_FOR) ? (" (ParFor) ") : ((kernelInfo[i]->getKernelType() == PARALLEL_REDUCE) @@ -154,7 +154,7 @@ int main(int argc, char* argv[]) { (kernelInfo[i]->getTime() / totalExecuteTime) * 100.0); else printf("- %s\n%s%c%f%c%" PRIu64 "%c%f%c%f%c%f\n", - kernelInfo[i]->getName(), + kernelInfo[i]->getName().c_str(), (kernelInfo[i]->getKernelType() == PARALLEL_FOR) ? (" (ParFor) ") : ((kernelInfo[i]->getKernelType() == PARALLEL_REDUCE) diff --git a/profiling/simple-kernel-timer/kp_shared.h b/profiling/simple-kernel-timer/kp_shared.h index d92f97128..29540ef98 100644 --- a/profiling/simple-kernel-timer/kp_shared.h +++ b/profiling/simple-kernel-timer/kp_shared.h @@ -42,9 +42,9 @@ inline bool compareKernelPerformanceInfo(KernelPerformanceInfo* left, }; inline int find_index(const std::vector& kernels, - const char* kernelName) { - for (unsigned int i = 0; i < kernels.size(); i++) { - if (strcmp(kernels[i]->getName(), kernelName) == 0) { + const std::string& kernelName) { + for (unsigned int i = 0; i < kernels.size(); ++i) { + if (kernels[i]->getName() == kernelName) { return i; } } diff --git a/profiling/space-time-stack/kp_space_time_stack.cpp b/profiling/space-time-stack/kp_space_time_stack.cpp index 292021787..e3a39a7af 100644 --- a/profiling/space-time-stack/kp_space_time_stack.cpp +++ b/profiling/space-time-stack/kp_space_time_stack.cpp @@ -31,6 +31,8 @@ #include #include +#include "common/utils/demangle.hpp" + #include "kp_core.hpp" #if USE_MPI @@ -741,7 +743,7 @@ struct State { } void begin_frame(const char* name, StackKind kind) { - std::string name_str(name); + std::string name_str(demangleNameKokkos(name)); stack_frame = stack_frame->get_child(std::move(name_str), kind); stack_frame->begin(); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..a5fa435c6 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(space-time-stack) diff --git a/tests/space-time-stack/CMakeLists.txt b/tests/space-time-stack/CMakeLists.txt new file mode 100644 index 000000000..de68a5015 --- /dev/null +++ b/tests/space-time-stack/CMakeLists.txt @@ -0,0 +1,18 @@ +# A function to add such "simple" tests in 'tests/CMakeLists.txt' might be a good option. +add_executable(test_space_time_stack_demangling) +target_sources( + test_space_time_stack_demangling + PRIVATE + test_demangling.cpp +) +target_link_libraries( + test_space_time_stack_demangling + PRIVATE + Kokkos::kokkos kokkostools +) +add_test( + NAME test_space_time_stack_demangling + COMMAND $ + --kokkos-tools-libs=$ + --kokkos-tools-args=1e-9 +) diff --git a/tests/space-time-stack/test_demangling.cpp b/tests/space-time-stack/test_demangling.cpp new file mode 100644 index 000000000..44977f620 --- /dev/null +++ b/tests/space-time-stack/test_demangling.cpp @@ -0,0 +1,93 @@ +#include +#include +#include + +#include "Kokkos_Core.hpp" +#include "kp_all.hpp" + +#include "common/utils/demangle.hpp" + +template +struct Tester { + struct TagNamed {}; + struct TagUnnamed {}; + + void apply(const execution_space& space) const { + //! Explicitly launch a kernel with a name and no tag. + Kokkos::parallel_for("named kernel", + Kokkos::RangePolicy(space, 0, size), + *this + ); + + //! Explicitly launch a kernel with a name and a tag. + Kokkos::parallel_for( + "named kernel", + Kokkos::RangePolicy(space, 0, size), *this); + + //! Explicitly launch a kernel with no name and no tag. + Kokkos::parallel_for(Kokkos::RangePolicy(space, 0, size), + *this + ); + + //! Explicitly launch a kernel with no name and a tag. + Kokkos::parallel_for( + Kokkos::RangePolicy(space, 0, size), + *this); + } + + KOKKOS_INLINE_FUNCTION + void operator()(const int index) const {} + + template + KOKKOS_INLINE_FUNCTION void operator()(const TagType, const int index) const { + } +}; + +static const std::string demangled_kokkos_exec_space = + KokkosTools::demangleName(typeid(Kokkos::DefaultExecutionSpace).name()); + +static const std::vector matchers{ + /// A kernel with a given name appears with the given name, no matter + /// the tag given. + "[0-9.e]+ sec [0-9.]+% 100.0% 0.0% ------ 2 named kernel \\[for\\]", + //! A kernel with no name and no tag appears with a demangled name. + "[0-9.e]+ sec [0-9.]+% 100.0% 0.0% ------ 1 Tester<" + + demangled_kokkos_exec_space + ", 5ul> \\[for\\]\n", + //! A kernel with no name and a tag appears with a demangled name. + "[0-9.e]+ sec [0-9.]+% 100.0% 0.0% ------ 1 Tester<" + + demangled_kokkos_exec_space + ", 5ul>/Tester<" + + demangled_kokkos_exec_space + ", 5ul>::TagUnnamed \\[for\\]" +}; + +int main(int argc, char *argv[]) { + //! Initialize @c Kokkos. + Kokkos::initialize(argc, argv); + + //! Redirect output for later analysis. + std::cout.flush(); + std::ostringstream output; + std::streambuf* coutbuf = std::cout.rdbuf(output.rdbuf()); + + //! Run tests. @todo Replace this with Google Test. + { + Tester tester{}; + tester.apply(Kokkos::DefaultExecutionSpace{}); + } + + //! Finalize @c Kokkos. + Kokkos::finalize(); + + //! Restore output buffer. + std::cout.flush(); + std::cout.rdbuf(coutbuf); + std::cout << output.str() << std::endl; + + //! Analyze test output. + for(const auto& matcher : matchers) { + if (!std::regex_search(output.str(), std::regex(matcher))) + throw std::runtime_error("Couln't find " + matcher + " in output\n" + + output.str()); + } + + return EXIT_SUCCESS; +}