25#include <amd_comgr/amd_comgr.h>
26#include <hsa/amd_hsa_elf.h>
43#include <unordered_map>
46#define THROW_COMGR(call) \
47 if(amd_comgr_status_s status = call) \
49 const char* reason = ""; \
50 amd_comgr_status_string(status, &reason); \
51 std::cerr << __FILE__ << ':' << __LINE__ << " code: " << status << " failed: " << reason \
53 throw std::exception(); \
56#define RETURN_COMGR(call) \
57 if(amd_comgr_status_s status = call) \
59 const char* reason = ""; \
60 amd_comgr_status_string(status, &reason); \
61 std::cerr << __FILE__ << ':' << __LINE__ << " code: " << status << " failed: " << reason \
63 return AMD_COMGR_STATUS_ERROR; \
81get_fallback_op_gfx1250(uint32_t header)
83 if((header >> 7) == 0)
return {
"v_vop_generic ", 4};
87 case 0b11001100:
return {
"v_vop3p_generic ", 8};
88 case 0b11001111:
return {
"v_vopd3_generic ", 12};
96 case 0b110001:
return {
"buffer_load_generic ", 12};
97 case 0b111011:
return {
"global_load_generic ", 12};
98 case 0b110100:
return {
"image_load_generic ", 12};
99 case 0b110010:
return {
"v_vopd_generic ", 8};
100 case 0b110101:
return {
"v_vopsd_generic ", 8};
101 case 0b110110:
return {
"ds_generic ", 8};
102 case 0b111101:
return {
"s_load_generic ", 8};
107 if(header == 0b10)
return {
"s_sop ", 4};
112class CodeObjectBinary
115 CodeObjectBinary(std::string _uri)
116 : m_uri(
std::move(_uri))
118 const std::string protocol_delim{
"://"};
120 size_t protocol_end = m_uri.find(protocol_delim);
121 std::string protocol = m_uri.substr(0, protocol_end);
122 protocol_end += protocol_delim.length();
124 std::transform(protocol.begin(), protocol.end(), protocol.begin(), [](
unsigned char c) {
125 return std::tolower(c);
129 size_t path_end = m_uri.find_first_of(
"#?", protocol_end);
130 if(path_end != std::string::npos)
132 path = m_uri.substr(protocol_end, path_end++ - protocol_end);
136 path = m_uri.substr(protocol_end);
140 std::string decoded_path;
141 decoded_path.reserve(path.length());
142 for(
size_t i = 0; i < path.length(); ++i)
144 if(path[i] ==
'%' && std::isxdigit(path[i + 1]) != 0 && std::isxdigit(path[i + 2]) != 0)
146 decoded_path += std::stoi(path.substr(i + 1, 2),
nullptr, 16);
151 decoded_path += path[i];
156 std::vector<std::string> tokens;
157 size_t pos, last = path_end;
158 while((pos = m_uri.find(
'&', last)) != std::string::npos)
160 tokens.emplace_back(m_uri.substr(last, pos - last));
163 if(last != std::string::npos)
165 tokens.emplace_back(m_uri.substr(last));
169 std::unordered_map<std::string, std::string> params;
170 std::for_each(tokens.begin(), tokens.end(), [&](std::string& token) {
171 size_t delim = token.find(
'=');
172 if(delim != std::string::npos)
174 params.emplace(token.substr(0, delim), token.substr(delim + 1));
178 buffer = std::vector<char>{};
182 if(
auto offset_it = params.find(
"offset"); offset_it != params.end())
184 offset = std::stoul(offset_it->second,
nullptr, 0);
187 if(
auto size_it = params.find(
"size"); size_it != params.end())
189 if((size = std::stoul(size_it->second,
nullptr, 0)) == 0)
return;
192 if(protocol ==
"memory")
throw std::runtime_error(protocol +
" protocol not supported!");
194 std::ifstream file(decoded_path, std::ios::in | std::ios::binary);
195 if(!file || !file.is_open())
throw std::runtime_error(
"could not open " + decoded_path);
199 file.ignore(std::numeric_limits<std::streamsize>::max());
200 size_t bytes = file.gcount();
203 if(bytes < offset)
throw std::runtime_error(
"invalid uri " + decoded_path);
205 size = bytes - offset;
208 file.seekg(offset, std::ios_base::beg);
210 file.read(buffer.data(), size);
214 std::vector<char> buffer;
222 uint64_t mem_size = 0;
225class DisassemblyInstance
228 DisassemblyInstance(
const char* codeobj_data, uint64_t codeobj_size)
230 buffer = std::vector<char>(codeobj_size, 0);
231 std::memcpy(buffer.data(), codeobj_data, codeobj_size);
233 THROW_COMGR(amd_comgr_create_data(AMD_COMGR_DATA_KIND_EXECUTABLE, &data));
234 THROW_COMGR(amd_comgr_set_data(data, buffer.size(), buffer.data()));
236 size_t isa_size = 128;
237 std::string input_isa{};
238 input_isa.resize(isa_size);
239 THROW_COMGR(amd_comgr_get_data_isa_name(data, &isa_size, input_isa.data()));
241 THROW_COMGR(amd_comgr_create_disassembly_info(
243 &DisassemblyInstance::memory_callback,
244 &DisassemblyInstance::inst_callback,
245 [](uint64_t,
void*) {},
248 if(input_isa.find(
"gfx1250") != std::string::npos) gfxip = 1250;
250 ~DisassemblyInstance()
252 amd_comgr_release_data(data);
253 amd_comgr_destroy_disassembly_info(info);
256 std::pair<std::string, size_t> ReadInstruction(uint64_t faddr)
259 uint64_t addr_in_buffer =
reinterpret_cast<uint64_t
>(buffer.data()) + faddr;
262 amd_comgr_disassemble_instruction(info, addr_in_buffer, (
void*)
this, &size_read);
263 if(_status != AMD_COMGR_STATUS_SUCCESS)
265 if(faddr + 4 > buffer.size()) THROW_COMGR(_status);
268 std::memcpy(&read, buffer.data() + faddr, 4);
270 FallbackOp fallback{};
272 if(gfxip == 1250) fallback = get_fallback_op_gfx1250(read >> 24);
274 if(fallback.str ==
nullptr || fallback.size == 0) THROW_COMGR(_status);
276 this->last_instruction = fallback.str;
277 size_read = fallback.size;
280 return {std::move(this->last_instruction), size_read};
283 std::map<uint64_t, SymbolInfo>& GetKernelMap()
286 THROW_COMGR(amd_comgr_iterate_symbols(data, &DisassemblyInstance::symbol_callback,
this));
291 static amd_comgr_status_t symbol_callback(amd_comgr_symbol_t symbol,
void* user_data)
293 amd_comgr_symbol_type_t type;
294 RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_TYPE, &type));
296 if(type != AMD_COMGR_SYMBOL_TYPE_FUNC)
return AMD_COMGR_STATUS_SUCCESS;
299 uint64_t mem_size = 0;
300 uint64_t name_size = 0;
301 RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_VALUE, &vaddr));
302 RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_SIZE, &mem_size));
304 amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_NAME_LENGTH, &name_size));
311 name.resize(name_size + 1);
313 RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_NAME, name.data()));
314 name.resize(name_size);
316 DisassemblyInstance& instance = *
static_cast<DisassemblyInstance*
>(user_data);
317 std::optional<uint64_t> faddr = instance.va2fo(vaddr);
319 if(faddr) instance.symbol_map[vaddr] = {name, *faddr, vaddr, mem_size};
320 return AMD_COMGR_STATUS_SUCCESS;
323 static uint64_t memory_callback(uint64_t from,
char* to, uint64_t size,
void* user_data)
325 DisassemblyInstance& instance = *
static_cast<DisassemblyInstance*
>(user_data);
326 int64_t copysize =
reinterpret_cast<int64_t
>(instance.buffer.data()) +
327 instance.buffer.size() -
static_cast<int64_t
>(from);
328 copysize = std::min<int64_t>(size, copysize);
330 std::memcpy(to, (
char*) from, copysize);
334 static void inst_callback(
const char* instruction,
void* user_data)
336 DisassemblyInstance& instance = *
static_cast<DisassemblyInstance*
>(user_data);
338 if(!instruction)
return;
340 while(*instruction ==
'\t' || *instruction ==
' ')
342 instance.last_instruction = instruction;
345 std::optional<uint64_t> va2fo(uint64_t va)
const
348 uint64_t slicesize = 0;
351 auto status = amd_comgr_map_elf_virtual_address_to_code_object_offset(
352 data, va, &offset, &slicesize, &nobits);
354 if(status != AMD_COMGR_STATUS_SUCCESS || nobits)
361 std::vector<char> buffer{};
362 std::string last_instruction{};
363 amd_comgr_disassembly_info_t info{};
364 amd_comgr_data_t data{};
365 std::map<uint64_t, SymbolInfo> symbol_map{};