From a84c9fa7d409a21aa123464a955eeccc22c324b7 Mon Sep 17 00:00:00 2001 From: Giovanni Lenzi Baraldi Date: Tue, 28 May 2024 23:15:11 -0300 Subject: [PATCH] Removing code object static library (#865) * Removing static library build for codeobj library * Moving codeobj library to amd_detail * Formatting * Formatting * Adding findDW * Adding libdw to common samples cmake --- samples/advanced_thread_trace/CMakeLists.txt | 31 +- samples/advanced_thread_trace/client.cpp | 2 +- samples/code_object_isa_decode/CMakeLists.txt | 91 ++++- samples/code_object_isa_decode/client.cpp | 2 +- samples/common/CMakeLists.txt | 59 ++- source/include/CMakeLists.txt | 1 - .../rocprofiler-sdk-codeobj/disassembly.hpp | 78 ---- source/include/rocprofiler-sdk/CMakeLists.txt | 1 + .../rocprofiler-sdk/amd_detail/CMakeLists.txt | 6 + .../rocprofiler-sdk-codeobj/CMakeLists.txt | 3 +- .../rocprofiler-sdk-codeobj/code_printing.hpp | 211 +++++++++- .../rocprofiler-sdk-codeobj/disassembly.hpp | 336 +++++++++++++++ .../rocprofiler-sdk-codeobj/segment.hpp | 0 .../rocprofiler-sdk-codeobj/CMakeLists.txt | 28 +- source/lib/rocprofiler-sdk-codeobj/LICENSE | 21 - source/lib/rocprofiler-sdk-codeobj/README.md | 1 - .../rocprofiler-sdk-codeobj/code_printing.cpp | 286 ------------- .../rocprofiler-sdk-codeobj/disassembly.cpp | 381 ------------------ .../tests/CMakeLists.txt | 2 +- .../tests/codeobj_library_test.cpp | 2 +- tests/thread-trace/CMakeLists.txt | 79 +++- tests/thread-trace/verify_data.cpp | 2 +- 22 files changed, 778 insertions(+), 845 deletions(-) delete mode 100644 source/include/rocprofiler-sdk-codeobj/disassembly.hpp create mode 100644 source/include/rocprofiler-sdk/amd_detail/CMakeLists.txt rename source/include/{ => rocprofiler-sdk/amd_detail}/rocprofiler-sdk-codeobj/CMakeLists.txt (93%) rename source/include/{ => rocprofiler-sdk/amd_detail}/rocprofiler-sdk-codeobj/code_printing.hpp (53%) create mode 100644 source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/disassembly.hpp rename source/include/{ => rocprofiler-sdk/amd_detail}/rocprofiler-sdk-codeobj/segment.hpp (100%) delete mode 100644 source/lib/rocprofiler-sdk-codeobj/LICENSE delete mode 100644 source/lib/rocprofiler-sdk-codeobj/README.md delete mode 100644 source/lib/rocprofiler-sdk-codeobj/code_printing.cpp delete mode 100644 source/lib/rocprofiler-sdk-codeobj/disassembly.cpp diff --git a/samples/advanced_thread_trace/CMakeLists.txt b/samples/advanced_thread_trace/CMakeLists.txt index 8dfd609e50..14c0ada5b3 100644 --- a/samples/advanced_thread_trace/CMakeLists.txt +++ b/samples/advanced_thread_trace/CMakeLists.txt @@ -26,25 +26,30 @@ foreach(_TYPE DEBUG MINSIZEREL RELEASE RELWITHDEBINFO) endforeach() find_package(rocprofiler-sdk REQUIRED) -find_package(amd_comgr REQUIRED) - -add_library(advanced-thread-trace-client SHARED) -target_sources(advanced-thread-trace-client PRIVATE client.cpp) - -target_link_libraries( - advanced-thread-trace-client - PRIVATE rocprofiler::rocprofiler rocprofiler::samples-build-flags - rocprofiler-sdk-codeobj rocprofiler::samples-common-library amd_comgr dw) +find_package( + amd_comgr + REQUIRED + CONFIG + HINTS + ${rocm_version_DIR} + ${ROCM_PATH} + PATHS + ${rocm_version_DIR} + ${ROCM_PATH} + PATH_SUFFIXES + lib/cmake/amd_comgr) set_source_files_properties(main.cpp PROPERTIES LANGUAGE HIP) set_source_files_properties(main.cpp PROPERTIES COMPILE_FLAGS "-g") add_executable(advanced-thread-trace) -target_sources(advanced-thread-trace PRIVATE main.cpp) -target_link_libraries(advanced-thread-trace PRIVATE advanced-thread-trace-client - rocprofiler::samples-build-flags) +target_sources(advanced-thread-trace PRIVATE main.cpp client.cpp) +target_link_libraries( + advanced-thread-trace + PRIVATE rocprofiler::rocprofiler amd_comgr rocprofiler::samples-common-library + rocprofiler::samples-build-flags) -rocprofiler_samples_get_preload_env(PRELOAD_ENV advanced-thread-trace-client) +rocprofiler_samples_get_preload_env(PRELOAD_ENV advanced-thread-trace) add_test(NAME advanced-thread-trace COMMAND $) diff --git a/samples/advanced_thread_trace/client.cpp b/samples/advanced_thread_trace/client.cpp index 15d1169d2a..ab8f76d0ea 100644 --- a/samples/advanced_thread_trace/client.cpp +++ b/samples/advanced_thread_trace/client.cpp @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include "common/defines.hpp" diff --git a/samples/code_object_isa_decode/CMakeLists.txt b/samples/code_object_isa_decode/CMakeLists.txt index c873f3e74b..90553bf9d8 100644 --- a/samples/code_object_isa_decode/CMakeLists.txt +++ b/samples/code_object_isa_decode/CMakeLists.txt @@ -25,25 +25,88 @@ foreach(_TYPE DEBUG MINSIZEREL RELEASE RELWITHDEBINFO) endif() endforeach() -find_package(rocprofiler-sdk REQUIRED) -find_package(amd_comgr REQUIRED) +find_package(PkgConfig) + +if(PkgConfig_FOUND) + set(ENV{PKG_CONFIG_SYSTEM_INCLUDE_PATH} "") + pkg_check_modules(DW libdw) + + if(DW_FOUND + AND DW_INCLUDE_DIRS + AND DW_LIBRARIES) + set(libdw_INCLUDE_DIR + "${DW_INCLUDE_DIRS}" + CACHE FILEPATH "libdw include directory") + set(libdw_LIBRARY + "${DW_LIBRARIES}" + CACHE FILEPATH "libdw libraries") + endif() +endif() + +if(NOT libdw_INCLUDE_DIR OR NOT libdw_LIBRARY) + find_path( + libdw_ROOT_DIR + NAMES include/elfutils/libdw.h + HINTS ${libdw_ROOT} + PATHS ${libdw_ROOT}) + + mark_as_advanced(libdw_ROOT_DIR) + + find_path( + libdw_INCLUDE_DIR + NAMES elfutils/libdw.h + HINTS ${libdw_ROOT} + PATHS ${libdw_ROOT} + PATH_SUFFIXES include) + + find_library( + libdw_LIBRARY + NAMES dw + HINTS ${libdw_ROOT} + PATHS ${libdw_ROOT} + PATH_SUFFIXES lib lib64) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(libdw DEFAULT_MSG libdw_LIBRARY libdw_INCLUDE_DIR) + +if(libdw_FOUND AND NOT TARGET libdw::libdw) + add_library(libdw::libdw INTERFACE IMPORTED) + if(TARGET PkgConfig::DW AND DW_FOUND) + target_link_libraries(libdw::libdw INTERFACE PkgConfig::DW) + else() + target_link_libraries(libdw::libdw INTERFACE ${libdw_LIBRARY}) + target_include_directories(libdw::libdw SYSTEM INTERFACE ${libdw_INCLUDE_DIR}) + endif() +endif() + +mark_as_advanced(libdw_INCLUDE_DIR libdw_LIBRARY) + +find_package(rocprofiler-sdk REQUIRED) +find_package( + amd_comgr + REQUIRED + CONFIG + HINTS + ${rocm_version_DIR} + ${ROCM_PATH} + PATHS + ${rocm_version_DIR} + ${ROCM_PATH} + PATH_SUFFIXES + lib/cmake/amd_comgr) -add_library(code-object-isa-decode-client SHARED) -target_sources(code-object-isa-decode-client PRIVATE client.cpp) set_source_files_properties(main.cpp PROPERTIES LANGUAGE HIP) set_source_files_properties(main.cpp PROPERTIES COMPILE_FLAGS "-g") -target_link_libraries( - code-object-isa-decode-client - PRIVATE rocprofiler::samples-common-library rocprofiler-sdk-codeobj - rocprofiler::rocprofiler amd_comgr dw) - -rocprofiler_samples_get_preload_env(PRELOAD_ENV code-object-isa-decode-client) - add_executable(code-object-isa-decode) -target_sources(code-object-isa-decode PRIVATE main.cpp) -target_link_libraries(code-object-isa-decode PRIVATE code-object-isa-decode-client - rocprofiler::samples-build-flags) +target_sources(code-object-isa-decode PRIVATE main.cpp client.cpp) +target_link_libraries( + code-object-isa-decode + PRIVATE rocprofiler::samples-common-library rocprofiler::rocprofiler amd_comgr + rocprofiler::samples-build-flags) + +rocprofiler_samples_get_preload_env(PRELOAD_ENV code-object-isa-decode) add_test(NAME code-object-isa-decode COMMAND $) diff --git a/samples/code_object_isa_decode/client.cpp b/samples/code_object_isa_decode/client.cpp index b554c6f5c0..43caaa97fd 100644 --- a/samples/code_object_isa_decode/client.cpp +++ b/samples/code_object_isa_decode/client.cpp @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include "common/defines.hpp" #include "common/filesystem.hpp" diff --git a/samples/common/CMakeLists.txt b/samples/common/CMakeLists.txt index b8480695fc..e5f63b8692 100644 --- a/samples/common/CMakeLists.txt +++ b/samples/common/CMakeLists.txt @@ -2,6 +2,63 @@ # common utilities for samples # +find_package(PkgConfig) + +if(PkgConfig_FOUND) + set(ENV{PKG_CONFIG_SYSTEM_INCLUDE_PATH} "") + pkg_check_modules(DW libdw) + + if(DW_FOUND + AND DW_INCLUDE_DIRS + AND DW_LIBRARIES) + set(libdw_INCLUDE_DIR + "${DW_INCLUDE_DIRS}" + CACHE FILEPATH "libdw include directory") + set(libdw_LIBRARY + "${DW_LIBRARIES}" + CACHE FILEPATH "libdw libraries") + endif() +endif() + +if(NOT libdw_INCLUDE_DIR OR NOT libdw_LIBRARY) + find_path( + libdw_ROOT_DIR + NAMES include/elfutils/libdw.h + HINTS ${libdw_ROOT} + PATHS ${libdw_ROOT}) + + mark_as_advanced(libdw_ROOT_DIR) + + find_path( + libdw_INCLUDE_DIR + NAMES elfutils/libdw.h + HINTS ${libdw_ROOT} + PATHS ${libdw_ROOT} + PATH_SUFFIXES include) + + find_library( + libdw_LIBRARY + NAMES dw + HINTS ${libdw_ROOT} + PATHS ${libdw_ROOT} + PATH_SUFFIXES lib lib64) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(libdw DEFAULT_MSG libdw_LIBRARY libdw_INCLUDE_DIR) + +if(libdw_FOUND AND NOT TARGET libdw::libdw) + add_library(libdw::libdw INTERFACE IMPORTED) + if(TARGET PkgConfig::DW AND DW_FOUND) + target_link_libraries(libdw::libdw INTERFACE PkgConfig::DW) + else() + target_link_libraries(libdw::libdw INTERFACE ${libdw_LIBRARY}) + target_include_directories(libdw::libdw SYSTEM INTERFACE ${libdw_INCLUDE_DIR}) + endif() +endif() + +mark_as_advanced(libdw_INCLUDE_DIR libdw_LIBRARY) + # default FAIL_REGULAR_EXPRESSION for tests set(ROCPROFILER_DEFAULT_FAIL_REGEX "threw an exception|Permission denied|Could not create logging file" @@ -24,7 +81,7 @@ cmake_path(GET CMAKE_CURRENT_SOURCE_DIR PARENT_PATH COMMON_LIBRARY_INCLUDE_DIR) add_library(rocprofiler-samples-common-library INTERFACE) add_library(rocprofiler::samples-common-library ALIAS rocprofiler-samples-common-library) target_link_libraries(rocprofiler-samples-common-library - INTERFACE rocprofiler::samples-build-flags) + INTERFACE rocprofiler::samples-build-flags libdw::libdw) target_compile_features(rocprofiler-samples-common-library INTERFACE cxx_std_17) target_include_directories(rocprofiler-samples-common-library INTERFACE ${COMMON_LIBRARY_INCLUDE_DIR}) diff --git a/source/include/CMakeLists.txt b/source/include/CMakeLists.txt index 8b8d02481e..d0c7e1ba82 100644 --- a/source/include/CMakeLists.txt +++ b/source/include/CMakeLists.txt @@ -5,4 +5,3 @@ set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "development") add_subdirectory(rocprofiler-sdk) add_subdirectory(rocprofiler-sdk-roctx) -add_subdirectory(rocprofiler-sdk-codeobj) diff --git a/source/include/rocprofiler-sdk-codeobj/disassembly.hpp b/source/include/rocprofiler-sdk-codeobj/disassembly.hpp deleted file mode 100644 index c244d7dc29..0000000000 --- a/source/include/rocprofiler-sdk-codeobj/disassembly.hpp +++ /dev/null @@ -1,78 +0,0 @@ -// MIT License -// -// Copyright (c) 2024 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. - -#pragma once - -#include -#include -#include -#include -#include - -namespace rocprofiler -{ -namespace codeobj -{ -namespace disassembly -{ -class CodeObjectBinary -{ -public: - CodeObjectBinary(const std::string& uri); - std::string m_uri; - std::vector buffer; -}; - -struct SymbolInfo -{ - std::string name{}; - uint64_t faddr = 0; - uint64_t vaddr = 0; - uint64_t mem_size = 0; -}; - -class DisassemblyInstance -{ -public: - DisassemblyInstance(const char* codeobj_data, uint64_t codeobj_size); - ~DisassemblyInstance(); - - std::pair ReadInstruction(uint64_t faddr); - std::map& GetKernelMap(); - - static uint64_t memory_callback(uint64_t from, char* to, uint64_t size, void* user_data); - static void inst_callback(const char* instruction, void* user_data); - static amd_comgr_status_t symbol_callback(amd_comgr_symbol_t symbol, void* user_data); - - std::optional va2fo(uint64_t va); - std::vector> getSegments(); - - std::vector buffer; - std::string last_instruction; - amd_comgr_disassembly_info_t info; - amd_comgr_data_t data; - std::map symbol_map; -}; - -} // namespace disassembly -} // namespace codeobj -} // namespace rocprofiler diff --git a/source/include/rocprofiler-sdk/CMakeLists.txt b/source/include/rocprofiler-sdk/CMakeLists.txt index f040f9de02..457ed4e554 100644 --- a/source/include/rocprofiler-sdk/CMakeLists.txt +++ b/source/include/rocprofiler-sdk/CMakeLists.txt @@ -42,3 +42,4 @@ add_subdirectory(hip) add_subdirectory(hsa) add_subdirectory(marker) add_subdirectory(cxx) +add_subdirectory(amd_detail) diff --git a/source/include/rocprofiler-sdk/amd_detail/CMakeLists.txt b/source/include/rocprofiler-sdk/amd_detail/CMakeLists.txt new file mode 100644 index 0000000000..473ff3bd13 --- /dev/null +++ b/source/include/rocprofiler-sdk/amd_detail/CMakeLists.txt @@ -0,0 +1,6 @@ +# +# +# Installation of amd_detail headers +# +# +add_subdirectory(rocprofiler-sdk-codeobj) diff --git a/source/include/rocprofiler-sdk-codeobj/CMakeLists.txt b/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/CMakeLists.txt similarity index 93% rename from source/include/rocprofiler-sdk-codeobj/CMakeLists.txt rename to source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/CMakeLists.txt index cdfed40b41..a997a7d023 100644 --- a/source/include/rocprofiler-sdk-codeobj/CMakeLists.txt +++ b/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/CMakeLists.txt @@ -20,5 +20,6 @@ set(CODEOBJ_PARSER_HEADERS code_printing.hpp disassembly.hpp segment.hpp) install( FILES ${CODEOBJ_PARSER_HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rocprofiler-sdk-codeobj + DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj COMPONENT development) diff --git a/source/include/rocprofiler-sdk-codeobj/code_printing.hpp b/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/code_printing.hpp similarity index 53% rename from source/include/rocprofiler-sdk-codeobj/code_printing.hpp rename to source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/code_printing.hpp index 98ff32044f..c0859d5c68 100644 --- a/source/include/rocprofiler-sdk-codeobj/code_printing.hpp +++ b/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/code_printing.hpp @@ -1,6 +1,6 @@ // MIT License // -// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2023 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 @@ -22,12 +22,19 @@ #pragma once +#include +#include + +#include +#include +#include #include #include #include #include #include #include + #include "disassembly.hpp" #include "segment.hpp" @@ -64,8 +71,105 @@ struct DSourceLine class CodeobjDecoderComponent { public: - CodeobjDecoderComponent(const void* codeobj_data, uint64_t codeobj_size); - ~CodeobjDecoderComponent(); + CodeobjDecoderComponent(const void* codeobj_data, uint64_t codeobj_size) + { + m_fd = -1; +#if defined(_GNU_SOURCE) && defined(MFD_ALLOW_SEALING) && defined(MFD_CLOEXEC) + m_fd = ::memfd_create(m_uri.c_str(), MFD_ALLOW_SEALING | MFD_CLOEXEC); +#endif + if(m_fd == -1) // If fail, attempt under /tmp + m_fd = ::open("/tmp", O_TMPFILE | O_RDWR, 0666); + + if(m_fd == -1) + { + printf("could not create a temporary file for code object\n"); + return; + } + + if(size_t size = ::write(m_fd, (const char*) codeobj_data, codeobj_size); + size != codeobj_size) + { + printf("could not write to the temporary file\n"); + return; + } + ::lseek(m_fd, 0, SEEK_SET); + fsync(m_fd); + + m_line_number_map = {}; + + std::unique_ptr dbg(dwarf_begin(m_fd, DWARF_C_READ), + [](Dwarf* _dbg) { dwarf_end(_dbg); }); + + if(dbg) + { + Dwarf_Off cu_offset{0}, next_offset; + size_t header_size; + + std::unordered_set used_addrs; + + while(!dwarf_nextcu( + dbg.get(), cu_offset, &next_offset, &header_size, nullptr, nullptr, nullptr)) + { + Dwarf_Die die; + if(!dwarf_offdie(dbg.get(), cu_offset + header_size, &die)) continue; + + Dwarf_Lines* lines; + size_t line_count; + if(dwarf_getsrclines(&die, &lines, &line_count)) continue; + + for(size_t i = 0; i < line_count; ++i) + { + Dwarf_Addr addr; + int line_number; + Dwarf_Line* line = dwarf_onesrcline(lines, i); + + if(line && !dwarf_lineaddr(line, &addr) && !dwarf_lineno(line, &line_number) && + line_number) + { + std::string src = dwarf_linesrc(line, nullptr, nullptr); + auto dwarf_line = src + ':' + std::to_string(line_number); + + if(used_addrs.find(addr) != used_addrs.end()) + { + size_t pos = m_line_number_map.lower_bound(addr); + m_line_number_map.data()[pos].str += ' ' + dwarf_line; + continue; + } + + used_addrs.insert(addr); + m_line_number_map.insert(DSourceLine{addr, 0, std::move(dwarf_line)}); + } + } + cu_offset = next_offset; + } + } + + // Can throw + disassembly = + std::make_unique((const char*) codeobj_data, codeobj_size); + if(m_line_number_map.size()) + { + size_t total_size = 0; + for(size_t i = 0; i < m_line_number_map.size() - 1; i++) + { + size_t s = m_line_number_map.get(i + 1).vaddr - m_line_number_map.get(i).vaddr; + m_line_number_map.data()[i].size = s; + total_size += s; + } + m_line_number_map.back().size = std::max(total_size, codeobj_size) - total_size; + } + try + { + m_symbol_map = disassembly->GetKernelMap(); // Can throw + } catch(...) + {} + + // disassemble_kernels(); + } + ~CodeobjDecoderComponent() + { + if(m_fd) ::close(m_fd); + } std::optional va2fo(uint64_t vaddr) { @@ -73,8 +177,28 @@ public: return {}; }; - std::shared_ptr disassemble_instruction(uint64_t faddr, uint64_t vaddr); - int m_fd; + std::shared_ptr disassemble_instruction(uint64_t faddr, uint64_t vaddr) + { + if(!disassembly) throw std::exception(); + + const char* cpp_line = nullptr; + + try + { + const DSourceLine& it = m_line_number_map.find_obj(vaddr); + cpp_line = it.str.data(); + } catch(...) + {} + + auto pair = disassembly->ReadInstruction(faddr); + auto inst = std::make_shared(std::move(pair.first), pair.second); + inst->faddr = faddr; + inst->vaddr = vaddr; + + if(cpp_line) inst->comment = cpp_line; + return inst; + } + int m_fd; cached_ordered_vector m_line_number_map; std::map m_symbol_map{}; @@ -87,15 +211,73 @@ public: class LoadedCodeobjDecoder { public: - LoadedCodeobjDecoder(const char* filepath, uint64_t load_addr, uint64_t memsize); - LoadedCodeobjDecoder(const void* data, uint64_t size, uint64_t load_addr, size_t memsize); - std::shared_ptr add_to_map(uint64_t ld_addr); + LoadedCodeobjDecoder(const char* filepath, uint64_t _load_addr, uint64_t _memsize) + : load_addr(_load_addr) + , load_end(_load_addr + _memsize) + { + if(!filepath) throw "Empty filepath."; - std::shared_ptr get(uint64_t addr); - uint64_t begin() const { return load_addr; }; - uint64_t end() const { return load_end; } - uint64_t size() const { return load_end - load_addr; } - bool inrange(uint64_t addr) const { return addr >= begin() && addr < end(); } + std::string_view fpath(filepath); + + if(fpath.rfind(".out") + 4 == fpath.size()) + { + std::ifstream file(filepath, std::ios::in | std::ios::binary); + + if(!file.is_open()) throw "Invalid filename " + std::string(filepath); + + std::vector buffer; + file.seekg(0, file.end); + buffer.resize(file.tellg()); + file.seekg(0, file.beg); + file.read(buffer.data(), buffer.size()); + + decoder = std::make_unique(buffer.data(), buffer.size()); + } + else + { + std::unique_ptr binary = std::make_unique(filepath); + auto& buffer = binary->buffer; + decoder = std::make_unique(buffer.data(), buffer.size()); + } + } + LoadedCodeobjDecoder(const void* data, uint64_t size, uint64_t _load_addr, size_t _memsize) + : load_addr(_load_addr) + , load_end(load_addr + _memsize) + { + decoder = + std::make_unique(reinterpret_cast(data), size); + } + std::shared_ptr add_to_map(uint64_t ld_addr) + { + if(!decoder || ld_addr < load_addr) throw std::out_of_range("Addr not in decoder"); + + uint64_t voffset = ld_addr - load_addr; + auto faddr = decoder->va2fo(voffset); + if(!faddr) throw std::out_of_range("Could not find file offset"); + + auto shared = decoder->disassemble_instruction(*faddr, voffset); + shared->ld_addr = ld_addr; + decoded_map[ld_addr] = shared; + return shared; + } + + std::shared_ptr get(uint64_t addr) + { + if(decoded_map.find(addr) != decoded_map.end()) return decoded_map[addr]; + try + { + return add_to_map(addr); + } catch(std::exception& e) + { + std::cerr << e.what() << " at addr " << std::hex << addr << std::dec << std::endl; + } + throw std::out_of_range("Invalid address"); + return nullptr; + } + uint64_t begin() const { return load_addr; }; + uint64_t end() const { return load_end; } + uint64_t size() const { return load_end - load_addr; } + bool inrange(uint64_t addr) const { return addr >= begin() && addr < end(); } const char* getSymbolName(uint64_t addr) const { @@ -112,8 +294,7 @@ public: if(!decoder) throw std::exception(); return decoder->m_symbol_map; } - std::vector> elf_segments{}; - const uint64_t load_addr; + const uint64_t load_addr; private: uint64_t load_end = 0; diff --git a/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/disassembly.hpp b/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/disassembly.hpp new file mode 100644 index 0000000000..48b8c6d36d --- /dev/null +++ b/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/disassembly.hpp @@ -0,0 +1,336 @@ +// MIT License +// +// Copyright (c) 2023 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. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define THROW_COMGR(call) \ + if(amd_comgr_status_s status = call) \ + { \ + const char* reason = ""; \ + amd_comgr_status_string(status, &reason); \ + std::cerr << __FILE__ << ':' << __LINE__ << " code: " << status << " failed: " << reason \ + << std::endl; \ + throw std::exception(); \ + } + +#define RETURN_COMGR(call) \ + if(amd_comgr_status_s status = call) \ + { \ + const char* reason = ""; \ + amd_comgr_status_string(status, &reason); \ + std::cerr << __FILE__ << ':' << __LINE__ << " code: " << status << " failed: " << reason \ + << std::endl; \ + return AMD_COMGR_STATUS_ERROR; \ + } + +#define CHECK_VA2FO(x, msg) \ + if(!(x)) \ + { \ + std::cerr << __FILE__ << ' ' << __LINE__ << ' ' << msg << std::endl; \ + return std::nullopt; \ + } + +namespace rocprofiler +{ +namespace codeobj +{ +namespace disassembly +{ +class CodeObjectBinary +{ +public: + CodeObjectBinary(const std::string& _uri) + : m_uri(_uri) + { + const std::string protocol_delim{"://"}; + + size_t protocol_end = m_uri.find(protocol_delim); + std::string protocol = m_uri.substr(0, protocol_end); + protocol_end += protocol_delim.length(); + + std::transform(protocol.begin(), protocol.end(), protocol.begin(), [](unsigned char c) { + return std::tolower(c); + }); + + std::string path; + size_t path_end = m_uri.find_first_of("#?", protocol_end); + if(path_end != std::string::npos) + { + path = m_uri.substr(protocol_end, path_end++ - protocol_end); + } + else + { + path = m_uri.substr(protocol_end); + } + + /* %-decode the string. */ + std::string decoded_path; + decoded_path.reserve(path.length()); + for(size_t i = 0; i < path.length(); ++i) + { + if(path[i] == '%' && std::isxdigit(path[i + 1]) && std::isxdigit(path[i + 2])) + { + decoded_path += std::stoi(path.substr(i + 1, 2), 0, 16); + i += 2; + } + else + { + decoded_path += path[i]; + } + } + + /* Tokenize the query/fragment. */ + std::vector tokens; + size_t pos, last = path_end; + while((pos = m_uri.find('&', last)) != std::string::npos) + { + tokens.emplace_back(m_uri.substr(last, pos - last)); + last = pos + 1; + } + if(last != std::string::npos) + { + tokens.emplace_back(m_uri.substr(last)); + } + + /* Create a tag-value map from the tokenized query/fragment. */ + std::unordered_map params; + std::for_each(tokens.begin(), tokens.end(), [&](std::string& token) { + size_t delim = token.find('='); + if(delim != std::string::npos) + { + params.emplace(token.substr(0, delim), token.substr(delim + 1)); + } + }); + + buffer = std::vector{}; + size_t offset = 0; + size_t size = 0; + + if(auto offset_it = params.find("offset"); offset_it != params.end()) + { + offset = std::stoul(offset_it->second, nullptr, 0); + } + + if(auto size_it = params.find("size"); size_it != params.end()) + { + if(!(size = std::stoul(size_it->second, nullptr, 0))) return; + } + + if(protocol == "memory") throw protocol + " protocol not supported!"; + + std::ifstream file(decoded_path, std::ios::in | std::ios::binary); + if(!file || !file.is_open()) throw "could not open " + decoded_path; + + if(!size) + { + file.ignore(std::numeric_limits::max()); + size_t bytes = file.gcount(); + file.clear(); + + if(bytes < offset) throw "invalid uri " + decoded_path + " (file size < offset)"; + + size = bytes - offset; + } + + file.seekg(offset, std::ios_base::beg); + buffer.resize(size); + file.read(&buffer[0], size); + } + + std::string m_uri; + std::vector buffer; +}; + +struct SymbolInfo +{ + std::string name{}; + uint64_t faddr = 0; + uint64_t vaddr = 0; + uint64_t mem_size = 0; +}; + +class DisassemblyInstance +{ +public: + DisassemblyInstance(const char* codeobj_data, uint64_t codeobj_size) + { + buffer = std::vector(codeobj_size, 0); + std::memcpy(buffer.data(), codeobj_data, codeobj_size); + + THROW_COMGR(amd_comgr_create_data(AMD_COMGR_DATA_KIND_EXECUTABLE, &data)); + THROW_COMGR(amd_comgr_set_data(data, buffer.size(), buffer.data())); + + size_t isa_size = 128; + std::string input_isa{}; + input_isa.resize(isa_size); + THROW_COMGR(amd_comgr_get_data_isa_name(data, &isa_size, input_isa.data())); + + THROW_COMGR(amd_comgr_create_disassembly_info( + input_isa.data(), + &DisassemblyInstance::memory_callback, + &DisassemblyInstance::inst_callback, + [](uint64_t, void*) {}, + &info)); + } + ~DisassemblyInstance() + { + amd_comgr_release_data(data); + amd_comgr_destroy_disassembly_info(info); + } + + std::pair ReadInstruction(uint64_t faddr) + { + uint64_t size_read; + uint64_t addr_in_buffer = reinterpret_cast(buffer.data()) + faddr; + + THROW_COMGR( + amd_comgr_disassemble_instruction(info, addr_in_buffer, (void*) this, &size_read)); + return {std::move(this->last_instruction), size_read}; + } + + std::map& GetKernelMap() + { + symbol_map = {}; + THROW_COMGR(amd_comgr_iterate_symbols(data, &DisassemblyInstance::symbol_callback, this)); + + return symbol_map; + } + + static amd_comgr_status_t symbol_callback(amd_comgr_symbol_t symbol, void* user_data) + { + amd_comgr_symbol_type_t type; + RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_TYPE, &type)); + + if(type != AMD_COMGR_SYMBOL_TYPE_FUNC) return AMD_COMGR_STATUS_SUCCESS; + + uint64_t vaddr = 0; + uint64_t mem_size = 0; + uint64_t name_size = 0; + RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_VALUE, &vaddr)); + RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_SIZE, &mem_size)); + RETURN_COMGR( + amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_NAME_LENGTH, &name_size)); + + std::string name; + name.resize(name_size); + + RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_NAME, name.data())); + + DisassemblyInstance& instance = *static_cast(user_data); + std::optional faddr = instance.va2fo(vaddr); + + if(faddr) instance.symbol_map[vaddr] = {name, *faddr, vaddr, mem_size}; + return AMD_COMGR_STATUS_SUCCESS; + } + + static uint64_t memory_callback(uint64_t from, char* to, uint64_t size, void* user_data) + { + DisassemblyInstance& instance = *static_cast(user_data); + int64_t copysize = reinterpret_cast(instance.buffer.data()) + + instance.buffer.size() - static_cast(from); + copysize = std::min(size, copysize); + std::memcpy(to, (char*) from, copysize); + return copysize; + } + + static void inst_callback(const char* instruction, void* user_data) + { + DisassemblyInstance& instance = *static_cast(user_data); + + if(!instruction) return; + + while(*instruction == '\t' || *instruction == ' ') + instruction++; + instance.last_instruction = instruction; + } + + std::optional va2fo(uint64_t va) + { + CHECK_VA2FO(buffer.size() > sizeof(Elf64_Ehdr), "buffer is not large enough"); + + uint8_t* e_ident = (uint8_t*) buffer.data(); + CHECK_VA2FO(e_ident, "e_ident is nullptr"); + + CHECK_VA2FO(e_ident[EI_MAG0] == ELFMAG0 || e_ident[EI_MAG1] == ELFMAG1 || + e_ident[EI_MAG2] == ELFMAG2 || e_ident[EI_MAG3] == ELFMAG3, + "unexpected ei_mag"); + + CHECK_VA2FO(e_ident[EI_CLASS] == ELFCLASS64, "unexpected ei_class"); + CHECK_VA2FO(e_ident[EI_DATA] == ELFDATA2LSB, "unexpected ei_data"); + CHECK_VA2FO(e_ident[EI_VERSION] == EV_CURRENT, "unexpected ei_version"); + CHECK_VA2FO(e_ident[EI_OSABI] == 64, "unexpected ei_osabi"); // ELFOSABI_AMDGPU_HSA + + CHECK_VA2FO(e_ident[EI_ABIVERSION] == 2 || // ELFABIVERSION_AMDGPU_HSA_V4 + e_ident[EI_ABIVERSION] == 3, + "unexpected ei_abiversion"); // ELFABIVERSION_AMDGPU_HSA_V5 + + Elf64_Ehdr* ehdr = (Elf64_Ehdr*) buffer.data(); + CHECK_VA2FO(ehdr, "ehdr is nullptr"); + CHECK_VA2FO(ehdr->e_type == ET_DYN, "unexpected e_type"); + CHECK_VA2FO(ehdr->e_machine == ELF::EM_AMDGPU, "unexpected e_machine"); + CHECK_VA2FO(ehdr->e_phoff != 0, "unexpected e_phoff"); + + CHECK_VA2FO(buffer.size() > ehdr->e_phoff + sizeof(Elf64_Phdr), + "buffer is not large enough"); + + Elf64_Phdr* phdr = (Elf64_Phdr*) ((uint8_t*) buffer.data() + ehdr->e_phoff); + CHECK_VA2FO(phdr, "phdr is nullptr"); + + for(uint16_t i = 0; i < ehdr->e_phnum; ++i) + { + if(phdr[i].p_type != PT_LOAD) continue; + if(va < phdr[i].p_vaddr || va >= (phdr[i].p_vaddr + phdr[i].p_memsz)) continue; + + return va + phdr[i].p_offset - phdr[i].p_vaddr; + } + return std::nullopt; + } + + std::vector buffer; + std::string last_instruction; + amd_comgr_disassembly_info_t info; + amd_comgr_data_t data; + std::map symbol_map; +}; + +} // namespace disassembly +} // namespace codeobj +} // namespace rocprofiler diff --git a/source/include/rocprofiler-sdk-codeobj/segment.hpp b/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/segment.hpp similarity index 100% rename from source/include/rocprofiler-sdk-codeobj/segment.hpp rename to source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/segment.hpp diff --git a/source/lib/rocprofiler-sdk-codeobj/CMakeLists.txt b/source/lib/rocprofiler-sdk-codeobj/CMakeLists.txt index 59b78ea3d9..637f589284 100644 --- a/source/lib/rocprofiler-sdk-codeobj/CMakeLists.txt +++ b/source/lib/rocprofiler-sdk-codeobj/CMakeLists.txt @@ -18,28 +18,18 @@ set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "development") -set(CODEOBJ_PARSER_SOURCES code_printing.cpp disassembly.cpp) +add_library(rocprofiler-sdk-codeobj INTERFACE) +add_library(rocprofiler::rocprofiler-sdk-codeobj ALIAS rocprofiler-sdk-codeobj) + +target_include_directories( + rocprofiler-sdk-codeobj + INTERFACE $ + $) -add_library(rocprofiler-sdk-codeobj STATIC) -target_sources(rocprofiler-sdk-codeobj PRIVATE ${CODEOBJ_PARSER_SOURCES}) target_link_libraries( rocprofiler-sdk-codeobj - PRIVATE rocprofiler::rocprofiler-amd-comgr rocprofiler::rocprofiler-dw - rocprofiler::rocprofiler-elf rocprofiler::rocprofiler-build-flags - rocprofiler::rocprofiler-common-library) - -target_include_directories(rocprofiler-sdk-codeobj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - -set_target_properties( - rocprofiler-sdk-codeobj - PROPERTIES LIBRARY_OUTPUT_DIRECTORY - ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/rocprofiler-sdk - POSITION_INDEPENDENT_CODE ON) - -install( - TARGETS rocprofiler-sdk-codeobj - DESTINATION ${CMAKE_INSTALL_LIBDIR} - EXPORT rocprofiler-sdk-codeobj-targets) + INTERFACE rocprofiler::rocprofiler-amd-comgr rocprofiler::rocprofiler-dw + rocprofiler::rocprofiler-elf) if(ROCPROFILER_BUILD_TESTS) add_subdirectory(tests) diff --git a/source/lib/rocprofiler-sdk-codeobj/LICENSE b/source/lib/rocprofiler-sdk-codeobj/LICENSE deleted file mode 100644 index b69c259b1a..0000000000 --- a/source/lib/rocprofiler-sdk-codeobj/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 AMD ROCmâ„¢ Software - -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. diff --git a/source/lib/rocprofiler-sdk-codeobj/README.md b/source/lib/rocprofiler-sdk-codeobj/README.md deleted file mode 100644 index bda6c9da14..0000000000 --- a/source/lib/rocprofiler-sdk-codeobj/README.md +++ /dev/null @@ -1 +0,0 @@ -# rocprofiler-codeobj-parser \ No newline at end of file diff --git a/source/lib/rocprofiler-sdk-codeobj/code_printing.cpp b/source/lib/rocprofiler-sdk-codeobj/code_printing.cpp deleted file mode 100644 index a93c5c7b8b..0000000000 --- a/source/lib/rocprofiler-sdk-codeobj/code_printing.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// MIT License -// -// Copyright (c) 2024 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#define C_API_BEGIN \ - try \ - { -#define C_API_END(returndata) \ - } \ - catch(std::exception & e) \ - { \ - std::string s = e.what(); \ - if(s.find("memory protocol not supported!") == std::string::npos) \ - std::cerr << "Codeobj API lookup: " << e.what() << std::endl; \ - return returndata; \ - } \ - catch(std::string & s) \ - { \ - if(s.find("memory protocol not supported!") == std::string::npos) \ - std::cerr << "Codeobj API lookup: " << s << std::endl; \ - return returndata; \ - } \ - catch(...) { return returndata; } - -namespace rocprofiler -{ -namespace codeobj -{ -namespace disassembly -{ -CodeobjDecoderComponent::CodeobjDecoderComponent(const void* codeobj_data, uint64_t codeobj_size) -{ - m_fd = -1; -#if defined(_GNU_SOURCE) && defined(MFD_ALLOW_SEALING) && defined(MFD_CLOEXEC) - m_fd = ::memfd_create(m_uri.c_str(), MFD_ALLOW_SEALING | MFD_CLOEXEC); -#endif - if(m_fd == -1) // If fail, attempt under /tmp - m_fd = ::open("/tmp", O_TMPFILE | O_RDWR, 0666); - - if(m_fd == -1) - { - printf("could not create a temporary file for code object\n"); - return; - } - - if(size_t size = ::write(m_fd, (const char*) codeobj_data, codeobj_size); size != codeobj_size) - { - printf("could not write to the temporary file\n"); - return; - } - ::lseek(m_fd, 0, SEEK_SET); - fsync(m_fd); - - m_line_number_map = {}; - - std::unique_ptr dbg(dwarf_begin(m_fd, DWARF_C_READ), - [](Dwarf* _dbg) { dwarf_end(_dbg); }); - - /*if (!dbg) { - rocprofiler::warning("Error opening Dwarf!\n"); - return; - } */ - - if(dbg) - { - Dwarf_Off cu_offset{0}, next_offset; - size_t header_size; - - std::unordered_set used_addrs; - - while(!dwarf_nextcu( - dbg.get(), cu_offset, &next_offset, &header_size, nullptr, nullptr, nullptr)) - { - Dwarf_Die die; - if(!dwarf_offdie(dbg.get(), cu_offset + header_size, &die)) continue; - - Dwarf_Lines* lines; - size_t line_count; - if(dwarf_getsrclines(&die, &lines, &line_count)) continue; - - for(size_t i = 0; i < line_count; ++i) - { - Dwarf_Addr addr; - int line_number; - Dwarf_Line* line = dwarf_onesrcline(lines, i); - - if(line && !dwarf_lineaddr(line, &addr) && !dwarf_lineno(line, &line_number) && - line_number) - { - std::string src = dwarf_linesrc(line, nullptr, nullptr); - auto dwarf_line = src + ':' + std::to_string(line_number); - - if(used_addrs.find(addr) != used_addrs.end()) - { - size_t pos = m_line_number_map.lower_bound(addr); - m_line_number_map.data()[pos].str += ' ' + dwarf_line; - continue; - } - - used_addrs.insert(addr); - m_line_number_map.insert(DSourceLine{addr, 0, std::move(dwarf_line)}); - } - } - cu_offset = next_offset; - } - } - - // Can throw - disassembly = std::make_unique((const char*) codeobj_data, codeobj_size); - if(m_line_number_map.size()) - { - size_t total_size = 0; - for(size_t i = 0; i < m_line_number_map.size() - 1; i++) - { - size_t s = m_line_number_map.get(i + 1).vaddr - m_line_number_map.get(i).vaddr; - m_line_number_map.data()[i].size = s; - total_size += s; - } - m_line_number_map.back().size = std::max(total_size, codeobj_size) - total_size; - } - try - { - m_symbol_map = disassembly->GetKernelMap(); // Can throw - } catch(...) - {} - - // disassemble_kernels(); -} - -CodeobjDecoderComponent::~CodeobjDecoderComponent() -{ - if(m_fd) ::close(m_fd); -} - -std::shared_ptr -CodeobjDecoderComponent::disassemble_instruction(uint64_t faddr, uint64_t vaddr) -{ - if(!disassembly) throw std::exception(); - - const char* cpp_line = nullptr; - - try - { - const DSourceLine& it = m_line_number_map.find_obj(vaddr); - cpp_line = it.str.data(); - } catch(...) - {} - - auto pair = disassembly->ReadInstruction(faddr); - auto inst = std::make_shared(std::move(pair.first), pair.second); - inst->faddr = faddr; - inst->vaddr = vaddr; - - if(cpp_line) inst->comment = cpp_line; - return inst; -} - -LoadedCodeobjDecoder::LoadedCodeobjDecoder(const char* filepath, - uint64_t _load_addr, - uint64_t mem_size) -: load_addr(_load_addr) -, load_end(load_addr + mem_size) -{ - if(!filepath) throw "Empty filepath."; - - std::string_view fpath(filepath); - - if(fpath.rfind(".out") + 4 == fpath.size()) - { - std::ifstream file(filepath, std::ios::in | std::ios::binary); - - if(!file.is_open()) throw "Invalid filename " + std::string(filepath); - - std::vector buffer; - file.seekg(0, file.end); - buffer.resize(file.tellg()); - file.seekg(0, file.beg); - file.read(buffer.data(), buffer.size()); - - decoder = std::make_unique(buffer.data(), buffer.size()); - } - else - { - std::unique_ptr binary = std::make_unique(filepath); - auto& buffer = binary->buffer; - decoder = std::make_unique(buffer.data(), buffer.size()); - } - - elf_segments = decoder->disassembly->getSegments(); -} - -LoadedCodeobjDecoder::LoadedCodeobjDecoder(const void* data, - size_t size, - uint64_t _load_addr, - uint64_t mem_size) -: load_addr(_load_addr) -, load_end(load_addr + mem_size) -{ - decoder = std::make_unique(reinterpret_cast(data), size); - elf_segments = decoder->disassembly->getSegments(); -} - -std::shared_ptr -LoadedCodeobjDecoder::add_to_map(uint64_t ld_addr) -{ - if(!decoder || ld_addr < load_addr) throw std::out_of_range("Addr not in decoder"); - - uint64_t voffset = ld_addr - load_addr; - auto faddr = decoder->va2fo(voffset); - if(!faddr) throw std::out_of_range("Could not find file offset"); - - auto shared = decoder->disassemble_instruction(*faddr, voffset); - shared->ld_addr = ld_addr; - decoded_map[ld_addr] = shared; - return shared; -} - -std::shared_ptr -LoadedCodeobjDecoder::get(uint64_t addr) -{ - if(decoded_map.find(addr) != decoded_map.end()) return decoded_map[addr]; - - try - { - return add_to_map(addr); - } catch(std::exception& e) - { - std::cerr << e.what() << " at addr " << std::hex << addr << std::dec << std::endl; - } - throw std::out_of_range("Invalid address"); - return nullptr; -} - -} // namespace disassembly -} // namespace codeobj -} // namespace rocprofiler diff --git a/source/lib/rocprofiler-sdk-codeobj/disassembly.cpp b/source/lib/rocprofiler-sdk-codeobj/disassembly.cpp deleted file mode 100644 index ab4fa6abbb..0000000000 --- a/source/lib/rocprofiler-sdk-codeobj/disassembly.cpp +++ /dev/null @@ -1,381 +0,0 @@ -// MIT License -// -// Copyright (c) 2024 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. - -#if !defined(_GNU_SOURCE) || !defined(_XOPEN_SOURCE) -# define _XOPEN_SOURCE 700 -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#define THROW_COMGR(call) \ - if(amd_comgr_status_s status = call) \ - { \ - const char* reason = ""; \ - amd_comgr_status_string(status, &reason); \ - std::cerr << __FILE__ << ':' << __LINE__ << " code: " << status << " failed: " << reason \ - << std::endl; \ - throw std::exception(); \ - } - -#define RETURN_COMGR(call) \ - if(amd_comgr_status_s status = call) \ - { \ - const char* reason = ""; \ - amd_comgr_status_string(status, &reason); \ - std::cerr << __FILE__ << ':' << __LINE__ << " code: " << status << " failed: " << reason \ - << std::endl; \ - return AMD_COMGR_STATUS_ERROR; \ - } - -namespace rocprofiler -{ -namespace codeobj -{ -namespace disassembly -{ -CodeObjectBinary::CodeObjectBinary(const std::string& uri) -: m_uri(uri) -{ - const std::string protocol_delim{"://"}; - - size_t protocol_end = m_uri.find(protocol_delim); - std::string protocol = m_uri.substr(0, protocol_end); - protocol_end += protocol_delim.length(); - - std::transform(protocol.begin(), protocol.end(), protocol.begin(), [](unsigned char c) { - return std::tolower(c); - }); - - std::string path; - size_t path_end = m_uri.find_first_of("#?", protocol_end); - if(path_end != std::string::npos) - { - path = m_uri.substr(protocol_end, path_end++ - protocol_end); - } - else - { - path = m_uri.substr(protocol_end); - } - - /* %-decode the string. */ - std::string decoded_path; - decoded_path.reserve(path.length()); - for(size_t i = 0; i < path.length(); ++i) - { - if(path[i] == '%' && std::isxdigit(path[i + 1]) && std::isxdigit(path[i + 2])) - { - decoded_path += std::stoi(path.substr(i + 1, 2), 0, 16); - i += 2; - } - else - { - decoded_path += path[i]; - } - } - - /* Tokenize the query/fragment. */ - std::vector tokens; - size_t pos, last = path_end; - while((pos = m_uri.find('&', last)) != std::string::npos) - { - tokens.emplace_back(m_uri.substr(last, pos - last)); - last = pos + 1; - } - if(last != std::string::npos) - { - tokens.emplace_back(m_uri.substr(last)); - } - - /* Create a tag-value map from the tokenized query/fragment. */ - std::unordered_map params; - std::for_each(tokens.begin(), tokens.end(), [&](std::string& token) { - size_t delim = token.find('='); - if(delim != std::string::npos) - { - params.emplace(token.substr(0, delim), token.substr(delim + 1)); - } - }); - - buffer = std::vector{}; - size_t offset = 0; - size_t size = 0; - - if(auto offset_it = params.find("offset"); offset_it != params.end()) - { - offset = std::stoul(offset_it->second, nullptr, 0); - } - - if(auto size_it = params.find("size"); size_it != params.end()) - { - if(!(size = std::stoul(size_it->second, nullptr, 0))) return; - } - - if(protocol == "memory") throw protocol + " protocol not supported!"; - - std::ifstream file(decoded_path, std::ios::in | std::ios::binary); - if(!file || !file.is_open()) throw "could not open " + decoded_path; - - if(!size) - { - file.ignore(std::numeric_limits::max()); - size_t bytes = file.gcount(); - file.clear(); - - if(bytes < offset) throw "invalid uri " + decoded_path + " (file size < offset)"; - - size = bytes - offset; - } - - file.seekg(offset, std::ios_base::beg); - buffer.resize(size); - file.read(&buffer[0], size); -} - -DisassemblyInstance::DisassemblyInstance(const char* codeobj_data, uint64_t codeobj_size) -{ - buffer = std::vector(codeobj_size, 0); - std::memcpy(buffer.data(), codeobj_data, codeobj_size); - - THROW_COMGR(amd_comgr_create_data(AMD_COMGR_DATA_KIND_EXECUTABLE, &data)); - THROW_COMGR(amd_comgr_set_data(data, buffer.size(), buffer.data())); - - size_t isa_size = 128; - std::string input_isa{}; - input_isa.resize(isa_size); - THROW_COMGR(amd_comgr_get_data_isa_name(data, &isa_size, input_isa.data())); - - THROW_COMGR(amd_comgr_create_disassembly_info( - input_isa.data(), - &DisassemblyInstance::memory_callback, - &DisassemblyInstance::inst_callback, - [](uint64_t, void*) {}, - &info)); -} - -amd_comgr_status_t -DisassemblyInstance::symbol_callback(amd_comgr_symbol_t symbol, void* user_data) -{ - amd_comgr_symbol_type_t type; - RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_TYPE, &type)); - - if(type != AMD_COMGR_SYMBOL_TYPE_FUNC) return AMD_COMGR_STATUS_SUCCESS; - - uint64_t vaddr = 0; - uint64_t mem_size = 0; - uint64_t name_size = 0; - RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_VALUE, &vaddr)); - RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_SIZE, &mem_size)); - RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_NAME_LENGTH, &name_size)); - - std::string name; - name.resize(name_size); - - RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_NAME, name.data())); - - DisassemblyInstance& instance = *static_cast(user_data); - std::optional faddr = instance.va2fo(vaddr); - - if(faddr) instance.symbol_map[vaddr] = {name, *faddr, vaddr, mem_size}; - return AMD_COMGR_STATUS_SUCCESS; -} - -std::map& -DisassemblyInstance::GetKernelMap() -{ - symbol_map = {}; - THROW_COMGR(amd_comgr_iterate_symbols(data, &DisassemblyInstance::symbol_callback, this)); - - return symbol_map; -} - -DisassemblyInstance::~DisassemblyInstance() -{ - amd_comgr_release_data(data); - amd_comgr_destroy_disassembly_info(info); -} - -std::pair -DisassemblyInstance::ReadInstruction(uint64_t faddr) -{ - uint64_t size_read; - uint64_t addr_in_buffer = reinterpret_cast(buffer.data()) + faddr; - - THROW_COMGR(amd_comgr_disassemble_instruction(info, addr_in_buffer, (void*) this, &size_read)); - return {std::move(this->last_instruction), size_read}; -} - -uint64_t -DisassemblyInstance::memory_callback(uint64_t from, char* to, uint64_t size, void* user_data) -{ - DisassemblyInstance& instance = *static_cast(user_data); - int64_t copysize = reinterpret_cast(instance.buffer.data()) + instance.buffer.size() - - static_cast(from); - copysize = std::min(size, copysize); - std::memcpy(to, (char*) from, copysize); - return copysize; -} - -void -DisassemblyInstance::inst_callback(const char* instruction, void* user_data) -{ - DisassemblyInstance& instance = *static_cast(user_data); - - if(!instruction) return; - - while(*instruction == '\t' || *instruction == ' ') - instruction++; - instance.last_instruction = instruction; -} - -#define CHECK_VA2FO(x, msg) \ - if(!(x)) \ - { \ - std::cerr << __FILE__ << ' ' << __LINE__ << ' ' << msg << std::endl; \ - return std::nullopt; \ - } - -// mem - input argument, start of the elf -// va - input argument, virtual address -// return file offset, if found -std::optional -DisassemblyInstance::va2fo(uint64_t va) -{ - CHECK_VA2FO(buffer.size() > sizeof(Elf64_Ehdr), "buffer is not large enough"); - - uint8_t* e_ident = (uint8_t*) buffer.data(); - CHECK_VA2FO(e_ident, "e_ident is nullptr"); - - CHECK_VA2FO(e_ident[EI_MAG0] == ELFMAG0 || e_ident[EI_MAG1] == ELFMAG1 || - e_ident[EI_MAG2] == ELFMAG2 || e_ident[EI_MAG3] == ELFMAG3, - "unexpected ei_mag"); - - CHECK_VA2FO(e_ident[EI_CLASS] == ELFCLASS64, "unexpected ei_class"); - CHECK_VA2FO(e_ident[EI_DATA] == ELFDATA2LSB, "unexpected ei_data"); - CHECK_VA2FO(e_ident[EI_VERSION] == EV_CURRENT, "unexpected ei_version"); - CHECK_VA2FO(e_ident[EI_OSABI] == 64, "unexpected ei_osabi"); // ELFOSABI_AMDGPU_HSA - - CHECK_VA2FO(e_ident[EI_ABIVERSION] == 2 || // ELFABIVERSION_AMDGPU_HSA_V4 - e_ident[EI_ABIVERSION] == 3, - "unexpected ei_abiversion"); // ELFABIVERSION_AMDGPU_HSA_V5 - - Elf64_Ehdr* ehdr = (Elf64_Ehdr*) buffer.data(); - CHECK_VA2FO(ehdr, "ehdr is nullptr"); - CHECK_VA2FO(ehdr->e_type == ET_DYN, "unexpected e_type"); - CHECK_VA2FO(ehdr->e_machine == ELF::EM_AMDGPU, "unexpected e_machine"); - CHECK_VA2FO(ehdr->e_phoff != 0, "unexpected e_phoff"); - - CHECK_VA2FO(buffer.size() > ehdr->e_phoff + sizeof(Elf64_Phdr), "buffer is not large enough"); - - Elf64_Phdr* phdr = (Elf64_Phdr*) ((uint8_t*) buffer.data() + ehdr->e_phoff); - CHECK_VA2FO(phdr, "phdr is nullptr"); - - for(uint16_t i = 0; i < ehdr->e_phnum; ++i) - { - if(phdr[i].p_type != PT_LOAD) continue; - if(va < phdr[i].p_vaddr || va >= (phdr[i].p_vaddr + phdr[i].p_memsz)) continue; - - return va + phdr[i].p_offset - phdr[i].p_vaddr; - } - return std::nullopt; -} - -#undef CHECK_VA2FO -#define CHECK_VA2FO(x, msg) \ - if(!(x)) \ - { \ - std::cerr << __FILE__ << ' ' << __LINE__ << ' ' << msg << std::endl; \ - return {}; \ - } - -std::vector> -DisassemblyInstance::getSegments() -{ - CHECK_VA2FO(buffer.size() > sizeof(Elf64_Ehdr), "buffer is not large enough"); - - uint8_t* e_ident = (uint8_t*) buffer.data(); - CHECK_VA2FO(e_ident, "e_ident is nullptr"); - - CHECK_VA2FO(e_ident[EI_MAG0] == ELFMAG0 || e_ident[EI_MAG1] == ELFMAG1 || - e_ident[EI_MAG2] == ELFMAG2 || e_ident[EI_MAG3] == ELFMAG3, - "unexpected ei_mag"); - - CHECK_VA2FO(e_ident[EI_CLASS] == ELFCLASS64, "unexpected ei_class"); - CHECK_VA2FO(e_ident[EI_DATA] == ELFDATA2LSB, "unexpected ei_data"); - CHECK_VA2FO(e_ident[EI_VERSION] == EV_CURRENT, "unexpected ei_version"); - CHECK_VA2FO(e_ident[EI_OSABI] == 64, "unexpected ei_osabi"); // ELFOSABI_AMDGPU_HSA - - CHECK_VA2FO(e_ident[EI_ABIVERSION] == 2 || // ELFABIVERSION_AMDGPU_HSA_V4 - e_ident[EI_ABIVERSION] == 3, - "unexpected ei_abiversion"); // ELFABIVERSION_AMDGPU_HSA_V5 - - Elf64_Ehdr* ehdr = (Elf64_Ehdr*) buffer.data(); - CHECK_VA2FO(ehdr, "ehdr is nullptr"); - CHECK_VA2FO(ehdr->e_type == ET_DYN, "unexpected e_type"); - CHECK_VA2FO(ehdr->e_machine == ELF::EM_AMDGPU, "unexpected e_machine"); - CHECK_VA2FO(ehdr->e_phoff != 0, "unexpected e_phoff"); - - CHECK_VA2FO(buffer.size() > ehdr->e_phoff + sizeof(Elf64_Phdr), "buffer is not large enough"); - - Elf64_Phdr* phdr = (Elf64_Phdr*) ((uint8_t*) buffer.data() + ehdr->e_phoff); - CHECK_VA2FO(phdr, "phdr is nullptr"); - - std::vector> segments; - for(Elf64_Half i = 0; i < ehdr->e_phnum; ++i) - { - if(phdr[i].p_type != PT_LOAD) continue; - - segments.push_back({phdr[i].p_vaddr - phdr[i].p_offset, phdr[i].p_memsz}); - } - - return segments; -} - -} // namespace disassembly -} // namespace codeobj -} // namespace rocprofiler diff --git a/source/lib/rocprofiler-sdk-codeobj/tests/CMakeLists.txt b/source/lib/rocprofiler-sdk-codeobj/tests/CMakeLists.txt index ccc609d265..9792eb8c9f 100644 --- a/source/lib/rocprofiler-sdk-codeobj/tests/CMakeLists.txt +++ b/source/lib/rocprofiler-sdk-codeobj/tests/CMakeLists.txt @@ -15,7 +15,7 @@ target_link_libraries( rocprofiler::rocprofiler-common-library GTest::gtest GTest::gtest_main - rocprofiler-sdk-codeobj) + rocprofiler::rocprofiler-sdk-codeobj) gtest_add_tests( TARGET codeobj-library-test diff --git a/source/lib/rocprofiler-sdk-codeobj/tests/codeobj_library_test.cpp b/source/lib/rocprofiler-sdk-codeobj/tests/codeobj_library_test.cpp index ea6acb3eb1..3ae1442fdf 100644 --- a/source/lib/rocprofiler-sdk-codeobj/tests/codeobj_library_test.cpp +++ b/source/lib/rocprofiler-sdk-codeobj/tests/codeobj_library_test.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/tests/thread-trace/CMakeLists.txt b/tests/thread-trace/CMakeLists.txt index e5018f332e..1bd77540d1 100644 --- a/tests/thread-trace/CMakeLists.txt +++ b/tests/thread-trace/CMakeLists.txt @@ -22,8 +22,74 @@ project( LANGUAGES CXX HIP VERSION 0.0.0) +find_package(PkgConfig) + +if(PkgConfig_FOUND) + set(ENV{PKG_CONFIG_SYSTEM_INCLUDE_PATH} "") + pkg_check_modules(DW libdw) + + if(DW_FOUND + AND DW_INCLUDE_DIRS + AND DW_LIBRARIES) + set(libdw_INCLUDE_DIR + "${DW_INCLUDE_DIRS}" + CACHE FILEPATH "libdw include directory") + set(libdw_LIBRARY + "${DW_LIBRARIES}" + CACHE FILEPATH "libdw libraries") + endif() +endif() + +if(NOT libdw_INCLUDE_DIR OR NOT libdw_LIBRARY) + find_path( + libdw_ROOT_DIR + NAMES include/elfutils/libdw.h + HINTS ${libdw_ROOT} + PATHS ${libdw_ROOT}) + + mark_as_advanced(libdw_ROOT_DIR) + + find_path( + libdw_INCLUDE_DIR + NAMES elfutils/libdw.h + HINTS ${libdw_ROOT} + PATHS ${libdw_ROOT} + PATH_SUFFIXES include) + + find_library( + libdw_LIBRARY + NAMES dw + HINTS ${libdw_ROOT} + PATHS ${libdw_ROOT} + PATH_SUFFIXES lib lib64) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(libdw DEFAULT_MSG libdw_LIBRARY libdw_INCLUDE_DIR) + +if(libdw_FOUND AND NOT TARGET libdw::libdw) + add_library(libdw::libdw INTERFACE IMPORTED) + if(TARGET PkgConfig::DW AND DW_FOUND) + target_link_libraries(libdw::libdw INTERFACE PkgConfig::DW) + else() + target_link_libraries(libdw::libdw INTERFACE ${libdw_LIBRARY}) + target_include_directories(libdw::libdw SYSTEM INTERFACE ${libdw_INCLUDE_DIR}) + endif() +endif() + find_package(rocprofiler-sdk REQUIRED) -find_package(amd_comgr REQUIRED) +find_package( + amd_comgr + REQUIRED + CONFIG + HINTS + ${rocm_version_DIR} + ${ROCM_PATH} + PATHS + ${rocm_version_DIR} + ${ROCM_PATH} + PATH_SUFFIXES + lib/cmake/amd_comgr) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS OFF) @@ -38,17 +104,11 @@ foreach(_TYPE DEBUG MINSIZEREL RELEASE RELWITHDEBINFO) endif() endforeach() -add_library(thread-trace-api-test-lib SHARED) -target_sources(thread-trace-api-test-lib PRIVATE verify_data.cpp) -target_link_libraries( - thread-trace-api-test-lib PRIVATE rocprofiler::rocprofiler rocprofiler-sdk-codeobj - amd_comgr dw) - set_source_files_properties(kernel_run.cpp PROPERTIES COMPILE_FLAGS "-g -O2") set_source_files_properties(kernel_run.cpp PROPERTIES LANGUAGE HIP) add_executable(thread-trace-api-test-binary) -target_sources(thread-trace-api-test-binary PRIVATE kernel_run.cpp) +target_sources(thread-trace-api-test-binary PRIVATE kernel_run.cpp verify_data.cpp) if(ROCPROFILER_MEMCHECK_PRELOAD_ENV) set(PRELOAD_ENV @@ -57,7 +117,8 @@ else() set(PRELOAD_ENV "LD_PRELOAD=$") endif() -target_link_libraries(thread-trace-api-test-binary PRIVATE thread-trace-api-test-lib) +target_link_libraries(thread-trace-api-test-binary PRIVATE rocprofiler::rocprofiler + libdw::libdw amd_comgr) add_test(NAME thread-trace-api-tests COMMAND $) diff --git a/tests/thread-trace/verify_data.cpp b/tests/thread-trace/verify_data.cpp index d148504406..126a0ecabe 100644 --- a/tests/thread-trace/verify_data.cpp +++ b/tests/thread-trace/verify_data.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include