From 78fd8cb3798c0aa85dd50cb409f9785650892ed8 Mon Sep 17 00:00:00 2001 From: Giovanni Lenzi Baraldi Date: Mon, 8 Jul 2024 16:59:40 -0300 Subject: [PATCH] Returning code object id information in code_printing.cpp:Instruction (#965) * Returning code object id information in code_printing.cpp:Instruction * Adding assertions * Simplifying decoder library --- samples/code_object_isa_decode/client.cpp | 1 + .../rocprofiler-sdk-codeobj/code_printing.hpp | 214 +++++++++--------- .../rocprofiler-sdk-codeobj/segment.hpp | 146 ++++-------- .../tests/codeobj_library_test.cpp | 59 ++--- 4 files changed, 175 insertions(+), 245 deletions(-) diff --git a/samples/code_object_isa_decode/client.cpp b/samples/code_object_isa_decode/client.cpp index 3ac1c2cb..255bbd85 100644 --- a/samples/code_object_isa_decode/client.cpp +++ b/samples/code_object_isa_decode/client.cpp @@ -162,6 +162,7 @@ tool_codeobj_tracing_callback(rocprofiler_callback_tracing_record_t record, while(vaddr < begin_end.second) { auto inst = codeobjTranslate.get(vaddr); + assert(inst != nullptr); if(inst->comment.size()) { std::string_view source = inst->comment; diff --git a/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/code_printing.hpp b/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/code_printing.hpp index c5471e38..6beead3b 100644 --- a/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/code_printing.hpp +++ b/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/code_printing.hpp @@ -44,6 +44,8 @@ namespace codeobj { namespace disassembly { +using marker_id_t = segment::marker_id_t; + struct Instruction { Instruction() = default; @@ -51,53 +53,47 @@ struct Instruction : inst(std::move(_inst)) , size(_size) {} - std::string inst; - std::string comment; - uint64_t faddr; - uint64_t vaddr; - uint64_t ld_addr; - size_t size; -}; - -struct DSourceLine -{ - uint64_t vaddr; - uint64_t size; - std::string str; - uint64_t begin() const { return vaddr; } - bool inrange(uint64_t addr) const { return addr >= vaddr && addr < vaddr + size; } + std::string inst{}; + std::string comment{}; + uint64_t faddr{0}; + uint64_t vaddr{0}; + size_t size{0}; + uint64_t ld_addr{0}; // Instruction load address, if from loaded codeobj + marker_id_t codeobj_id{0}; // Instruction code object load id, if from loaded codeobj }; class CodeobjDecoderComponent { -public: - CodeobjDecoderComponent(const void* codeobj_data, uint64_t codeobj_size) + struct ProtectedFd { - m_fd = -1; + ProtectedFd(std::string_view uri) + { #if defined(_GNU_SOURCE) && defined(MFD_ALLOW_SEALING) && defined(MFD_CLOEXEC) - m_fd = ::memfd_create(m_uri.c_str(), MFD_ALLOW_SEALING | MFD_CLOEXEC); + m_fd = ::memfd_create(uri.data(), 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(m_fd == -1) m_fd = ::open("/tmp", O_TMPFILE | O_RDWR, 0666); + if(m_fd == -1) throw std::runtime_error("Could not create a file for codeobj!"); } - - if(size_t size = ::write(m_fd, (const char*) codeobj_data, codeobj_size); - size != codeobj_size) + ~ProtectedFd() { - printf("could not write to the temporary file\n"); - return; + if(m_fd != -1) ::close(m_fd); } - ::lseek(m_fd, 0, SEEK_SET); - fsync(m_fd); + int m_fd{-1}; + }; + +public: + CodeobjDecoderComponent(const char* codeobj_data, uint64_t codeobj_size) + { + ProtectedFd prot(""); + if(::write(prot.m_fd, codeobj_data, codeobj_size) != static_cast(codeobj_size)) + throw std::runtime_error("Could not write to temporary file!"); + + ::lseek(prot.m_fd, 0, SEEK_SET); + fsync(prot.m_fd); m_line_number_map = {}; - std::unique_ptr dbg(dwarf_begin(m_fd, DWARF_C_READ), + std::unique_ptr dbg(dwarf_begin(prot.m_fd, DWARF_C_READ), [](Dwarf* _dbg) { dwarf_end(_dbg); }); if(dbg) @@ -105,7 +101,7 @@ class CodeobjDecoderComponent Dwarf_Off cu_offset{0}, next_offset; size_t header_size; - std::unordered_set used_addrs; + std::map line_addrs; while(!dwarf_nextcu( dbg.get(), cu_offset, &next_offset, &header_size, nullptr, nullptr, nullptr)) @@ -129,47 +125,42 @@ class CodeobjDecoderComponent 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()) + if(line_addrs.find(addr) != line_addrs.end()) { - size_t pos = m_line_number_map.lower_bound(addr); - m_line_number_map.data()[pos].str += ' ' + dwarf_line; + line_addrs.at(addr) += ' ' + dwarf_line; continue; } - used_addrs.insert(addr); - m_line_number_map.insert(DSourceLine{addr, 0, std::move(dwarf_line)}); + line_addrs.emplace(addr, 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++) + auto it = line_addrs.begin(); + if(it != line_addrs.end()) { - 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; + while(std::next(it) != line_addrs.end()) + { + uint64_t delta = std::next(it)->first - it->first; + auto segment = segment::address_range_t{it->first, delta, 0}; + m_line_number_map.emplace(segment, std::move(it->second)); + it++; + } + auto segment = segment::address_range_t{it->first, codeobj_size - it->first, 0}; + m_line_number_map.emplace(segment, std::move(it->second)); } - m_line_number_map.back().size = std::max(total_size, codeobj_size) - total_size; } + + // Can throw + disassembly = std::make_unique(codeobj_data, codeobj_size); try { m_symbol_map = disassembly->GetKernelMap(); // Can throw } catch(...) {} - - // disassemble_kernels(); - } - ~CodeobjDecoderComponent() - { - if(m_fd) ::close(m_fd); } + ~CodeobjDecoderComponent() {} std::optional va2fo(uint64_t vaddr) { @@ -181,31 +172,22 @@ class CodeobjDecoderComponent { 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_unique(std::move(pair.first), pair.second); inst->faddr = faddr; inst->vaddr = vaddr; - if(cpp_line) inst->comment = cpp_line; + auto it = m_line_number_map.find({vaddr, 0, 0}); + if(it != m_line_number_map.end()) inst->comment = it->second; + return inst; } - int m_fd; - - cached_ordered_vector m_line_number_map; - std::map m_symbol_map{}; - std::string m_uri; + std::map m_symbol_map{}; std::vector> instructions{}; std::unique_ptr disassembly{}; + + std::map m_line_number_map{}; }; class LoadedCodeobjDecoder @@ -255,7 +237,8 @@ class LoadedCodeobjDecoder auto faddr = decoder->va2fo(voffset); if(!faddr) return nullptr; - auto unique = decoder->disassemble_instruction(*faddr, voffset); + auto unique = decoder->disassemble_instruction(*faddr, voffset); + if(unique == nullptr || unique->size == 0) return nullptr; unique->ld_addr = ld_addr; return unique; } @@ -283,7 +266,7 @@ class LoadedCodeobjDecoder const uint64_t load_addr; private: - uint64_t load_end = 0; + uint64_t load_end{0}; std::unique_ptr decoder{nullptr}; }; @@ -297,42 +280,53 @@ class CodeobjMap CodeobjMap() = default; virtual ~CodeobjMap() = default; - virtual void addDecoder(const char* filepath, - codeobj_marker_id_t id, - uint64_t load_addr, - uint64_t memsize) + virtual void addDecoder(const char* filepath, + marker_id_t id, + uint64_t load_addr, + uint64_t memsize) { decoders[id] = std::make_shared(filepath, load_addr, memsize); } - virtual void addDecoder(const void* data, - size_t memory_size, - codeobj_marker_id_t id, - uint64_t load_addr, - uint64_t memsize) + virtual void addDecoder(const void* data, + size_t memory_size, + marker_id_t id, + uint64_t load_addr, + uint64_t memsize) { decoders[id] = std::make_shared(data, memory_size, load_addr, memsize); } - virtual bool removeDecoderbyId(codeobj_marker_id_t id) { return decoders.erase(id) != 0; } + virtual bool removeDecoderbyId(marker_id_t id) { return decoders.erase(id) != 0; } - std::unique_ptr get(codeobj_marker_id_t id, uint64_t offset) + std::unique_ptr get(marker_id_t id, uint64_t offset) { - auto& decoder = decoders.at(id); - return decoder->get(decoder->begin() + offset); + try + { + auto& decoder = decoders.at(id); + auto inst = decoder->get(decoder->begin() + offset); + if(inst != nullptr) inst->codeobj_id = id; + return inst; + } catch(std::out_of_range&) + {} + return nullptr; } - const char* getSymbolName(codeobj_marker_id_t id, uint64_t offset) + const char* getSymbolName(marker_id_t id, uint64_t offset) { - auto& decoder = decoders.at(id); - uint64_t vaddr = decoder->begin() + offset; - if(decoder->inrange(vaddr)) return decoder->getSymbolName(vaddr); + try + { + auto& decoder = decoders.at(id); + uint64_t vaddr = decoder->begin() + offset; + if(decoder->inrange(vaddr)) return decoder->getSymbolName(vaddr); + } catch(std::out_of_range&) + {} return nullptr; } protected: - std::unordered_map> decoders{}; + std::unordered_map> decoders{}; }; /** @@ -346,39 +340,39 @@ class CodeobjAddressTranslate : public CodeobjMap CodeobjAddressTranslate() = default; ~CodeobjAddressTranslate() override = default; - virtual void addDecoder(const char* filepath, - codeobj_marker_id_t id, - uint64_t load_addr, - uint64_t memsize) override + virtual void addDecoder(const char* filepath, + marker_id_t id, + uint64_t load_addr, + uint64_t memsize) override { this->Super::addDecoder(filepath, id, load_addr, memsize); auto ptr = decoders.at(id); - table.insert({ptr->begin(), ptr->size(), id, 0}); + table.insert({ptr->begin(), ptr->size(), id}); } - virtual void addDecoder(const void* data, - size_t memory_size, - codeobj_marker_id_t id, - uint64_t load_addr, - uint64_t memsize) override + virtual void addDecoder(const void* data, + size_t memory_size, + marker_id_t id, + uint64_t load_addr, + uint64_t memsize) override { this->Super::addDecoder(data, memory_size, id, load_addr, memsize); auto ptr = decoders.at(id); - table.insert({ptr->begin(), ptr->size(), id, 0}); + table.insert({ptr->begin(), ptr->size(), id}); } - virtual bool removeDecoder(codeobj_marker_id_t id, uint64_t load_addr) + virtual bool removeDecoder(marker_id_t id, uint64_t load_addr) { return table.remove(load_addr) && this->Super::removeDecoderbyId(id); } std::unique_ptr get(uint64_t vaddr) { - auto& addr_range = table.find_codeobj_in_range(vaddr); - return this->Super::get(addr_range.id, vaddr - addr_range.vbegin); + auto addr_range = table.find_codeobj_in_range(vaddr); + return this->Super::get(addr_range.id, vaddr - addr_range.addr); } - std::unique_ptr get(codeobj_marker_id_t id, uint64_t offset) + std::unique_ptr get(marker_id_t id, uint64_t offset) { if(id == 0) return get(offset); @@ -410,7 +404,7 @@ class CodeobjAddressTranslate : public CodeobjMap return symbols; } - std::map getSymbolMap(codeobj_marker_id_t id) const + std::map getSymbolMap(marker_id_t id) const { if(decoders.find(id) == decoders.end()) return {}; @@ -424,7 +418,7 @@ class CodeobjAddressTranslate : public CodeobjMap } private: - CodeobjTableTranslator table; + segment::CodeobjTableTranslator table{}; }; } // namespace disassembly diff --git a/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/segment.hpp b/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/segment.hpp index f80f4ead..bcc188f6 100644 --- a/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/segment.hpp +++ b/source/include/rocprofiler-sdk/amd_detail/rocprofiler-sdk-codeobj/segment.hpp @@ -24,139 +24,69 @@ #include #include #include +#include #include #include #include -using codeobj_marker_id_t = size_t; - -template -class ordered_vector : public std::vector +namespace rocprofiler { - using Super = std::vector; - -public: - void insert(const Type& elem) - { - size_t loc = lower_bound(elem.begin()); - if(this->size() && get(loc).begin() < elem.begin()) loc++; - this->Super::insert(this->begin() + loc, elem); - } - bool remove(const Type& elem) - { - if(!this->size()) return false; - size_t loc = lower_bound(elem.begin()); - if(get(loc) != elem) return false; +namespace codeobj +{ +namespace segment +{ +using marker_id_t = size_t; - this->Super::erase(this->begin() + loc); - return true; - } - bool remove(uint64_t elem_begin) - { - if(!this->size()) return false; - size_t loc = lower_bound(elem_begin); - if(get(loc).begin() != elem_begin) return false; +struct address_range_t +{ + uint64_t addr{0}; + uint64_t size{0}; + marker_id_t id{0}; - this->Super::erase(this->begin() + loc); - return true; - } - size_t lower_bound(size_t addr) const + bool operator==(const address_range_t& other) const { - if(!this->size()) return 0; - return binary_search(addr, 0, this->size() - 1); + return (addr >= other.addr && addr < other.addr + other.size) || + (other.addr >= addr && other.addr < addr + size); } - - size_t binary_search(size_t addr, size_t s, size_t e) const + bool operator<(const address_range_t& other) const { - if(s >= e) - return s; - else if(s + 1 == e) - return (get(e).begin() <= addr) ? e : s; - - size_t mid = (s + e) / 2; - if(get(mid).begin() <= addr) - return binary_search(addr, mid, e); - else - return binary_search(addr, s, mid); + if(*this == other) return false; + return addr < other.addr; } - const Type& get(size_t i) const { return this->operator[](i); } + bool inrange(uint64_t _addr) const { return addr <= _addr && addr + size > _addr; }; }; /** * @brief Finds a candidate codeobj for the given vaddr */ -template -class cached_ordered_vector : public ordered_vector +class CodeobjTableTranslator : public std::set { - using Super = ordered_vector; + using Super = std::set; public: - cached_ordered_vector() { reset(); } - - const Type& find_obj(uint64_t addr) - { - if(testCache(addr)) return get(cached_segment); - - size_t lb = this->lower_bound(addr); - if(lb >= this->size() || !get(lb).inrange(addr)) - throw std::string("segment addr out of range"); - - cached_segment = lb; - return get(cached_segment); - } - - uint64_t find_addr(uint64_t addr) { return find_obj(addr).begin(); } - - bool testCache(uint64_t addr) const + address_range_t find_codeobj_in_range(uint64_t addr) { - return this->cached_segment < this->size() && get(cached_segment).inrange(addr); + if(!cached_segment.inrange(addr)) + { + auto it = this->find(address_range_t{addr, 0, 0}); + if(it == this->end()) throw std::exception(); + cached_segment = *it; + } + return cached_segment; } - const Type& get(size_t index) const { return this->data()[index]; } - - void insert(const Type& elem) { this->Super::insert(elem); } - void insert_list(std::vector arange) - { - for(auto& elem : arange) - push_back(elem); - std::sort(this->begin(), this->end(), [](const Type& a, const Type& b) { - return a.begin() < b.begin(); - }); - }; - - void reset() { cached_segment = ~0; } - void clear() - { - reset(); - this->Super::clear(); - } - bool remove(uint64_t addr) + void clear_cache() { cached_segment = {}; } + bool remove(const address_range_t& range) { - reset(); - return this->Super::remove(addr); + clear_cache(); + return this->erase(range) != 0; } + bool remove(uint64_t addr) { return remove(address_range_t{addr, 0, 0}); } private: - size_t cached_segment = ~0; + address_range_t cached_segment{}; }; -struct address_range_t -{ - uint64_t vbegin; - uint64_t size; - codeobj_marker_id_t id; - uint64_t offset; - - bool operator<(const address_range_t& other) const { return vbegin < other.vbegin; } - bool inrange(uint64_t addr) const { return addr >= vbegin && addr < vbegin + size; }; - uint64_t begin() const { return vbegin; } -}; - -/** - * @brief Finds a candidate codeobj for the given vaddr - */ -class CodeobjTableTranslator : public cached_ordered_vector -{ -public: - const address_range_t& find_codeobj_in_range(uint64_t addr) { return this->find_obj(addr); } -}; +} // namespace segment +} // namespace codeobj +} // namespace rocprofiler 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 3ae1442f..dc9d633e 100644 --- a/source/lib/rocprofiler-sdk-codeobj/tests/codeobj_library_test.cpp +++ b/source/lib/rocprofiler-sdk-codeobj/tests/codeobj_library_test.cpp @@ -84,6 +84,8 @@ GetCodeobjContents() TEST(codeobj_library, segment_test) { + using CodeobjTableTranslator = rocprofiler::codeobj::segment::CodeobjTableTranslator; + CodeobjTableTranslator table; std::unordered_set used_addr{}; @@ -92,21 +94,30 @@ TEST(codeobj_library, segment_test) for(int j = 0; j < 2500; j++) { size_t addr = rand() % 10000000; - size_t size = (rand() % 10) + 1; + size_t size = 1; if(used_addr.find(addr) != used_addr.end()) continue; used_addr.insert(addr); - table.insert({addr, addr + size, 0, 0}); + table.insert({addr, size, 0}); } - for(size_t i = 1; i < table.size(); i++) - ASSERT_LT(table[i - 1], table[i]); + ASSERT_NE(table.begin(), table.end()); + { + auto it = std::next(table.begin()); + while(it != table.end()) + { + ASSERT_LT(*std::prev(it), *it); + it++; + } + } + std::vector addr_leftover(used_addr.begin(), used_addr.end()); for(size_t i = 0; i < 2400; i++) { - size_t idx = rand() % table.size(); - auto rdelem = table[idx]; - used_addr.erase(rdelem.vbegin); - ASSERT_NE(table.remove(rdelem.vbegin), 0); + size_t idx = rand() % addr_leftover.size(); + auto addr = addr_leftover.at(idx); + ASSERT_EQ(table.remove(addr), true); + addr_leftover.erase(addr_leftover.begin() + idx); + used_addr.erase(addr); } } } @@ -181,12 +192,14 @@ TEST(codeobj_library, loaded_codeobj_component) TEST(codeobj_library, codeobj_map_test) { + using marker_id_t = rocprofiler::codeobj::segment::marker_id_t; + const std::vector& objdata = rocprofiler::testing::codeobjhelper::GetCodeobjContents(); constexpr size_t laddr1 = 0x1000; constexpr size_t laddr3 = 0x3000; uint64_t kaddr = [&objdata]() { - CodeobjDecoderComponent comp((const void*) objdata.data(), objdata.size()); + CodeobjDecoderComponent comp(objdata.data(), objdata.size()); for(auto& [addr, _] : comp.m_symbol_map) return addr; return 0ul; @@ -195,19 +208,11 @@ TEST(codeobj_library, codeobj_map_test) EXPECT_NE(kaddr, 0); disassembly::CodeobjMap map; - map.addDecoder((const void*) objdata.data(), - objdata.size(), - codeobj_marker_id_t{1}, - laddr1, - objdata.size()); - map.addDecoder((const void*) objdata.data(), - objdata.size(), - codeobj_marker_id_t{3}, - laddr3, - objdata.size()); - - EXPECT_EQ(map.get(codeobj_marker_id_t{1}, kaddr)->inst, - map.get(codeobj_marker_id_t{3}, kaddr)->inst); + const void* objdataptr = (const void*) objdata.data(); + map.addDecoder(objdataptr, objdata.size(), marker_id_t{1}, laddr1, objdata.size()); + map.addDecoder(objdataptr, objdata.size(), marker_id_t{3}, laddr3, objdata.size()); + + EXPECT_EQ(map.get(marker_id_t{1}, kaddr)->inst, map.get(marker_id_t{3}, kaddr)->inst); ASSERT_EQ(map.removeDecoderbyId(1), true); ASSERT_EQ(map.removeDecoderbyId(3), true); @@ -216,6 +221,8 @@ TEST(codeobj_library, codeobj_map_test) TEST(codeobj_library, codeobj_table_test) { + using marker_id_t = rocprofiler::codeobj::segment::marker_id_t; + const std::vector& hiplines = codeobjhelper::GetHipccOutput(); const std::vector& objdata = codeobjhelper::GetCodeobjContents(); constexpr size_t laddr1 = 0x1000; @@ -225,7 +232,7 @@ TEST(codeobj_library, codeobj_table_test) uint64_t kaddr = 0, memsize = 0; std::tie(kaddr, memsize) = [&objdata]() { - CodeobjDecoderComponent comp((const void*) objdata.data(), objdata.size()); + CodeobjDecoderComponent comp(objdata.data(), objdata.size()); for(auto& [addr, symbol] : comp.m_symbol_map) return std::pair(addr, symbol.mem_size); return std::pair(0, 0); @@ -233,10 +240,8 @@ TEST(codeobj_library, codeobj_table_test) ASSERT_NE(kaddr, 0); ASSERT_NE(memsize, 0); - map.addDecoder( - (const void*) objdata.data(), objdata.size(), codeobj_marker_id_t{1}, laddr1, 0x2000); - map.addDecoder( - (const void*) objdata.data(), objdata.size(), codeobj_marker_id_t{3}, laddr3, 0x2000); + map.addDecoder((const void*) objdata.data(), objdata.size(), marker_id_t{1}, laddr1, 0x2000); + map.addDecoder((const void*) objdata.data(), objdata.size(), marker_id_t{3}, laddr3, 0x2000); EXPECT_NE(map.get(laddr1 + kaddr).get(), nullptr); EXPECT_NE(map.get(laddr3 + kaddr).get(), nullptr);