Gbaraldi/att tool (#766)

* Enabling codeobj and thread trace samples

* Updating aqlprofile_v2 header

* Codeobj and thread trace samples with output log files

* Fixing clang format

* Cmake formatting

* Adding coverage to codeobj

* Comment trace sample

* Adding ATT Parser API

* Fixing forwarding to aqlprofile

* Clang formatting

* Clang tidy

* Adding option to print memory kernels

* Clang format

* Remove default from switch case

* Separating  client/main on codeobj sample for ASAn

* Formatting

* Gbaraldi/att tool rebase (#801)

* Enabling codeobj and thread trace samples

* Updating aqlprofile_v2 header

* Codeobj and thread trace samples with output log files

* Fixing clang format

* Cmake formatting

* Adding coverage to codeobj

* Comment trace sample

* Removing python from workflow

* Adding ATT Parser API

* Fixing forwarding to aqlprofile

* Clang formatting

* Clang tidy

* Adding option to print memory kernels

* Clang format

* Remove default from switch case

* Separating  client/main on codeobj sample for ASAn

* Formatting

* Enabling codeobj and thread trace samples

* Updating aqlprofile_v2 header

* Codeobj and thread trace samples with output log files

* Fixing clang format

* Cmake formatting

* Adding coverage to codeobj

* Comment trace sample

* Adding ATT Parser API

* Fixing forwarding to aqlprofile

* Clang formatting

* Clang tidy

* Adding option to print memory kernels

* Clang format

* Remove default from switch case

* Separating  client/main on codeobj sample for ASAn

* Formatting

* Fix codeobj library

* Allow thread trace in parallel with other service

* Zeroing the HSA signals

* Adding exception wrappers in ATT sample

* Removed force configure

* Remove force configure from ISA decode

* Removing codecov flag

* Gbaraldi/att tool tests (#828)

* Adding tests for codeobj ISA decode

* Adding ATT tests

* Adding ATT integration tests

* Formatting

* Changing codeobj binary extension

* Renaming codeobj library spaces

* Fixing samples

* Formatting

* Formatting

* Fixing int test

* Fixing linker error

* Fixing memory fault

* Moving kernel ot inside namespace

* ASAN linking fix

* Removing unecessary headers

* Formatting

* Fixing target_cu

* Remove codeobj binary

* Revert "Remove codeobj binary"

This reverts commit 7d286f89d8096bc36925cd79cd742a5e6d10d179.

* Enable memory snapshot

* adding comgr

---------

Co-authored-by: Ammar ELWazir <ammar.elwazir@amd.com>

[ROCm/rocprofiler-sdk commit: 099ac7c72d]
This commit is contained in:
Giovanni Lenzi Baraldi
2024-05-03 18:45:47 -03:00
committed by GitHub
parent 4417b435cf
commit ea56f79495
38 changed files with 1829 additions and 753 deletions
@@ -16,19 +16,17 @@
# USE OR OTHER DEALINGS # IN THE SOFTWARE.
# ##############################################################################
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "codeobj")
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "development")
set(LIB_CODEOBJ_PARSER_SOURCES code_object_track.cpp code_printing.cpp disassembly.cpp)
set(LIB_CODEOBJ_PARSER_HEADERS code_object_track.hpp code_printing.hpp disassembly.hpp
segment.hpp)
set(CODEOBJ_PARSER_SOURCES code_printing.cpp disassembly.cpp)
add_library(rocprofiler-sdk-codeobj STATIC)
target_sources(rocprofiler-sdk-codeobj PRIVATE ${LIB_CODEOBJ_PARSER_SOURCES})
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-memcheck rocprofiler::rocprofiler-common-library)
rocprofiler::rocprofiler-common-library)
target_include_directories(rocprofiler-sdk-codeobj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
@@ -40,11 +38,9 @@ set_target_properties(
install(
TARGETS rocprofiler-sdk-codeobj
DESTINATION ${CMAKE_INSTALL_LIBDIR}/rocprofiler-sdk
COMPONENT tools
DESTINATION ${CMAKE_INSTALL_LIBDIR}
EXPORT rocprofiler-sdk-codeobj-targets)
install(
FILES ${LIB_CODEOBJ_PARSER_HEADERS}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rocprofiler-sdk-codeobj
COMPONENT tools)
if(ROCPROFILER_BUILD_TESTS)
add_subdirectory(tests)
endif()
@@ -1,196 +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 <sys/mman.h>
#include <algorithm>
#include <atomic>
#include <cassert>
#include <cinttypes>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <functional>
#include <iostream>
#include <map>
#include <mutex>
#include <optional>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include "code_object_track.hpp"
void
CodeobjRecorder::Load(uint64_t addr,
uint64_t load_size,
const std::string& URI,
uint64_t mem_addr,
uint64_t mem_size,
uint64_t id)
{
Load(std::make_shared<CodeobjCaptureInstance>(
addr, load_size, URI, mem_addr, mem_size, id, capture_mode));
}
void
CodeobjCaptureInstance::copyCodeobjFromFile(uint64_t offset, uint64_t size, const std::string& path)
{
std::ifstream file(path, std::ios::in | std::ios::binary);
if(!file)
{
printf("could not open `%s'\n", path.c_str());
return;
}
if(!size)
{
file.ignore(std::numeric_limits<std::streamsize>::max());
size_t bytes = file.gcount();
file.clear();
if(bytes < offset)
{
printf("invalid uri `%s' (file size < offset)\n", path.c_str());
return;
}
size = bytes - offset;
}
file.seekg(offset, std::ios_base::beg);
buffer.resize(size);
file.read(&buffer[0], size);
}
void CodeobjCaptureInstance::copyCodeobjFromMemory(uint64_t, uint64_t)
{
// buffer.resize(mem_size);
// std::memcpy(buffer.data(), (uint64_t*)mem_addr, mem_size);
}
std::pair<size_t, size_t>
CodeobjCaptureInstance::parse_uri()
{
const std::string protocol_delim{"://"};
size_t protocol_end = URI.find(protocol_delim);
protocol = 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 = URI.find_first_of("#?", protocol_end);
if(path_end != std::string::npos)
{
path = URI.substr(protocol_end, path_end++ - protocol_end);
}
else
{
path = URI.substr(protocol_end);
}
/* %-decode the string. */
decoded_path = std::string{};
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<std::string> tokens;
size_t pos, last = path_end;
while((pos = URI.find('&', last)) != std::string::npos)
{
tokens.emplace_back(URI.substr(last, pos - last));
last = pos + 1;
}
if(last != std::string::npos) tokens.emplace_back(URI.substr(last));
/* Create a tag-value map from the tokenized query/fragment. */
std::unordered_map<std::string, std::string> 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));
}
});
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))) throw std::exception();
}
return {offset, size};
}
void
CodeobjCaptureInstance::reset(codeobj_capture_mode_t mode)
{
if(static_cast<int>(mode) <= static_cast<int>(capture_mode)) return;
capture_mode = mode;
if(!buffer.empty()) return;
size_t offset, size;
try
{
std::tie(offset, size) = parse_uri();
} catch(...)
{
std::cerr << "Error parsing URI " << URI << std::endl;
return;
}
if(protocol == "file")
{
if(mode == ROCPROFILER_CODEOBJ_CAPTURE_COPY_FILE_AND_MEMORY)
copyCodeobjFromFile(offset, size, decoded_path);
}
else if(protocol == "memory")
{
if(mode != ROCPROFILER_CODEOBJ_CAPTURE_SYMBOLS_ONLY)
copyCodeobjFromMemory(mem_addr, mem_size);
}
else
{
printf("\"%s\" protocol not supported\n", protocol.c_str());
}
}
@@ -1,158 +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 <fstream>
#include <memory>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <vector>
/**
* Enum defines how code object is captured for ATT and PC Sampling
*/
enum codeobj_capture_mode_t
{
/**
* Capture file and memory paths for the loaded code object
*/
ROCPROFILER_CODEOBJ_CAPTURE_SYMBOLS_ONLY = 0,
/**
* Capture symbols for file:// and memory:// type objects,
* and generate a copy of all kernel code for objects under memory://
*/
ROCPROFILER_CODEOBJ_CAPTURE_COPY_MEMORY = 1,
/**
* Capture symbols and all kernel code for file:// and memory:// type objects
*/
ROCPROFILER_CODEOBJ_CAPTURE_COPY_FILE_AND_MEMORY = 2,
ROCPROFILER_CODEOBJ_CAPTURE_LAST = 3,
};
/**
* A class to keep track of currently loaded code objects.
* Only the public static methods are thread-safe and expected to be used.
*/
class CodeobjCaptureInstance
{
public:
CodeobjCaptureInstance(uint64_t _addr,
uint64_t _load_size,
const std::string& _uri,
uint64_t _mem_addr,
uint64_t _mem_size,
uint64_t id,
codeobj_capture_mode_t mode)
: addr(_addr)
, load_size(_load_size)
, load_id(id)
, URI(_uri)
, mem_addr(_mem_addr)
, mem_size(_mem_size)
{
reset(mode);
};
const uint64_t addr;
const uint64_t load_size;
const uint64_t load_id;
private:
void reset(codeobj_capture_mode_t mode);
std::pair<size_t, size_t> parse_uri();
void DecodePath();
void copyCodeobjFromFile(uint64_t offset, uint64_t size, const std::string& path);
void copyCodeobjFromMemory(uint64_t, uint64_t);
std::string URI{};
std::string decoded_path{};
std::string protocol{};
std::vector<char> buffer{};
uint64_t mem_addr = 0;
uint64_t mem_size = 0;
codeobj_capture_mode_t capture_mode = ROCPROFILER_CODEOBJ_CAPTURE_SYMBOLS_ONLY;
};
typedef std::shared_ptr<CodeobjCaptureInstance> CodeobjPtr;
template <>
struct std::hash<CodeobjPtr>
{
uint64_t operator()(const CodeobjPtr& p) const { return p->load_id; }
};
template <>
struct std::equal_to<CodeobjPtr>
{
bool operator()(const CodeobjPtr& a, const CodeobjPtr& b) const
{
return (a->addr == b->addr) && (a->load_id == b->load_id);
};
};
/**
* A class to keep track of the history of loaded code objets.
* Only the public static methods are thread-safe and expected to be used.
*/
class CodeobjRecorder
{
public:
CodeobjRecorder(codeobj_capture_mode_t mode)
: capture_mode(mode){};
void Load(uint64_t _addr,
uint64_t _load_size,
const std::string& _uri,
uint64_t mem_addr,
uint64_t mem_size,
uint64_t id);
void Load(CodeobjPtr capture)
{
std::lock_guard<std::shared_mutex> lk(mutex);
captures[capture->load_id] = capture;
}
void Unload(uint64_t id)
{
std::lock_guard<std::shared_mutex> lk(mutex);
captures.erase(id);
};
public:
std::shared_mutex mutex;
std::vector<CodeobjPtr> get()
{
std::vector<CodeobjPtr> vec;
std::shared_lock<std::shared_mutex> lk(mutex);
for(auto& [k, v] : captures)
vec.push_back(v);
return vec;
};
private:
codeobj_capture_mode_t capture_mode;
std::unordered_map<uint64_t, CodeobjPtr> captures;
};
@@ -20,8 +20,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "lib/rocprofiler-sdk-codeobj/code_printing.hpp"
#include <algorithm>
#include <fstream>
#include <iomanip>
@@ -35,6 +33,8 @@
#include <unordered_map>
#include <vector>
#include <rocprofiler-sdk-codeobj/code_printing.hpp>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
@@ -72,7 +72,13 @@
} \
catch(...) { return returndata; }
CodeobjDecoderComponent::CodeobjDecoderComponent(const char* codeobj_data, uint64_t codeobj_size)
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)
@@ -87,7 +93,7 @@ CodeobjDecoderComponent::CodeobjDecoderComponent(const char* codeobj_data, uint6
return;
}
if(size_t size = ::write(m_fd, codeobj_data, codeobj_size); size != codeobj_size)
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;
@@ -150,7 +156,7 @@ CodeobjDecoderComponent::CodeobjDecoderComponent(const char* codeobj_data, uint6
}
// Can throw
disassembly = std::make_unique<DisassemblyInstance>(codeobj_data, codeobj_size);
disassembly = std::make_unique<DisassemblyInstance>((const char*) codeobj_data, codeobj_size);
if(m_line_number_map.size())
{
size_t total_size = 0;
@@ -250,7 +256,7 @@ 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->disassembly->va2fo(voffset);
auto faddr = decoder->va2fo(voffset);
if(!faddr) throw std::out_of_range("Could not find file offset");
auto shared = decoder->disassemble_instruction(*faddr, voffset);
@@ -275,4 +281,6 @@ LoadedCodeobjDecoder::get(uint64_t addr)
return nullptr;
}
#define PUBLIC_API __attribute__((visibility("default")))
} // namespace disassembly
} // namespace codeobj
} // namespace rocprofiler
@@ -1,257 +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 <map>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include "disassembly.hpp"
#include "segment.hpp"
struct Instruction
{
Instruction() = default;
Instruction(std::string&& _inst, size_t _size)
: 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; }
};
class CodeobjDecoderComponent
{
public:
CodeobjDecoderComponent(const char* codeobj_data, uint64_t codeobj_size);
~CodeobjDecoderComponent();
std::shared_ptr<Instruction> disassemble_instruction(uint64_t faddr, uint64_t vaddr);
int m_fd;
cached_ordered_vector<DSourceLine> m_line_number_map;
std::map<uint64_t, SymbolInfo> m_symbol_map{};
std::string m_uri;
std::vector<std::shared_ptr<Instruction>> instructions{};
std::unique_ptr<DisassemblyInstance> disassembly{};
};
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<Instruction> add_to_map(uint64_t ld_addr);
std::shared_ptr<Instruction> 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(); }
const char* getSymbolName(uint64_t addr) const
{
if(!decoder) return nullptr;
auto it = decoder->m_symbol_map.find(addr - load_addr);
if(it != decoder->m_symbol_map.end()) return it->second.name.data();
return nullptr;
}
std::map<uint64_t, SymbolInfo>& getSymbolMap() const
{
if(!decoder) throw std::exception();
return decoder->m_symbol_map;
}
std::vector<std::pair<uint64_t, uint64_t>> elf_segments{};
const uint64_t load_addr;
private:
uint64_t load_end = 0;
std::unordered_map<uint64_t, std::shared_ptr<Instruction>> decoded_map;
std::unique_ptr<CodeobjDecoderComponent> decoder{nullptr};
};
/**
* @brief Maps ID and offsets into instructions
*/
class CodeobjMap
{
public:
CodeobjMap() = default;
virtual void addDecoder(const char* filepath,
codeobj_marker_id_t id,
uint64_t load_addr,
uint64_t memsize)
{
decoders[id] = std::make_shared<LoadedCodeobjDecoder>(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)
{
decoders[id] =
std::make_shared<LoadedCodeobjDecoder>(data, memory_size, load_addr, memsize);
}
virtual bool removeDecoderbyId(codeobj_marker_id_t id) { return decoders.erase(id) != 0; }
std::shared_ptr<Instruction> get(codeobj_marker_id_t id, uint64_t offset)
{
auto& decoder = decoders.at(id);
return decoder->get(decoder->begin() + offset);
}
const char* getSymbolName(codeobj_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);
return nullptr;
}
protected:
std::unordered_map<codeobj_marker_id_t, std::shared_ptr<LoadedCodeobjDecoder>> decoders{};
};
/**
* @brief Translates virtual addresses to elf file offsets
*/
class CodeobjAddressTranslate : protected CodeobjMap
{
using Super = CodeobjMap;
public:
CodeobjAddressTranslate() = default;
virtual void addDecoder(const char* filepath,
codeobj_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});
}
virtual void addDecoder(const void* data,
size_t memory_size,
codeobj_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});
}
virtual bool removeDecoder(codeobj_marker_id_t id, uint64_t load_addr)
{
return table.remove(load_addr) && this->Super::removeDecoderbyId(id);
}
std::shared_ptr<Instruction> get(uint64_t vaddr)
{
auto& addr_range = table.find_codeobj_in_range(vaddr);
return this->Super::get(addr_range.id, vaddr - addr_range.vbegin);
}
std::shared_ptr<Instruction> get(codeobj_marker_id_t id, uint64_t offset)
{
if(id == 0)
return get(offset);
else
return this->Super::get(id, offset);
}
const char* getSymbolName(uint64_t vaddr)
{
for(auto& [_, decoder] : decoders)
{
if(!decoder->inrange(vaddr)) continue;
return decoder->getSymbolName(vaddr);
}
return nullptr;
}
void getSymbolMap(const std::shared_ptr<LoadedCodeobjDecoder>& dec,
std::unordered_map<uint64_t, SymbolInfo>& symbols) const
{
try
{
auto& smap = dec->getSymbolMap();
for(auto& [vaddr, sym] : smap)
symbols[vaddr + dec->load_addr] = sym;
} catch(std::exception& e)
{
return;
};
}
std::unordered_map<uint64_t, SymbolInfo> getSymbolMap() const
{
std::unordered_map<uint64_t, SymbolInfo> symbols;
for(auto& [_, dec] : decoders)
this->getSymbolMap(dec, symbols);
return symbols;
}
std::unordered_map<uint64_t, SymbolInfo> getSymbolMap(codeobj_marker_id_t id) const
{
std::unordered_map<uint64_t, SymbolInfo> symbols;
auto it = decoders.find(id);
if(it == decoders.end()) return symbols;
this->getSymbolMap(it->second, symbols);
return symbols;
}
private:
CodeobjTableTranslator table;
};
@@ -50,9 +50,8 @@
#include <cstdlib>
#include <cstring>
#include <elfutils/libdw.h>
#include <hsa/amd_hsa_elf.h>
#include "lib/rocprofiler-sdk-codeobj/code_printing.hpp"
#include <rocprofiler-sdk-codeobj/code_printing.hpp>
#define THROW_COMGR(call) \
if(amd_comgr_status_s status = call) \
@@ -74,6 +73,12 @@
return AMD_COMGR_STATUS_ERROR; \
}
namespace rocprofiler
{
namespace codeobj
{
namespace disassembly
{
CodeObjectBinary::CodeObjectBinary(const std::string& uri)
: m_uri(uri)
{
@@ -151,7 +156,7 @@ CodeObjectBinary::CodeObjectBinary(const std::string& uri)
if(!(size = std::stoul(size_it->second, nullptr, 0))) return;
}
if(protocol != "file") throw protocol + " protocol not supported!";
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;
@@ -370,3 +375,7 @@ DisassemblyInstance::getSegments()
return segments;
}
} // namespace disassembly
} // namespace codeobj
} // namespace rocprofiler
@@ -1,68 +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 <amd_comgr/amd_comgr.h>
#include <limits>
#include <memory>
#include <string>
#include <vector>
class CodeObjectBinary
{
public:
CodeObjectBinary(const std::string& uri);
std::string m_uri;
std::vector<char> 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<std::string, size_t> ReadInstruction(uint64_t faddr);
std::map<uint64_t, SymbolInfo>& 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<uint64_t> va2fo(uint64_t va);
std::vector<std::pair<uint64_t, uint64_t>> getSegments();
std::vector<char> buffer;
std::string last_instruction;
amd_comgr_disassembly_info_t info;
amd_comgr_data_t data;
std::map<uint64_t, SymbolInfo> symbol_map;
};
@@ -1,162 +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 <algorithm>
#include <iostream>
#include <random>
#include <string>
#include <unordered_set>
#include <vector>
using codeobj_marker_id_t = size_t;
template <typename Type>
class ordered_vector : public std::vector<Type>
{
using Super = std::vector<Type>;
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;
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;
this->Super::erase(this->begin() + loc);
return true;
}
size_t lower_bound(size_t addr) const
{
if(!this->size()) return 0;
return binary_search(addr, 0, this->size() - 1);
}
size_t binary_search(size_t addr, size_t s, size_t e) 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);
}
const Type& get(size_t i) const { return this->operator[](i); }
};
/**
* @brief Finds a candidate codeobj for the given vaddr
*/
template <typename Type>
class cached_ordered_vector : public ordered_vector<Type>
{
using Super = ordered_vector<Type>;
public:
cached_ordered_vector<Type>() { 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
{
return this->cached_segment < this->size() && get(cached_segment).inrange(addr);
}
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<Type> 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)
{
reset();
return this->Super::remove(addr);
}
private:
size_t cached_segment = ~0;
};
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<address_range_t>
{
public:
const address_range_t& find_codeobj_in_range(uint64_t addr) { return this->find_obj(addr); }
};
@@ -0,0 +1,33 @@
rocprofiler_deactivate_clang_tidy()
include(GoogleTest)
add_executable(codeobj-library-test)
set(CODEOBJ_LIB_TEST_SOURCES "codeobj_library_test.cpp")
target_sources(codeobj-library-test PRIVATE ${CODEOBJ_LIB_TEST_SOURCES})
target_link_libraries(
codeobj-library-test
PRIVATE rocprofiler::rocprofiler-static-library
rocprofiler::rocprofiler-glog
rocprofiler::rocprofiler-hsa-runtime
rocprofiler::rocprofiler-hip
rocprofiler::rocprofiler-common-library
GTest::gtest
GTest::gtest_main
rocprofiler-sdk-codeobj)
gtest_add_tests(
TARGET codeobj-library-test
SOURCES ${CODEOBJ_LIB_TEST_SOURCES}
TEST_LIST codeobj-library-test_TESTS
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(${codeobj-library-test_TESTS} PROPERTIES TIMEOUT 10 LABELS
"unittests")
target_compile_definitions(codeobj-library-test
PRIVATE -DCODEOBJ_BINARY_DIR=\"${CMAKE_CURRENT_BINARY_DIR}/\")
configure_file(smallkernel.b smallkernel.b COPYONLY)
configure_file(hipcc_output.s hipcc_output.s COPYONLY)
@@ -0,0 +1,259 @@
// 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.
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <fstream>
#include <rocprofiler-sdk-codeobj/code_printing.hpp>
#include <string_view>
#include <vector>
#ifndef CODEOBJ_BINARY_DIR
static_assert(false && "Please define CODEOBJ_BINARY_DIR to codeobj tests binary, "
"e.g. ../source/lib/rocprofiler-sdk-codeobj/tests/");
#endif
namespace rocprofiler
{
namespace testing
{
namespace codeobjhelper
{
std::string
removeNull(std::string_view s)
{
std::string u(s);
while(u.find("null") != std::string::npos)
u = u.substr(0, u.find("null")) + "0x0" + u.substr(u.find("null") + 4);
return u;
}
static const std::vector<std::string>&
GetHipccOutput()
{
static std::vector<std::string> result = []() {
std::ifstream file(CODEOBJ_BINARY_DIR "hipcc_output.s");
std::vector<std::string> ret;
while(file.good())
{
std::string s;
getline(file, s);
ret.push_back(removeNull(s));
}
return ret;
}();
return result;
}
static const std::vector<char>&
GetCodeobjContents()
{
static std::vector<char> buffer = []() {
std::string filename = CODEOBJ_BINARY_DIR "smallkernel.b";
std::ifstream file(filename.data(), std::ios::binary);
using iterator_t = std::istreambuf_iterator<char>;
return std::vector<char>(iterator_t(file), iterator_t());
}();
return buffer;
}
} // namespace codeobjhelper
} // namespace testing
} // namespace rocprofiler
TEST(codeobj_library, segment_test)
{
CodeobjTableTranslator table;
std::unordered_set<size_t> used_addr{};
for(size_t ITER = 0; ITER < 50; ITER++)
{
for(int j = 0; j < 2500; j++)
{
size_t addr = rand() % 10000000;
size_t size = (rand() % 10) + 1;
if(used_addr.find(addr) != used_addr.end()) continue;
used_addr.insert(addr);
table.insert({addr, addr + size, 0, 0});
}
for(size_t i = 1; i < table.size(); i++)
ASSERT_LT(table[i - 1], table[i]);
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);
}
}
}
namespace disassembly = rocprofiler::codeobj::disassembly;
namespace codeobjhelper = rocprofiler::testing::codeobjhelper;
using CodeobjDecoderComponent = rocprofiler::codeobj::disassembly::CodeobjDecoderComponent;
using LoadedCodeobjDecoder = rocprofiler::codeobj::disassembly::LoadedCodeobjDecoder;
TEST(codeobj_library, file_opens)
{
ASSERT_NE(codeobjhelper::GetHipccOutput().size(), 0);
ASSERT_NE(codeobjhelper::GetCodeobjContents().size(), 0);
}
TEST(codeobj_library, decoder_component)
{
const std::vector<std::string>& hiplines = codeobjhelper::GetHipccOutput();
const std::vector<char>& objdata = codeobjhelper::GetCodeobjContents();
constexpr size_t loaded_offset = 0x3000;
CodeobjDecoderComponent component(objdata.data(), objdata.size());
std::string kernel_with_protocol = "file://" CODEOBJ_BINARY_DIR "smallkernel.b";
LoadedCodeobjDecoder loadecomp(kernel_with_protocol.data(), loaded_offset, objdata.size());
ASSERT_EQ(component.m_symbol_map.size(), 1);
for(auto& [kaddr, symbol] : component.m_symbol_map)
{
ASSERT_NE(symbol.name.find("reproducible_runtime"), std::string::npos);
ASSERT_NE(symbol.mem_size, 0);
size_t it = 0;
size_t vaddr = kaddr;
while(vaddr < kaddr + symbol.mem_size)
{
if(!component.va2fo(vaddr))
{
ASSERT_NE(0, 0);
}
uint64_t faddr = *component.va2fo(vaddr);
ASSERT_EQ(faddr - symbol.faddr, vaddr - kaddr);
auto instruction = component.disassemble_instruction(faddr, vaddr);
auto loaded_instruction = loadecomp.get(vaddr + loaded_offset);
ASSERT_NE(codeobjhelper::removeNull(instruction->inst).find(hiplines.at(it)),
std::string::npos);
ASSERT_EQ(instruction->inst, loaded_instruction->inst);
vaddr += instruction->size;
it++;
}
}
}
TEST(codeobj_library, loaded_codeobj_component)
{
const std::vector<char>& objdata = rocprofiler::testing::codeobjhelper::GetCodeobjContents();
constexpr size_t offset = 0x1000;
constexpr size_t memsize = 0x1000;
LoadedCodeobjDecoder decoder((const void*) objdata.data(), objdata.size(), offset, memsize);
for(auto& [kaddr, symbol] : decoder.getSymbolMap())
{
ASSERT_NE(symbol.name.find("reproducible_runtime"), std::string::npos);
ASSERT_NE(symbol.mem_size, 0);
}
}
TEST(codeobj_library, codeobj_map_test)
{
const std::vector<char>& 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());
for(auto& [addr, _] : comp.m_symbol_map)
return addr;
return 0ul;
}();
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);
ASSERT_EQ(map.removeDecoderbyId(1), true);
ASSERT_EQ(map.removeDecoderbyId(3), true);
ASSERT_EQ(map.removeDecoderbyId(1), false);
}
TEST(codeobj_library, codeobj_table_test)
{
const std::vector<std::string>& hiplines = codeobjhelper::GetHipccOutput();
const std::vector<char>& objdata = codeobjhelper::GetCodeobjContents();
constexpr size_t laddr1 = 0x1000;
constexpr size_t laddr3 = 0x3000;
disassembly::CodeobjAddressTranslate map;
uint64_t kaddr = 0, memsize = 0;
std::tie(kaddr, memsize) = [&objdata]() {
CodeobjDecoderComponent comp((const void*) objdata.data(), objdata.size());
for(auto& [addr, symbol] : comp.m_symbol_map)
return std::pair<uint64_t, uint64_t>(addr, symbol.mem_size);
return std::pair<uint64_t, uint64_t>(0, 0);
}();
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);
EXPECT_NE(map.get(laddr1 + kaddr).get(), nullptr);
EXPECT_NE(map.get(laddr3 + kaddr).get(), nullptr);
EXPECT_EQ(map.get(laddr1 + kaddr)->inst, map.get(laddr3 + kaddr)->inst);
size_t it = 0;
size_t vaddr = kaddr;
while(vaddr < kaddr + memsize)
{
auto instruction = map.get(laddr1 + vaddr);
ASSERT_NE(codeobjhelper::removeNull(instruction->inst).find(hiplines.at(it)),
std::string::npos);
vaddr += instruction->size;
it++;
}
ASSERT_EQ(map.removeDecoderbyId(1), true);
ASSERT_EQ(map.removeDecoderbyId(3), true);
ASSERT_EQ(map.removeDecoderbyId(1), false);
}
@@ -0,0 +1,17 @@
s_load_b64 s[0:1], s[0:1], 0x0
s_getreg_b32 s2, hwreg(HW_REG_SHADER_CYCLES, 0, 20)
s_waitcnt vmcnt(0) lgkmcnt(0)
s_waitcnt_vscnt null, 0x0
s_barrier
s_waitcnt vmcnt(0) lgkmcnt(0)
s_waitcnt_vscnt null, 0x0
buffer_gl0_inv
s_getreg_b32 s3, hwreg(HW_REG_SHADER_CYCLES, 0, 20)
s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_1)
s_sub_u32 s4, s3, s2
s_subb_u32 s5, 0, 0
v_cmp_lt_i64_e64 s3, s[4:5], s[0:1]
s_delay_alu instid0(VALU_DEP_1)
s_and_b32 vcc_lo, exec_lo, s3
s_cbranch_vccnz 65520
s_endpgm