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:
committed by
GitHub
parent
4417b435cf
commit
ea56f79495
@@ -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)
|
||||
+259
@@ -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
|
||||
Binary file not shown.
Reference in New Issue
Block a user